opt-in to send last userinteraction in presence
This commit is contained in:
		
							parent
							
								
									6639d0f23b
								
							
						
					
					
						commit
						71e9117176
					
				| 
						 | 
				
			
			@ -19,6 +19,7 @@
 | 
			
		|||
* XEP-0280: Message Carbons
 | 
			
		||||
* XEP-0308: Last Message Correction
 | 
			
		||||
* XEP-0313: Message Archive Management
 | 
			
		||||
* XEP-0319: Last User Interaction in Presence
 | 
			
		||||
* XEP-0333: Chat Markers
 | 
			
		||||
* XEP-0352: Client State Indication
 | 
			
		||||
* XEP-0357: Push Notifications
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,6 @@ public class Contact implements ListItem, Blockable {
 | 
			
		|||
	public static final String LAST_PRESENCE = "last_presence";
 | 
			
		||||
	public static final String LAST_TIME = "last_time";
 | 
			
		||||
	public static final String GROUPS = "groups";
 | 
			
		||||
	public Lastseen lastseen = new Lastseen();
 | 
			
		||||
	protected String accountUuid;
 | 
			
		||||
	protected String systemName;
 | 
			
		||||
	protected String serverName;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,9 +49,14 @@ public class Contact implements ListItem, Blockable {
 | 
			
		|||
	protected Account account;
 | 
			
		||||
	protected Avatar avatar;
 | 
			
		||||
 | 
			
		||||
	private boolean mActive = false;
 | 
			
		||||
	private long mLastseen = 0;
 | 
			
		||||
	private String mLastPresence = null;
 | 
			
		||||
 | 
			
		||||
	public Contact(final String account, final String systemName, final String serverName,
 | 
			
		||||
			final Jid jid, final int subscription, final String photoUri,
 | 
			
		||||
			final String systemAccount, final String keys, final String avatar, final Lastseen lastseen, final String groups) {
 | 
			
		||||
			final String systemAccount, final String keys, final String avatar, final long lastseen,
 | 
			
		||||
				   final String presence, final String groups) {
 | 
			
		||||
		this.accountUuid = account;
 | 
			
		||||
		this.systemName = systemName;
 | 
			
		||||
		this.serverName = serverName;
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +79,8 @@ public class Contact implements ListItem, Blockable {
 | 
			
		|||
		} catch (JSONException e) {
 | 
			
		||||
			this.groups = new JSONArray();
 | 
			
		||||
		}
 | 
			
		||||
		this.lastseen = lastseen;
 | 
			
		||||
		this.mLastseen = lastseen;
 | 
			
		||||
		this.mLastPresence = presence;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Contact(final Jid jid) {
 | 
			
		||||
| 
						 | 
				
			
			@ -83,9 +88,6 @@ public class Contact implements ListItem, Blockable {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	public static Contact fromCursor(final Cursor cursor) {
 | 
			
		||||
		final Lastseen lastseen = new Lastseen(
 | 
			
		||||
				cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
 | 
			
		||||
				cursor.getLong(cursor.getColumnIndex(LAST_TIME)));
 | 
			
		||||
		final Jid jid;
 | 
			
		||||
		try {
 | 
			
		||||
			jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)), true);
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +104,8 @@ public class Contact implements ListItem, Blockable {
 | 
			
		|||
				cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
 | 
			
		||||
				cursor.getString(cursor.getColumnIndex(KEYS)),
 | 
			
		||||
				cursor.getString(cursor.getColumnIndex(AVATAR)),
 | 
			
		||||
				lastseen,
 | 
			
		||||
				cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
 | 
			
		||||
				cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
 | 
			
		||||
				cursor.getString(cursor.getColumnIndex(GROUPS)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -197,8 +200,8 @@ public class Contact implements ListItem, Blockable {
 | 
			
		|||
			values.put(PHOTOURI, photoUri);
 | 
			
		||||
			values.put(KEYS, keys.toString());
 | 
			
		||||
			values.put(AVATAR, avatar == null ? null : avatar.getFilename());
 | 
			
		||||
			values.put(LAST_PRESENCE, lastseen.presence);
 | 
			
		||||
			values.put(LAST_TIME, lastseen.time);
 | 
			
		||||
			values.put(LAST_PRESENCE, mLastPresence);
 | 
			
		||||
			values.put(LAST_TIME, mLastseen);
 | 
			
		||||
			values.put(GROUPS, groups.toString());
 | 
			
		||||
			return values;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -517,18 +520,32 @@ public class Contact implements ListItem, Blockable {
 | 
			
		|||
		this.commonName = cn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static class Lastseen {
 | 
			
		||||
		public long time;
 | 
			
		||||
		public String presence;
 | 
			
		||||
	public void flagActive() {
 | 
			
		||||
		this.mActive = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		public Lastseen() {
 | 
			
		||||
			this(null, 0);
 | 
			
		||||
		}
 | 
			
		||||
	public void flagInactive() {
 | 
			
		||||
		this.mActive = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		public Lastseen(final String presence, final long time) {
 | 
			
		||||
			this.presence = presence;
 | 
			
		||||
			this.time = time;
 | 
			
		||||
		}
 | 
			
		||||
	public boolean isActive() {
 | 
			
		||||
		return this.mActive;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setLastseen(long timestamp) {
 | 
			
		||||
		this.mLastseen = Math.max(timestamp, mLastseen);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public long getLastseen() {
 | 
			
		||||
		return this.mLastseen;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setLastPresence(String presence) {
 | 
			
		||||
		this.mLastPresence = presence;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getLastPresence() {
 | 
			
		||||
		return this.mLastPresence;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public final class Options {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,8 +16,6 @@ import eu.siacs.conversations.Config;
 | 
			
		|||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 | 
			
		||||
import eu.siacs.conversations.services.XmppConnectionService;
 | 
			
		||||
import eu.siacs.conversations.utils.PhoneHelper;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jid.Jid;
 | 
			
		||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 | 
			
		||||
 | 
			
		||||
public abstract class AbstractGenerator {
 | 
			
		||||
	private final String[] FEATURES = {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +31,8 @@ public abstract class AbstractGenerator {
 | 
			
		|||
			"http://jabber.org/protocol/nick+notify",
 | 
			
		||||
			"urn:xmpp:ping",
 | 
			
		||||
			"jabber:iq:version",
 | 
			
		||||
			"http://jabber.org/protocol/chatstates"};
 | 
			
		||||
			"http://jabber.org/protocol/chatstates"
 | 
			
		||||
	};
 | 
			
		||||
	private final String[] MESSAGE_CONFIRMATION_FEATURES = {
 | 
			
		||||
			"urn:xmpp:chat-markers:0",
 | 
			
		||||
			"urn:xmpp:receipts"
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +44,7 @@ public abstract class AbstractGenerator {
 | 
			
		|||
	protected final String IDENTITY_NAME = "Conversations";
 | 
			
		||||
	protected final String IDENTITY_TYPE = "phone";
 | 
			
		||||
 | 
			
		||||
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
 | 
			
		||||
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
 | 
			
		||||
 | 
			
		||||
	protected XmppConnectionService mXmppConnectionService;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,7 @@
 | 
			
		|||
package eu.siacs.conversations.parser;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
 | 
			
		||||
import eu.siacs.conversations.entities.Account;
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +12,6 @@ import eu.siacs.conversations.services.XmppConnectionService;
 | 
			
		|||
import eu.siacs.conversations.xml.Element;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jid.Jid;
 | 
			
		||||
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
 | 
			
		||||
 | 
			
		||||
public abstract class AbstractParser {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,42 +21,48 @@ public abstract class AbstractParser {
 | 
			
		|||
		this.mXmppConnectionService = service;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static Long getTimestamp(Element element, Long defaultValue) {
 | 
			
		||||
	public static Long parseTimestamp(Element element, Long d) {
 | 
			
		||||
		Element delay = element.findChild("delay","urn:xmpp:delay");
 | 
			
		||||
		if (delay != null) {
 | 
			
		||||
			String stamp = delay.getAttribute("stamp");
 | 
			
		||||
			if (stamp != null) {
 | 
			
		||||
				try {
 | 
			
		||||
					return AbstractParser.parseTimestamp(delay.getAttribute("stamp")).getTime();
 | 
			
		||||
					return AbstractParser.parseTimestamp(delay.getAttribute("stamp"));
 | 
			
		||||
				} catch (ParseException e) {
 | 
			
		||||
					return defaultValue;
 | 
			
		||||
					return d;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return defaultValue;
 | 
			
		||||
		return d;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected long getTimestamp(Element packet) {
 | 
			
		||||
		return getTimestamp(packet,System.currentTimeMillis());
 | 
			
		||||
	public static long parseTimestamp(Element element) {
 | 
			
		||||
		return parseTimestamp(element, System.currentTimeMillis());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static Date parseTimestamp(String timestamp) throws ParseException {
 | 
			
		||||
	public static long parseTimestamp(String timestamp) throws ParseException {
 | 
			
		||||
		timestamp = timestamp.replace("Z", "+0000");
 | 
			
		||||
		SimpleDateFormat dateFormat;
 | 
			
		||||
		long ms;
 | 
			
		||||
		if (timestamp.charAt(19) == '.' && timestamp.length() >= 25) {
 | 
			
		||||
			String millis = timestamp.substring(19,timestamp.length() - 5);
 | 
			
		||||
			try {
 | 
			
		||||
				double fractions = Double.parseDouble("0" + millis);
 | 
			
		||||
				ms = Math.round(1000 * fractions);
 | 
			
		||||
			} catch (NumberFormatException e) {
 | 
			
		||||
				ms = 0;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			ms = 0;
 | 
			
		||||
		}
 | 
			
		||||
		timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5,timestamp.length());
 | 
			
		||||
		dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
 | 
			
		||||
		return dateFormat.parse(timestamp);
 | 
			
		||||
		return Math.min(dateFormat.parse(timestamp).getTime()+ms, System.currentTimeMillis());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected void updateLastseen(long timestamp, final Account account, final Jid from) {
 | 
			
		||||
		final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
 | 
			
		||||
	protected void updateLastseen(final Account account, final Jid from) {
 | 
			
		||||
		final Contact contact = account.getRoster().getContact(from);
 | 
			
		||||
		if (timestamp >= contact.lastseen.time) {
 | 
			
		||||
			contact.lastseen.time = timestamp;
 | 
			
		||||
			if (!presence.isEmpty()) {
 | 
			
		||||
				contact.lastseen.presence = presence;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		contact.setLastPresence(from.isBareJid() ? "" : from.getResourcepart());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected String avatarData(Element items) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,12 +7,12 @@ import android.util.Pair;
 | 
			
		|||
import net.java.otr4j.session.Session;
 | 
			
		||||
import net.java.otr4j.session.SessionStatus;
 | 
			
		||||
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ import eu.siacs.conversations.http.HttpConnectionManager;
 | 
			
		|||
import eu.siacs.conversations.services.MessageArchiveService;
 | 
			
		||||
import eu.siacs.conversations.services.XmppConnectionService;
 | 
			
		||||
import eu.siacs.conversations.utils.CryptoHelper;
 | 
			
		||||
import eu.siacs.conversations.utils.Xmlns;
 | 
			
		||||
import eu.siacs.conversations.xml.Element;
 | 
			
		||||
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
 | 
			
		||||
import eu.siacs.conversations.xmpp.chatstate.ChatState;
 | 
			
		||||
| 
						 | 
				
			
			@ -328,7 +329,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		if (timestamp == null) {
 | 
			
		||||
			timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis());
 | 
			
		||||
			timestamp = AbstractParser.parseTimestamp(packet);
 | 
			
		||||
		}
 | 
			
		||||
		final String body = packet.getBody();
 | 
			
		||||
		final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
 | 
			
		||||
| 
						 | 
				
			
			@ -439,7 +440,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
 | 
			
		|||
					message.setType(Message.TYPE_PRIVATE);
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				updateLastseen(timestamp, account, from);
 | 
			
		||||
				updateLastseen(account, from);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -601,7 +602,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
 | 
			
		|||
					mXmppConnectionService.markRead(conversation);
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				updateLastseen(timestamp, account, from);
 | 
			
		||||
				final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED);
 | 
			
		||||
				Message message = displayedMessage == null ? null : displayedMessage.prev();
 | 
			
		||||
				while (message != null
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ package eu.siacs.conversations.parser;
 | 
			
		|||
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import java.text.ParseException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +207,20 @@ public class PresenceParser extends AbstractParser implements
 | 
			
		|||
				mXmppConnectionService.fetchCaps(account, from, presence);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			final Element idle = packet.findChild("idle","urn:xmpp:idle:1");
 | 
			
		||||
			if (idle != null) {
 | 
			
		||||
				contact.flagInactive();
 | 
			
		||||
				String since = idle.getAttribute("since");
 | 
			
		||||
				try {
 | 
			
		||||
					contact.setLastseen(AbstractParser.parseTimestamp(since));
 | 
			
		||||
				} catch (NullPointerException | ParseException e) {
 | 
			
		||||
					contact.setLastseen(System.currentTimeMillis());
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				contact.flagActive();
 | 
			
		||||
				contact.setLastseen(AbstractParser.parseTimestamp(packet));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			PgpEngine pgp = mXmppConnectionService.getPgpEngine();
 | 
			
		||||
			Element x = packet.findChild("x", "jabber:x:signed");
 | 
			
		||||
			if (pgp != null && x != null) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,6 @@ import android.os.Build;
 | 
			
		|||
import android.os.Bundle;
 | 
			
		||||
import android.os.FileObserver;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import android.os.Looper;
 | 
			
		||||
import android.os.PowerManager;
 | 
			
		||||
import android.os.PowerManager.WakeLock;
 | 
			
		||||
import android.os.SystemClock;
 | 
			
		||||
| 
						 | 
				
			
			@ -80,6 +79,7 @@ import eu.siacs.conversations.entities.Roster;
 | 
			
		|||
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
 | 
			
		||||
import eu.siacs.conversations.entities.Transferable;
 | 
			
		||||
import eu.siacs.conversations.entities.TransferablePlaceholder;
 | 
			
		||||
import eu.siacs.conversations.generator.AbstractGenerator;
 | 
			
		||||
import eu.siacs.conversations.generator.IqGenerator;
 | 
			
		||||
import eu.siacs.conversations.generator.MessageGenerator;
 | 
			
		||||
import eu.siacs.conversations.generator.PresenceGenerator;
 | 
			
		||||
| 
						 | 
				
			
			@ -141,6 +141,9 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
	private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
 | 
			
		||||
	private final IqGenerator mIqGenerator = new IqGenerator(this);
 | 
			
		||||
	private final List<String> mInProgressAvatarFetches = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
	private long mLastActivity = 0;
 | 
			
		||||
 | 
			
		||||
	public DatabaseBackend databaseBackend;
 | 
			
		||||
	private ContentObserver contactObserver = new ContentObserver(null) {
 | 
			
		||||
		@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -1584,6 +1587,7 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
 | 
			
		||||
	public void setOnConversationListChangedListener(OnConversationUpdate listener) {
 | 
			
		||||
		synchronized (this) {
 | 
			
		||||
			this.mLastActivity = System.currentTimeMillis();
 | 
			
		||||
			if (checkListeners()) {
 | 
			
		||||
				switchToForeground();
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -1796,15 +1800,21 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	private void switchToForeground() {
 | 
			
		||||
		final boolean broadcastLastActivity = broadcastLastActivity();
 | 
			
		||||
		for (Conversation conversation : getConversations()) {
 | 
			
		||||
			conversation.setIncomingChatState(ChatState.ACTIVE);
 | 
			
		||||
		}
 | 
			
		||||
		for (Account account : getAccounts()) {
 | 
			
		||||
			if (account.getStatus() == Account.State.ONLINE) {
 | 
			
		||||
				account.deactivateGracePeriod();
 | 
			
		||||
				XmppConnection connection = account.getXmppConnection();
 | 
			
		||||
				if (connection != null && connection.getFeatures().csi()) {
 | 
			
		||||
					connection.sendActive();
 | 
			
		||||
				final XmppConnection connection = account.getXmppConnection();
 | 
			
		||||
				if (connection != null ) {
 | 
			
		||||
					if (connection.getFeatures().csi()) {
 | 
			
		||||
						connection.sendActive();
 | 
			
		||||
					}
 | 
			
		||||
					if (broadcastLastActivity) {
 | 
			
		||||
						sendPresence(account, false); //send new presence but don't include idle because we are not
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1812,6 +1822,7 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	private void switchToBackground() {
 | 
			
		||||
		final boolean broadcastLastActivity = broadcastLastActivity();
 | 
			
		||||
		for (Account account : getAccounts()) {
 | 
			
		||||
			if (account.getStatus() == Account.State.ONLINE) {
 | 
			
		||||
				XmppConnection connection = account.getXmppConnection();
 | 
			
		||||
| 
						 | 
				
			
			@ -1819,6 +1830,9 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
					if (connection.getFeatures().csi()) {
 | 
			
		||||
						connection.sendInactive();
 | 
			
		||||
					}
 | 
			
		||||
					if (broadcastLastActivity) {
 | 
			
		||||
						sendPresence(account, broadcastLastActivity);
 | 
			
		||||
					}
 | 
			
		||||
					if (Config.CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND && mPushManagementService.available(account)) {
 | 
			
		||||
						connection.waitForPush();
 | 
			
		||||
						cancelWakeUpCall(account.getUuid().hashCode());
 | 
			
		||||
| 
						 | 
				
			
			@ -2253,6 +2267,7 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
	private void disconnect(Account account, boolean force) {
 | 
			
		||||
		if ((account.getStatus() == Account.State.ONLINE)
 | 
			
		||||
				|| (account.getStatus() == Account.State.DISABLED)) {
 | 
			
		||||
			final XmppConnection connection = account.getXmppConnection();
 | 
			
		||||
			if (!force) {
 | 
			
		||||
				List<Conversation> conversations = getConversations();
 | 
			
		||||
				for (Conversation conversation : conversations) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2270,7 +2285,7 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
				}
 | 
			
		||||
				sendOfflinePresence(account);
 | 
			
		||||
			}
 | 
			
		||||
			account.getXmppConnection().disconnect(force);
 | 
			
		||||
			connection.disconnect(force);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2814,6 +2829,10 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
		return getPreferences().getBoolean("show_connection_options", false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public boolean broadcastLastActivity() {
 | 
			
		||||
		return getPreferences().getBoolean("last_activity", false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public int unreadCount() {
 | 
			
		||||
		int count = 0;
 | 
			
		||||
		for (Conversation conversation : getConversations()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3052,6 +3071,10 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	public void sendPresence(final Account account) {
 | 
			
		||||
		sendPresence(account, checkListeners() && broadcastLastActivity());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void sendPresence(final Account account, final boolean includeIdleTimestamp) {
 | 
			
		||||
		PresencePacket packet;
 | 
			
		||||
		if (manuallyChangePresence()) {
 | 
			
		||||
			packet =  mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
 | 
			
		||||
| 
						 | 
				
			
			@ -3062,6 +3085,10 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
		} else {
 | 
			
		||||
			packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
 | 
			
		||||
		}
 | 
			
		||||
		if (mLastActivity > 0 && includeIdleTimestamp) {
 | 
			
		||||
			long since = Math.min(mLastActivity, System.currentTimeMillis()); //don't send future dates
 | 
			
		||||
			packet.addChild("idle","urn:xmpp:idle:1").setAttribute("since", AbstractGenerator.getTimestamp(since));
 | 
			
		||||
		}
 | 
			
		||||
		sendPresencePacket(account, packet);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3072,9 +3099,10 @@ public class XmppConnectionService extends Service {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	public void refreshAllPresences() {
 | 
			
		||||
		boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity();
 | 
			
		||||
		for (Account account : getAccounts()) {
 | 
			
		||||
			if (!account.isOptionSet(Account.OPTION_DISABLED)) {
 | 
			
		||||
				sendPresence(account);
 | 
			
		||||
				sendPresence(account, includeIdleTimestamp);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,6 +104,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
 | 
			
		|||
		}
 | 
			
		||||
	};
 | 
			
		||||
	private Jid accountJid;
 | 
			
		||||
	private TextView lastseen;
 | 
			
		||||
	private Jid contactJid;
 | 
			
		||||
	private TextView contactJidTv;
 | 
			
		||||
	private TextView accountJidTv;
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +115,8 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
 | 
			
		|||
	private QuickContactBadge badge;
 | 
			
		||||
	private LinearLayout keys;
 | 
			
		||||
	private LinearLayout tags;
 | 
			
		||||
	private boolean showDynamicTags;
 | 
			
		||||
	private boolean showDynamicTags = false;
 | 
			
		||||
	private boolean showLastSeen = false;
 | 
			
		||||
	private String messageFingerprint;
 | 
			
		||||
 | 
			
		||||
	private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
 | 
			
		||||
| 
						 | 
				
			
			@ -203,6 +205,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
 | 
			
		|||
 | 
			
		||||
		contactJidTv = (TextView) findViewById(R.id.details_contactjid);
 | 
			
		||||
		accountJidTv = (TextView) findViewById(R.id.details_account);
 | 
			
		||||
		lastseen = (TextView) findViewById(R.id.details_lastseen);
 | 
			
		||||
		statusMessage = (TextView) findViewById(R.id.status_message);
 | 
			
		||||
		send = (CheckBox) findViewById(R.id.details_send_presence);
 | 
			
		||||
		receive = (CheckBox) findViewById(R.id.details_receive_presence);
 | 
			
		||||
| 
						 | 
				
			
			@ -220,9 +223,14 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
 | 
			
		|||
			getActionBar().setHomeButtonEnabled(true);
 | 
			
		||||
			getActionBar().setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void onStart() {
 | 
			
		||||
		super.onStart();
 | 
			
		||||
		final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
 | 
			
		||||
		this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
 | 
			
		||||
		this.showLastSeen = preferences.getBoolean("last_activity", false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -371,6 +379,18 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
 | 
			
		|||
			statusMessage.setVisibility(View.GONE);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (contact.isBlocked() && !this.showDynamicTags) {
 | 
			
		||||
			lastseen.setVisibility(View.VISIBLE);
 | 
			
		||||
			lastseen.setText(R.string.contact_blocked);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (showLastSeen && contact.getLastseen() > 0) {
 | 
			
		||||
				lastseen.setVisibility(View.VISIBLE);
 | 
			
		||||
				lastseen.setText(UIHelper.lastseen(getApplicationContext(), contact.isActive(), contact.getLastseen()));
 | 
			
		||||
			} else {
 | 
			
		||||
				lastseen.setVisibility(View.GONE);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (contact.getPresences().size() > 1) {
 | 
			
		||||
			contactJidTv.setText(contact.getDisplayJid() + " ("
 | 
			
		||||
					+ contact.getPresences().size() + ")");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,7 +162,8 @@ public class SettingsActivity extends XmppActivity implements
 | 
			
		|||
				"away_when_screen_off",
 | 
			
		||||
				"allow_message_correction",
 | 
			
		||||
				"treat_vibrate_as_silent",
 | 
			
		||||
				"manually_change_presence");
 | 
			
		||||
				"manually_change_presence",
 | 
			
		||||
				"last_activity");
 | 
			
		||||
		if (name.equals("resource")) {
 | 
			
		||||
			String resource = preferences.getString("resource", "mobile")
 | 
			
		||||
					.toLowerCase(Locale.US);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -912,7 +912,7 @@ public abstract class XmppActivity extends Activity {
 | 
			
		|||
				final String[] presencesArray = presences.asStringArray();
 | 
			
		||||
				int preselectedPresence = 0;
 | 
			
		||||
				for (int i = 0; i < presencesArray.length; ++i) {
 | 
			
		||||
					if (presencesArray[i].equals(contact.lastseen.presence)) {
 | 
			
		||||
					if (presencesArray[i].equals(contact.getLastPresence())) {
 | 
			
		||||
						preselectedPresence = i;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,12 +107,10 @@ public class UIHelper {
 | 
			
		|||
			.get(Calendar.DAY_OF_YEAR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static String lastseen(Context context, long time) {
 | 
			
		||||
		if (time == 0) {
 | 
			
		||||
			return context.getString(R.string.never_seen);
 | 
			
		||||
		}
 | 
			
		||||
	public static String lastseen(Context context, boolean active, long time) {
 | 
			
		||||
		long difference = (System.currentTimeMillis() - time) / 1000;
 | 
			
		||||
		if (difference < 60) {
 | 
			
		||||
		active = active && difference <= 300;
 | 
			
		||||
		if (active || difference < 60) {
 | 
			
		||||
			return context.getString(R.string.last_seen_now);
 | 
			
		||||
		} else if (difference < 60 * 2) {
 | 
			
		||||
			return context.getString(R.string.last_seen_min);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,5 @@
 | 
			
		|||
package eu.siacs.conversations.utils;
 | 
			
		||||
 | 
			
		||||
import eu.siacs.conversations.Config;
 | 
			
		||||
 | 
			
		||||
public final class Xmlns {
 | 
			
		||||
	public static final String BLOCKING = "urn:xmpp:blocking";
 | 
			
		||||
	public static final String ROSTER = "jabber:iq:roster";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ public class MessagePacket extends AbstractAcknowledgeableStanza {
 | 
			
		|||
		if (packet == null) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		Long timestamp = AbstractParser.getTimestamp(forwarded,null);
 | 
			
		||||
		Long timestamp = AbstractParser.parseTimestamp(forwarded, null);
 | 
			
		||||
		return new Pair(packet,timestamp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,12 +53,20 @@
 | 
			
		|||
                    android:orientation="horizontal">
 | 
			
		||||
                </LinearLayout>
 | 
			
		||||
 | 
			
		||||
                <TextView
 | 
			
		||||
                    android:id="@+id/details_lastseen"
 | 
			
		||||
                    android:layout_marginTop="4dp"
 | 
			
		||||
                    android:layout_width="wrap_content"
 | 
			
		||||
                    android:layout_height="wrap_content"
 | 
			
		||||
                    android:textColor="@color/black54"
 | 
			
		||||
                    android:textSize="?attr/TextSizeBody" />
 | 
			
		||||
 | 
			
		||||
                <TextView
 | 
			
		||||
                    android:layout_marginTop="8dp"
 | 
			
		||||
                    android:id="@+id/status_message"
 | 
			
		||||
                    android:layout_width="wrap_content"
 | 
			
		||||
                    android:layout_height="wrap_content"
 | 
			
		||||
                    android:textColor="@color/black54"
 | 
			
		||||
                    android:textColor="@color/black87"
 | 
			
		||||
                    android:textStyle="italic"
 | 
			
		||||
                    android:textSize="?attr/TextSizeBody" />
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -653,4 +653,7 @@
 | 
			
		|||
	<string name="gp_short">Short</string>
 | 
			
		||||
	<string name="gp_medium">Medium</string>
 | 
			
		||||
	<string name="gp_long">Long</string>
 | 
			
		||||
	<string name="pref_broadcast_last_activity">Broadcast last activity</string>
 | 
			
		||||
	<string name="pref_broadcast_last_activity_summary">Let all your contacts know when use Conversations</string>
 | 
			
		||||
	<string name="pref_privacy">Privacy</string>
 | 
			
		||||
</resources>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,8 @@
 | 
			
		|||
            android:key="resource"
 | 
			
		||||
            android:summary="@string/pref_xmpp_resource_summary"
 | 
			
		||||
            android:title="@string/pref_xmpp_resource"/>
 | 
			
		||||
    </PreferenceCategory>
 | 
			
		||||
    <PreferenceCategory android:title="@string/pref_privacy">
 | 
			
		||||
        <CheckBoxPreference
 | 
			
		||||
            android:defaultValue="true"
 | 
			
		||||
            android:key="confirm_messages"
 | 
			
		||||
| 
						 | 
				
			
			@ -26,11 +28,13 @@
 | 
			
		|||
            android:key="chat_states"
 | 
			
		||||
            android:summary="@string/pref_chat_states_summary"
 | 
			
		||||
            android:title="@string/pref_chat_states"/>
 | 
			
		||||
 | 
			
		||||
    </PreferenceCategory>
 | 
			
		||||
    <PreferenceCategory
 | 
			
		||||
        android:key="notifications"
 | 
			
		||||
        android:title="@string/pref_notification_settings">
 | 
			
		||||
        <CheckBoxPreference
 | 
			
		||||
            android:defaultValue="false"
 | 
			
		||||
            android:key="last_activity"
 | 
			
		||||
            android:title="@string/pref_broadcast_last_activity"
 | 
			
		||||
            android:summary="@string/pref_broadcast_last_activity_summary"/>
 | 
			
		||||
        </PreferenceCategory>
 | 
			
		||||
    <PreferenceCategory android:title="@string/pref_notification_settings">
 | 
			
		||||
        <CheckBoxPreference
 | 
			
		||||
            android:defaultValue="true"
 | 
			
		||||
            android:key="show_notification"
 | 
			
		||||
| 
						 | 
				
			
			@ -88,8 +92,7 @@
 | 
			
		|||
            android:entryValues="@array/grace_periods_values"
 | 
			
		||||
            />
 | 
			
		||||
    </PreferenceCategory>
 | 
			
		||||
    <PreferenceCategory
 | 
			
		||||
        android:title="@string/pref_attachments">
 | 
			
		||||
    <PreferenceCategory android:title="@string/pref_attachments">
 | 
			
		||||
        <ListPreference
 | 
			
		||||
            android:defaultValue="524288"
 | 
			
		||||
            android:entries="@array/filesizes"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue