diff options
Diffstat (limited to '')
21 files changed, 173 insertions, 81 deletions
@@ -120,6 +120,9 @@ My Bitcoin Address is: `1NxSU1YxYzJVDpX1rcESAA3NJki7kRgeeu` #### How do I create an account? XMPP, like email, is a federated protocol, which means that there is not one company you can create an *official XMPP account* with. Instead there are hundreds, or even thousands, of providers out there. One of those providers is our very own [conversations.im](https://account.conversations.im). If you don’t like to use *conversations.im* use a web search engine of your choice to find another provider. Or maybe your university has one. Or you can run your own. Or ask a friend to run one. Once you've found one, you can use Conversations to create an account. Just select *register new account* on server within the create account dialog. +##### Running your own +If you have a server somewhere and are willing to put some work in, the best alternative-in the spirit of federation-is to run your own. We recommand either [Prosody](https://prosody.im/) or [ejabberd](https://www.ejabberd.im/). Both of which have their own strengths. Ejabberd is slightly more mature nowadays but Prosody is arguably easier to set up. (For prosody you need a couple of so called [community modules](https://modules.prosody.im/) most of which are maintained by the same people that develop Prosody.) + #### Where can I set up a custom hostname / port Conversations will automatically look up the SRV records for your domain name which can point to any hostname port combination. If your server doesn’t provide diff --git a/docs/XEPs.md b/docs/XEPs.md index 873ea998..48ce4815 100644 --- a/docs/XEPs.md +++ b/docs/XEPs.md @@ -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 diff --git a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java index 9032ba25..4aa17e04 100644 --- a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java +++ b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java @@ -702,7 +702,7 @@ public class MemorizingTrustManager implements X509TrustManager { } @Override public boolean verify(String hostname, SSLSession session) { - return verify(hostname, session, true); + return verify(hostname, session, false); } diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index ee2f3f19..9e53e72b 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -83,7 +83,7 @@ public final class Config { public static final boolean DISABLE_HTTP_UPLOAD = false; public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts - public static final boolean BACKGROUND_STANZA_LOGGING = false; + public static final boolean BACKGROUND_STANZA_LOGGING = false; //log all stanzas that were received while the app is in background public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false; diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 8721d9c4..70878a73 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -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 { diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 9e839683..ff9a20c6 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -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; diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java index 03f19ed8..4699d286 100644 --- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java @@ -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) { diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java index 189df4a7..06d4e37e 100644 --- a/src/main/java/eu/siacs/conversations/parser/IqParser.java +++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java @@ -277,11 +277,6 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { - if (Config.BACKGROUND_STANZA_LOGGING && (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET)) { - Element first = packet.getChildren().size() > 0 ? packet.getChildren().get(0) : null; - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": IQ request from "+packet.getFrom()+(first == null ? "" : " "+first)); - } - if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) { return; } else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 48862c47..354128f0 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -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()) { @@ -536,9 +537,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } } else if (!packet.hasChild("body")){ //no body - if (Config.BACKGROUND_STANZA_LOGGING && mXmppConnectionService.checkListeners()) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + packet+ " (carbon="+Boolean.toString(isCarbon)+")"); - } Conversation conversation = mXmppConnectionService.find(account, from.toBareJid()); if (isTypeGroupChat) { if (packet.hasChild("subject")) { @@ -601,7 +599,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 diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index b5718b61..b11a6eff 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -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) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index fc8159af..5b0c4591 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -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,10 +1822,14 @@ 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(); if (connection != null) { + if (broadcastLastActivity) { + sendPresence(account, broadcastLastActivity); + } if (connection.getFeatures().csi()) { connection.sendInactive(); } @@ -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); } } } diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index de0979a6..b67aae18 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -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() + ")"); diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index cd843b25..22e12633 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -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); diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 5dfa5083..c6301643 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -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; } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index 4ab74818..acc7f28c 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -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); diff --git a/src/main/java/eu/siacs/conversations/utils/Xmlns.java b/src/main/java/eu/siacs/conversations/utils/Xmlns.java index ad30b3e6..de0a29ce 100644 --- a/src/main/java/eu/siacs/conversations/utils/Xmlns.java +++ b/src/main/java/eu/siacs/conversations/utils/Xmlns.java @@ -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"; diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 1128907e..928d4fa0 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -637,6 +637,9 @@ public class XmppConnection implements Runnable { } ++stanzasReceived; lastPacketReceived = SystemClock.elapsedRealtime(); + if (Config.BACKGROUND_STANZA_LOGGING && mXmppConnectionService.checkListeners()) { + Log.d(Config.LOGTAG,"[background stanza] "+element); + } return element; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index 941b4b5f..ac75a5e5 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -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); } diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml index 0facc651..3c9d203b 100644 --- a/src/main/res/layout/activity_contact_details.xml +++ b/src/main/res/layout/activity_contact_details.xml @@ -54,11 +54,19 @@ </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" /> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 757ebb0a..dee7502f 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -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 User Interaction</string> + <string name="pref_broadcast_last_activity_summary">Let all your contacts know when use Conversations</string> + <string name="pref_privacy">Privacy</string> </resources> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index fb0103ed..eb1ff571 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -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" |