aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs
diff options
context:
space:
mode:
authorlookshe <github@lookshe.org>2015-01-03 23:19:05 +0100
committerlookshe <github@lookshe.org>2015-01-03 23:19:05 +0100
commit95e2a539517c27b3235acd582f17968c8e301e81 (patch)
tree213bbeed798751e949376d85f4d7d0bd30c5fbfa /src/main/java/eu/siacs
parent48717dd7d37c066ab626fc626a2ced626ef21d42 (diff)
parent4f4eff2353f4e359b5582c8e808a4e88631c3e74 (diff)
Merge branch 'master' of ssh://git.fucktheforce.de/conversations
Conflicts: src/main/java/eu/siacs/conversations/Config.java src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java src/main/java/eu/siacs/conversations/utils/UIHelper.java
Diffstat (limited to 'src/main/java/eu/siacs')
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java8
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/OtrEngine.java9
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/Plain.java2
-rw-r--r--src/main/java/eu/siacs/conversations/entities/AbstractEntity.java1
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Account.java95
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Blockable.java11
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Bookmark.java16
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java64
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java281
-rw-r--r--src/main/java/eu/siacs/conversations/entities/DownloadableFile.java16
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ListItem.java8
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java71
-rw-r--r--src/main/java/eu/siacs/conversations/entities/MucOptions.java66
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Roster.java10
-rw-r--r--src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java12
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java104
-rw-r--r--src/main/java/eu/siacs/conversations/generator/MessageGenerator.java10
-rw-r--r--src/main/java/eu/siacs/conversations/parser/AbstractParser.java70
-rw-r--r--src/main/java/eu/siacs/conversations/parser/IqParser.java137
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java104
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java99
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java17
-rw-r--r--src/main/java/eu/siacs/conversations/services/MessageArchiveService.java366
-rw-r--r--src/main/java/eu/siacs/conversations/services/NotificationService.java202
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java648
-rw-r--r--src/main/java/eu/siacs/conversations/ui/AbstractSearchableListItemActivity.java124
-rw-r--r--src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java41
-rw-r--r--src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java75
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java107
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java125
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java24
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java76
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java382
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java336
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java324
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java100
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java81
-rw-r--r--src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java315
-rw-r--r--src/main/java/eu/siacs/conversations/ui/TimePreference.java105
-rw-r--r--src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java296
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java98
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java19
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java22
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java61
-rw-r--r--src/main/java/eu/siacs/conversations/utils/DNSHelper.java29
-rw-r--r--src/main/java/eu/siacs/conversations/utils/PhoneHelper.java5
-rw-r--r--src/main/java/eu/siacs/conversations/utils/Validator.java14
-rw-r--r--src/main/java/eu/siacs/conversations/utils/Xmlns.java7
-rw-r--r--src/main/java/eu/siacs/conversations/utils/XmppUri.java3
-rw-r--r--src/main/java/eu/siacs/conversations/utils/zlib/ZLibInputStream.java54
-rw-r--r--src/main/java/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java95
-rw-r--r--src/main/java/eu/siacs/conversations/xml/Element.java31
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java7
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/OnContactStatusChanged.java2
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/OnUpdateBlocklist.java13
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java413
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/forms/Data.java10
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java6
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java4
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java2
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java6
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java18
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java63
63 files changed, 3729 insertions, 2191 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 8b46335a..ad874660 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -20,10 +20,18 @@ public final class Config {
public static final boolean PARSE_EMOTICONS = false;
public static final boolean UTF8_EMOTICONS = false;
+
+ public static final int PAGE_SIZE = 50;
+ public static final int MAX_NUM_PAGES = 3;
+
public static final int PROGRESS_UI_UPDATE_INTERVAL = 750;
public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
+ public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
+ public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
+ public static final int MAM_MAX_MESSAGES = 500;
+
private Config() {
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
index 642d0ed0..7f7b350c 100644
--- a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
@@ -34,7 +34,7 @@ import net.java.otr4j.crypto.OtrCryptoException;
import net.java.otr4j.session.InstanceTag;
import net.java.otr4j.session.SessionID;
-public class OtrEngine implements OtrEngineHost {
+public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost {
private Account account;
private OtrPolicy otrPolicy;
@@ -126,7 +126,7 @@ public class OtrEngine implements OtrEngineHost {
@Override
public byte[] getLocalFingerprintRaw(SessionID arg0) {
try {
- return new OtrCryptoEngineImpl().getFingerprintRaw(getPublicKey());
+ return getFingerprintRaw(getPublicKey());
} catch (OtrCryptoException e) {
return null;
}
@@ -180,6 +180,7 @@ public class OtrEngine implements OtrEngineHost {
packet.setBody(body);
packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy", "urn:xmpp:hints");
+ packet.addChild("no-store", "urn:xmpp:hints");
packet.setType(MessagePacket.TYPE_CHAT);
account.getXmppConnection().sendMessagePacket(packet);
}
@@ -257,10 +258,10 @@ public class OtrEngine implements OtrEngineHost {
Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
if (conversation!=null) {
if (approved) {
- conversation.getContact().addOtrFingerprint(CryptoHelper.prettifyFingerprint(fingerprint));
+ conversation.getContact().addOtrFingerprint(fingerprint);
}
conversation.smp().hint = null;
- conversation.smp().status = Conversation.Smp.STATUS_FINISHED;
+ conversation.smp().status = Conversation.Smp.STATUS_VERIFIED;
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/Plain.java b/src/main/java/eu/siacs/conversations/crypto/sasl/Plain.java
index c7dedc5e..40a55151 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/Plain.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/Plain.java
@@ -14,7 +14,7 @@ public class Plain extends SaslMechanism {
@Override
public int getPriority() {
- return 0;
+ return 10;
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java b/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java
index 92b8a729..957b0a14 100644
--- a/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java
+++ b/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java
@@ -17,5 +17,4 @@ public abstract class AbstractEntity {
public boolean equals(AbstractEntity entity) {
return this.getUuid().equals(entity.getUuid());
}
-
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 538d0ec2..b0cde62c 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -10,9 +10,12 @@ import net.java.otr4j.crypto.OtrCryptoException;
import org.json.JSONException;
import org.json.JSONObject;
+import java.security.PublicKey;
import java.security.interfaces.DSAPublicKey;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -56,7 +59,7 @@ public class Account extends AbstractEntity {
SECURITY_ERROR(true),
INCOMPATIBLE_SERVER(true);
- private boolean isError;
+ private final boolean isError;
public boolean isError() {
return this.isError;
@@ -116,11 +119,11 @@ public class Account extends AbstractEntity {
protected boolean online = false;
private OtrEngine otrEngine = null;
private XmppConnection xmppConnection = null;
- private Presences presences = new Presences();
private long mEndGracePeriod = 0L;
private String otrFingerprint;
- private Roster roster = null;
+ private final Roster roster = new Roster(this);
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
+ private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
public Account() {
this.uuid = "0";
@@ -150,7 +153,7 @@ public class Account extends AbstractEntity {
this.avatar = avatar;
}
- public static Account fromCursor(Cursor cursor) {
+ public static Account fromCursor(final Cursor cursor) {
Jid jid = null;
try {
jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)),
@@ -166,11 +169,11 @@ public class Account extends AbstractEntity {
cursor.getString(cursor.getColumnIndex(AVATAR)));
}
- public boolean isOptionSet(int option) {
+ public boolean isOptionSet(final int option) {
return ((options & (1 << option)) != 0);
}
- public void setOption(int option, boolean value) {
+ public void setOption(final int option, final boolean value) {
if (value) {
this.options |= 1 << option;
} else {
@@ -241,34 +244,18 @@ public class Account extends AbstractEntity {
return keys;
}
- public String getSSLFingerprint() {
- if (keys.has("ssl_cert")) {
- try {
- return keys.getString("ssl_cert");
- } catch (JSONException e) {
- return null;
- }
- } else {
- return null;
- }
- }
-
- public void setSSLCertFingerprint(String fingerprint) {
- this.setKey("ssl_cert", fingerprint);
- }
-
- public boolean setKey(String keyName, String keyValue) {
+ public boolean setKey(final String keyName, final String keyValue) {
try {
this.keys.put(keyName, keyValue);
return true;
- } catch (JSONException e) {
+ } catch (final JSONException e) {
return false;
}
}
@Override
public ContentValues getContentValues() {
- ContentValues values = new ContentValues();
+ final ContentValues values = new ContentValues();
values.put(UUID, uuid);
values.put(USERNAME, jid.getLocalpart());
values.put(SERVER, jid.getDomainpart());
@@ -280,7 +267,7 @@ public class Account extends AbstractEntity {
return values;
}
- public void initOtrEngine(XmppConnectionService context) {
+ public void initOtrEngine(final XmppConnectionService context) {
this.otrEngine = new OtrEngine(context, this);
}
@@ -292,7 +279,7 @@ public class Account extends AbstractEntity {
return this.xmppConnection;
}
- public void setXmppConnection(XmppConnection connection) {
+ public void setXmppConnection(final XmppConnection connection) {
this.xmppConnection = connection;
}
@@ -302,8 +289,8 @@ public class Account extends AbstractEntity {
if (this.otrEngine == null) {
return null;
}
- DSAPublicKey publicKey = (DSAPublicKey) this.otrEngine.getPublicKey();
- if (publicKey == null) {
+ final PublicKey publicKey = this.otrEngine.getPublicKey();
+ if (publicKey == null || !(publicKey instanceof DSAPublicKey)) {
return null;
}
this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey);
@@ -324,31 +311,19 @@ public class Account extends AbstractEntity {
}
}
- public void setRosterVersion(String version) {
+ public void setRosterVersion(final String version) {
this.rosterVersion = version;
}
- public void updatePresence(String resource, int status) {
- this.presences.updatePresence(resource, status);
- }
-
- public void removePresence(String resource) {
- this.presences.removePresence(resource);
- }
-
- public void clearPresences() {
- this.presences = new Presences();
- }
-
public int countPresences() {
- return this.presences.size();
+ return this.getRoster().getContact(this.getJid().toBareJid()).getPresences().size();
}
public String getPgpSignature() {
if (keys.has("pgp_signature")) {
try {
return keys.getString("pgp_signature");
- } catch (JSONException e) {
+ } catch (final JSONException e) {
return null;
}
} else {
@@ -357,9 +332,6 @@ public class Account extends AbstractEntity {
}
public Roster getRoster() {
- if (this.roster == null) {
- this.roster = new Roster(this);
- }
return this.roster;
}
@@ -367,12 +339,12 @@ public class Account extends AbstractEntity {
return this.bookmarks;
}
- public void setBookmarks(List<Bookmark> bookmarks) {
+ public void setBookmarks(final List<Bookmark> bookmarks) {
this.bookmarks = bookmarks;
}
public boolean hasBookmarkFor(final Jid conferenceJid) {
- for (Bookmark bookmark : this.bookmarks) {
+ for (final Bookmark bookmark : this.bookmarks) {
final Jid jid = bookmark.getJid();
if (jid != null && jid.equals(conferenceJid.toBareJid())) {
return true;
@@ -381,7 +353,7 @@ public class Account extends AbstractEntity {
return false;
}
- public boolean setAvatar(String filename) {
+ public boolean setAvatar(final String filename) {
if (this.avatar != null && this.avatar.equals(filename)) {
return false;
} else {
@@ -408,11 +380,32 @@ public class Account extends AbstractEntity {
}
public String getShareableUri() {
- String fingerprint = this.getOtrFingerprint();
+ final String fingerprint = this.getOtrFingerprint();
if (fingerprint != null) {
return "xmpp:" + this.getJid().toBareJid().toString() + "?otr-fingerprint="+fingerprint;
} else {
return "xmpp:" + this.getJid().toBareJid().toString();
}
}
+
+ public boolean isBlocked(final ListItem contact) {
+ final Jid jid = contact.getJid();
+ return jid != null && (blocklist.contains(jid.toBareJid()) || blocklist.contains(jid.toDomainJid()));
+ }
+
+ public boolean isBlocked(final Jid jid) {
+ return jid != null && blocklist.contains(jid.toBareJid());
+ }
+
+ public Collection<Jid> getBlocklist() {
+ return this.blocklist;
+ }
+
+ public void clearBlocklist() {
+ getBlocklist().clear();
+ }
+
+ public boolean isOnlineAndConnected() {
+ return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Blockable.java b/src/main/java/eu/siacs/conversations/entities/Blockable.java
new file mode 100644
index 00000000..dbcd55c4
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/entities/Blockable.java
@@ -0,0 +1,11 @@
+package eu.siacs.conversations.entities;
+
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public interface Blockable {
+ public boolean isBlocked();
+ public boolean isDomainBlocked();
+ public Jid getBlockedJid();
+ public Jid getJid();
+ public Account getAccount();
+}
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index 559e2f2d..f81f1a87 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -6,7 +6,6 @@ import java.util.Locale;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
public class Bookmark extends Element implements ListItem {
@@ -60,16 +59,7 @@ public class Bookmark extends Element implements ListItem {
@Override
public Jid getJid() {
- final String jid = this.getAttribute("jid");
- if (jid != null) {
- try {
- return Jid.fromString(jid);
- } catch (final InvalidJidException e) {
- return null;
- }
- } else {
- return null;
- }
+ return this.getAttributeAsJid("jid");
}
@Override
@@ -102,9 +92,7 @@ public class Bookmark extends Element implements ListItem {
}
public boolean autojoin() {
- String autojoin = this.getAttribute("autojoin");
- return (autojoin != null && (autojoin.equalsIgnoreCase("true") || autojoin
- .equalsIgnoreCase("1")));
+ return this.getAttributeAsBoolean("autojoin");
}
public String getPassword() {
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index 6a6b41d6..698e0322 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -16,7 +16,7 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
-public class Contact implements ListItem {
+public class Contact implements ListItem, Blockable {
public static final String TABLENAME = "contacts";
public static final String SYSTEMNAME = "systemname";
@@ -47,8 +47,8 @@ public class Contact implements ListItem {
protected Account account;
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 Jid jid, final int subscription, final String photoUri,
+ final String systemAccount, final String keys, final String avatar, final Lastseen lastseen, final String groups) {
this.accountUuid = account;
this.systemName = systemName;
this.serverName = serverName;
@@ -122,11 +122,10 @@ public class Contact implements ListItem {
@Override
public List<Tag> getTags() {
- ArrayList<Tag> tags = new ArrayList<Tag>();
- for (String group : getGroups()) {
+ final ArrayList<Tag> tags = new ArrayList<>();
+ for (final String group : getGroups()) {
tags.add(new Tag(group, UIHelper.getColorForName(group)));
}
- int status = getMostAvailableStatus();
switch (getMostAvailableStatus()) {
case Presences.CHAT:
case Presences.ONLINE:
@@ -142,6 +141,9 @@ public class Contact implements ListItem {
tags.add(new Tag("dnd", 0xffe51c23));
break;
}
+ if (isBlocked()) {
+ tags.add(new Tag("blocked", 0xff2e2f3b));
+ }
return tags;
}
@@ -176,7 +178,7 @@ public class Contact implements ListItem {
}
public ContentValues getContentValues() {
- ContentValues values = new ContentValues();
+ final ContentValues values = new ContentValues();
values.put(ACCOUNT, accountUuid);
values.put(SYSTEMNAME, systemName);
values.put(SERVERNAME, serverName);
@@ -213,11 +215,11 @@ public class Contact implements ListItem {
this.presences = pres;
}
- public void updatePresence(String resource, int status) {
+ public void updatePresence(final String resource, final int status) {
this.presences.updatePresence(resource, status);
}
- public void removePresence(String resource) {
+ public void removePresence(final String resource) {
this.presences.removePresence(resource);
}
@@ -266,13 +268,15 @@ public class Contact implements ListItem {
}
public ArrayList<String> getOtrFingerprints() {
- ArrayList<String> fingerprints = new ArrayList<String>();
+ final ArrayList<String> fingerprints = new ArrayList<String>();
try {
if (this.keys.has("otr_fingerprints")) {
- JSONArray prints = this.keys
- .getJSONArray("otr_fingerprints");
+ final JSONArray prints = this.keys.getJSONArray("otr_fingerprints");
for (int i = 0; i < prints.length(); ++i) {
- fingerprints.add(prints.getString(i));
+ final String print = prints.isNull(i) ? null : prints.getString(i);
+ if (print != null && !print.isEmpty()) {
+ fingerprints.add(prints.getString(i));
+ }
}
}
} catch (final JSONException ignored) {
@@ -335,8 +339,8 @@ public class Contact implements ListItem {
public boolean showInRoster() {
return (this.getOption(Contact.Options.IN_ROSTER) && (!this
- .getOption(Contact.Options.DIRTY_DELETE)))
- || (this.getOption(Contact.Options.DIRTY_PUSH));
+ .getOption(Contact.Options.DIRTY_DELETE)))
+ || (this.getOption(Contact.Options.DIRTY_PUSH));
}
public void parseSubscriptionFromElement(Element item) {
@@ -426,7 +430,7 @@ public class Contact implements ListItem {
if (this.keys.has("otr_fingerprints")) {
JSONArray newPrints = new JSONArray();
JSONArray oldPrints = this.keys
- .getJSONArray("otr_fingerprints");
+ .getJSONArray("otr_fingerprints");
for (int i = 0; i < oldPrints.length(); ++i) {
if (!oldPrints.getString(i).equals(fingerprint)) {
newPrints.put(oldPrints.getString(i));
@@ -449,28 +453,46 @@ public class Contact implements ListItem {
public String getShareableUri() {
if (getOtrFingerprints().size() >= 1) {
String otr = getOtrFingerprints().get(0);
- return "xmpp:" + getJid().toBareJid().toString() + "?otr-fingerprint=" + otr.replace(" ", "");
+ return "xmpp:" + getJid().toBareJid().toString() + "?otr-fingerprint=" + otr;
} else {
return "xmpp:" + getJid().toBareJid().toString();
}
}
+ @Override
+ public boolean isBlocked() {
+ return getAccount().isBlocked(this);
+ }
+
+ @Override
+ public boolean isDomainBlocked() {
+ return getAccount().isBlocked(this.getJid().toDomainJid());
+ }
+
+ @Override
+ public Jid getBlockedJid() {
+ if (isDomainBlocked()) {
+ return getJid().toDomainJid();
+ } else {
+ return getJid();
+ }
+ }
+
public static class Lastseen {
public long time;
public String presence;
public Lastseen() {
- time = 0;
- presence = null;
+ this(null, 0);
}
public Lastseen(final String presence, final long time) {
- this.time = time;
this.presence = presence;
+ this.time = time;
}
}
- public class Options {
+ public final class Options {
public static final int TO = 0;
public static final int FROM = 1;
public static final int ASKING = 2;
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index a7da0bc2..470bd290 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -16,12 +16,15 @@ import org.json.JSONObject;
import java.security.interfaces.DSAPublicKey;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
-public class Conversation extends AbstractEntity {
+public class Conversation extends AbstractEntity implements Blockable {
public static final String TABLENAME = "conversations";
public static final int STATUS_AVAILABLE = 0;
@@ -43,6 +46,7 @@ public class Conversation extends AbstractEntity {
public static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
+ public static final String ATTRIBUTE_LAST_MESSAGE_TRANSMITTED = "last_message_transmitted";
private String name;
private String contactUuid;
@@ -72,6 +76,140 @@ public class Conversation extends AbstractEntity {
private Bookmark bookmark;
+ private boolean messagesLeftOnServer = true;
+
+ public boolean hasMessagesLeftOnServer() {
+ return messagesLeftOnServer;
+ }
+
+ public void setHasMessagesLeftOnServer(boolean value) {
+ this.messagesLeftOnServer = value;
+ }
+
+ public Message findUnsentMessageWithUuid(String uuid) {
+ synchronized(this.messages) {
+ for (final Message message : this.messages) {
+ final int s = message.getStatus();
+ if ((s == Message.STATUS_UNSEND || s == Message.STATUS_WAITING) && message.getUuid().equals(uuid)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void findWaitingMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for(Message message : this.messages) {
+ if (message.getStatus() == Message.STATUS_WAITING) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findMessagesWithFiles(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
+ && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findMessageWithFileAndUuid(String uuid) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getType() == Message.TYPE_IMAGE
+ && message.getEncryption() != Message.ENCRYPTION_PGP
+ && message.getUuid().equals(uuid)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void clearMessages() {
+ synchronized (this.messages) {
+ this.messages.clear();
+ }
+ }
+
+ public void trim() {
+ synchronized (this.messages) {
+ final int size = messages.size();
+ final int maxsize = Config.PAGE_SIZE * Config.MAX_NUM_PAGES;
+ if (size > maxsize) {
+ this.messages.subList(0, size - maxsize).clear();
+ }
+ }
+ }
+
+ public void findUnsentMessagesWithOtrEncryption(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_WAITING)
+ && (message.getEncryption() == Message.ENCRYPTION_OTR)) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findUnsentTextMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getType() != Message.TYPE_IMAGE
+ && message.getStatus() == Message.STATUS_UNSEND) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findSentMessageWithUuid(String uuid) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (uuid.equals(message.getUuid())
+ || (message.getStatus() >= Message.STATUS_SEND && uuid
+ .equals(message.getRemoteMsgId()))) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void populateWithMessages(final List<Message> messages) {
+ synchronized (this.messages) {
+ messages.clear();
+ messages.addAll(this.messages);
+ }
+ }
+
+ @Override
+ public boolean isBlocked() {
+ return getContact().isBlocked();
+ }
+
+ @Override
+ public boolean isDomainBlocked() {
+ return getContact().isDomainBlocked();
+ }
+
+ @Override
+ public Jid getBlockedJid() {
+ return getContact().getBlockedJid();
+ }
+
+
+ public interface OnMessageFound {
+ public void onMessageFound(final Message message);
+ }
+
public Conversation(final String name, final Account account, final Jid contactJid,
final int mode) {
this(java.util.UUID.randomUUID().toString(), name, null, account
@@ -98,18 +236,11 @@ public class Conversation extends AbstractEntity {
}
}
- public List<Message> getMessages() {
- return messages;
- }
-
public boolean isRead() {
- return (this.messages == null) || (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
- }
+ return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
+ }
public void markRead() {
- if (this.messages == null) {
- return;
- }
for (int i = this.messages.size() - 1; i >= 0; --i) {
if (messages.get(i).isRead()) {
break;
@@ -119,9 +250,6 @@ public class Conversation extends AbstractEntity {
}
public Message getLatestMarkableMessage() {
- if (this.messages == null) {
- return null;
- }
for (int i = this.messages.size() - 1; i >= 0; --i) {
if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED
&& this.messages.get(i).markable) {
@@ -130,13 +258,13 @@ public class Conversation extends AbstractEntity {
} else {
return this.messages.get(i);
}
- }
+ }
}
return null;
}
public Message getLatestMessage() {
- if ((this.messages == null) || (this.messages.size() == 0)) {
+ if (this.messages.size() == 0) {
Message message = new Message(this, "", Message.ENCRYPTION_NONE);
message.setTime(getCreated());
return message;
@@ -158,7 +286,7 @@ public class Conversation extends AbstractEntity {
if (generatedName != null) {
return generatedName;
} else {
- return getContactJid().getLocalpart();
+ return getJid().getLocalpart();
}
}
} else {
@@ -166,10 +294,6 @@ public class Conversation extends AbstractEntity {
}
}
- public String getProfilePhotoString() {
- return this.getContact().getProfilePhoto();
- }
-
public String getAccountUuid() {
return this.accountUuid;
}
@@ -182,11 +306,12 @@ public class Conversation extends AbstractEntity {
return this.account.getRoster().getContact(this.contactJid);
}
- public void setAccount(Account account) {
+ public void setAccount(final Account account) {
this.account = account;
}
- public Jid getContactJid() {
+ @Override
+ public Jid getJid() {
return this.contactJid;
}
@@ -213,14 +338,14 @@ public class Conversation extends AbstractEntity {
}
public static Conversation fromCursor(Cursor cursor) {
- Jid jid;
- try {
- jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)));
- } catch (final InvalidJidException e) {
- // Borked DB..
- jid = null;
- }
- return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
+ Jid jid;
+ try {
+ jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)));
+ } catch (final InvalidJidException e) {
+ // Borked DB..
+ jid = null;
+ }
+ return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(NAME)),
cursor.getString(cursor.getColumnIndex(CONTACT)),
cursor.getString(cursor.getColumnIndex(ACCOUNT)),
@@ -247,9 +372,9 @@ public class Conversation extends AbstractEntity {
if (this.otrSession != null) {
return this.otrSession;
} else {
- final SessionID sessionId = new SessionID(this.getContactJid().toBareJid().toString(),
- presence,
- "xmpp");
+ final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(),
+ presence,
+ "xmpp");
this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine());
try {
if (sendStart) {
@@ -288,7 +413,7 @@ public class Conversation extends AbstractEntity {
} catch (OtrException e) {
this.resetOtrSession();
}
- }
+ }
}
public boolean endOtrIfNeeded() {
@@ -315,30 +440,29 @@ public class Conversation extends AbstractEntity {
return this.otrSession != null;
}
- public String getOtrFingerprint() {
+ public synchronized String getOtrFingerprint() {
if (this.otrFingerprint == null) {
try {
- if (getOtrSession() == null) {
- return "";
+ if (getOtrSession() == null || getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
+ return null;
}
- DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession()
- .getRemotePublicKey();
- StringBuilder builder = new StringBuilder(
- new OtrCryptoEngineImpl().getFingerprint(remotePubKey));
- builder.insert(8, " ");
- builder.insert(17, " ");
- builder.insert(26, " ");
- builder.insert(35, " ");
- this.otrFingerprint = builder.toString();
- } catch (final OtrCryptoException ignored) {
-
+ DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey();
+ this.otrFingerprint = getAccount().getOtrEngine().getFingerprint(remotePubKey);
+ } catch (final OtrCryptoException | UnsupportedOperationException ignored) {
+ return null;
}
}
return this.otrFingerprint;
}
- public void verifyOtrFingerprint() {
- getContact().addOtrFingerprint(getOtrFingerprint());
+ public boolean verifyOtrFingerprint() {
+ final String fingerprint = getOtrFingerprint();
+ if (fingerprint != null) {
+ getContact().addOtrFingerprint(fingerprint);
+ return true;
+ } else {
+ return false;
+ }
}
public boolean isOtrFingerprintVerified() {
@@ -450,9 +574,11 @@ public class Conversation extends AbstractEntity {
}
public boolean hasDuplicateMessage(Message message) {
- for (int i = this.getMessages().size() - 1; i >= 0; --i) {
- if (this.messages.get(i).equals(message)) {
- return true;
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).equals(message)) {
+ return true;
+ }
}
}
return false;
@@ -460,7 +586,7 @@ public class Conversation extends AbstractEntity {
public Message findSentMessageWithBody(String body) {
synchronized (this.messages) {
- for (int i = this.getMessages().size() - 1; i >= 0; --i) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
Message message = this.messages.get(i);
if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) && message.getBody() != null && message.getBody().equals(body)) {
return message;
@@ -470,6 +596,31 @@ public class Conversation extends AbstractEntity {
}
}
+ public boolean setLastMessageTransmitted(long value) {
+ long before = getLastMessageTransmitted();
+ if (value - before > 1000) {
+ this.setAttribute(ATTRIBUTE_LAST_MESSAGE_TRANSMITTED, String.valueOf(value));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public long getLastMessageTransmitted() {
+ long timestamp = getLongAttribute(ATTRIBUTE_LAST_MESSAGE_TRANSMITTED,0);
+ if (timestamp == 0) {
+ synchronized (this.messages) {
+ for(int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = this.messages.get(i);
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ return message.getTimeSent();
+ }
+ }
+ }
+ }
+ return timestamp;
+ }
+
public void setMutedTill(long value) {
this.setAttribute(ATTRIBUTE_MUTED_TILL, String.valueOf(value));
}
@@ -535,12 +686,32 @@ public class Conversation extends AbstractEntity {
}
}
+ public void sort() {
+ synchronized (this.messages) {
+ Collections.sort(this.messages, new Comparator<Message>() {
+ @Override
+ public int compare(Message left, Message right) {
+ if (left.getTimeSent() < right.getTimeSent()) {
+ return -1;
+ } else if (left.getTimeSent() > right.getTimeSent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ });
+ for(Message message : this.messages) {
+ message.untie();
+ }
+ }
+ }
+
public class Smp {
public static final int STATUS_NONE = 0;
public static final int STATUS_CONTACT_REQUESTED = 1;
public static final int STATUS_WE_REQUESTED = 2;
public static final int STATUS_FAILED = 3;
- public static final int STATUS_FINISHED = 4;
+ public static final int STATUS_VERIFIED = 4;
public String secret = null;
public String hint = null;
diff --git a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
index 25f33907..7c8f95d1 100644
--- a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
+++ b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
@@ -56,12 +56,16 @@ public class DownloadableFile extends File {
public String getMimeType() {
String path = this.getAbsolutePath();
- String mime = URLConnection.guessContentTypeFromName(path);
- if (mime != null) {
- return mime;
- } else if (mime == null && path.endsWith(".webp")) {
- return "image/webp";
- } else {
+ try {
+ String mime = URLConnection.guessContentTypeFromName(path.replace("#",""));
+ if (mime != null) {
+ return mime;
+ } else if (mime == null && path.endsWith(".webp")) {
+ return "image/webp";
+ } else {
+ return "";
+ }
+ } catch (final StringIndexOutOfBoundsException e) {
return "";
}
}
diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java
index db9fbc37..efc1c2b9 100644
--- a/src/main/java/eu/siacs/conversations/entities/ListItem.java
+++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java
@@ -12,10 +12,10 @@ public interface ListItem extends Comparable<ListItem> {
public List<Tag> getTags();
public final class Tag {
- private String name;
- private int color;
+ private final String name;
+ private final int color;
- public Tag(String name, int color) {
+ public Tag(final String name, final int color) {
this.name = name;
this.color = color;
}
@@ -28,4 +28,6 @@ public interface ListItem extends Comparable<ListItem> {
return this.name;
}
}
+
+ public boolean match(final String needle);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 47861d06..b5a1897d 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -45,6 +45,7 @@ public class Message extends AbstractEntity {
public static String STATUS = "status";
public static String TYPE = "type";
public static String REMOTE_MSG_ID = "remoteMsgId";
+ public static String SERVER_MSG_ID = "serverMsgId";
public static String RELATIVE_FILE_PATH = "relativeFilePath";
public boolean markable = false;
protected String conversationUuid;
@@ -59,6 +60,7 @@ public class Message extends AbstractEntity {
protected String relativeFilePath;
protected boolean read = true;
protected String remoteMsgId = null;
+ protected String serverMsgId = null;
protected Conversation conversation = null;
protected Downloadable downloadable = null;
private Message mNextMessage = null;
@@ -75,7 +77,7 @@ public class Message extends AbstractEntity {
public Message(Conversation conversation, String body, int encryption, int status) {
this(java.util.UUID.randomUUID().toString(),
conversation.getUuid(),
- conversation.getContactJid() == null ? null : conversation.getContactJid().toBareJid(),
+ conversation.getJid() == null ? null : conversation.getJid().toBareJid(),
null,
body,
System.currentTimeMillis(),
@@ -83,13 +85,15 @@ public class Message extends AbstractEntity {
status,
TYPE_TEXT,
null,
+ null,
null);
this.conversation = conversation;
}
private Message(final String uuid, final String conversationUUid, final Jid counterpart,
- final Jid trueCounterpart, final String body, final long timeSent,
- final int encryption, final int status, final int type, final String remoteMsgId, final String relativeFilePath) {
+ final Jid trueCounterpart, final String body, final long timeSent,
+ final int encryption, final int status, final int type, final String remoteMsgId,
+ final String relativeFilePath, final String serverMsgId) {
this.uuid = uuid;
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
@@ -101,6 +105,7 @@ public class Message extends AbstractEntity {
this.type = type;
this.remoteMsgId = remoteMsgId;
this.relativeFilePath = relativeFilePath;
+ this.serverMsgId = serverMsgId;
}
public static Message fromCursor(Cursor cursor) {
@@ -136,7 +141,8 @@ public class Message extends AbstractEntity {
cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(TYPE)),
cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
- cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)));
+ cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)),
+ cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)));
}
public static Message createStatusMessage(Conversation conversation) {
@@ -168,6 +174,7 @@ public class Message extends AbstractEntity {
values.put(TYPE, type);
values.put(REMOTE_MSG_ID, remoteMsgId);
values.put(RELATIVE_FILE_PATH, relativeFilePath);
+ values.put(SERVER_MSG_ID,serverMsgId);
return values;
}
@@ -199,7 +206,7 @@ public class Message extends AbstractEntity {
return null;
} else {
return this.conversation.getAccount().getRoster()
- .getContactFromRoster(this.trueCounterpart);
+ .getContactFromRoster(this.trueCounterpart);
}
}
}
@@ -248,6 +255,14 @@ public class Message extends AbstractEntity {
this.remoteMsgId = id;
}
+ public String getServerMsgId() {
+ return this.serverMsgId;
+ }
+
+ public void setServerMsgId(String id) {
+ this.serverMsgId = id;
+ }
+
public boolean isRead() {
return this.read;
}
@@ -293,38 +308,43 @@ public class Message extends AbstractEntity {
}
public boolean equals(Message message) {
- return (this.remoteMsgId != null) && (this.body != null) && (this.counterpart != null) && this.remoteMsgId.equals(message.getRemoteMsgId()) && this.body.equals(message.getBody()) && this.counterpart.equals(message.getCounterpart());
+ if (this.serverMsgId != null && message.getServerMsgId() != null) {
+ return this.serverMsgId.equals(message.getServerMsgId());
+ } else {
+ return this.body != null
+ && this.counterpart != null
+ && ((this.remoteMsgId != null && this.remoteMsgId.equals(message.getRemoteMsgId()))
+ || this.uuid.equals(message.getRemoteMsgId())) && this.body.equals(message.getBody())
+ && this.counterpart.equals(message.getCounterpart());
+ }
}
public Message next() {
- if (this.mNextMessage == null) {
- synchronized (this.conversation.messages) {
+ synchronized (this.conversation.messages) {
+ if (this.mNextMessage == null) {
int index = this.conversation.messages.indexOf(this);
- if (index < 0
- || index >= this.conversation.getMessages().size() - 1) {
+ if (index < 0 || index >= this.conversation.messages.size() - 1) {
this.mNextMessage = null;
} else {
- this.mNextMessage = this.conversation.messages
- .get(index + 1);
+ this.mNextMessage = this.conversation.messages.get(index + 1);
}
}
+ return this.mNextMessage;
}
- return this.mNextMessage;
}
public Message prev() {
- if (this.mPreviousMessage == null) {
- synchronized (this.conversation.messages) {
+ synchronized (this.conversation.messages) {
+ if (this.mPreviousMessage == null) {
int index = this.conversation.messages.indexOf(this);
if (index <= 0 || index > this.conversation.messages.size()) {
this.mPreviousMessage = null;
} else {
- this.mPreviousMessage = this.conversation.messages
- .get(index - 1);
+ this.mPreviousMessage = this.conversation.messages.get(index - 1);
}
}
+ return this.mPreviousMessage;
}
- return this.mPreviousMessage;
}
public boolean mergeable(final Message message) {
@@ -368,7 +388,7 @@ public class Message extends AbstractEntity {
if (!url.getProtocol().equalsIgnoreCase("http")
&& !url.getProtocol().equalsIgnoreCase("https")) {
return false;
- }
+ }
if (url.getPath() == null) {
return false;
}
@@ -382,14 +402,14 @@ public class Message extends AbstractEntity {
String[] extensionParts = filename.split("\\.");
if (extensionParts.length == 2
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
- extensionParts[extensionParts.length - 1])) {
+ extensionParts[extensionParts.length - 1])) {
return true;
} else if (extensionParts.length == 3
&& Arrays
.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
.contains(extensionParts[extensionParts.length - 1])
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
- extensionParts[extensionParts.length - 2])) {
+ extensionParts[extensionParts.length - 2])) {
return true;
} else {
return false;
@@ -493,6 +513,15 @@ public class Message extends AbstractEntity {
}
}
+ public void untie() {
+ this.mNextMessage = null;
+ this.mPreviousMessage = null;
+ }
+
+ public boolean isFileOrImage() {
+ return type == TYPE_FILE || type == TYPE_IMAGE;
+ }
+
public class ImageParams {
public URL url;
public long size = 0;
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index c8706fc9..97a63532 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -80,20 +80,20 @@ public class MucOptions {
public void setRole(String role) {
role = role.toLowerCase();
- switch (role) {
- case "moderator":
- this.role = ROLE_MODERATOR;
- break;
- case "participant":
- this.role = ROLE_PARTICIPANT;
- break;
- case "visitor":
- this.role = ROLE_VISITOR;
- break;
- default:
- this.role = ROLE_NONE;
- break;
- }
+ switch (role) {
+ case "moderator":
+ this.role = ROLE_MODERATOR;
+ break;
+ case "participant":
+ this.role = ROLE_PARTICIPANT;
+ break;
+ case "visitor":
+ this.role = ROLE_VISITOR;
+ break;
+ default:
+ this.role = ROLE_NONE;
+ break;
+ }
}
public int getAffiliation() {
@@ -164,7 +164,7 @@ public class MucOptions {
}
public void processPacket(PresencePacket packet, PgpEngine pgp) {
- final Jid from = packet.getFrom();
+ final Jid from = packet.getFrom();
if (!from.isBareJid()) {
final String name = from.getResourcepart();
final String type = packet.getAttribute("type");
@@ -179,7 +179,7 @@ public class MucOptions {
user.setAffiliation(item.getAttribute("affiliation"));
user.setRole(item.getAttribute("role"));
user.setJid(item.getAttributeAsJid("jid"));
- if (codes.contains(STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(this.conversation.getContactJid())) {
+ if (codes.contains(STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(this.conversation.getJid())) {
this.isOnline = true;
this.error = ERROR_NO_ERROR;
self = user;
@@ -204,14 +204,14 @@ public class MucOptions {
msg = "";
}
user.setPgpKeyId(pgp.fetchKeyId(account, msg,
- signed.getContent()));
+ signed.getContent()));
}
}
}
}
} else if (type.equals("unavailable")) {
if (codes.contains(STATUS_CODE_SELF_PRESENCE) ||
- packet.getFrom().equals(this.conversation.getContactJid())) {
+ packet.getFrom().equals(this.conversation.getJid())) {
if (codes.contains(STATUS_CODE_CHANGED_NICK)) {
this.mNickChangingInProgress = true;
} else if (codes.contains(STATUS_CODE_KICKED)) {
@@ -282,8 +282,8 @@ public class MucOptions {
&& conversation.getBookmark().getNick() != null
&& !conversation.getBookmark().getNick().isEmpty()) {
return conversation.getBookmark().getNick();
- } else if (!conversation.getContactJid().isBareJid()) {
- return conversation.getContactJid().getResourcepart();
+ } else if (!conversation.getJid().isBareJid()) {
+ return conversation.getJid().getResourcepart();
} else {
return account.getUsername();
}
@@ -334,14 +334,14 @@ public class MucOptions {
public String createNameFromParticipants() {
if (users.size() >= 2) {
List<String> names = new ArrayList<String>();
- for (User user : users) {
- Contact contact = user.getContact();
- if (contact != null && !contact.getDisplayName().isEmpty()) {
- names.add(contact.getDisplayName().split("\\s+")[0]);
- } else {
- names.add(user.getName());
- }
+ for (User user : users) {
+ Contact contact = user.getContact();
+ if (contact != null && !contact.getDisplayName().isEmpty()) {
+ names.add(contact.getDisplayName().split("\\s+")[0]);
+ } else {
+ names.add(user.getName());
}
+ }
StringBuilder builder = new StringBuilder();
for (int i = 0; i < names.size(); ++i) {
builder.append(names.get(i));
@@ -388,12 +388,12 @@ public class MucOptions {
}
public Jid createJoinJid(String nick) {
- try {
- return Jid.fromString(this.conversation.getContactJid().toBareJid().toString() + "/"+nick);
- } catch (final InvalidJidException e) {
- return null;
- }
- }
+ try {
+ return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/"+nick);
+ } catch (final InvalidJidException e) {
+ return null;
+ }
+ }
public Jid getTrueCounterpart(String counterpart) {
for (User user : this.getUsers()) {
diff --git a/src/main/java/eu/siacs/conversations/entities/Roster.java b/src/main/java/eu/siacs/conversations/entities/Roster.java
index 12a89cec..1a81a419 100644
--- a/src/main/java/eu/siacs/conversations/entities/Roster.java
+++ b/src/main/java/eu/siacs/conversations/entities/Roster.java
@@ -7,7 +7,7 @@ import java.util.concurrent.ConcurrentHashMap;
import eu.siacs.conversations.xmpp.jid.Jid;
public class Roster {
- Account account;
+ final Account account;
final ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<>();
private String version = null;
@@ -19,7 +19,7 @@ public class Roster {
if (jid == null) {
return null;
}
- Contact contact = contacts.get(jid.toBareJid().toString());
+ final Contact contact = contacts.get(jid.toBareJid().toString());
if (contact != null && contact.showInRoster()) {
return contact;
} else {
@@ -32,7 +32,7 @@ public class Roster {
if (contacts.containsKey(bareJid.toString())) {
return contacts.get(bareJid.toString());
} else {
- Contact contact = new Contact(bareJid);
+ final Contact contact = new Contact(bareJid);
contact.setAccount(account);
contacts.put(bareJid.toString(), contact);
return contact;
@@ -46,13 +46,13 @@ public class Roster {
}
public void markAllAsNotInRoster() {
- for (Contact contact : getContacts()) {
+ for (final Contact contact : getContacts()) {
contact.resetOption(Contact.Options.IN_ROSTER);
}
}
public void clearSystemAccounts() {
- for (Contact contact : getContacts()) {
+ for (final Contact contact : getContacts()) {
contact.setPhotoUri(null);
contact.setSystemName(null);
contact.setSystemAccount(null);
diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
index f46e7ba4..870ee757 100644
--- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -4,9 +4,12 @@ import android.util.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -20,9 +23,11 @@ public abstract class AbstractGenerator {
"http://jabber.org/protocol/disco#info",
"urn:xmpp:avatar:metadata+notify",
"urn:xmpp:ping"};
- public final String IDENTITY_NAME = "Conversations 0.9.3";
+ public final String IDENTITY_NAME = "Conversations 0.10";
public final String IDENTITY_TYPE = "phone";
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+
protected XmppConnectionService mXmppConnectionService;
protected AbstractGenerator(XmppConnectionService service) {
@@ -46,4 +51,9 @@ public abstract class AbstractGenerator {
byte[] sha1 = md.digest(s.toString().getBytes());
return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
}
+
+ public static String getTimestamp(long time) {
+ DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return DATE_FORMAT.format(time);
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 5d674748..f94dc5d7 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -4,52 +4,56 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator {
- public IqGenerator(XmppConnectionService service) {
+ public IqGenerator(final XmppConnectionService service) {
super(service);
}
- public IqPacket discoResponse(IqPacket request) {
- IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT);
+ public IqPacket discoResponse(final IqPacket request) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT);
packet.setId(request.getId());
- packet.setTo(request.getFrom());
- Element query = packet.addChild("query",
+ packet.setTo(request.getFrom());
+ final Element query = packet.addChild("query",
"http://jabber.org/protocol/disco#info");
query.setAttribute("node", request.query().getAttribute("node"));
- Element identity = query.addChild("identity");
+ final Element identity = query.addChild("identity");
identity.setAttribute("category", "client");
identity.setAttribute("type", this.IDENTITY_TYPE);
identity.setAttribute("name", IDENTITY_NAME);
- List<String> features = Arrays.asList(FEATURES);
+ final List<String> features = Arrays.asList(FEATURES);
Collections.sort(features);
- for (String feature : features) {
+ for (final String feature : features) {
query.addChild("feature").setAttribute("var", feature);
}
return packet;
}
- protected IqPacket publish(String node, Element item) {
- IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
- Element pubsub = packet.addChild("pubsub",
+ protected IqPacket publish(final String node, final Element item) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ final Element pubsub = packet.addChild("pubsub",
"http://jabber.org/protocol/pubsub");
- Element publish = pubsub.addChild("publish");
+ final Element publish = pubsub.addChild("publish");
publish.setAttribute("node", node);
publish.addChild(item);
return packet;
}
protected IqPacket retrieve(String node, Element item) {
- IqPacket packet = new IqPacket(IqPacket.TYPE_GET);
- Element pubsub = packet.addChild("pubsub",
+ final IqPacket packet = new IqPacket(IqPacket.TYPE_GET);
+ final Element pubsub = packet.addChild("pubsub",
"http://jabber.org/protocol/pubsub");
- Element items = pubsub.addChild("items");
+ final Element items = pubsub.addChild("items");
items.setAttribute("node", node);
if (item != null) {
items.addChild(item);
@@ -58,19 +62,19 @@ public class IqGenerator extends AbstractGenerator {
}
public IqPacket publishAvatar(Avatar avatar) {
- Element item = new Element("item");
+ final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum);
- Element data = item.addChild("data", "urn:xmpp:avatar:data");
+ final Element data = item.addChild("data", "urn:xmpp:avatar:data");
data.setContent(avatar.image);
return publish("urn:xmpp:avatar:data", item);
}
- public IqPacket publishAvatarMetadata(Avatar avatar) {
- Element item = new Element("item");
+ public IqPacket publishAvatarMetadata(final Avatar avatar) {
+ final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum);
- Element metadata = item
- .addChild("metadata", "urn:xmpp:avatar:metadata");
- Element info = metadata.addChild("info");
+ final Element metadata = item
+ .addChild("metadata", "urn:xmpp:avatar:metadata");
+ final Element info = metadata.addChild("info");
info.setAttribute("bytes", avatar.size);
info.setAttribute("id", avatar.sha1sum);
info.setAttribute("height", avatar.height);
@@ -79,10 +83,10 @@ public class IqGenerator extends AbstractGenerator {
return publish("urn:xmpp:avatar:metadata", item);
}
- public IqPacket retrieveAvatar(Avatar avatar) {
- Element item = new Element("item");
+ public IqPacket retrieveAvatar(final Avatar avatar) {
+ final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum);
- IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
+ final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
packet.setTo(avatar.owner);
return packet;
}
@@ -94,4 +98,54 @@ public class IqGenerator extends AbstractGenerator {
}
return packet;
}
+
+ public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ final Element query = packet.query("urn:xmpp:mam:0");
+ query.setAttribute("queryid",mam.getQueryId());
+ final Data data = new Data();
+ data.setFormType("urn:xmpp:mam:0");
+ if (mam.getWith()!=null) {
+ data.put("with", mam.getWith().toString());
+ }
+ data.put("start",getTimestamp(mam.getStart()));
+ data.put("end",getTimestamp(mam.getEnd()));
+ query.addChild(data);
+ if (mam.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("before").setContent(mam.getReference());
+ } else if (mam.getReference() != null) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getReference());
+ }
+ return packet;
+ }
+ public IqPacket generateGetBlockList() {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
+ iq.addChild("blocklist", Xmlns.BLOCKING);
+
+ return iq;
+ }
+
+ public IqPacket generateSetBlockRequest(final Jid jid) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+ final Element block = iq.addChild("block", Xmlns.BLOCKING);
+ block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ return iq;
+ }
+
+ public IqPacket generateSetUnblockRequest(final Jid jid) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+ final Element block = iq.addChild("unblock", Xmlns.BLOCKING);
+ block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ return iq;
+ }
+
+ public IqPacket generateSetPassword(final Account account, final String newPassword) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ packet.setTo(account.getServer());
+ final Element query = packet.addChild("query", Xmlns.REGISTER);
+ final Jid jid = account.getJid();
+ query.addChild("username").setContent(jid.getLocalpart());
+ query.addChild("password").setContent(newPassword);
+ return packet;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index ca5417e0..8e99888b 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -114,7 +114,7 @@ public class MessageGenerator extends AbstractGenerator {
private MessagePacket generateError(MessagePacket origin) {
MessagePacket packet = new MessagePacket();
packet.setId(origin.getId());
- packet.setTo(origin.getFrom());
+ packet.setTo(origin.getFrom());
packet.setBody(origin.getBody());
packet.setType(MessagePacket.TYPE_ERROR);
return packet;
@@ -135,7 +135,7 @@ public class MessageGenerator extends AbstractGenerator {
String subject) {
MessagePacket packet = new MessagePacket();
packet.setType(MessagePacket.TYPE_GROUPCHAT);
- packet.setTo(conversation.getContactJid().toBareJid());
+ packet.setTo(conversation.getJid().toBareJid());
Element subjectChild = new Element("subject");
subjectChild.setContent(subject);
packet.addChild(subjectChild);
@@ -149,13 +149,13 @@ public class MessageGenerator extends AbstractGenerator {
packet.setTo(contact);
packet.setFrom(conversation.getAccount().getJid());
Element x = packet.addChild("x", "jabber:x:conference");
- x.setAttribute("jid", conversation.getContactJid().toBareJid().toString());
+ x.setAttribute("jid", conversation.getJid().toBareJid().toString());
return packet;
}
public MessagePacket invite(Conversation conversation, Jid contact) {
MessagePacket packet = new MessagePacket();
- packet.setTo(conversation.getContactJid().toBareJid());
+ packet.setTo(conversation.getJid().toBareJid());
packet.setFrom(conversation.getAccount().getJid());
Element x = new Element("x");
x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
@@ -170,7 +170,7 @@ public class MessageGenerator extends AbstractGenerator {
MessagePacket originalMessage, String namespace) {
MessagePacket receivedPacket = new MessagePacket();
receivedPacket.setType(MessagePacket.TYPE_NORMAL);
- receivedPacket.setTo(originalMessage.getFrom());
+ receivedPacket.setTo(originalMessage.getFrom());
receivedPacket.setFrom(account.getJid());
Element received = receivedPacket.addChild("received", namespace);
received.setAttribute("id", originalMessage.getId());
diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index eedfca16..08070c08 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -2,8 +2,6 @@ package eu.siacs.conversations.parser;
import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.Locale;
@@ -11,7 +9,6 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
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;
public abstract class AbstractParser {
@@ -24,54 +21,39 @@ public abstract class AbstractParser {
protected long getTimestamp(Element packet) {
long now = System.currentTimeMillis();
- ArrayList<String> stamps = new ArrayList<>();
- for (Element child : packet.getChildren()) {
- if (child.getName().equals("delay")) {
- stamps.add(child.getAttribute("stamp").replace("Z", "+0000"));
- }
+ Element delay = packet.findChild("delay");
+ if (delay == null) {
+ return now;
}
- Collections.sort(stamps);
- if (stamps.size() >= 1) {
- try {
- String stamp = stamps.get(stamps.size() - 1);
- if (stamp.contains(".")) {
- Date date = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US)
- .parse(stamp);
- if (now < date.getTime()) {
- return now;
- } else {
- return date.getTime();
- }
- } else {
- Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",
- Locale.US).parse(stamp);
- if (now < date.getTime()) {
- return now;
- } else {
- return date.getTime();
- }
- }
- } catch (ParseException e) {
- return now;
- }
- } else {
+ String stamp = delay.getAttribute("stamp");
+ if (stamp == null) {
+ return now;
+ }
+ try {
+ long time = parseTimestamp(stamp).getTime();
+ return now < time ? now : time;
+ } catch (ParseException e) {
return now;
}
}
+ public static Date parseTimestamp(String timestamp) throws ParseException {
+ timestamp = timestamp.replace("Z", "+0000");
+ SimpleDateFormat dateFormat;
+ if (timestamp.contains(".")) {
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
+ } else {
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
+ }
+ return dateFormat.parse(timestamp);
+ }
+
protected void updateLastseen(final Element packet, final Account account,
final boolean presenceOverwrite) {
- Jid from;
- try {
- from = Jid.fromString(packet.getAttribute("from")).toBareJid();
- } catch (final InvalidJidException e) {
- // TODO: Handle this?
- from = null;
- }
- String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
- Contact contact = account.getRoster().getContact(from);
- long timestamp = getTimestamp(packet);
+ final Jid from = packet.getAttributeAsJid("from");
+ final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
+ final Contact contact = account.getRoster().getContact(from);
+ final long timestamp = getTimestamp(packet);
if (timestamp >= contact.lastseen.time) {
contact.lastseen.time = timestamp;
if (!presence.isEmpty() && presenceOverwrite) {
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index aeec56d0..e84545fc 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -2,36 +2,40 @@ package eu.siacs.conversations.parser;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collection;
+
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqParser extends AbstractParser implements OnIqPacketReceived {
- public IqParser(XmppConnectionService service) {
+ public IqParser(final XmppConnectionService service) {
super(service);
}
- public void rosterItems(Account account, Element query) {
- String version = query.getAttribute("ver");
+ private void rosterItems(final Account account, final Element query) {
+ final String version = query.getAttribute("ver");
if (version != null) {
account.getRoster().setVersion(version);
}
- for (Element item : query.getChildren()) {
+ for (final Element item : query.getChildren()) {
if (item.getName().equals("item")) {
final Jid jid = item.getAttributeAsJid("jid");
if (jid == null) {
- break;
+ continue;
}
- String name = item.getAttribute("name");
- String subscription = item.getAttribute("subscription");
- Contact contact = account.getRoster().getContact(jid);
+ final String name = item.getAttribute("name");
+ final String subscription = item.getAttribute("subscription");
+ final Contact contact = account.getRoster().getContact(jid);
if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
contact.setServerName(name);
contact.parseGroupsFromElement(item);
@@ -54,53 +58,104 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
mXmppConnectionService.updateRosterUi();
}
- public String avatarData(IqPacket packet) {
- Element pubsub = packet.findChild("pubsub",
+ public String avatarData(final IqPacket packet) {
+ final Element pubsub = packet.findChild("pubsub",
"http://jabber.org/protocol/pubsub");
if (pubsub == null) {
return null;
}
- Element items = pubsub.findChild("items");
+ final Element items = pubsub.findChild("items");
if (items == null) {
return null;
}
return super.avatarData(items);
}
+ public static boolean fromServer(final Account account, final IqPacket packet) {
+ return packet.getFrom() == null
+ || packet.getFrom().equals(account.getServer())
+ || packet.getFrom().equals(account.getJid().toBareJid())
+ || packet.getFrom().equals(account.getJid());
+ }
+
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.hasChild("query", "jabber:iq:roster")) {
- final Jid from = packet.getFrom();
- if ((from == null) || (from.equals(account.getJid().toBareJid()))) {
- Element query = packet.findChild("query");
- this.rosterItems(account, query);
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.hasChild("query", Xmlns.ROSTER) && fromServer(account, packet)) {
+ final Element query = packet.findChild("query");
+ // If this is in response to a query for the whole roster:
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ account.getRoster().markAllAsNotInRoster();
}
- } else {
- if (packet.getFrom() == null) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": received iq with invalid from "+packet.toString());
- return;
- } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
- || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
- mXmppConnectionService.getJingleConnectionManager()
- .deliverIbbPacket(account, packet);
- } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
- IqPacket response = mXmppConnectionService.getIqGenerator()
- .discoResponse(packet);
- account.getXmppConnection().sendIqPacket(response, null);
- } else if (packet.hasChild("ping", "urn:xmpp:ping")) {
- IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
+ this.rosterItems(account, query);
+ } else if ((packet.hasChild("block", Xmlns.BLOCKING) || packet.hasChild("blocklist", Xmlns.BLOCKING)) &&
+ fromServer(account, packet)) {
+ // Block list or block push.
+ Log.d(Config.LOGTAG, "Received blocklist update from server");
+ final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
+ final Element block = packet.findChild("block", Xmlns.BLOCKING);
+ final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
+ (block != null ? block.getChildren() : null);
+ // If this is a response to a blocklist query, clear the block list and replace with the new one.
+ // Otherwise, just update the existing blocklist.
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ account.clearBlocklist();
+ }
+ if (items != null) {
+ final Collection<Jid> jids = new ArrayList<>(items.size());
+ // Create a collection of Jids from the packet
+ for (final Element item : items) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid != null) {
+ jids.add(jid);
+ }
+ }
+ }
+ account.getBlocklist().addAll(jids);
+ }
+ // Update the UI
+ mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ } else if (packet.hasChild("unblock", Xmlns.BLOCKING) &&
+ fromServer(account, packet) && packet.getType() == IqPacket.TYPE_SET) {
+ Log.d(Config.LOGTAG, "Received unblock update from server");
+ final Collection<Element> items = packet.findChild("unblock", Xmlns.BLOCKING).getChildren();
+ if (items.size() == 0) {
+ // No children to unblock == unblock all
+ account.getBlocklist().clear();
} else {
- if ((packet.getType() == IqPacket.TYPE_GET)
- || (packet.getType() == IqPacket.TYPE_SET)) {
- IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR);
- Element error = response.addChild("error");
- error.setAttribute("type", "cancel");
- error.addChild("feature-not-implemented",
- "urn:ietf:params:xml:ns:xmpp-stanzas");
- account.getXmppConnection().sendIqPacket(response, null);
+ final Collection<Jid> jids = new ArrayList<>(items.size());
+ for (final Element item : items) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid != null) {
+ jids.add(jid);
+ }
+ }
}
+ account.getBlocklist().removeAll(jids);
}
+ mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
+ || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
+ mXmppConnectionService.getJingleConnectionManager()
+ .deliverIbbPacket(account, packet);
+ } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
+ final IqPacket response = mXmppConnectionService.getIqGenerator()
+ .discoResponse(packet);
+ account.getXmppConnection().sendIqPacket(response, null);
+ } else if (packet.hasChild("ping", "urn:xmpp:ping")) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE_RESULT);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else {
+ if ((packet.getType() == IqPacket.TYPE_GET)
+ || (packet.getType() == IqPacket.TYPE_SET)) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE_ERROR);
+ final Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("feature-not-implemented",
+ "urn:ietf:params:xml:ns:xmpp-stanzas");
+ account.getXmppConnection().sendIqPacket(response, null);
+ }
}
}
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 782675da..44cda261 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -10,11 +10,11 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
@@ -60,12 +60,12 @@ public class MessageParser extends AbstractParser implements
}
private Message parseOtrChat(MessagePacket packet, Account account) {
- boolean properlyAddressed = (!packet.getTo().isBareJid())
- || (account.countPresences() == 1);
- final Jid from = packet.getFrom();
- if (from == null) {
+ final Jid to = packet.getTo();
+ final Jid from = packet.getFrom();
+ if (to == null || from == null) {
return null;
}
+ boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1;
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, from.toBareJid(), false);
String presence;
@@ -124,10 +124,6 @@ public class MessageParser extends AbstractParser implements
finishedMessage.setCounterpart(from);
return finishedMessage;
} catch (Exception e) {
- String receivedId = packet.getId();
- if (receivedId != null) {
- mXmppConnectionService.replyWithNotAcceptable(account, packet);
- }
conversation.resetOtrSession();
return null;
}
@@ -230,7 +226,7 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.getConversations(), account,
to.toBareJid());
if (conversation != null) {
- mXmppConnectionService.markRead(conversation, false);
+ mXmppConnectionService.markRead(conversation);
}
}
}
@@ -277,6 +273,69 @@ public class MessageParser extends AbstractParser implements
return finishedMessage;
}
+ private Message parseMamMessage(MessagePacket packet, final Account account) {
+ final Element result = packet.findChild("result","urn:xmpp:mam:0");
+ if (result == null ) {
+ return null;
+ }
+ final MessageArchiveService.Query query = this.mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
+ if (query!=null) {
+ query.incrementTotalCount();
+ }
+ final Element forwarded = result.findChild("forwarded","urn:xmpp:forward:0");
+ if (forwarded == null) {
+ return null;
+ }
+ final Element message = forwarded.findChild("message");
+ if (message == null) {
+ return null;
+ }
+ final Element body = message.findChild("body");
+ if (body == null || message.hasChild("private","urn:xmpp:carbons:2") || message.hasChild("no-copy","urn:xmpp:hints")) {
+ return null;
+ }
+ int encryption;
+ String content = getPgpBody(message);
+ if (content != null) {
+ encryption = Message.ENCRYPTION_PGP;
+ } else {
+ encryption = Message.ENCRYPTION_NONE;
+ content = body.getContent();
+ }
+ if (content == null) {
+ return null;
+ }
+ final long timestamp = getTimestamp(forwarded);
+ final Jid to = message.getAttributeAsJid("to");
+ final Jid from = message.getAttributeAsJid("from");
+ Jid counterpart;
+ int status;
+ Conversation conversation;
+ if (from!=null && to != null && from.toBareJid().equals(account.getJid().toBareJid())) {
+ status = Message.STATUS_SEND;
+ conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false,query);
+ counterpart = to;
+ } else if (from !=null && to != null) {
+ status = Message.STATUS_RECEIVED;
+ conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false,query);
+ counterpart = from;
+ } else {
+ return null;
+ }
+ Message finishedMessage = new Message(conversation,content,encryption,status);
+ finishedMessage.setTime(timestamp);
+ finishedMessage.setCounterpart(counterpart);
+ finishedMessage.setRemoteMsgId(message.getAttribute("id"));
+ finishedMessage.setServerMsgId(result.getAttribute("id"));
+ if (conversation.hasDuplicateMessage(finishedMessage)) {
+ return null;
+ }
+ if (query!=null) {
+ query.incrementMessageCount();
+ }
+ return finishedMessage;
+ }
+
private void parseError(final MessagePacket packet, final Account account) {
final Jid from = packet.getFrom();
mXmppConnectionService.markMessage(account, from.toBareJid(),
@@ -444,12 +503,22 @@ public class MessageParser extends AbstractParser implements
if (message != null) {
if (message.getStatus() == Message.STATUS_SEND) {
account.activateGracePeriod();
- mXmppConnectionService.markRead(
- message.getConversation(), false);
+ mXmppConnectionService.markRead(message.getConversation());
} else {
message.markUnread();
}
}
+ } else if (packet.hasChild("result","urn:xmpp:mam:0")) {
+ message = parseMamMessage(packet, account);
+ if (message != null) {
+ Conversation conversation = message.getConversation();
+ conversation.add(message);
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ }
+ return;
+ } else if (packet.hasChild("fin","urn:xmpp:mam:0")) {
+ Element fin = packet.findChild("fin","urn:xmpp:mam:0");
+ mXmppConnectionService.getMessageArchiveService().processFin(fin);
} else {
parseNonMessage(packet, account);
}
@@ -459,8 +528,7 @@ public class MessageParser extends AbstractParser implements
if (message.getStatus() == Message.STATUS_RECEIVED) {
message.markUnread();
} else {
- mXmppConnectionService.markRead(message.getConversation(),
- false);
+ mXmppConnectionService.markRead(message.getConversation());
account.activateGracePeriod();
}
}
@@ -491,12 +559,16 @@ public class MessageParser extends AbstractParser implements
}
Conversation conversation = message.getConversation();
conversation.add(message);
+ if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) {
+ if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
+ mXmppConnectionService.updateConversation(conversation);
+ }
+ }
if (message.getStatus() == Message.STATUS_RECEIVED
&& conversation.getOtrSession() != null
&& !conversation.getOtrSession().getSessionID().getUserID()
.equals(message.getCounterpart().getResourcepart())) {
- Log.d(Config.LOGTAG, "ending because of reasons");
conversation.endOtrIfNeeded();
}
@@ -509,7 +581,7 @@ public class MessageParser extends AbstractParser implements
if (message.trusted() && message.bodyContainsDownloadable()) {
this.mXmppConnectionService.getHttpConnectionManager()
.createNewConnection(message);
- } else {
+ } else if (!message.isRead()) {
mXmppConnectionService.getNotificationService().push(message);
}
mXmppConnectionService.updateConversationUi();
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index 43c8fa8d..accb56ea 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -45,69 +45,56 @@ public class PresenceParser extends AbstractParser implements
}
final Jid from = packet.getFrom();
String type = packet.getAttribute("type");
- if (from.toBareJid().equals(account.getJid().toBareJid())) {
+ Contact contact = account.getRoster().getContact(packet.getFrom());
+ if (type == null) {
+ String presence;
if (!from.isBareJid()) {
- if (type == null) {
- account.updatePresence(from.getResourcepart(),
- Presences.parseShow(packet.findChild("show")));
- } else if (type.equals("unavailable")) {
- account.removePresence(from.getResourcepart());
- account.deactivateGracePeriod();
- }
+ presence = from.getResourcepart();
+ } else {
+ presence = "";
}
- } else {
- Contact contact = account.getRoster().getContact(packet.getFrom());
- if (type == null) {
- String presence;
- if (!from.isBareJid()) {
- presence = from.getResourcepart();
- } else {
- presence = "";
- }
- int sizeBefore = contact.getPresences().size();
- contact.updatePresence(presence,
- Presences.parseShow(packet.findChild("show")));
- PgpEngine pgp = mXmppConnectionService.getPgpEngine();
- if (pgp != null) {
- Element x = packet.findChild("x", "jabber:x:signed");
- if (x != null) {
- Element status = packet.findChild("status");
- String msg;
- if (status != null) {
- msg = status.getContent();
- } else {
- msg = "";
- }
- contact.setPgpKeyId(pgp.fetchKeyId(account, msg,
- x.getContent()));
+ int sizeBefore = contact.getPresences().size();
+ contact.updatePresence(presence,
+ Presences.parseShow(packet.findChild("show")));
+ PgpEngine pgp = mXmppConnectionService.getPgpEngine();
+ if (pgp != null) {
+ Element x = packet.findChild("x", "jabber:x:signed");
+ if (x != null) {
+ Element status = packet.findChild("status");
+ String msg;
+ if (status != null) {
+ msg = status.getContent();
+ } else {
+ msg = "";
}
+ contact.setPgpKeyId(pgp.fetchKeyId(account, msg,
+ x.getContent()));
}
- boolean online = sizeBefore < contact.getPresences().size();
- updateLastseen(packet, account, true);
- mXmppConnectionService.onContactStatusChanged
- .onContactStatusChanged(contact, online);
- } else if (type.equals("unavailable")) {
- if (from.isBareJid()) {
- contact.clearPresences();
- } else {
- contact.removePresence(from.getResourcepart());
- }
- mXmppConnectionService.onContactStatusChanged
- .onContactStatusChanged(contact, false);
- } else if (type.equals("subscribe")) {
- if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- mXmppConnectionService.sendPresencePacket(account,
- mPresenceGenerator.sendPresenceUpdatesTo(contact));
- } else {
- contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
- }
}
- Element nick = packet.findChild("nick",
- "http://jabber.org/protocol/nick");
- if (nick != null) {
- contact.setPresenceName(nick.getContent());
+ boolean online = sizeBefore < contact.getPresences().size();
+ updateLastseen(packet, account, false);
+ mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online);
+ } else if (type.equals("unavailable")) {
+ if (from.isBareJid()) {
+ contact.clearPresences();
+ } else {
+ contact.removePresence(from.getResourcepart());
+ }
+ mXmppConnectionService.onContactStatusChanged
+ .onContactStatusChanged(contact, false);
+ } else if (type.equals("subscribe")) {
+ if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
+ mXmppConnectionService.sendPresencePacket(account,
+ mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ } else {
+ contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
}
}
+ Element nick = packet.findChild("nick",
+ "http://jabber.org/protocol/nick");
+ if (nick != null) {
+ contact.setPresenceName(nick.getContent());
+ }
mXmppConnectionService.updateRosterUi();
}
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 55fcff2e..5fa61491 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -22,7 +22,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 11;
+ private static final int DATABASE_VERSION = 13;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -65,6 +65,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ Message.RELATIVE_FILE_PATH + " TEXT, "
+ + Message.SERVER_MSG_ID + " TEXT, "
+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ Message.CONVERSATION + ") REFERENCES "
+ Conversation.TABLENAME + "(" + Conversation.UUID
@@ -121,6 +122,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("delete from "+Contact.TABLENAME);
db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL");
}
+ if (oldVersion < 12 && newVersion >= 12) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.SERVER_MSG_ID + " TEXT");
+ }
+ if (oldVersion < 13 && newVersion >= 13) {
+ db.execSQL("delete from "+Contact.TABLENAME);
+ db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL");
+ }
}
public static synchronized DatabaseBackend getInstance(Context context) {
@@ -223,9 +232,9 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return conversation;
}
- public void updateConversation(Conversation conversation) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = { conversation.getUuid() };
+ public void updateConversation(final Conversation conversation) {
+ final SQLiteDatabase db = this.getWritableDatabase();
+ final String[] args = { conversation.getUuid() };
db.update(Conversation.TABLENAME, conversation.getContentValues(),
Conversation.UUID + "=?", args);
}
diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
new file mode 100644
index 00000000..82111243
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
@@ -0,0 +1,366 @@
+package eu.siacs.conversations.services;
+
+import android.util.Log;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.generator.AbstractGenerator;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
+
+ private final XmppConnectionService mXmppConnectionService;
+
+ private final HashSet<Query> queries = new HashSet<Query>();
+ private final ArrayList<Query> pendingQueries = new ArrayList<Query>();
+
+ public enum PagingOrder {
+ NORMAL,
+ REVERSE
+ };
+
+ public MessageArchiveService(final XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public void catchup(final Account account) {
+ long startCatchup = getLastMessageTransmitted(account);
+ long endCatchup = account.getXmppConnection().getLastSessionEstablished();
+ if (startCatchup == 0) {
+ return;
+ } else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
+ startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
+ List<Conversation> conversations = mXmppConnectionService.getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
+ this.query(conversation,startCatchup);
+ }
+ }
+ }
+ final Query query = new Query(account, startCatchup, endCatchup);
+ this.queries.add(query);
+ this.execute(query);
+ }
+
+ private long getLastMessageTransmitted(final Account account) {
+ long timestamp = 0;
+ for(final Conversation conversation : mXmppConnectionService.getConversations()) {
+ if (conversation.getAccount() == account) {
+ long tmp = conversation.getLastMessageTransmitted();
+ if (tmp > timestamp) {
+ timestamp = tmp;
+ }
+ }
+ }
+ return timestamp;
+ }
+
+ public Query query(final Conversation conversation) {
+ return query(conversation,conversation.getAccount().getXmppConnection().getLastSessionEstablished());
+ }
+
+ public Query query(final Conversation conversation, long end) {
+ return this.query(conversation,conversation.getLastMessageTransmitted(),end);
+ }
+
+ public Query query(Conversation conversation, long start, long end) {
+ synchronized (this.queries) {
+ if (start > end) {
+ return null;
+ }
+ final Query query = new Query(conversation, start, end,PagingOrder.REVERSE);
+ this.queries.add(query);
+ this.execute(query);
+ return query;
+ }
+ }
+
+ public void executePendingQueries(final Account account) {
+ List<Query> pending = new ArrayList<>();
+ synchronized(this.pendingQueries) {
+ for(Iterator<Query> iterator = this.pendingQueries.iterator(); iterator.hasNext();) {
+ Query query = iterator.next();
+ if (query.getAccount() == account) {
+ pending.add(query);
+ iterator.remove();
+ }
+ }
+ }
+ for(Query query : pending) {
+ this.execute(query);
+ }
+ }
+
+ private void execute(final Query query) {
+ final Account account= query.getAccount();
+ if (account.getStatus() == Account.State.ONLINE) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString());
+ IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
+ this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_ERROR) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
+ finalizeQuery(query);
+ }
+ }
+ });
+ } else {
+ synchronized (this.pendingQueries) {
+ this.pendingQueries.add(query);
+ }
+ }
+ }
+
+ private void finalizeQuery(Query query) {
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ }
+ final Conversation conversation = query.getConversation();
+ if (conversation != null) {
+ conversation.sort();
+ if (conversation.setLastMessageTransmitted(query.getEnd())) {
+ this.mXmppConnectionService.databaseBackend.updateConversation(conversation);
+ }
+ conversation.setHasMessagesLeftOnServer(query.getMessageCount() > 0);
+ if (query.hasCallback()) {
+ query.callback();
+ } else {
+ this.mXmppConnectionService.updateConversationUi();
+ }
+ } else {
+ for(Conversation tmp : this.mXmppConnectionService.getConversations()) {
+ if (tmp.getAccount() == query.getAccount()) {
+ tmp.sort();
+ if (tmp.setLastMessageTransmitted(query.getEnd())) {
+ this.mXmppConnectionService.databaseBackend.updateConversation(tmp);
+ }
+ }
+ }
+ }
+ }
+
+ public boolean queryInProgress(Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
+ synchronized (this.queries) {
+ for(Query query : queries) {
+ if (query.conversation == conversation) {
+ if (!query.hasCallback() && callback != null) {
+ query.setCallback(callback);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public void processFin(Element fin) {
+ if (fin == null) {
+ return;
+ }
+ Query query = findQuery(fin.getAttribute("queryid"));
+ if (query == null) {
+ return;
+ }
+ boolean complete = fin.getAttributeAsBoolean("complete");
+ Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
+ Element last = set == null ? null : set.findChild("last");
+ Element first = set == null ? null : set.findChild("first");
+ Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
+ boolean abort = (query.getStart() == 0 && query.getTotalCount() >= Config.PAGE_SIZE) || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
+ if (complete || relevant == null || abort) {
+ this.finalizeQuery(query);
+ Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid().toString()+": finished mam after "+query.getTotalCount()+" messages");
+ } else {
+ final Query nextQuery;
+ if (query.getPagingOrder() == PagingOrder.NORMAL) {
+ nextQuery = query.next(last == null ? null : last.getContent());
+ } else {
+ nextQuery = query.prev(first == null ? null : first.getContent());
+ }
+ this.execute(nextQuery);
+ this.finalizeQuery(query);
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ this.queries.add(nextQuery);
+ }
+ }
+ }
+
+ public Query findQuery(String id) {
+ if (id == null) {
+ return null;
+ }
+ synchronized (this.queries) {
+ for(Query query : this.queries) {
+ if (query.getQueryId().equals(id)) {
+ return query;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
+ this.catchup(account);
+ }
+ }
+
+ public class Query {
+ private int totalCount = 0;
+ private int messageCount = 0;
+ private long start;
+ private long end;
+ private Jid with = null;
+ private String queryId;
+ private String reference = null;
+ private Account account;
+ private Conversation conversation;
+ private PagingOrder pagingOrder = PagingOrder.NORMAL;
+ private XmppConnectionService.OnMoreMessagesLoaded callback = null;
+
+
+ public Query(Conversation conversation, long start, long end) {
+ this(conversation.getAccount(), start, end);
+ this.conversation = conversation;
+ this.with = conversation.getJid().toBareJid();
+ }
+
+ public Query(Conversation conversation, long start, long end, PagingOrder order) {
+ this(conversation,start,end);
+ this.pagingOrder = order;
+ }
+
+ public Query(Account account, long start, long end) {
+ this.account = account;
+ this.start = start;
+ this.end = end;
+ this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ }
+
+ private Query page(String reference) {
+ Query query = new Query(this.account,this.start,this.end);
+ query.reference = reference;
+ query.conversation = conversation;
+ query.with = with;
+ query.totalCount = totalCount;
+ query.callback = callback;
+ return query;
+ }
+
+ public Query next(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.NORMAL;
+ return query;
+ }
+
+ public Query prev(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.REVERSE;
+ return query;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public PagingOrder getPagingOrder() {
+ return this.pagingOrder;
+ }
+
+ public String getQueryId() {
+ return queryId;
+ }
+
+ public Jid getWith() {
+ return with;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public void setCallback(XmppConnectionService.OnMoreMessagesLoaded callback) {
+ this.callback = callback;
+ }
+
+ public void callback() {
+ if (this.callback != null) {
+ this.callback.onMoreMessagesLoaded(messageCount,conversation);
+ if (messageCount == 0) {
+ this.callback.informUser(R.string.no_more_history_on_server);
+ }
+ }
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public Conversation getConversation() {
+ return conversation;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public void incrementTotalCount() {
+ this.totalCount++;
+ }
+
+ public void incrementMessageCount() {
+ this.messageCount++;
+ }
+
+ public int getTotalCount() {
+ return this.totalCount;
+ }
+
+ public int getMessageCount() {
+ return this.messageCount;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("with=");
+ if (this.with==null) {
+ builder.append("*");
+ } else {
+ builder.append(with.toString());
+ }
+ builder.append(", start=");
+ builder.append(AbstractGenerator.getTimestamp(this.start));
+ builder.append(", end=");
+ builder.append(AbstractGenerator.getTimestamp(this.end));
+ if (this.reference!=null) {
+ if (this.pagingOrder == PagingOrder.NORMAL) {
+ builder.append(", after=");
+ } else {
+ builder.append(", before=");
+ }
+ builder.append(this.reference);
+ }
+ return builder.toString();
+ }
+
+ public boolean hasCallback() {
+ return this.callback != null;
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index f649f9d4..a30cf2f1 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -16,9 +16,11 @@ import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.app.TaskStackBuilder;
import android.text.Html;
import android.util.DisplayMetrics;
+import android.util.Log;
import java.io.FileNotFoundException;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.regex.Matcher;
@@ -33,16 +35,17 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
+import eu.siacs.conversations.ui.TimePreference;
public class NotificationService {
private XmppConnectionService mXmppConnectionService;
- private LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<String, ArrayList<Message>>();
+ private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>();
- public static int NOTIFICATION_ID = 0x2342;
- public static int FOREGROUND_NOTIFICATION_ID = 0x8899;
- public static int ERROR_NOTIFICATION_ID = 0x5678;
+ public static final int NOTIFICATION_ID = 0x2342;
+ public static final int FOREGROUND_NOTIFICATION_ID = 0x8899;
+ public static final int ERROR_NOTIFICATION_ID = 0x5678;
private Conversation mOpenConversation;
private boolean mIsInForeground;
@@ -52,46 +55,61 @@ public class NotificationService {
this.mXmppConnectionService = service;
}
- public boolean notify(Message message) {
+ public boolean notify(final Message message) {
return (message.getStatus() == Message.STATUS_RECEIVED)
- && notificationsEnabled()
- && !message.getConversation().isMuted()
- && (message.getConversation().getMode() == Conversation.MODE_SINGLE
+ && notificationsEnabled()
+ && !message.getConversation().isMuted()
+ && (message.getConversation().getMode() == Conversation.MODE_SINGLE
|| conferenceNotificationsEnabled()
|| wasHighlightedOrPrivate(message)
- );
+ );
}
public boolean notificationsEnabled() {
return mXmppConnectionService.getPreferences().getBoolean("show_notification", true);
}
+ public boolean isQuietHours() {
+ if (!mXmppConnectionService.getPreferences().getBoolean("enable_quiet_hours", false)) {
+ return false;
+ }
+ final long startTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_start", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
+ final long endTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_end", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
+ final long nowTime = Calendar.getInstance().getTimeInMillis() % Config.MILLISECONDS_IN_DAY;
+
+ if (endTime < startTime) {
+ return nowTime > startTime || nowTime < endTime;
+ } else {
+ return nowTime > startTime && nowTime < endTime;
+ }
+ }
+
public boolean conferenceNotificationsEnabled() {
return mXmppConnectionService.getPreferences().getBoolean("always_notify_in_conference", false);
}
- public void push(Message message) {
+ public void push(final Message message) {
if (!notify(message)) {
return;
}
- PowerManager pm = (PowerManager) mXmppConnectionService
- .getSystemService(Context.POWER_SERVICE);
- boolean isScreenOn = pm.isScreenOn();
+ final PowerManager pm = (PowerManager) mXmppConnectionService
+ .getSystemService(Context.POWER_SERVICE);
+ final boolean isScreenOn = pm.isScreenOn();
if (this.mIsInForeground && isScreenOn
&& this.mOpenConversation == message.getConversation()) {
return;
- }
+ }
synchronized (notifications) {
- String conversationUuid = message.getConversationUuid();
+ final String conversationUuid = message.getConversationUuid();
if (notifications.containsKey(conversationUuid)) {
notifications.get(conversationUuid).add(message);
} else {
- ArrayList<Message> mList = new ArrayList<Message>();
+ final ArrayList<Message> mList = new ArrayList<>();
mList.add(message);
notifications.put(conversationUuid, mList);
}
- Account account = message.getConversation().getAccount();
+ final Account account = message.getConversation().getAccount();
updateNotification((!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn)
&& !account.inGracePeriod()
&& !this.inMiniGracePeriod(account));
@@ -106,21 +124,20 @@ public class NotificationService {
}
}
- public void clear(Conversation conversation) {
+ public void clear(final Conversation conversation) {
synchronized (notifications) {
notifications.remove(conversation.getUuid());
updateNotification(false);
}
}
- private void updateNotification(boolean notify) {
- NotificationManager notificationManager = (NotificationManager) mXmppConnectionService
- .getSystemService(Context.NOTIFICATION_SERVICE);
- SharedPreferences preferences = mXmppConnectionService.getPreferences();
+ private void updateNotification(final boolean notify) {
+ final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ final SharedPreferences preferences = mXmppConnectionService.getPreferences();
- String ringtone = preferences.getString("notification_ringtone", null);
- boolean vibrate = preferences.getBoolean("vibrate_on_notification",
- true);
+ final String ringtone = preferences.getString("notification_ringtone", null);
+ final boolean vibrate = preferences.getBoolean("vibrate_on_notification", true);
if (notifications.size() == 0) {
notificationManager.cancel(NOTIFICATION_ID);
@@ -128,16 +145,16 @@ public class NotificationService {
if (notify) {
this.markLastNotification();
}
- Builder mBuilder;
+ final Builder mBuilder;
if (notifications.size() == 1) {
mBuilder = buildSingleConversations(notify);
} else {
mBuilder = buildMultipleConversation();
}
- if (notify) {
+ if (notify && !isQuietHours()) {
if (vibrate) {
- int dat = 70;
- long[] pattern = {0, 3 * dat, dat, dat};
+ final int dat = 70;
+ final long[] pattern = {0, 3 * dat, dat, dat};
mBuilder.setVibrate(pattern);
}
if (ringtone != null) {
@@ -147,27 +164,27 @@ public class NotificationService {
mBuilder.setSmallIcon(R.drawable.ic_notification);
mBuilder.setDeleteIntent(createDeleteIntent());
mBuilder.setLights(0xffffffff, 2000, 4000);
- Notification notification = mBuilder.build();
+ final Notification notification = mBuilder.build();
notificationManager.notify(NOTIFICATION_ID, notification);
}
}
private Builder buildMultipleConversation() {
- Builder mBuilder = new NotificationCompat.Builder(
+ final Builder mBuilder = new NotificationCompat.Builder(
mXmppConnectionService);
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
style.setBigContentTitle(notifications.size()
+ " "
+ mXmppConnectionService
.getString(R.string.unread_conversations));
- StringBuilder names = new StringBuilder();
+ final StringBuilder names = new StringBuilder();
Conversation conversation = null;
for (ArrayList<Message> messages : notifications.values()) {
if (messages.size() > 0) {
conversation = messages.get(0).getConversation();
String name = conversation.getName();
style.addLine(Html.fromHtml("<b>" + name + "</b> "
- + getReadableBody(messages.get(0))));
+ + getReadableBody(messages.get(0))));
names.append(name);
names.append(", ");
}
@@ -183,46 +200,45 @@ public class NotificationService {
mBuilder.setStyle(style);
if (conversation != null) {
mBuilder.setContentIntent(createContentIntent(conversation
- .getUuid()));
+ .getUuid()));
}
return mBuilder;
}
- private Builder buildSingleConversations(boolean notify) {
- Builder mBuilder = new NotificationCompat.Builder(
+ private Builder buildSingleConversations(final boolean notify) {
+ final Builder mBuilder = new NotificationCompat.Builder(
mXmppConnectionService);
- ArrayList<Message> messages = notifications.values().iterator().next();
+ final ArrayList<Message> messages = notifications.values().iterator().next();
if (messages.size() >= 1) {
- Conversation conversation = messages.get(0).getConversation();
+ final Conversation conversation = messages.get(0).getConversation();
mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService()
.get(conversation, getPixel(64)));
mBuilder.setContentTitle(conversation.getName());
- Message message;
+ final Message message;
if ((message = getImage(messages)) != null) {
modifyForImage(mBuilder, message, messages, notify);
} else {
modifyForTextOnly(mBuilder, messages, notify);
}
mBuilder.setContentIntent(createContentIntent(conversation
- .getUuid()));
+ .getUuid()));
}
return mBuilder;
-
}
- private void modifyForImage(Builder builder, Message message,
- ArrayList<Message> messages, boolean notify) {
+ private void modifyForImage(final Builder builder, final Message message,
+ final ArrayList<Message> messages, final boolean notify) {
try {
- Bitmap bitmap = mXmppConnectionService.getFileBackend()
- .getThumbnail(message, getPixel(288), false);
- ArrayList<Message> tmp = new ArrayList<Message>();
- for (Message msg : messages) {
+ final Bitmap bitmap = mXmppConnectionService.getFileBackend()
+ .getThumbnail(message, getPixel(288), false);
+ final ArrayList<Message> tmp = new ArrayList<>();
+ for (final Message msg : messages) {
if (msg.getType() == Message.TYPE_TEXT
&& msg.getDownloadable() == null) {
tmp.add(msg);
- }
+ }
}
- BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
+ final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
bigPictureStyle.bigPicture(bitmap);
if (tmp.size() > 0) {
bigPictureStyle.setSummaryText(getMergedBodies(tmp));
@@ -231,13 +247,13 @@ public class NotificationService {
builder.setContentText(mXmppConnectionService.getString(R.string.image_file));
}
builder.setStyle(bigPictureStyle);
- } catch (FileNotFoundException e) {
+ } catch (final FileNotFoundException e) {
modifyForTextOnly(builder, messages, notify);
}
}
- private void modifyForTextOnly(Builder builder,
- ArrayList<Message> messages, boolean notify) {
+ private void modifyForTextOnly(final Builder builder,
+ final ArrayList<Message> messages, final boolean notify) {
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText(getMergedBodies(messages)));
builder.setContentText(getReadableBody(messages.get(0)));
@@ -246,19 +262,19 @@ public class NotificationService {
}
}
- private Message getImage(ArrayList<Message> messages) {
- for (Message message : messages) {
+ private Message getImage(final ArrayList<Message> messages) {
+ for (final Message message : messages) {
if (message.getType() == Message.TYPE_IMAGE
&& message.getDownloadable() == null
&& message.getEncryption() != Message.ENCRYPTION_PGP) {
return message;
- }
+ }
}
return null;
}
- private String getMergedBodies(ArrayList<Message> messages) {
- StringBuilder text = new StringBuilder();
+ private String getMergedBodies(final ArrayList<Message> messages) {
+ final StringBuilder text = new StringBuilder();
for (int i = 0; i < messages.size(); ++i) {
text.append(getReadableBody(messages.get(i)));
if (i != messages.size() - 1) {
@@ -268,10 +284,10 @@ public class NotificationService {
return text.toString();
}
- private String getReadableBody(Message message) {
+ private String getReadableBody(final Message message) {
if (message.getDownloadable() != null
&& (message.getDownloadable().getStatus() == Downloadable.STATUS_OFFER || message
- .getDownloadable().getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE)) {
+ .getDownloadable().getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE)) {
if (message.getType() == Message.TYPE_FILE) {
return mXmppConnectionService.getString(R.string.file_offered_for_download);
} else {
@@ -283,27 +299,27 @@ public class NotificationService {
R.string.encrypted_message_received).toString();
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
return mXmppConnectionService.getText(R.string.decryption_failed)
- .toString();
+ .toString();
} else if (message.getType() == Message.TYPE_FILE) {
DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
return mXmppConnectionService.getString(R.string.file,file.getMimeType());
} else if (message.getType() == Message.TYPE_IMAGE) {
return mXmppConnectionService.getText(R.string.image_file)
- .toString();
+ .toString();
} else {
return message.getBody().trim();
}
}
- private PendingIntent createContentIntent(String conversationUuid) {
- TaskStackBuilder stackBuilder = TaskStackBuilder
- .create(mXmppConnectionService);
+ private PendingIntent createContentIntent(final String conversationUuid) {
+ final TaskStackBuilder stackBuilder = TaskStackBuilder
+ .create(mXmppConnectionService);
stackBuilder.addParentStack(ConversationActivity.class);
- Intent viewConversationIntent = new Intent(mXmppConnectionService,
+ final Intent viewConversationIntent = new Intent(mXmppConnectionService,
ConversationActivity.class);
viewConversationIntent.setAction(Intent.ACTION_VIEW);
- if (conversationUuid!=null) {
+ if (conversationUuid != null) {
viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
conversationUuid);
viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
@@ -311,36 +327,34 @@ public class NotificationService {
stackBuilder.addNextIntent(viewConversationIntent);
- PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
- PendingIntent.FLAG_UPDATE_CURRENT);
- return resultPendingIntent;
+ return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
}
private PendingIntent createDeleteIntent() {
- Intent intent = new Intent(mXmppConnectionService,
+ final Intent intent = new Intent(mXmppConnectionService,
XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION);
return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
}
private PendingIntent createDisableForeground() {
- Intent intent = new Intent(mXmppConnectionService,
+ final Intent intent = new Intent(mXmppConnectionService,
XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_DISABLE_FOREGROUND);
return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
}
- private boolean wasHighlightedOrPrivate(Message message) {
- String nick = message.getConversation().getMucOptions().getActualNick();
- Pattern highlight = generateNickHighlightPattern(nick);
+ private boolean wasHighlightedOrPrivate(final Message message) {
+ final String nick = message.getConversation().getMucOptions().getActualNick();
+ final Pattern highlight = generateNickHighlightPattern(nick);
if (message.getBody() == null || nick == null) {
return false;
}
- Matcher m = highlight.matcher(message.getBody());
+ final Matcher m = highlight.matcher(message.getBody());
return (m.find() || message.getType() == Message.TYPE_PRIVATE);
}
- private static Pattern generateNickHighlightPattern(String nick) {
+ private static Pattern generateNickHighlightPattern(final String nick) {
// We expect a word boundary, i.e. space or start of string, followed by
// the
// nick (matched in case-insensitive manner), followed by optional
@@ -350,17 +364,20 @@ public class NotificationService {
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
}
- public void setOpenConversation(Conversation conversation) {
+ public void setOpenConversation(final Conversation conversation) {
this.mOpenConversation = conversation;
}
- public void setIsInForeground(boolean foreground) {
+ public void setIsInForeground(final boolean foreground) {
+ if (foreground != this.mIsInForeground) {
+ Log.d(Config.LOGTAG,"setIsInForeground("+Boolean.toString(foreground)+")");
+ }
this.mIsInForeground = foreground;
}
- private int getPixel(int dp) {
- DisplayMetrics metrics = mXmppConnectionService.getResources()
- .getDisplayMetrics();
+ private int getPixel(final int dp) {
+ final DisplayMetrics metrics = mXmppConnectionService.getResources()
+ .getDisplayMetrics();
return ((int) (dp * metrics.density));
}
@@ -368,14 +385,14 @@ public class NotificationService {
this.mLastNotification = SystemClock.elapsedRealtime();
}
- private boolean inMiniGracePeriod(Account account) {
- int miniGrace = account.getStatus() == Account.State.ONLINE ? Config.MINI_GRACE_PERIOD
- : Config.MINI_GRACE_PERIOD * 2;
+ private boolean inMiniGracePeriod(final Account account) {
+ final int miniGrace = account.getStatus() == Account.State.ONLINE ? Config.MINI_GRACE_PERIOD
+ : Config.MINI_GRACE_PERIOD * 2;
return SystemClock.elapsedRealtime() < (this.mLastNotification + miniGrace);
}
public Notification createForegroundNotification() {
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
+ final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
mBuilder.setSmallIcon(R.drawable.ic_stat_communication_import_export);
mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service));
mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_disable));
@@ -386,14 +403,14 @@ public class NotificationService {
}
public void updateErrorNotification() {
- NotificationManager mNotificationManager = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE);
- List<Account> errors = new ArrayList<>();
- for (Account account : mXmppConnectionService.getAccounts()) {
+ final NotificationManager mNotificationManager = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE);
+ final List<Account> errors = new ArrayList<>();
+ for (final Account account : mXmppConnectionService.getAccounts()) {
if (account.hasErrorStatus()) {
errors.add(account);
}
}
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
+ final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
if (errors.size() == 0) {
mNotificationManager.cancel(ERROR_NOTIFICATION_ID);
return;
@@ -410,13 +427,12 @@ public class NotificationService {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mXmppConnectionService);
stackBuilder.addParentStack(ConversationActivity.class);
- Intent manageAccountsIntent = new Intent(mXmppConnectionService,ManageAccountActivity.class);
+ final Intent manageAccountsIntent = new Intent(mXmppConnectionService,ManageAccountActivity.class);
stackBuilder.addNextIntent(manageAccountsIntent);
- PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
+ final PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
- Notification notification = mBuilder.build();
- mNotificationManager.notify(ERROR_NOTIFICATION_ID, notification);
+ mNotificationManager.notify(ERROR_NOTIFICATION_ID, mBuilder.build());
}
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 41a40224..6bdc55a1 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -32,20 +32,16 @@ import net.java.otr4j.session.SessionStatus;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
-import java.util.TimeZone;
+import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import de.duenndns.ssl.MemorizingTrustManager;
@@ -53,11 +49,11 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
-import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.DownloadablePlaceholder;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
@@ -78,12 +74,17 @@ import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
import eu.siacs.conversations.utils.PRNGFixes;
import eu.siacs.conversations.utils.PhoneHelper;
+import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnBindListener;
import eu.siacs.conversations.xmpp.OnContactStatusChanged;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.OnMessageAcknowledged;
+import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
+import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.OnStatusChanged;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
+import eu.siacs.conversations.xmpp.PacketReceived;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.forms.Field;
@@ -97,11 +98,12 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
-public class XmppConnectionService extends Service {
+public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener {
+
+ public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
+ private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+ public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
- public static String ACTION_CLEAR_NOTIFICATION = "clear_notification";
- private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
- public static String ACTION_DISABLE_FOREGROUND = "disable_foreground";
private ContentObserver contactObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
@@ -126,7 +128,7 @@ public class XmppConnectionService extends Service {
conversation.resetOtrSession();
}
if (online && (contact.getPresences().size() == 1)) {
- sendUnsendMessages(conversation);
+ sendUnsentMessages(conversation);
}
}
}
@@ -135,18 +137,19 @@ public class XmppConnectionService extends Service {
private MemorizingTrustManager mMemorizingTrustManager;
private NotificationService mNotificationService = new NotificationService(
this);
- private MessageParser mMessageParser = new MessageParser(this);
- private PresenceParser mPresenceParser = new PresenceParser(this);
+ private OnMessagePacketReceived mMessageParser = new MessageParser(this);
+ private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
private IqParser mIqParser = new IqParser(this);
private MessageGenerator mMessageGenerator = new MessageGenerator(this);
private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
private List<Account> accounts;
- private final CopyOnWriteArrayList<Conversation> conversations = new CopyOnWriteArrayList<Conversation>();
+ private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this);
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
this);
private AvatarService mAvatarService = new AvatarService(this);
+ private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private OnConversationUpdate mOnConversationUpdate = null;
private Integer convChangedListenerCount = 0;
private OnAccountUpdate mOnAccountUpdate = null;
@@ -165,12 +168,13 @@ public class XmppConnectionService extends Service {
for (Conversation conversation : account.pendingConferenceJoins) {
joinMuc(conversation);
}
+ mMessageArchiveService.executePendingQueries(account);
mJingleConnectionManager.cancelInTransmission();
List<Conversation> conversations = getConversations();
for (Conversation conversation : conversations) {
if (conversation.getAccount() == account) {
conversation.startOtrIfNeeded();
- sendUnsendMessages(conversation);
+ sendUnsentMessages(conversation);
}
}
if (connection != null && connection.getFeatures().csi()) {
@@ -209,13 +213,16 @@ public class XmppConnectionService extends Service {
getNotificationService().updateErrorNotification();
}
};
+
private int accountChangedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
+ private OnUpdateBlocklist mOnUpdateBlocklist = null;
+ private int updateBlocklistListenerCount = 0;
private int rosterChangedListenerCount = 0;
private OnMucRosterUpdate mOnMucRosterUpdate = null;
private int mucRosterChangedListenerCount = 0;
private SecureRandom mRandom;
- private FileObserver fileObserver = new FileObserver(
+ private final FileObserver fileObserver = new FileObserver(
FileBackend.getConversationsImageDirectory()) {
@Override
@@ -225,7 +232,7 @@ public class XmppConnectionService extends Service {
}
}
};
- private OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
+ private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
@Override
public void onJinglePacketReceived(Account account, JinglePacket packet) {
@@ -239,43 +246,41 @@ public class XmppConnectionService extends Service {
private PendingIntent pendingPingIntent = null;
private WakeLock wakeLock;
private PowerManager pm;
- private OnBindListener mOnBindListener = new OnBindListener() {
+ private final OnBindListener mOnBindListener = new OnBindListener() {
@Override
public void onBind(final Account account) {
account.getRoster().clearPresences();
- account.clearPresences(); // self presences
account.pendingConferenceJoins.clear();
account.pendingConferenceLeaves.clear();
fetchRosterFromServer(account);
fetchBookmarks(account);
- sendPresencePacket(account,
- mPresenceGenerator.sendPresence(account));
+ sendPresencePacket(account,mPresenceGenerator.sendPresence(account));
connectMultiModeConversations(account);
updateConversationUi();
}
};
- private OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
+ private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
@Override
public void onMessageAcknowledged(Account account, String uuid) {
- for (Conversation conversation : getConversations()) {
+ for (final Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) {
- for (Message message : conversation.getMessages()) {
- if ((message.getStatus() == Message.STATUS_UNSEND || message
- .getStatus() == Message.STATUS_WAITING)
- && message.getUuid().equals(uuid)) {
- markMessage(message, Message.STATUS_SEND);
- return;
- }
+ Message message = conversation.findUnsentMessageWithUuid(uuid);
+ if (message != null) {
+ markMessage(message, Message.STATUS_SEND);
+ if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
+ databaseBackend.updateConversation(conversation);
+ }
}
}
}
}
};
private LruCache<String, Bitmap> mBitmapCache;
- private IqGenerator mIqGenerator = new IqGenerator(this);
+ private final IqGenerator mIqGenerator = new IqGenerator(this);
+ private Thread mPhoneContactMergerThread;
public PgpEngine getPgpEngine() {
if (pgpServiceConnection.isBound()) {
@@ -299,7 +304,9 @@ public class XmppConnectionService extends Service {
return this.mAvatarService;
}
- public void attachFileToConversation(Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+ public void attachFileToConversation(final Conversation conversation,
+ final Uri uri,
+ final UiCallback<Message> callback) {
final Message message;
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
message = new Message(conversation, "",
@@ -359,13 +366,13 @@ public class XmppConnectionService extends Service {
@Override
public void run() {
try {
- DownloadableFile file = getFileBackend().copyImageToPrivateStorage(message, uri);
+ getFileBackend().copyImageToPrivateStorage(message, uri);
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
getPgpEngine().encrypt(message, callback);
} else {
callback.success(message);
}
- } catch (FileBackend.FileCopyException e) {
+ } catch (final FileBackend.FileCopyException e) {
callback.error(e.getResId(), message);
}
}
@@ -384,7 +391,7 @@ public class XmppConnectionService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.getAction() != null) {
if (intent.getAction().equals(ACTION_MERGE_PHONE_CONTACTS)) {
- mergePhoneContactsWithRoster();
+ PhoneHelper.loadPhoneContacts(getApplicationContext(), new ArrayList<Bundle>(), this);
return START_STICKY;
} else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
logoutAndSave();
@@ -479,11 +486,11 @@ public class XmppConnectionService extends Service {
this.mMemorizingTrustManager = new MemorizingTrustManager(
getApplicationContext());
- int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- int cacheSize = maxMemory / 8;
+ final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ final int cacheSize = maxMemory / 8;
this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
- protected int sizeOf(String key, Bitmap bitmap) {
+ protected int sizeOf(final String key, final Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
@@ -491,12 +498,12 @@ public class XmppConnectionService extends Service {
this.databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
this.accounts = databaseBackend.getAccounts();
- for (Account account : this.accounts) {
+ for (final Account account : this.accounts) {
account.initOtrEngine(this);
this.databaseBackend.readRoster(account.getRoster());
}
initConversations();
- this.mergePhoneContactsWithRoster();
+ PhoneHelper.loadPhoneContacts(getApplicationContext(),new ArrayList<Bundle>(), this);
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
this.fileObserver.startWatching();
@@ -517,7 +524,7 @@ public class XmppConnectionService extends Service {
}
@Override
- public void onTaskRemoved(Intent rootIntent) {
+ public void onTaskRemoved(final Intent rootIntent) {
super.onTaskRemoved(rootIntent);
if (!getPreferences().getBoolean("keep_foreground_service",false)) {
this.logoutAndSave();
@@ -525,7 +532,7 @@ public class XmppConnectionService extends Service {
}
private void logoutAndSave() {
- for (Account account : accounts) {
+ for (final Account account : accounts) {
databaseBackend.writeRoster(account.getRoster());
if (account.getXmppConnection() != null) {
disconnect(account, false);
@@ -578,26 +585,26 @@ public class XmppConnectionService extends Service {
}
- public XmppConnection createConnection(Account account) {
- SharedPreferences sharedPref = getPreferences();
+ public XmppConnection createConnection(final Account account) {
+ final SharedPreferences sharedPref = getPreferences();
account.setResource(sharedPref.getString("resource", "mobile")
.toLowerCase(Locale.getDefault()));
- XmppConnection connection = new XmppConnection(account, this);
+ final XmppConnection connection = new XmppConnection(account, this);
connection.setOnMessagePacketReceivedListener(this.mMessageParser);
connection.setOnStatusChangedListener(this.statusListener);
connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
connection.setOnJinglePacketReceivedListener(this.jingleListener);
connection.setOnBindListener(this.mOnBindListener);
- connection
- .setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
return connection;
}
- public void sendMessage(Message message) {
- Account account = message.getConversation().getAccount();
+ public void sendMessage(final Message message) {
+ final Account account = message.getConversation().getAccount();
account.deactivateGracePeriod();
- Conversation conv = message.getConversation();
+ final Conversation conv = message.getConversation();
MessagePacket packet = null;
boolean saveInDb = true;
boolean send = false;
@@ -641,12 +648,22 @@ public class XmppConnectionService extends Service {
}
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
message.getConversation().endOtrIfNeeded();
- failWaitingOtrMessages(message.getConversation());
+ message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message,Message.STATUS_SEND_FAILED);
+ }
+ });
packet = mMessageGenerator.generatePgpChat(message);
send = true;
} else {
message.getConversation().endOtrIfNeeded();
- failWaitingOtrMessages(message.getConversation());
+ message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message,Message.STATUS_SEND_FAILED);
+ }
+ });
packet = mMessageGenerator.generateChat(message);
send = true;
}
@@ -689,16 +706,17 @@ public class XmppConnectionService extends Service {
updateConversationUi();
}
- private void sendUnsendMessages(Conversation conversation) {
- for (int i = 0; i < conversation.getMessages().size(); ++i) {
- int status = conversation.getMessages().get(i).getStatus();
- if (status == Message.STATUS_WAITING) {
- resendMessage(conversation.getMessages().get(i));
+ private void sendUnsentMessages(final Conversation conversation) {
+ conversation.findWaitingMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ resendMessage(message);
}
- }
+ });
}
- private void resendMessage(Message message) {
+ private void resendMessage(final Message message) {
Account account = message.getConversation().getAccount();
MessagePacket packet = null;
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
@@ -725,7 +743,7 @@ public class XmppConnectionService extends Service {
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
mJingleConnectionManager.createNewConnection(message);
}
- } catch (InvalidJidException e) {
+ } catch (final InvalidJidException ignored) {
}
}
@@ -768,47 +786,35 @@ public class XmppConnectionService extends Service {
}
}
- public void fetchRosterFromServer(Account account) {
- IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
+ public void fetchRosterFromServer(final Account account) {
+ final IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
if (!"".equals(account.getRosterVersion())) {
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": fetching roster version " + account.getRosterVersion());
} else {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
}
- iqPacket.query("jabber:iq:roster").setAttribute("ver",
+ iqPacket.query(Xmlns.ROSTER).setAttribute("ver",
account.getRosterVersion());
- account.getXmppConnection().sendIqPacket(iqPacket,
- new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account,
- IqPacket packet) {
- Element query = packet.findChild("query");
- if (query != null) {
- account.getRoster().markAllAsNotInRoster();
- mIqParser.rosterItems(account, query);
- }
- }
- });
+ account.getXmppConnection().sendIqPacket(iqPacket, mIqParser);
}
- public void fetchBookmarks(Account account) {
- IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
- Element query = iqPacket.query("jabber:iq:private");
+ public void fetchBookmarks(final Account account) {
+ final IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
+ final Element query = iqPacket.query("jabber:iq:private");
query.addChild("storage", "storage:bookmarks");
- OnIqPacketReceived callback = new OnIqPacketReceived() {
+ final PacketReceived callback = new OnIqPacketReceived() {
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element query = packet.query();
- List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
- Element storage = query.findChild("storage",
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ final Element query = packet.query();
+ final List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
+ final Element storage = query.findChild("storage",
"storage:bookmarks");
if (storage != null) {
- for (Element item : storage.getChildren()) {
+ for (final Element item : storage.getChildren()) {
if (item.getName().equals("conference")) {
- Bookmark bookmark = Bookmark.parse(item, account);
+ final Bookmark bookmark = Bookmark.parse(item, account);
bookmarks.add(bookmark);
Conversation conversation = find(bookmark);
if (conversation != null) {
@@ -826,7 +832,6 @@ public class XmppConnectionService extends Service {
}
};
sendIqPacket(account, iqPacket, callback);
-
}
public void pushBookmarks(Account account) {
@@ -839,53 +844,56 @@ public class XmppConnectionService extends Service {
sendIqPacket(account, iqPacket, null);
}
- private void mergePhoneContactsWithRoster() {
- PhoneHelper.loadPhoneContacts(getApplicationContext(),
- new OnPhoneContactsLoadedListener() {
- @Override
- public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
- for (Account account : accounts) {
- account.getRoster().clearSystemAccounts();
+ public void onPhoneContactsLoaded(final List<Bundle> phoneContacts) {
+ if (mPhoneContactMergerThread != null) {
+ mPhoneContactMergerThread.interrupt();
+ }
+ mPhoneContactMergerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(Config.LOGTAG,"start merging phone contacts with roster");
+ for (Account account : accounts) {
+ account.getRoster().clearSystemAccounts();
+ for (Bundle phoneContact : phoneContacts) {
+ if (Thread.interrupted()) {
+ Log.d(Config.LOGTAG,"interrupted merging phone contacts");
+ return;
}
- for (Bundle phoneContact : phoneContacts) {
- for (Account account : accounts) {
- Jid jid;
- try {
- jid = Jid.fromString(phoneContact.getString("jid"));
- } catch (final InvalidJidException e) {
- // TODO: Warn if contact import fails here?
- break;
- }
- final Contact contact = account.getRoster()
- .getContact(jid);
- String systemAccount = phoneContact
- .getInt("phoneid")
- + "#"
- + phoneContact.getString("lookup");
- contact.setSystemAccount(systemAccount);
- contact.setPhotoUri(phoneContact
- .getString("photouri"));
- contact.setSystemName(phoneContact
- .getString("displayname"));
- getAvatarService().clear(contact);
- }
+ Jid jid;
+ try {
+ jid = Jid.fromString(phoneContact.getString("jid"));
+ } catch (final InvalidJidException e) {
+ continue;
}
+ final Contact contact = account.getRoster().getContact(jid);
+ String systemAccount = phoneContact.getInt("phoneid")
+ + "#"
+ + phoneContact.getString("lookup");
+ contact.setSystemAccount(systemAccount);
+ contact.setPhotoUri(phoneContact.getString("photouri"));
+ getAvatarService().clear(contact);
+ contact.setSystemName(phoneContact.getString("displayname"));
}
- });
+ }
+ Log.d(Config.LOGTAG,"finished merging phone contacts");
+ updateAccountUi();
+ }
+ });
+ mPhoneContactMergerThread.start();
}
private void initConversations() {
synchronized (this.conversations) {
- Hashtable<String, Account> accountLookupTable = new Hashtable<>();
+ final Map<String, Account> accountLookupTable = new Hashtable<>();
for (Account account : this.accounts) {
accountLookupTable.put(account.getUuid(), account);
}
this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
- for (Conversation conv : this.conversations) {
- Account account = accountLookupTable.get(conv.getAccountUuid());
- conv.setAccount(account);
- conv.addAll(0, databaseBackend.getMessages(conv, 50));
- checkDeletedFiles(conv);
+ for (Conversation conversation : this.conversations) {
+ Account account = accountLookupTable.get(conversation.getAccountUuid());
+ conversation.setAccount(account);
+ conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
+ checkDeletedFiles(conversation);
}
}
}
@@ -895,28 +903,26 @@ public class XmppConnectionService extends Service {
}
private void checkDeletedFiles(Conversation conversation) {
- for (Message message : conversation.getMessages()) {
- if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
- && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
if (!getFileBackend().isFileAvailable(message)) {
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
}
- }
- }
+ }
+ });
}
private void markFileDeleted(String uuid) {
for (Conversation conversation : getConversations()) {
- for (Message message : conversation.getMessages()) {
- if (message.getType() == Message.TYPE_IMAGE
- && message.getEncryption() != Message.ENCRYPTION_PGP
- && message.getUuid().equals(uuid)) {
- if (!getFileBackend().isFileAvailable(message)) {
- message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
- updateConversationUi();
- }
- return;
- }
+ Message message = conversation.findMessageWithFileAndUuid(uuid);
+ if (message != null) {
+ if (!getFileBackend().isFileAvailable(message)) {
+ message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
+ updateConversationUi();
+ }
+ return;
}
}
}
@@ -952,22 +958,43 @@ public class XmppConnectionService extends Service {
});
}
- public int loadMoreMessages(Conversation conversation, long timestamp) {
- List<Message> messages = databaseBackend.getMessages(conversation, 50,
- timestamp);
- for (Message message : messages) {
- message.setConversation(conversation);
+ public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) {
+ Log.d(Config.LOGTAG,"load more messages for "+conversation.getName() + " prior to "+MessageGenerator.getTimestamp(timestamp));
+ if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation,callback)) {
+ return;
}
- conversation.addAll(0, messages);
- return messages.size();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ final Account account = conversation.getAccount();
+ List<Message> messages = databaseBackend.getMessages(conversation, 50,timestamp);
+ if (messages.size() > 0) {
+ conversation.addAll(0, messages);
+ callback.onMoreMessagesLoaded(messages.size(), conversation);
+ } else if (conversation.hasMessagesLeftOnServer()
+ && account.isOnlineAndConnected()
+ && account.getXmppConnection().getFeatures().mam()) {
+ MessageArchiveService.Query query = getMessageArchiveService().query(conversation,0,timestamp - 1);
+ if (query != null) {
+ query.setCallback(callback);
+ }
+ callback.informUser(R.string.fetching_history_from_server);
+ }
+ }
+ }).start();
+ }
+
+ public interface OnMoreMessagesLoaded {
+ public void onMoreMessagesLoaded(int count,Conversation conversation);
+ public void informUser(int r);
}
public List<Account> getAccounts() {
return this.accounts;
}
- public Conversation find(List<Conversation> haystack, Contact contact) {
- for (Conversation conversation : haystack) {
+ public Conversation find(final Iterable<Conversation> haystack, final Contact contact) {
+ for (final Conversation conversation : haystack) {
if (conversation.getContact() == contact) {
return conversation;
}
@@ -975,23 +1002,24 @@ public class XmppConnectionService extends Service {
return null;
}
- public Conversation find(final List<Conversation> haystack,
- final Account account,
- final Jid jid) {
+ public Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid) {
if (jid == null ) {
return null;
}
- for (Conversation conversation : haystack) {
+ for (final Conversation conversation : haystack) {
if ((account == null || conversation.getAccount() == account)
- && (conversation.getContactJid().toBareJid().equals(jid.toBareJid()))) {
+ && (conversation.getJid().toBareJid().equals(jid.toBareJid()))) {
return conversation;
}
}
return null;
}
- public Conversation findOrCreateConversation(final Account account, final Jid jid,
- final boolean muc) {
+ public Conversation findOrCreateConversation(final Account account, final Jid jid,final boolean muc) {
+ return this.findOrCreateConversation(account,jid,muc,null);
+ }
+
+ public Conversation findOrCreateConversation(final Account account, final Jid jid,final boolean muc, final MessageArchiveService.Query query) {
synchronized (this.conversations) {
Conversation conversation = find(account, jid);
if (conversation != null) {
@@ -1006,7 +1034,7 @@ public class XmppConnectionService extends Service {
} else {
conversation.setMode(Conversation.MODE_SINGLE);
}
- conversation.addAll(0, databaseBackend.getMessages(conversation, 50));
+ conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
this.databaseBackend.updateConversation(conversation);
} else {
String conversationName;
@@ -1025,6 +1053,13 @@ public class XmppConnectionService extends Service {
}
this.databaseBackend.createConversation(conversation);
}
+ if (query == null) {
+ this.mMessageArchiveService.query(conversation);
+ } else {
+ if (query.getConversation() == null) {
+ this.mMessageArchiveService.query(conversation,query.getStart());
+ }
+ }
this.conversations.add(conversation);
updateConversationUi();
return conversation;
@@ -1051,13 +1086,7 @@ public class XmppConnectionService extends Service {
}
}
- public void clearConversationHistory(Conversation conversation) {
- this.databaseBackend.deleteMessagesInConversation(conversation);
- conversation.getMessages().clear();
- updateConversationUi();
- }
-
- public void createAccount(Account account) {
+ public void createAccount(final Account account) {
account.initOtrEngine(this);
databaseBackend.createAccount(account);
this.accounts.add(account);
@@ -1065,7 +1094,7 @@ public class XmppConnectionService extends Service {
updateAccountUi();
}
- public void updateAccount(Account account) {
+ public void updateAccount(final Account account) {
this.statusListener.onStatusChanged(account);
databaseBackend.updateAccount(account);
reconnectAccount(account, false);
@@ -1073,9 +1102,30 @@ public class XmppConnectionService extends Service {
getNotificationService().updateErrorNotification();
}
- public void deleteAccount(Account account) {
+ public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) {
+ final IqPacket iq = getIqGenerator().generateSetPassword(account, newPassword);
+ sendIqPacket(account, iq, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ account.setPassword(newPassword);
+ databaseBackend.updateAccount(account);
+ callback.onPasswordChangeSucceeded();
+ } else {
+ callback.onPasswordChangeFailed();
+ }
+ }
+ });
+ }
+
+ public interface OnAccountPasswordChanged {
+ public void onPasswordChangeSucceeded();
+ public void onPasswordChangeFailed();
+ }
+
+ public void deleteAccount(final Account account) {
synchronized (this.conversations) {
- for (Conversation conversation : conversations) {
+ for (final Conversation conversation : conversations) {
if (conversation.getAccount() == account) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
leaveMuc(conversation);
@@ -1147,7 +1197,7 @@ public class XmppConnectionService extends Service {
}
}
- public void setOnRosterUpdateListener(OnRosterUpdate listener) {
+ public void setOnRosterUpdateListener(final OnRosterUpdate listener) {
synchronized (this) {
if (checkListeners()) {
switchToForeground();
@@ -1172,6 +1222,31 @@ public class XmppConnectionService extends Service {
}
}
+ public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnUpdateBlocklist = listener;
+ if (this.updateBlocklistListenerCount < 2) {
+ this.updateBlocklistListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnUpdateBlocklistListener() {
+ synchronized (this) {
+ this.updateBlocklistListenerCount--;
+ if (this.updateBlocklistListenerCount <= 0) {
+ this.updateBlocklistListenerCount = 0;
+ this.mOnUpdateBlocklist = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
synchronized (this) {
if (checkListeners()) {
@@ -1199,7 +1274,9 @@ public class XmppConnectionService extends Service {
private boolean checkListeners() {
return (this.mOnAccountUpdate == null
- && this.mOnConversationUpdate == null && this.mOnRosterUpdate == null);
+ && this.mOnConversationUpdate == null
+ && this.mOnRosterUpdate == null
+ && this.mOnUpdateBlocklist == null);
}
private void switchToForeground() {
@@ -1227,11 +1304,12 @@ public class XmppConnectionService extends Service {
Log.d(Config.LOGTAG, "app switched into background");
}
- public void connectMultiModeConversations(Account account) {
+ private void connectMultiModeConversations(Account account) {
List<Conversation> conversations = getConversations();
for (Conversation conversation : conversations) {
if ((conversation.getMode() == Conversation.MODE_MULTI)
&& (conversation.getAccount() == account)) {
+ conversation.resetMucOptions();
joinMuc(conversation);
}
}
@@ -1251,29 +1329,18 @@ public class XmppConnectionService extends Service {
PresencePacket packet = new PresencePacket();
packet.setFrom(conversation.getAccount().getJid());
packet.setTo(joinJid);
- Element x = new Element("x");
- x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
+ Element x = packet.addChild("x","http://jabber.org/protocol/muc");
if (conversation.getMucOptions().getPassword() != null) {
- Element password = x.addChild("password");
- password.setContent(conversation.getMucOptions().getPassword());
+ x.addChild("password").setContent(conversation.getMucOptions().getPassword());
}
+ x.addChild("history").setAttribute("since",PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted()));
String sig = account.getPgpSignature();
if (sig != null) {
packet.addChild("status").setContent("online");
packet.addChild("x", "jabber:x:signed").setContent(sig);
}
- if (conversation.getMessages().size() != 0) {
- final SimpleDateFormat mDateFormat = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
- mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- Date date = new Date(conversation.getLatestMessage()
- .getTimeSent() + 1000);
- x.addChild("history").setAttribute("since",
- mDateFormat.format(date));
- }
- packet.addChild(x);
sendPresencePacket(account, packet);
- if (!joinJid.equals(conversation.getContactJid())) {
+ if (!joinJid.equals(conversation.getJid())) {
conversation.setContactJid(joinJid);
databaseBackend.updateConversation(conversation);
}
@@ -1315,7 +1382,7 @@ public class XmppConnectionService extends Service {
@Override
public void onFailure() {
- callback.error(R.string.nick_in_use,conversation);
+ callback.error(R.string.nick_in_use, conversation);
}
});
@@ -1349,14 +1416,14 @@ public class XmppConnectionService extends Service {
account.pendingConferenceLeaves.remove(conversation);
if (account.getStatus() == Account.State.ONLINE) {
PresencePacket packet = new PresencePacket();
- packet.setTo(conversation.getContactJid());
+ packet.setTo(conversation.getJid());
packet.setFrom(conversation.getAccount().getJid());
packet.setAttribute("type", "unavailable");
sendPresencePacket(conversation.getAccount(), packet);
conversation.getMucOptions().setOffline();
conversation.deregisterWithBookmark();
Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
- + ": leaving muc " + conversation.getContactJid());
+ + ": leaving muc " + conversation.getJid());
} else {
account.pendingConferenceLeaves.add(conversation);
}
@@ -1381,8 +1448,8 @@ public class XmppConnectionService extends Service {
return null;
}
- public void createAdhocConference(final Account account, final List<Jid> jids, final UiCallback<Conversation> callback) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": creating adhoc conference with "+ jids.toString());
+ public void createAdhocConference(final Account account, final Iterable<Jid> jids, final UiCallback<Conversation> callback) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString());
if (account.getStatus() == Account.State.ONLINE) {
try {
String server = findConferenceServer(account);
@@ -1434,7 +1501,7 @@ public class XmppConnectionService extends Service {
public void pushConferenceConfiguration(final Conversation conversation,final Bundle options, final OnConferenceOptionsPushed callback) {
IqPacket request = new IqPacket(IqPacket.TYPE_GET);
- request.setTo(conversation.getContactJid().toBareJid());
+ request.setTo(conversation.getJid().toBareJid());
request.query("http://jabber.org/protocol/muc#owner");
sendIqPacket(conversation.getAccount(),request,new OnIqPacketReceived() {
@Override
@@ -1448,7 +1515,7 @@ public class XmppConnectionService extends Service {
}
data.submit();
IqPacket set = new IqPacket(IqPacket.TYPE_SET);
- set.setTo(conversation.getContactJid().toBareJid());
+ set.setTo(conversation.getJid().toBareJid());
set.query("http://jabber.org/protocol/muc#owner").addChild(data);
sendIqPacket(account, set, new OnIqPacketReceived() {
@Override
@@ -1486,7 +1553,7 @@ public class XmppConnectionService extends Service {
if (conversation.endOtrIfNeeded()) {
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": ended otr session with "
- + conversation.getContactJid());
+ + conversation.getJid());
}
}
}
@@ -1528,36 +1595,35 @@ public class XmppConnectionService extends Service {
}
public void onOtrSessionEstablished(Conversation conversation) {
- Account account = conversation.getAccount();
- List<Message> messages = conversation.getMessages();
- Session otrSession = conversation.getOtrSession();
+ final Account account = conversation.getAccount();
+ final Session otrSession = conversation.getOtrSession();
Log.d(Config.LOGTAG,
account.getJid().toBareJid() + " otr session established with "
- + conversation.getContactJid() + "/"
+ + conversation.getJid() + "/"
+ otrSession.getSessionID().getUserID());
- for (Message msg : messages) {
- if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING)
- && (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
+ conversation.findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
SessionID id = otrSession.getSessionID();
try {
- msg.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
+ message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
} catch (InvalidJidException e) {
- break;
+ return;
}
- if (msg.getType() == Message.TYPE_TEXT) {
- MessagePacket outPacket = mMessageGenerator
- .generateOtrChat(msg, true);
+ if (message.getType() == Message.TYPE_TEXT) {
+ MessagePacket outPacket = mMessageGenerator.generateOtrChat(message, true);
if (outPacket != null) {
- msg.setStatus(Message.STATUS_SEND);
- databaseBackend.updateMessage(msg);
+ message.setStatus(Message.STATUS_SEND);
+ databaseBackend.updateMessage(message);
sendMessagePacket(account, outPacket);
}
- } else if (msg.getType() == Message.TYPE_IMAGE || msg.getType() == Message.TYPE_FILE) {
- mJingleConnectionManager.createNewConnection(msg);
+ } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
+ mJingleConnectionManager.createNewConnection(message);
}
- }
- }
- updateConversationUi();
+ updateConversationUi();
+ }
+ });
}
public boolean renewSymmetricKey(Conversation conversation) {
@@ -1587,17 +1653,17 @@ public class XmppConnectionService extends Service {
return false;
}
- public void pushContactToServer(Contact contact) {
+ public void pushContactToServer(final Contact contact) {
contact.resetOption(Contact.Options.DIRTY_DELETE);
contact.setOption(Contact.Options.DIRTY_PUSH);
- Account account = contact.getAccount();
+ final Account account = contact.getAccount();
if (account.getStatus() == Account.State.ONLINE) {
- boolean ask = contact.getOption(Contact.Options.ASKING);
- boolean sendUpdates = contact
+ final boolean ask = contact.getOption(Contact.Options.ASKING);
+ final boolean sendUpdates = contact
.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
&& contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
- IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
- iq.query("jabber:iq:roster").addChild(contact.asElement());
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+ iq.query(Xmlns.ROSTER).addChild(contact.asElement());
account.getXmppConnection().sendIqPacket(iq, null);
if (sendUpdates) {
sendPresencePacket(account,
@@ -1610,7 +1676,8 @@ public class XmppConnectionService extends Service {
}
}
- public void publishAvatar(Account account, Uri image,
+ public void publishAvatar(final Account account,
+ final Uri image,
final UiCallback<Avatar> callback) {
final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
final int size = Config.AVATAR_SIZE;
@@ -1630,13 +1697,13 @@ public class XmppConnectionService extends Service {
callback.error(R.string.error_saving_avatar, avatar);
return;
}
- IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
+ final IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
this.sendIqPacket(account, packet, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket result) {
if (result.getType() == IqPacket.TYPE_RESULT) {
- IqPacket packet = XmppConnectionService.this.mIqGenerator
+ final IqPacket packet = XmppConnectionService.this.mIqGenerator
.publishAvatarMetadata(avatar);
sendIqPacket(account, packet, new OnIqPacketReceived() {
@@ -1679,7 +1746,7 @@ public class XmppConnectionService extends Service {
@Override
public void onIqPacketReceived(Account account, IqPacket result) {
final String ERROR = account.getJid().toBareJid()
- + ": fetching avatar for " + avatar.owner + " failed ";
+ + ": fetching avatar for " + avatar.owner + " failed ";
if (result.getType() == IqPacket.TYPE_RESULT) {
avatar.image = mIqParser.avatarData(result);
if (avatar.image != null) {
@@ -1693,7 +1760,7 @@ public class XmppConnectionService extends Service {
updateAccountUi();
} else {
Contact contact = account.getRoster()
- .getContact(avatar.owner);
+ .getContact(avatar.owner);
contact.setAvatar(avatar.getFilename());
getAvatarService().clear(contact);
updateConversationUi();
@@ -1769,7 +1836,7 @@ public class XmppConnectionService extends Service {
Account account = contact.getAccount();
if (account.getStatus() == Account.State.ONLINE) {
IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
- Element item = iq.query("jabber:iq:roster").addChild("item");
+ Element item = iq.query(Xmlns.ROSTER).addChild("item");
item.setAttribute("jid", contact.getJid().toString());
item.setAttribute("subscription", "remove");
account.getXmppConnection().sendIqPacket(iq, null);
@@ -1812,12 +1879,13 @@ public class XmppConnectionService extends Service {
public void resetSendingToWaiting(Account account) {
for (Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) {
- for (Message message : conversation.getMessages()) {
- if (message.getType() != Message.TYPE_IMAGE
- && message.getStatus() == Message.STATUS_UNSEND) {
+ conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
markMessage(message, Message.STATUS_WAITING);
- }
- }
+ }
+ });
}
}
}
@@ -1828,7 +1896,7 @@ public class XmppConnectionService extends Service {
return false;
} else {
for (Conversation conversation : getConversations()) {
- if (conversation.getContactJid().equals(recipient)
+ if (conversation.getJid().equals(recipient)
&& conversation.getAccount().equals(account)) {
return markMessage(conversation, uuid, status);
}
@@ -1842,15 +1910,13 @@ public class XmppConnectionService extends Service {
if (uuid == null) {
return false;
} else {
- for (Message message : conversation.getMessages()) {
- if (uuid.equals(message.getUuid())
- || (message.getStatus() >= Message.STATUS_SEND && uuid
- .equals(message.getRemoteMsgId()))) {
- markMessage(message, status);
- return true;
- }
+ Message message = conversation.findSentMessageWithUuid(uuid);
+ if (message!=null) {
+ markMessage(message,status);
+ return true;
+ } else {
+ return false;
}
- return false;
}
}
@@ -1904,6 +1970,12 @@ public class XmppConnectionService extends Service {
}
}
+ public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
+ if (mOnUpdateBlocklist != null) {
+ mOnUpdateBlocklist.OnUpdateBlocklist(status);
+ }
+ }
+
public void updateMucRosterUi() {
if (mOnMucRosterUpdate != null) {
mOnMucRosterUpdate.onMucRosterUpdate();
@@ -1928,29 +2000,22 @@ public class XmppConnectionService extends Service {
return null;
}
- public void markRead(Conversation conversation, boolean calledByUi) {
+ public void markRead(final Conversation conversation) {
mNotificationService.clear(conversation);
- final Message markable = conversation.getLatestMarkableMessage();
conversation.markRead();
- if (confirmMessages() && markable != null && markable.getRemoteMsgId() != null && calledByUi) {
+ }
+
+ public void sendReadMarker(final Conversation conversation) {
+ final Message markable = conversation.getLatestMarkableMessage();
+ this.markRead(conversation);
+ if (confirmMessages() && markable != null && markable.getRemoteMsgId() != null) {
Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()+ ": sending read marker to " + markable.getCounterpart().toString());
Account account = conversation.getAccount();
final Jid to = markable.getCounterpart();
MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId());
this.sendMessagePacket(conversation.getAccount(),packet);
}
- if (!calledByUi) {
- updateConversationUi();
- }
- }
-
- public void failWaitingOtrMessages(Conversation conversation) {
- for (Message message : conversation.getMessages()) {
- if (message.getEncryption() == Message.ENCRYPTION_OTR
- && message.getStatus() == Message.STATUS_WAITING) {
- markMessage(message, Message.STATUS_SEND_FAILED);
- }
- }
+ updateConversationUi();
}
public SecureRandom getRNG() {
@@ -1969,14 +2034,6 @@ public class XmppConnectionService extends Service {
return this.mBitmapCache;
}
- public void replyWithNotAcceptable(Account account, MessagePacket packet) {
- if (account.getStatus() == Account.State.ONLINE) {
- MessagePacket error = this.mMessageGenerator
- .generateNotAcceptable(packet);
- sendMessagePacket(account, error);
- }
- }
-
public void syncRosterToDisk(final Account account) {
new Thread(new Runnable() {
@@ -1989,12 +2046,12 @@ public class XmppConnectionService extends Service {
}
public List<String> getKnownHosts() {
- List<String> hosts = new ArrayList<>();
- for (Account account : getAccounts()) {
+ final List<String> hosts = new ArrayList<>();
+ for (final Account account : getAccounts()) {
if (!hosts.contains(account.getServer().toString())) {
hosts.add(account.getServer().toString());
}
- for (Contact contact : account.getRoster().getContacts()) {
+ for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInRoster()) {
final String server = contact.getServer().toString();
if (server != null && !hosts.contains(server)) {
@@ -2007,10 +2064,10 @@ public class XmppConnectionService extends Service {
}
public List<String> getKnownConferenceHosts() {
- ArrayList<String> mucServers = new ArrayList<>();
- for (Account account : accounts) {
+ final ArrayList<String> mucServers = new ArrayList<>();
+ for (final Account account : accounts) {
if (account.getXmppConnection() != null) {
- String server = account.getXmppConnection().getMucServer();
+ final String server = account.getXmppConnection().getMucServer();
if (server != null && !mucServers.contains(server)) {
mucServers.add(server);
}
@@ -2033,9 +2090,8 @@ public class XmppConnectionService extends Service {
}
}
- public void sendIqPacket(Account account, IqPacket packet,
- OnIqPacketReceived callback) {
- XmppConnection connection = account.getXmppConnection();
+ public void sendIqPacket(final Account account, final IqPacket packet, final PacketReceived callback) {
+ final XmppConnection connection = account.getXmppConnection();
if (connection != null) {
connection.sendIqPacket(packet, callback);
}
@@ -2053,10 +2109,16 @@ public class XmppConnectionService extends Service {
return this.mIqGenerator;
}
+ public IqParser getIqParser() { return this.mIqParser; }
+
public JingleConnectionManager getJingleConnectionManager() {
return this.mJingleConnectionManager;
}
+ public MessageArchiveService getMessageArchiveService() {
+ return this.mMessageArchiveService;
+ }
+
public List<Contact> findContacts(Jid jid) {
ArrayList<Contact> contacts = new ArrayList<>();
for (Account account : getAccounts()) {
@@ -2078,8 +2140,8 @@ public class XmppConnectionService extends Service {
return this.mHttpConnectionManager;
}
- public void resendFailedMessages(Message message) {
- List<Message> messages = new ArrayList<>();
+ public void resendFailedMessages(final Message message) {
+ final Collection<Message> messages = new ArrayList<>();
Message current = message;
while (current.getStatus() == Message.STATUS_SEND_FAILED) {
messages.add(current);
@@ -2089,12 +2151,23 @@ public class XmppConnectionService extends Service {
break;
}
}
- for (Message msg : messages) {
+ for (final Message msg : messages) {
markMessage(msg, Message.STATUS_WAITING);
this.resendMessage(msg);
}
}
+ public void clearConversationHistory(final Conversation conversation) {
+ conversation.clearMessages();
+ conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ databaseBackend.deleteMessagesInConversation(conversation);
+ }
+ }).start();
+ }
+
public interface OnConversationUpdate {
public void onConversationUpdate();
}
@@ -2121,4 +2194,35 @@ public class XmppConnectionService extends Service {
return XmppConnectionService.this;
}
}
+
+ public void sendBlockRequest(final Blockable blockable) {
+ if (blockable != null && blockable.getBlockedJid() != null) {
+ final Jid jid = blockable.getBlockedJid();
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid), new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ account.getBlocklist().add(jid);
+ updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ }
+ }
+ });
+ }
+ }
+
+ public void sendUnblockRequest(final Blockable blockable) {
+ if (blockable != null && blockable.getJid() != null) {
+ final Jid jid = blockable.getBlockedJid();
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ account.getBlocklist().remove(jid);
+ updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ }
+ }
+ });
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/ui/AbstractSearchableListItemActivity.java b/src/main/java/eu/siacs/conversations/ui/AbstractSearchableListItemActivity.java
new file mode 100644
index 00000000..1a9fc95c
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/AbstractSearchableListItemActivity.java
@@ -0,0 +1,124 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.ui.adapter.ListItemAdapter;
+
+public abstract class AbstractSearchableListItemActivity extends XmppActivity {
+ private ListView mListView;
+ private final List<ListItem> listItems = new ArrayList<>();
+ private ArrayAdapter<ListItem> mListItemsAdapter;
+
+ private EditText mSearchEditText;
+
+ private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
+
+ @Override
+ public boolean onMenuItemActionExpand(final MenuItem item) {
+ mSearchEditText.post(new Runnable() {
+
+ @Override
+ public void run() {
+ mSearchEditText.requestFocus();
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mSearchEditText,
+ InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(final MenuItem item) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ mSearchEditText.setText("");
+ filterContacts();
+ return true;
+ }
+ };
+
+ private final TextWatcher mSearchTextWatcher = new TextWatcher() {
+
+ @Override
+ public void afterTextChanged(final Editable editable) {
+ filterContacts(editable.toString());
+ }
+
+ @Override
+ public void beforeTextChanged(final CharSequence s, final int start, final int count,
+ final int after) {
+ }
+
+ @Override
+ public void onTextChanged(final CharSequence s, final int start, final int before,
+ final int count) {
+ }
+ };
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ public List<ListItem> getListItems() {
+ return listItems;
+ }
+
+ public EditText getSearchEditText() {
+ return mSearchEditText;
+ }
+
+ public ArrayAdapter<ListItem> getListItemAdapter() {
+ return mListItemsAdapter;
+ }
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_choose_contact);
+ mListView = (ListView) findViewById(R.id.choose_contact_list);
+ mListView.setFastScrollEnabled(true);
+ mListItemsAdapter = new ListItemAdapter(this, listItems);
+ mListView.setAdapter(mListItemsAdapter);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.choose_contact, menu);
+ final MenuItem menuSearchView = menu.findItem(R.id.action_search);
+ final View mSearchView = menuSearchView.getActionView();
+ mSearchEditText = (EditText) mSearchView
+ .findViewById(R.id.search_field);
+ mSearchEditText.addTextChangedListener(mSearchTextWatcher);
+ menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
+ return true;
+ }
+
+ protected void filterContacts() {
+ filterContacts(null);
+ }
+
+ protected abstract void filterContacts(final String needle);
+
+ @Override
+ void onBackendConnected() {
+ filterContacts();
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
new file mode 100644
index 00000000..9cf7e9f8
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
@@ -0,0 +1,41 @@
+package eu.siacs.conversations.ui;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Blockable;
+import eu.siacs.conversations.services.XmppConnectionService;
+
+public final class BlockContactDialog {
+ public static void show(final Context context,
+ final XmppConnectionService xmppConnectionService,
+ final Blockable blockable) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final boolean isBlocked = blockable.isBlocked();
+ builder.setNegativeButton(R.string.cancel, null);
+
+ if (blockable.getJid().isDomainJid() || blockable.getAccount().isBlocked(blockable.getJid().toDomainJid())) {
+ builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
+ builder.setMessage(context.getResources().getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text,
+ blockable.getJid().toDomainJid()));
+ } else {
+ builder.setTitle(isBlocked ? R.string.action_unblock_contact : R.string.action_block_contact);
+ builder.setMessage(context.getResources().getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text,
+ blockable.getJid().toBareJid()));
+ }
+ builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ if (isBlocked) {
+ xmppConnectionService.sendUnblockRequest(blockable);
+ } else {
+ xmppConnectionService.sendBlockRequest(blockable);
+ }
+ }
+ });
+ builder.create().show();
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
new file mode 100644
index 00000000..13d7f4fc
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
@@ -0,0 +1,75 @@
+package eu.siacs.conversations.ui;
+
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.View;
+import android.widget.AdapterView;
+
+import java.util.Collections;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class BlocklistActivity extends AbstractSearchableListItemActivity implements OnUpdateBlocklist {
+
+ private Account account = null;
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+
+ @Override
+ public boolean onItemLongClick(final AdapterView<?> parent,
+ final View view,
+ final int position,
+ final long id) {
+ BlockContactDialog.show(parent.getContext(), xmppConnectionService,(Contact) getListItems().get(position));
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public void onBackendConnected() {
+ for (final Account account : xmppConnectionService.getAccounts()) {
+ if (account.getJid().toString().equals(getIntent().getStringExtra("account"))) {
+ this.account = account;
+ break;
+ }
+ }
+ filterContacts();
+ }
+
+ @Override
+ protected void filterContacts(final String needle) {
+ getListItems().clear();
+ if (account != null) {
+ for (final Jid jid : account.getBlocklist()) {
+ final Contact contact = account.getRoster().getContact(jid);
+ if (contact.match(needle) && contact.isBlocked()) {
+ getListItems().add(contact);
+ }
+ }
+ Collections.sort(getListItems());
+ }
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ getListItemAdapter().notifyDataSetChanged();
+ }
+ });
+ }
+
+ @Override
+ public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
+ final Editable editable = getSearchEditText().getText();
+ if (editable != null) {
+ filterContacts(editable.toString());
+ } else {
+ filterContacts();
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
new file mode 100644
index 00000000..54c064c6
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
@@ -0,0 +1,107 @@
+package eu.siacs.conversations.ui;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class ChangePasswordActivity extends XmppActivity implements XmppConnectionService.OnAccountPasswordChanged {
+
+ private Button mChangePasswordButton;
+ private View.OnClickListener mOnChangePasswordButtonClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mAccount != null) {
+ final String currentPassword = mCurrentPassword.getText().toString();
+ final String newPassword = mNewPassword.getText().toString();
+ final String newPasswordConfirm = mNewPasswordConfirm.getText().toString();
+ if (!currentPassword.equals(mAccount.getPassword())) {
+ mCurrentPassword.requestFocus();
+ mCurrentPassword.setError(getString(R.string.account_status_unauthorized));
+ } else if (!newPassword.equals(newPasswordConfirm)) {
+ mNewPasswordConfirm.requestFocus();
+ mNewPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
+ } else if (newPassword.trim().isEmpty()) {
+ mNewPassword.requestFocus();
+ mNewPassword.setError(getString(R.string.password_should_not_be_empty));
+ } else {
+ mCurrentPassword.setError(null);
+ mNewPassword.setError(null);
+ mNewPasswordConfirm.setError(null);
+ xmppConnectionService.updateAccountPasswordOnServer(mAccount, newPassword, ChangePasswordActivity.this);
+ mChangePasswordButton.setEnabled(false);
+ mChangePasswordButton.setTextColor(getSecondaryTextColor());
+ mChangePasswordButton.setText(R.string.updating);
+ }
+ }
+ }
+ };
+ private EditText mCurrentPassword;
+ private EditText mNewPassword;
+ private EditText mNewPasswordConfirm;
+ private Account mAccount;
+
+ @Override
+ void onBackendConnected() {
+ try {
+ final String jid = getIntent() == null ? null : getIntent().getStringExtra("account");
+ if (jid != null) {
+ this.mAccount = xmppConnectionService.findAccountByJid(Jid.fromString(jid));
+ }
+ } catch (final InvalidJidException ignored) {
+
+ }
+
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_change_password);
+ Button mCancelButton = (Button) findViewById(R.id.left_button);
+ mCancelButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ finish();
+ }
+ });
+ this.mChangePasswordButton = (Button) findViewById(R.id.right_button);
+ this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
+ this.mCurrentPassword = (EditText) findViewById(R.id.current_password);
+ this.mNewPassword = (EditText) findViewById(R.id.new_password);
+ this.mNewPasswordConfirm = (EditText) findViewById(R.id.new_password_confirm);
+ }
+
+ @Override
+ public void onPasswordChangeSucceeded() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ChangePasswordActivity.this,R.string.password_changed,Toast.LENGTH_LONG).show();
+ finish();
+ }
+ });
+ }
+
+ @Override
+ public void onPasswordChangeFailed() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mNewPassword.setError(getString(R.string.could_not_change_password));
+ mChangePasswordButton.setEnabled(true);
+ mChangePasswordButton.setTextColor(getPrimaryTextColor());
+ mChangePasswordButton.setText(R.string.change_password);
+ }
+ });
+
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
index e7254933..70b353c6 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
@@ -1,101 +1,33 @@
package eu.siacs.conversations.ui;
-import java.util.ArrayList;
-import java.util.Collections;
-
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.ListView;
-import eu.siacs.conversations.R;
+
+import java.util.Collections;
+
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem;
-import eu.siacs.conversations.ui.adapter.ListItemAdapter;
-
-public class ChooseContactActivity extends XmppActivity {
-
- private ListView mListView;
- private ArrayList<ListItem> contacts = new ArrayList<>();
- private ArrayAdapter<ListItem> mContactsAdapter;
-
- private EditText mSearchEditText;
-
- private TextWatcher mSearchTextWatcher = new TextWatcher() {
-
- @Override
- public void afterTextChanged(Editable editable) {
- filterContacts(editable.toString());
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
- }
- };
-
- private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
-
- @Override
- public boolean onMenuItemActionExpand(MenuItem item) {
- mSearchEditText.post(new Runnable() {
-
- @Override
- public void run() {
- mSearchEditText.requestFocus();
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mSearchEditText,
- InputMethodManager.SHOW_IMPLICIT);
- }
- });
-
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(MenuItem item) {
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- mSearchEditText.setText("");
- filterContacts(null);
- return true;
- }
- };
+public class ChooseContactActivity extends AbstractSearchableListItemActivity {
@Override
- public void onCreate(Bundle savedInstanceState) {
+ public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_choose_contact);
- mListView = (ListView) findViewById(R.id.choose_contact_list);
- mListView.setFastScrollEnabled(true);
- mContactsAdapter = new ListItemAdapter(this, contacts);
- mListView.setAdapter(mContactsAdapter);
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
- public void onItemClick(AdapterView<?> arg0, View arg1,
- int position, long arg3) {
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
+ public void onItemClick(final AdapterView<?> parent, final View view,
+ final int position, final long id) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
InputMethodManager.HIDE_IMPLICIT_ONLY);
- Intent request = getIntent();
- Intent data = new Intent();
- ListItem mListItem = contacts.get(position);
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ final ListItem mListItem = getListItems().get(position);
data.putExtra("contact", mListItem.getJid().toString());
String account = request.getStringExtra("account");
if (account == null && mListItem instanceof Contact) {
@@ -108,38 +40,21 @@ public class ChooseContactActivity extends XmppActivity {
finish();
}
});
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.choose_contact, menu);
- MenuItem menuSearchView = menu.findItem(R.id.action_search);
- View mSearchView = menuSearchView.getActionView();
- mSearchEditText = (EditText) mSearchView
- .findViewById(R.id.search_field);
- mSearchEditText.addTextChangedListener(mSearchTextWatcher);
- menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
- return true;
}
- @Override
- void onBackendConnected() {
- filterContacts(null);
- }
-
- protected void filterContacts(String needle) {
- this.contacts.clear();
- for (Account account : xmppConnectionService.getAccounts()) {
+ protected void filterContacts(final String needle) {
+ getListItems().clear();
+ for (final Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
- for (Contact contact : account.getRoster().getContacts()) {
+ for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInRoster() && contact.match(needle)) {
- this.contacts.add(contact);
+ getListItems().add(contact);
}
}
}
}
- Collections.sort(this.contacts);
- mContactsAdapter.notifyDataSetChanged();
+ Collections.sort(getListItems());
+ getListItemAdapter().notifyDataSetChanged();
}
-
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index 2e36c545..eeb015f3 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -157,8 +157,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override
public void onValueEdited(String value) {
MessagePacket packet = xmppConnectionService
- .getMessageGenerator().conferenceSubject(
- mConversation, value);
+ .getMessageGenerator().conferenceSubject(
+ mConversation, value);
xmppConnectionService.sendMessagePacket(
mConversation.getAccount(), packet);
}
@@ -191,7 +191,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override
protected String getShareableUri() {
if (mConversation != null) {
- return "xmpp:" + mConversation.getContactJid().toBareJid().toString() + "?join";
+ return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
} else {
return "";
}
@@ -202,7 +202,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
Account account = mConversation.getAccount();
- if (account.hasBookmarkFor(mConversation.getContactJid().toBareJid())) {
+ if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
menuItemSaveBookmark.setVisible(false);
menuItemDeleteBookmark.setVisible(true);
} else {
@@ -263,9 +263,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
protected void saveAsBookmark() {
Account account = mConversation.getAccount();
- Bookmark bookmark = new Bookmark(account, mConversation.getContactJid().toBareJid());
- if (!mConversation.getContactJid().isBareJid()) {
- bookmark.setNick(mConversation.getContactJid().getResourcepart());
+ Bookmark bookmark = new Bookmark(account, mConversation.getJid().toBareJid());
+ if (!mConversation.getJid().isBareJid()) {
+ bookmark.setNick(mConversation.getJid().getResourcepart());
}
bookmark.setAutojoin(true);
account.getBookmarks().add(bookmark);
@@ -288,7 +288,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
if (uuid != null) {
this.mConversation = xmppConnectionService
- .findConversationByUuid(uuid);
+ .findConversationByUuid(uuid);
if (this.mConversation != null) {
populateView();
}
@@ -297,11 +297,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
private void populateView() {
mAccountJid.setText(getString(R.string.using_account, mConversation
- .getAccount().getJid().toBareJid()));
+ .getAccount().getJid().toBareJid()));
mYourPhoto.setImageBitmap(avatarService().get(
- mConversation.getAccount(), getPixel(48)));
+ mConversation.getAccount(), getPixel(48)));
setTitle(mConversation.getName());
- mFullJid.setText(mConversation.getContactJid().toBareJid().toString());
+ mFullJid.setText(mConversation.getJid().toBareJid().toString());
mYourNick.setText(mConversation.getMucOptions().getActualNick());
mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
if (mConversation.getMucOptions().online()) {
@@ -338,7 +338,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
registerForContextMenu(view);
view.setTag(user);
TextView name = (TextView) view
- .findViewById(R.id.contact_display_name);
+ .findViewById(R.id.contact_display_name);
TextView key = (TextView) view.findViewById(R.id.key);
TextView role = (TextView) view.findViewById(R.id.contact_jid);
if (user.getPgpKeyId() != 0) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index 4259371a..657ae75b 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -37,11 +37,13 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
+import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
-public class ContactDetailsActivity extends XmppActivity implements OnAccountUpdate, OnRosterUpdate {
+public class ContactDetailsActivity extends XmppActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist {
public static final String ACTION_VIEW_CONTACT = "view_contact";
private Contact contact;
@@ -50,7 +52,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
@Override
public void onClick(DialogInterface dialog, int which) {
ContactDetailsActivity.this.xmppConnectionService
- .deleteContactOnServer(contact);
+ .deleteContactOnServer(contact);
ContactDetailsActivity.this.finish();
}
};
@@ -58,14 +60,14 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
@Override
public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
+ boolean isChecked) {
if (isChecked) {
if (contact
.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
xmppConnectionService.sendPresencePacket(contact
- .getAccount(),
+ .getAccount(),
xmppConnectionService.getPresenceGenerator()
- .sendPresenceUpdatesTo(contact));
+ .sendPresenceUpdatesTo(contact));
} else {
contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
}
@@ -73,7 +75,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
xmppConnectionService.sendPresencePacket(contact.getAccount(),
xmppConnectionService.getPresenceGenerator()
- .stopPresenceUpdatesTo(contact));
+ .stopPresenceUpdatesTo(contact));
}
}
};
@@ -81,15 +83,15 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
@Override
public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
+ boolean isChecked) {
if (isChecked) {
xmppConnectionService.sendPresencePacket(contact.getAccount(),
xmppConnectionService.getPresenceGenerator()
- .requestPresenceUpdatesFrom(contact));
+ .requestPresenceUpdatesFrom(contact));
} else {
xmppConnectionService.sendPresencePacket(contact.getAccount(),
xmppConnectionService.getPresenceGenerator()
- .stopPresenceUpdatesFrom(contact));
+ .stopPresenceUpdatesFrom(contact));
}
}
};
@@ -127,7 +129,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
ContactDetailsActivity.this);
builder.setTitle(getString(R.string.action_add_phone_book));
builder.setMessage(getString(R.string.add_phone_book_text,
- contact.getJid()));
+ contact.getJid()));
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.add), addToPhonebook);
builder.create().show();
@@ -166,7 +168,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
}
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
try {
@@ -188,15 +190,17 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
keys = (LinearLayout) findViewById(R.id.details_contact_keys);
tags = (LinearLayout) findViewById(R.id.tags);
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setDisplayHomeAsUpEnabled(true);
+ if (getActionBar() != null) {
+ getActionBar().setHomeButtonEnabled(true);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
}
@Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
+ public boolean onOptionsItemSelected(final MenuItem menuItem) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(getString(R.string.cancel), null);
switch (menuItem.getItemId()) {
@@ -205,11 +209,11 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
break;
case R.id.action_delete_contact:
builder.setTitle(getString(R.string.action_delete_contact))
- .setMessage(
- getString(R.string.remove_contact_text,
- contact.getJid()))
- .setPositiveButton(getString(R.string.delete),
- removeFromRoster).create().show();
+ .setMessage(
+ getString(R.string.remove_contact_text,
+ contact.getJid()))
+ .setPositiveButton(getString(R.string.delete),
+ removeFromRoster).create().show();
break;
case R.id.action_edit_contact:
if (contact.getSystemAccount() == null) {
@@ -219,7 +223,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
public void onValueEdited(String value) {
contact.setServerName(value);
ContactDetailsActivity.this.xmppConnectionService
- .pushContactToServer(contact);
+ .pushContactToServer(contact);
populateView();
}
});
@@ -285,7 +289,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange);
lastseen.setText(UIHelper.lastseen(getApplicationContext(),
- contact.lastseen.time));
+ contact.lastseen.time));
if (contact.getPresences().size() > 1) {
contactJidTv.setText(contact.getJid() + " ("
@@ -294,7 +298,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
contactJidTv.setText(contact.getJid().toString());
}
accountJidTv.setText(getString(R.string.using_account, contact
- .getAccount().getJid().toBareJid()));
+ .getAccount().getJid().toBareJid()));
prepareContactBadge(badge, contact);
if (contact.getSystemAccount() == null) {
badge.setOnClickListener(onBadgeClick);
@@ -309,10 +313,10 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
TextView key = (TextView) view.findViewById(R.id.key);
TextView keyType = (TextView) view.findViewById(R.id.key_type);
ImageButton remove = (ImageButton) view
- .findViewById(R.id.button_remove);
+ .findViewById(R.id.button_remove);
remove.setVisibility(View.VISIBLE);
keyType.setText("OTR Fingerprint");
- key.setText(otrFingerprint);
+ key.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
keys.addView(view);
remove.setOnClickListener(new OnClickListener() {
@@ -334,7 +338,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
@Override
public void onClick(View v) {
PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
- .getPgpEngine();
+ .getPgpEngine();
if (pgp != null) {
PendingIntent intent = pgp.getIntentForKey(contact);
if (intent != null) {
@@ -363,8 +367,8 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
} else {
tags.setVisibility(View.VISIBLE);
tags.removeAllViewsInLayout();
- for(ListItem.Tag tag : tagList) {
- TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tags,false);
+ for(final ListItem.Tag tag : tagList) {
+ final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tags,false);
tv.setText(tag.getName());
tv.setBackgroundColor(tag.getColor());
tags.addView(tv);
@@ -393,8 +397,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
public void onClick(DialogInterface dialog, int which) {
if (contact.deleteOtrFingerprint(fingerprint)) {
populateView();
- xmppConnectionService.syncRosterToDisk(contact
- .getAccount());
+ xmppConnectionService.syncRosterToDisk(contact.getAccount());
}
}
@@ -406,7 +409,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
public void onBackendConnected() {
if ((accountJid != null) && (contactJid != null)) {
Account account = xmppConnectionService
- .findAccountByJid(accountJid);
+ .findAccountByJid(accountJid);
if (account == null) {
return;
}
@@ -414,4 +417,15 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
populateView();
}
}
+
+ @Override
+ public void OnUpdateBlocklist(final Status status) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ populateView();
+ }
+ });
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index 5b0fa562..5cfc1116 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -9,13 +9,13 @@ import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
+import android.media.MediaActionSound;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.v4.widget.SlidingPaneLayout;
import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
-import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -28,10 +28,14 @@ import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.Toast;
+import net.java.otr4j.session.SessionStatus;
+
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
@@ -40,27 +44,23 @@ import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdat
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.utils.ExceptionHelper;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
-public class ConversationActivity extends XmppActivity implements
- OnAccountUpdate, OnConversationUpdate, OnRosterUpdate {
+public class ConversationActivity extends XmppActivity
+ implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist {
public static final String VIEW_CONVERSATION = "viewConversation";
public static final String CONVERSATION = "conversationUuid";
public static final String TEXT = "text";
public static final String NICK = "nick";
- public static final String PRESENCE = "eu.siacs.conversations.presence";
public static final int REQUEST_SEND_MESSAGE = 0x0201;
public static final int REQUEST_DECRYPT_PGP = 0x0202;
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
- private static final int REQUEST_ATTACH_IMAGE_DIALOG = 0x0203;
- private static final int REQUEST_IMAGE_CAPTURE = 0x0204;
- private static final int REQUEST_RECORD_AUDIO = 0x0205;
- private static final int REQUEST_SEND_PGP_IMAGE = 0x0206;
- private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0208;
private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
+ private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
private static final String STATE_PANEL_OPEN = "state_panel_open";
private static final String STATE_PENDING_URI = "state_pending_uri";
@@ -81,6 +81,8 @@ public class ConversationActivity extends XmppActivity implements
private Toast prepareFileToast;
+ private boolean mActivityPaused = false;
+
public List<Conversation> getConversationList() {
return this.conversationList;
@@ -94,10 +96,6 @@ public class ConversationActivity extends XmppActivity implements
this.mSelectedConversation = conversation;
}
- public ListView getConversationListView() {
- return this.listView;
- }
-
public void showConversationsOverview() {
if (mContentView instanceof SlidingPaneLayout) {
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
@@ -144,12 +142,12 @@ public class ConversationActivity extends XmppActivity implements
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {mOpenConverstaion = savedInstanceState.getString(
- STATE_OPEN_CONVERSATION, null);
- mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
- String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
- if (pending != null) {
- mPendingImageUri = Uri.parse(pending);
- }
+ STATE_OPEN_CONVERSATION, null);
+ mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
+ String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
+ if (pending != null) {
+ mPendingImageUri = Uri.parse(pending);
+ }
}
setContentView(R.layout.fragment_conversations_overview);
@@ -172,7 +170,7 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void onItemClick(AdapterView<?> arg0, View clickedView,
- int position, long arg3) {
+ int position, long arg3) {
if (getSelectedConversation() != conversationList.get(position)) {
setSelectedConversation(conversationList.get(position));
ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
@@ -188,7 +186,7 @@ public class ConversationActivity extends XmppActivity implements
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
mSlidingPaneLayout.setParallaxDistance(150);
mSlidingPaneLayout
- .setShadowResource(R.drawable.es_slidingpane_shadow);
+ .setShadowResource(R.drawable.es_slidingpane_shadow);
mSlidingPaneLayout.setSliderFadeColor(0);
mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
@@ -199,7 +197,7 @@ public class ConversationActivity extends XmppActivity implements
hideKeyboard();
if (xmppConnectionServiceBound) {
xmppConnectionService.getNotificationService()
- .setOpenConversation(null);
+ .setOpenConversation(null);
}
closeContextMenu();
}
@@ -244,7 +242,7 @@ public class ConversationActivity extends XmppActivity implements
if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
ab.setTitle(conversation.getName());
} else {
- ab.setTitle(conversation.getContactJid().toBareJid().toString());
+ ab.setTitle(conversation.getJid().toBareJid().toString());
}
} else {
ab.setDisplayHomeAsUpEnabled(false);
@@ -258,28 +256,33 @@ public class ConversationActivity extends XmppActivity implements
this.updateActionBarTitle();
this.invalidateOptionsMenu();
if (xmppConnectionServiceBound) {
- xmppConnectionService.getNotificationService().setOpenConversation(getSelectedConversation());
- if (!getSelectedConversation().isRead()) {
- xmppConnectionService.markRead(getSelectedConversation(), true);
- listView.invalidateViews();
- }
+ final Conversation conversation = getSelectedConversation();
+ xmppConnectionService.getNotificationService().setOpenConversation(conversation);
+ sendReadMarkerIfNecessary(conversation);
+ }
+ }
+
+ public void sendReadMarkerIfNecessary(final Conversation conversation) {
+ if (!mActivityPaused && !conversation.isRead()) {
+ xmppConnectionService.sendReadMarker(conversation);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.conversations, menu);
- MenuItem menuSecure = menu.findItem(R.id.action_security);
- MenuItem menuArchive = menu.findItem(R.id.action_archive);
- MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
- MenuItem menuContactDetails = menu
- .findItem(R.id.action_contact_details);
- MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
- MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
- MenuItem menuAdd = menu.findItem(R.id.action_add);
- MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
- MenuItem menuMute = menu.findItem(R.id.action_mute);
- MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
+ final MenuItem menuSecure = menu.findItem(R.id.action_security);
+ final MenuItem menuArchive = menu.findItem(R.id.action_archive);
+ final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
+ final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details);
+ final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
+ final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
+ final MenuItem menuAdd = menu.findItem(R.id.action_add);
+ final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
+ final MenuItem menuMute = menu.findItem(R.id.action_mute);
+ final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
+ final MenuItem menuBlock = menu.findItem(R.id.action_block);
+ final MenuItem menuUnblock = menu.findItem(R.id.action_unblock);
if (isConversationsOverviewVisable()
&& isConversationsOverviewHideable()) {
@@ -292,19 +295,33 @@ public class ConversationActivity extends XmppActivity implements
menuClearHistory.setVisible(false);
menuMute.setVisible(false);
menuUnmute.setVisible(false);
+ menuBlock.setVisible(false);
+ menuUnblock.setVisible(false);
} else {
menuAdd.setVisible(!isConversationsOverviewHideable());
if (this.getSelectedConversation() != null) {
if (this.getSelectedConversation().getLatestMessage()
.getEncryption() != Message.ENCRYPTION_NONE) {
menuSecure.setIcon(R.drawable.ic_action_secure);
- }
+ }
if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
menuContactDetails.setVisible(false);
menuAttach.setVisible(false);
+ menuBlock.setVisible(false);
+ menuUnblock.setVisible(false);
} else {
menuMucDetails.setVisible(false);
menuInviteContact.setTitle(R.string.conference_with);
+ if (this.getSelectedConversation().isBlocked()) {
+ menuBlock.setVisible(false);
+ } else {
+ menuUnblock.setVisible(false);
+ }
+ final Account account = this.getSelectedConversation().getAccount();
+ if (!(account.isOnlineAndConnected() && account.getXmppConnection().getFeatures().blocking())) {
+ menuBlock.setVisible(false);
+ menuUnblock.setVisible(false);
+ }
}
if (this.getSelectedConversation().isMuted()) {
menuMute.setVisible(false);
@@ -321,33 +338,37 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void onPresenceSelected() {
- if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) {
- mPendingImageUri = xmppConnectionService.getFileBackend()
- .getTakePhotoUri();
- Intent takePictureIntent = new Intent(
- MediaStore.ACTION_IMAGE_CAPTURE);
- takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
- mPendingImageUri);
- if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
- startActivityForResult(takePictureIntent,
- REQUEST_IMAGE_CAPTURE);
+ Intent intent = new Intent();
+ boolean chooser = false;
+ switch (attachmentChoice) {
+ case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ chooser = true;
+ break;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ mPendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+ intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT,mPendingImageUri);
+ break;
+ case ATTACHMENT_CHOICE_CHOOSE_FILE:
+ chooser = true;
+ intent.setType("*/*");
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ break;
+ case ATTACHMENT_CHOICE_RECORD_VOICE:
+ intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
+ break;
+ }
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ if (chooser) {
+ startActivityForResult(
+ Intent.createChooser(intent,getString(R.string.perform_action_with)),
+ attachmentChoice);
+ } else {
+ startActivityForResult(intent, attachmentChoice);
}
- } else if (attachmentChoice == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
- Intent attachFileIntent = new Intent();
- attachFileIntent.setType("image/*");
- attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
- Intent chooser = Intent.createChooser(attachFileIntent,
- getString(R.string.attach_file));
- startActivityForResult(chooser, REQUEST_ATTACH_IMAGE_DIALOG);
- } else if (attachmentChoice == ATTACHMENT_CHOICE_CHOOSE_FILE) {
- Intent attachFileIntent = new Intent();
- //attachFileIntent.setType("file/*");
- attachFileIntent.setType("*/*");
- attachFileIntent.addCategory(Intent.CATEGORY_OPENABLE);
- attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
- Intent chooser = Intent.createChooser(attachFileIntent,
- getString(R.string.attach_file));
- startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG);
}
}
});
@@ -364,7 +385,7 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void userInputRequried(PendingIntent pi,
- Contact contact) {
+ Contact contact) {
ConversationActivity.this.runIntent(pi,
attachmentChoice);
}
@@ -381,18 +402,18 @@ public class ConversationActivity extends XmppActivity implements
});
} else {
final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
- .findFragmentByTag("conversation");
+ .findFragmentByTag("conversation");
if (fragment != null) {
fragment.showNoPGPKeyDialog(false,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
- int which) {
+ int which) {
conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
+ .setNextEncryption(Message.ENCRYPTION_NONE);
xmppConnectionService.databaseBackend
- .updateConversation(conversation);
+ .updateConversation(conversation);
selectPresenceToAttachFile(attachmentChoice);
}
});
@@ -402,7 +423,7 @@ public class ConversationActivity extends XmppActivity implements
showInstallPgpDialog();
}
} else if (getSelectedConversation().getNextEncryption(
- forceEncryption()) == Message.ENCRYPTION_NONE) {
+ forceEncryption()) == Message.ENCRYPTION_NONE) {
selectPresenceToAttachFile(attachmentChoice);
} else {
selectPresenceToAttachFile(attachmentChoice);
@@ -410,7 +431,7 @@ public class ConversationActivity extends XmppActivity implements
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onOptionsItemSelected(final MenuItem item) {
if (item.getItemId() == android.R.id.home) {
showConversationsOverview();
return true;
@@ -455,6 +476,12 @@ public class ConversationActivity extends XmppActivity implements
case R.id.action_unmute:
unmuteConversation(getSelectedConversation());
break;
+ case R.id.action_block:
+ BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
+ break;
+ case R.id.action_unblock:
+ BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
+ break;
default:
break;
}
@@ -483,7 +510,7 @@ public class ConversationActivity extends XmppActivity implements
View dialogView = getLayoutInflater().inflate(
R.layout.dialog_clear_history, null);
final CheckBox endConversationCheckBox = (CheckBox) dialogView
- .findViewById(R.id.end_conversation_checkbox);
+ .findViewById(R.id.end_conversation_checkbox);
builder.setView(dialogView);
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.delete_messages),
@@ -491,10 +518,12 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void onClick(DialogInterface dialog, int which) {
- ConversationActivity.this.xmppConnectionService
- .clearConversationHistory(conversation);
+ ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation);
if (endConversationCheckBox.isChecked()) {
endConversation(conversation);
+ } else {
+ updateConversationList();
+ ConversationActivity.this.mConversationFragment.updateMessages();
}
}
});
@@ -508,28 +537,68 @@ public class ConversationActivity extends XmppActivity implements
}
PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
attachFilePopup.inflate(R.menu.attachment_choices);
- attachFilePopup
- .setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
+ attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
+ }
+ attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.attach_choose_picture:
- attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
- break;
- case R.id.attach_take_picture:
- attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
- break;
- case R.id.attach_record_voice:
- attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
- break;
- }
- return false;
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.attach_choose_picture:
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
+ break;
+ case R.id.attach_take_picture:
+ attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
+ break;
+ case R.id.attach_choose_file:
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
+ break;
+ case R.id.attach_record_voice:
+ attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
+ break;
}
- });
+ return false;
+ }
+ });
attachFilePopup.show();
}
+ public void verifyOtrSessionDialog(final Conversation conversation, View view) {
+ if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
+ Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show();
+ return;
+ }
+ if (view == null) {
+ return;
+ }
+ PopupMenu popup = new PopupMenu(this, view);
+ popup.inflate(R.menu.verification_choices);
+ popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class);
+ intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
+ intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
+ intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
+ switch (menuItem.getItemId()) {
+ case R.id.scan_fingerprint:
+ intent.putExtra("mode",VerifyOTRActivity.MODE_SCAN_FINGERPRINT);
+ break;
+ case R.id.ask_question:
+ intent.putExtra("mode",VerifyOTRActivity.MODE_ASK_QUESTION);
+ break;
+ case R.id.manual_verification:
+ intent.putExtra("mode",VerifyOTRActivity.MODE_MANUAL_VERIFICATION);
+ break;
+ }
+ startActivity(intent);
+ return true;
+ }
+ });
+ popup.show();
+ }
+
protected void selectEncryptionDialog(final Conversation conversation) {
View menuItemView = findViewById(R.id.action_security);
if (menuItemView == null) {
@@ -537,7 +606,7 @@ public class ConversationActivity extends XmppActivity implements
}
PopupMenu popup = new PopupMenu(this, menuItemView);
final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
- .findFragmentByTag("conversation");
+ .findFragmentByTag("conversation");
if (fragment != null) {
popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@@ -557,7 +626,7 @@ public class ConversationActivity extends XmppActivity implements
if (conversation.getAccount().getKeys()
.has("pgp_signature")) {
conversation
- .setNextEncryption(Message.ENCRYPTION_PGP);
+ .setNextEncryption(Message.ENCRYPTION_PGP);
item.setChecked(true);
} else {
announcePgp(conversation.getAccount(),
@@ -572,7 +641,7 @@ public class ConversationActivity extends XmppActivity implements
break;
}
xmppConnectionService.databaseBackend
- .updateConversation(conversation);
+ .updateConversation(conversation);
fragment.updateChatMsgHint();
return true;
}
@@ -597,11 +666,11 @@ public class ConversationActivity extends XmppActivity implements
break;
case Message.ENCRYPTION_PGP:
popup.getMenu().findItem(R.id.encryption_choice_pgp)
- .setChecked(true);
+ .setChecked(true);
break;
default:
popup.getMenu().findItem(R.id.encryption_choice_none)
- .setChecked(true);
+ .setChecked(true);
break;
}
popup.show();
@@ -617,17 +686,17 @@ public class ConversationActivity extends XmppActivity implements
new OnClickListener() {
@Override
- public void onClick(DialogInterface dialog, int which) {
- long till;
+ public void onClick(final DialogInterface dialog, final int which) {
+ final long till;
if (durations[which] == -1) {
till = Long.MAX_VALUE;
} else {
till = SystemClock.elapsedRealtime()
- + (durations[which] * 1000);
+ + (durations[which] * 1000);
}
conversation.setMutedTill(till);
ConversationActivity.this.xmppConnectionService.databaseBackend
- .updateConversation(conversation);
+ .updateConversation(conversation);
updateConversationList();
ConversationActivity.this.mConversationFragment.updateMessages();
invalidateOptionsMenu();
@@ -676,6 +745,28 @@ public class ConversationActivity extends XmppActivity implements
}
@Override
+ public void onPause() {
+ super.onPause();
+ this.mActivityPaused = true;
+ if (this.xmppConnectionServiceBound) {
+ this.xmppConnectionService.getNotificationService().setIsInForeground(false);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ }
+ this.mActivityPaused = false;
+ if (this.xmppConnectionServiceBound) {
+ this.xmppConnectionService.getNotificationService().setIsInForeground(true);
+ }
+ }
+
+ @Override
public void onSaveInstanceState(final Bundle savedInstanceState) {
Conversation conversation = getSelectedConversation();
if (conversation != null) {
@@ -692,6 +783,7 @@ public class ConversationActivity extends XmppActivity implements
@Override
void onBackendConnected() {
+ this.xmppConnectionService.getNotificationService().setIsInForeground(true);
updateConversationList();
if (xmppConnectionService.getAccounts().size() == 0) {
startActivity(new Intent(this, EditAccountActivity.class));
@@ -752,11 +844,11 @@ public class ConversationActivity extends XmppActivity implements
}
private void selectConversationByUuid(String uuid) {
- for (Conversation aConversationList : conversationList) {
- if (aConversationList.getUuid().equals(uuid)) {
- setSelectedConversation(aConversationList);
- }
- }
+ for (Conversation aConversationList : conversationList) {
+ if (aConversationList.getUuid().equals(uuid)) {
+ setSelectedConversation(aConversationList);
+ }
+ }
}
@Override
@@ -767,50 +859,35 @@ public class ConversationActivity extends XmppActivity implements
@Override
protected void onActivityResult(int requestCode, int resultCode,
- final Intent data) {
+ final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_DECRYPT_PGP) {
mConversationFragment.hideSnackbar();
mConversationFragment.updateMessages();
- } else if (requestCode == REQUEST_ATTACH_IMAGE_DIALOG) {
+ } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
mPendingImageUri = data.getData();
if (xmppConnectionServiceBound) {
- attachImageToConversation(getSelectedConversation(),
- mPendingImageUri);
+ attachImageToConversation(getSelectedConversation(),mPendingImageUri);
mPendingImageUri = null;
}
- } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
+ } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
mPendingFileUri = data.getData();
if (xmppConnectionServiceBound) {
- attachFileToConversation(getSelectedConversation(),
- mPendingFileUri);
+ attachFileToConversation(getSelectedConversation(),mPendingFileUri);
mPendingFileUri = null;
}
- } else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
-
- } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
- attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
- } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
- attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
- } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(getSelectedConversation().getAccount(),
- getSelectedConversation());
- } else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
- // encryptTextMessage();
- } else if (requestCode == REQUEST_IMAGE_CAPTURE && mPendingImageUri != null) {
+ } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO && mPendingImageUri != null) {
if (xmppConnectionServiceBound) {
- attachImageToConversation(getSelectedConversation(),
- mPendingImageUri);
+ attachImageToConversation(getSelectedConversation(),mPendingImageUri);
mPendingImageUri = null;
}
- Intent intent = new Intent(
- Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(mPendingImageUri);
sendBroadcast(intent);
}
} else {
- if (requestCode == REQUEST_IMAGE_CAPTURE) {
+ if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
mPendingImageUri = null;
}
}
@@ -848,10 +925,8 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void userInputRequried(PendingIntent pi,
- Message object) {
+ Message object) {
hidePrepareFileToast();
- ConversationActivity.this.runIntent(pi,
- ConversationActivity.REQUEST_SEND_PGP_IMAGE);
}
@Override
@@ -881,7 +956,7 @@ public class ConversationActivity extends XmppActivity implements
public void updateConversationList() {
xmppConnectionService
- .populateWithOrderedConversations(conversationList);
+ .populateWithOrderedConversations(conversationList);
listAdapter.notifyDataSetChanged();
}
@@ -899,7 +974,7 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void userInputRequried(PendingIntent pi,
- Message message) {
+ Message message) {
ConversationActivity.this.runIntent(pi,
ConversationActivity.REQUEST_SEND_MESSAGE);
}
@@ -951,7 +1026,7 @@ public class ConversationActivity extends XmppActivity implements
updateConversationList();
if (conversationList.size() == 0) {
startActivity(new Intent(getApplicationContext(),
- StartConversationActivity.class));
+ StartConversationActivity.class));
finish();
}
ConversationActivity.this.mConversationFragment.updateMessages();
@@ -964,12 +1039,31 @@ public class ConversationActivity extends XmppActivity implements
public void onRosterUpdate() {
runOnUiThread(new Runnable() {
- @Override
- public void run() {
- updateConversationList();
- ConversationActivity.this.mConversationFragment.updateMessages();
- updateActionBarTitle();
- }
- });
+ @Override
+ public void run() {
+ updateConversationList();
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ updateActionBarTitle();
+ }
+ });
+ }
+
+ @Override
+ public void OnUpdateBlocklist(Status status) {
+ invalidateOptionsMenu();
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ }
+ });
+ }
+
+ public void unblockConversation(final Blockable conversation) {
+ xmppConnectionService.sendUnblockRequest(conversation);
+ }
+
+ public void blockConversation(final Blockable conversation) {
+ xmppConnectionService.sendBlockRequest(conversation);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 0edc6b6f..93a4cc7e 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -103,7 +103,9 @@ public class ConversationFragment extends Fragment {
private RelativeLayout snackbar;
private TextView snackbarMessage;
private TextView snackbarAction;
- private boolean messagesLoaded = false;
+ private boolean messagesLoaded = true;
+ private Toast messageLoaderToast;
+
private OnScrollListener mOnScrollListener = new OnScrollListener() {
@Override
@@ -114,20 +116,69 @@ public class ConversationFragment extends Fragment {
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
+ int visibleItemCount, int totalItemCount) {
synchronized (ConversationFragment.this.messageList) {
- if (firstVisibleItem == 0 && messagesLoaded) {
+ if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent();
messagesLoaded = false;
- int size = activity.xmppConnectionService.loadMoreMessages(conversation, timestamp);
- ConversationFragment.this.messageList.clear();
- ConversationFragment.this.messageList.addAll(conversation.getMessages());
- updateStatusMessages();
- messageListAdapter.notifyDataSetChanged();
- if (size != 0) {
- messagesLoaded = true;
- }
- messagesView.setSelectionFromTop(size + 1, 0);
+ activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
+ @Override
+ public void onMoreMessagesLoaded(final int count, Conversation conversation) {
+ if (ConversationFragment.this.conversation != conversation) {
+ return;
+ }
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final int oldPosition = messagesView.getFirstVisiblePosition();
+ View v = messagesView.getChildAt(0);
+ final int pxOffset = (v == null) ? 0 : v.getTop();
+ ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
+ updateStatusMessages();
+ messageListAdapter.notifyDataSetChanged();
+ if (count != 0) {
+ final int newPosition = oldPosition + count;
+ int offset = 0;
+ try {
+ Message tmpMessage = messageList.get(newPosition);
+
+ while(tmpMessage.wasMergedIntoPrevious()) {
+ offset++;
+ tmpMessage = tmpMessage.prev();
+ }
+ } catch (final IndexOutOfBoundsException ignored) {
+
+ }
+ messagesView.setSelectionFromTop(newPosition - offset, pxOffset);
+ messagesLoaded = true;
+ if (messageLoaderToast != null) {
+ messageLoaderToast.cancel();
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void informUser(final int resId) {
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (messageLoaderToast != null) {
+ messageLoaderToast.cancel();
+ }
+ if (ConversationFragment.this.conversation != conversation) {
+ return;
+ }
+ messageLoaderToast = Toast.makeText(activity,resId,Toast.LENGTH_LONG);
+ messageLoaderToast.show();
+ }
+ });
+
+ }
+ });
+
}
}
}
@@ -153,13 +204,7 @@ public class ConversationFragment extends Fragment {
@Override
public void onClick(View v) {
- if (conversation.getOtrFingerprint() != null) {
- Intent intent = new Intent(getActivity(), VerifyOTRActivity.class);
- intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
- intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
- intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
- startActivity(intent);
- }
+ activity.verifyOtrSessionDialog(conversation,v);
}
};
private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<>();
@@ -170,7 +215,7 @@ public class ConversationFragment extends Fragment {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEND) {
InputMethodManager imm = (InputMethodManager) v.getContext()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
sendMessage();
return true;
@@ -213,7 +258,7 @@ public class ConversationFragment extends Fragment {
}
Message message = new Message(conversation, mEditMessage.getText()
.toString(), conversation.getNextEncryption(activity
- .forceEncryption()));
+ .forceEncryption()));
if (conversation.getMode() == Conversation.MODE_MULTI) {
if (conversation.getNextCounterpart() != null) {
message.setCounterpart(conversation.getNextCounterpart());
@@ -234,13 +279,13 @@ public class ConversationFragment extends Fragment {
if (conversation.getMode() == Conversation.MODE_MULTI
&& conversation.getNextCounterpart() != null) {
this.mEditMessage.setHint(getString(
- R.string.send_private_message_to,
- conversation.getNextCounterpart().getResourcepart()));
+ R.string.send_private_message_to,
+ conversation.getNextCounterpart().getResourcepart()));
} else {
switch (conversation.getNextEncryption(activity.forceEncryption())) {
case Message.ENCRYPTION_NONE:
mEditMessage
- .setHint(getString(R.string.send_plain_text_message));
+ .setHint(getString(R.string.send_plain_text_message));
break;
case Message.ENCRYPTION_OTR:
mEditMessage.setHint(getString(R.string.send_otr_message));
@@ -256,7 +301,7 @@ public class ConversationFragment extends Fragment {
@Override
public View onCreateView(final LayoutInflater inflater,
- ViewGroup container, Bundle savedInstanceState) {
+ ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_conversation,
container, false);
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
@@ -289,49 +334,51 @@ public class ConversationFragment extends Fragment {
messageListAdapter = new MessageAdapter((ConversationActivity) getActivity(), this.messageList);
messageListAdapter.setOnContactPictureClicked(new OnContactPictureClicked() {
- @Override
- public void onContactPictureClicked(Message message) {
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- if (message.getCounterpart() != null) {
- if (!message.getCounterpart().isBareJid()) {
- highlightInConference(message.getCounterpart().getResourcepart());
- } else {
- highlightInConference(message.getCounterpart().toString());
- }
- }
+ @Override
+ public void onContactPictureClicked(Message message) {
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (message.getCounterpart() != null) {
+ if (!message.getCounterpart().isBareJid()) {
+ highlightInConference(message.getCounterpart().getResourcepart());
} else {
- Contact contact = message.getConversation()
- .getContact();
- if (contact.showInRoster()) {
- activity.switchToContactDetails(contact);
- } else {
- activity.showAddToRosterDialog(message
- .getConversation());
- }
+ highlightInConference(message.getCounterpart().toString());
}
+ }
+ } else {
+ Contact contact = message.getConversation()
+ .getContact();
+ if (contact.showInRoster()) {
+ activity.switchToContactDetails(contact);
} else {
- Account account = message.getConversation().getAccount();
- Intent intent = new Intent(activity, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- startActivity(intent);
+ activity.showAddToRosterDialog(message
+ .getConversation());
}
}
- });
+ } else {
+ Account account = message.getConversation().getAccount();
+ Intent intent = new Intent(activity, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+ }
+ });
messageListAdapter
- .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
+ .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
- @Override
- public void onContactPictureLongClicked(Message message) {
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- if (message.getCounterpart() != null) {
- privateMessageWith(message.getCounterpart());
- }
+ @Override
+ public void onContactPictureLongClicked(Message message) {
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (message.getCounterpart() != null) {
+ privateMessageWith(message.getCounterpart());
}
}
+ } else {
+ activity.showQrCode();
}
- });
+ }
+ });
messagesView.setAdapter(messageListAdapter);
registerForContextMenu(messagesView);
@@ -341,7 +388,7 @@ public class ConversationFragment extends Fragment {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
+ ContextMenuInfo menuInfo) {
synchronized (this.messageList) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
@@ -351,7 +398,8 @@ public class ConversationFragment extends Fragment {
}
private void populateContextMenu(ContextMenu menu) {
- if (this.selectedMessage.getType() != Message.TYPE_STATUS) {
+ final Message m = this.selectedMessage;
+ if (m.getType() != Message.TYPE_STATUS) {
activity.getMenuInflater().inflate(R.menu.message_context, menu);
menu.setHeaderTitle(R.string.message_options);
MenuItem copyText = menu.findItem(R.id.copy_text);
@@ -360,29 +408,26 @@ public class ConversationFragment extends Fragment {
MenuItem copyUrl = menu.findItem(R.id.copy_url);
MenuItem downloadImage = menu.findItem(R.id.download_image);
MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
- if (this.selectedMessage.getType() != Message.TYPE_TEXT
- || this.selectedMessage.getDownloadable() != null) {
+ if (m.getType() != Message.TYPE_TEXT || m.getDownloadable() != null) {
copyText.setVisible(false);
}
- if (this.selectedMessage.getType() != Message.TYPE_IMAGE
- || this.selectedMessage.getDownloadable() != null) {
+ if (m.getType() != Message.TYPE_IMAGE || m.getDownloadable() != null) {
shareImage.setVisible(false);
}
- if (this.selectedMessage.getStatus() != Message.STATUS_SEND_FAILED) {
+ if (m.getStatus() != Message.STATUS_SEND_FAILED) {
sendAgain.setVisible(false);
}
- if ((this.selectedMessage.getType() != Message.TYPE_IMAGE && this.selectedMessage
- .getDownloadable() == null)
- || this.selectedMessage.getImageParams().url == null) {
+ if ((m.getType() != Message.TYPE_IMAGE && m.getDownloadable() == null)
+ || m.getImageParams().url == null) {
copyUrl.setVisible(false);
}
- if (this.selectedMessage.getType() != Message.TYPE_TEXT
- || this.selectedMessage.getDownloadable() != null
- || !this.selectedMessage.bodyContainsDownloadable()) {
+ if (m.getType() != Message.TYPE_TEXT
+ || m.getDownloadable() != null
+ || !m.bodyContainsDownloadable()) {
downloadImage.setVisible(false);
}
- if (this.selectedMessage.getDownloadable() == null
- || this.selectedMessage.getDownloadable() instanceof DownloadablePlaceholder) {
+ if (!((m.getDownloadable() != null && !(m.getDownloadable() instanceof DownloadablePlaceholder))
+ || (m.isFileOrImage() && m.getStatus() == Message.STATUS_WAITING))) {
cancelTransmission.setVisible(false);
}
}
@@ -419,16 +464,16 @@ public class ConversationFragment extends Fragment {
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM,
activity.xmppConnectionService.getFileBackend()
- .getJingleFileUri(message));
+ .getJingleFileUri(message));
shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setType("image/webp");
activity.startActivity(Intent.createChooser(shareIntent,
- getText(R.string.share_with)));
+ getText(R.string.share_with)));
}
private void copyText(Message message) {
if (activity.copyTextToClipboard(message.getMergedBody(),
- R.string.message_text)) {
+ R.string.message_text)) {
Toast.makeText(activity, R.string.message_copied_to_clipboard,
Toast.LENGTH_SHORT).show();
}
@@ -448,21 +493,23 @@ public class ConversationFragment extends Fragment {
private void copyUrl(Message message) {
if (activity.copyTextToClipboard(
- message.getImageParams().url.toString(), R.string.image_url)) {
+ message.getImageParams().url.toString(), R.string.image_url)) {
Toast.makeText(activity, R.string.url_copied_to_clipboard,
Toast.LENGTH_SHORT).show();
- }
+ }
}
private void downloadImage(Message message) {
activity.xmppConnectionService.getHttpConnectionManager()
- .createNewConnection(message);
+ .createNewConnection(message);
}
private void cancelTransmission(Message message) {
Downloadable downloadable = message.getDownloadable();
if (downloadable!=null) {
downloadable.cancel();
+ } else {
+ activity.xmppConnectionService.markMessage(message,Message.STATUS_SEND_FAILED);
}
}
@@ -478,9 +525,9 @@ public class ConversationFragment extends Fragment {
mEditMessage.getText().insert(0, nick + ": ");
} else {
if (mEditMessage.getText().charAt(
- mEditMessage.getSelectionStart() - 1) != ' ') {
+ mEditMessage.getSelectionStart() - 1) != ' ') {
nick = " " + nick;
- }
+ }
mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
nick + " ");
}
@@ -501,6 +548,7 @@ public class ConversationFragment extends Fragment {
}
if (this.conversation != null) {
this.conversation.setNextMessage(mEditMessage.getText().toString());
+ this.conversation.trim();
}
this.activity = (ConversationActivity) getActivity();
this.conversation = conversation;
@@ -511,8 +559,13 @@ public class ConversationFragment extends Fragment {
}
this.mEditMessage.setText("");
this.mEditMessage.append(this.conversation.getNextMessage());
- this.messagesView.invalidate();
+ this.messagesView.invalidateViews();
updateMessages();
+ this.messagesLoaded = true;
+ int size = this.messageList.size();
+ if (size > 0) {
+ messagesView.setSelection(size - 1);
+ }
}
public void updateMessages() {
@@ -524,12 +577,30 @@ public class ConversationFragment extends Fragment {
final ConversationActivity activity = (ConversationActivity) getActivity();
if (this.conversation != null) {
final Contact contact = this.conversation.getContact();
- if (this.conversation.isMuted()) {
+ if (this.conversation.isBlocked()) {
+ showSnackbar(R.string.contact_blocked, R.string.unblock,
+ new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ v.setVisibility(View.INVISIBLE);
+ }
+ });
+ if (conversation.isDomainBlocked()) {
+ BlockContactDialog.show(getActivity(), ((ConversationActivity) getActivity()).xmppConnectionService, conversation);
+ } else {
+ ((ConversationActivity) getActivity()).unblockConversation(conversation);
+ }
+ }
+ });
+ } else if (this.conversation.isMuted()) {
showSnackbar(R.string.notifications_disabled, R.string.enable,
new OnClickListener() {
@Override
- public void onClick(View v) {
+ public void onClick(final View v) {
activity.unmuteConversation(conversation);
}
});
@@ -542,7 +613,7 @@ public class ConversationFragment extends Fragment {
@Override
public void onClick(View v) {
activity.xmppConnectionService
- .createContact(contact);
+ .createContact(contact);
activity.switchToContactDetails(contact);
}
});
@@ -579,31 +650,24 @@ public class ConversationFragment extends Fragment {
default:
break;
}
- }
- this.messageList.clear();
- if (this.conversation.getMessages().size() == 0) {
- messagesLoaded = false;
- } else {
- this.messageList.addAll(this.conversation.getMessages());
- messagesLoaded = true;
- for (Message message : this.messageList) {
- if (message.getEncryption() == Message.ENCRYPTION_PGP
- && (message.getStatus() == Message.STATUS_RECEIVED || message
+ }
+ conversation.populateWithMessages(ConversationFragment.this.messageList);
+ for (Message message : this.messageList) {
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ && (message.getStatus() == Message.STATUS_RECEIVED || message
.getStatus() >= Message.STATUS_SEND)
- && message.getDownloadable() == null) {
- if (!mEncryptedMessages.contains(message)) {
- mEncryptedMessages.add(message);
- }
+ && message.getDownloadable() == null) {
+ if (!mEncryptedMessages.contains(message)) {
+ mEncryptedMessages.add(message);
}
- }
- decryptNext();
- updateStatusMessages();
+ }
}
+ decryptNext();
+ updateStatusMessages();
this.messageListAdapter.notifyDataSetChanged();
updateChatMsgHint();
if (!activity.isConversationsOverviewVisable() || !activity.isConversationsOverviewHideable()) {
- activity.xmppConnectionService.markRead(conversation, true);
- activity.updateConversationList();
+ activity.sendReadMarkerIfNecessary(conversation);
}
this.updateSendButton();
}
@@ -667,44 +731,44 @@ public class ConversationFragment extends Fragment {
switch (c.getContact().getMostAvailableStatus()) {
case Presences.CHAT:
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_online);
+ .setImageResource(R.drawable.ic_action_send_now_online);
break;
case Presences.ONLINE:
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_online);
+ .setImageResource(R.drawable.ic_action_send_now_online);
break;
case Presences.AWAY:
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_away);
+ .setImageResource(R.drawable.ic_action_send_now_away);
break;
case Presences.XA:
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_away);
+ .setImageResource(R.drawable.ic_action_send_now_away);
break;
case Presences.DND:
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_dnd);
+ .setImageResource(R.drawable.ic_action_send_now_dnd);
break;
default:
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_offline);
+ .setImageResource(R.drawable.ic_action_send_now_offline);
break;
}
} else if (c.getMode() == Conversation.MODE_MULTI) {
if (c.getMucOptions().online()) {
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_online);
+ .setImageResource(R.drawable.ic_action_send_now_online);
} else {
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_offline);
+ .setImageResource(R.drawable.ic_action_send_now_offline);
}
} else {
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_offline);
+ .setImageResource(R.drawable.ic_action_send_now_offline);
}
} else {
this.mSendButton
- .setImageResource(R.drawable.ic_action_send_now_offline);
+ .setImageResource(R.drawable.ic_action_send_now_offline);
}
}
@@ -716,8 +780,7 @@ public class ConversationFragment extends Fragment {
return;
} else {
if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
- this.messageList.add(i + 1,
- Message.createStatusMessage(conversation));
+ this.messageList.add(i + 1,Message.createStatusMessage(conversation));
return;
}
}
@@ -728,19 +791,30 @@ public class ConversationFragment extends Fragment {
protected void makeFingerprintWarning() {
if (conversation.smpRequested()) {
- showSnackbar(R.string.smp_requested, R.string.verify, clickToVerify);
+ showSnackbar(R.string.smp_requested, R.string.verify, new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(activity, VerifyOTRActivity.class);
+ intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
+ intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
+ intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
+ intent.putExtra("mode",VerifyOTRActivity.MODE_ANSWER_QUESTION);
+ startActivity(intent);
+ }
+ });
} else if (conversation.hasValidOtrSession() && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
&& (!conversation.isOtrFingerprintVerified())) {
showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
- }
+ }
}
- protected void showSnackbar(int message, int action,
- OnClickListener clickListener) {
+ protected void showSnackbar(final int message, final int action,
+ final OnClickListener clickListener) {
snackbar.setVisibility(View.VISIBLE);
snackbar.setOnClickListener(null);
snackbarMessage.setText(message);
snackbarMessage.setOnClickListener(null);
+ snackbarAction.setVisibility(View.VISIBLE);
snackbarAction.setText(action);
snackbarAction.setOnClickListener(clickListener);
}
@@ -767,7 +841,7 @@ public class ConversationFragment extends Fragment {
@Override
public void userInputRequried(PendingIntent pi,
- Contact contact) {
+ Contact contact) {
activity.runIntent(
pi,
ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
@@ -791,11 +865,11 @@ public class ConversationFragment extends Fragment {
@Override
public void onClick(DialogInterface dialog,
- int which) {
+ int which) {
conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
+ .setNextEncryption(Message.ENCRYPTION_NONE);
xmppService.databaseBackend
- .updateConversation(conversation);
+ .updateConversation(conversation);
message.setEncryption(Message.ENCRYPTION_NONE);
xmppService.sendMessage(message);
messageSent();
@@ -806,9 +880,9 @@ public class ConversationFragment extends Fragment {
if (conversation.getMucOptions().pgpKeysInUse()) {
if (!conversation.getMucOptions().everybodyHasKeys()) {
Toast warning = Toast
- .makeText(getActivity(),
- R.string.missing_public_keys,
- Toast.LENGTH_LONG);
+ .makeText(getActivity(),
+ R.string.missing_public_keys,
+ Toast.LENGTH_LONG);
warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
warning.show();
}
@@ -820,12 +894,12 @@ public class ConversationFragment extends Fragment {
@Override
public void onClick(DialogInterface dialog,
- int which) {
+ int which) {
conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
+ .setNextEncryption(Message.ENCRYPTION_NONE);
message.setEncryption(Message.ENCRYPTION_NONE);
xmppService.databaseBackend
- .updateConversation(conversation);
+ .updateConversation(conversation);
xmppService.sendMessage(message);
messageSent();
}
@@ -838,7 +912,7 @@ public class ConversationFragment extends Fragment {
}
public void showNoPGPKeyDialog(boolean plural,
- DialogInterface.OnClickListener listener) {
+ DialogInterface.OnClickListener listener) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIconAttribute(android.R.attr.alertDialogIcon);
if (plural) {
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index 8fad66cf..ea45b75e 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -19,6 +19,7 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.Toast;
@@ -28,13 +29,12 @@ import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper;
-import eu.siacs.conversations.utils.Validator;
import eu.siacs.conversations.xmpp.XmppConnection.Features;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
-public class EditAccountActivity extends XmppActivity implements OnAccountUpdate {
+public class EditAccountActivity extends XmppActivity implements OnAccountUpdate{
private AutoCompleteTextView mAccountJid;
private EditText mPassword;
@@ -42,10 +42,15 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private CheckBox mRegisterNew;
private Button mCancelButton;
private Button mSaveButton;
+ private TableLayout mMoreTable;
private LinearLayout mStats;
private TextView mServerInfoSm;
+ private TextView mServerInfoRosterVersion;
private TextView mServerInfoCarbons;
+ private TextView mServerInfoMam;
+ private TextView mServerInfoCSI;
+ private TextView mServerInfoBlocking;
private TextView mServerInfoPep;
private TextView mSessionEst;
private TextView mOtrFingerprint;
@@ -58,22 +63,16 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private boolean mFetchingAvatar = false;
- private OnClickListener mSaveButtonClickListener = new OnClickListener() {
+ private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
@Override
- public void onClick(View v) {
- if (mAccount != null
- && mAccount.getStatus() == Account.State.DISABLED) {
+ public void onClick(final View v) {
+ if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED) {
mAccount.setOption(Account.OPTION_DISABLED, false);
xmppConnectionService.updateAccount(mAccount);
return;
}
- if (!Validator.isValidJid(mAccountJid.getText().toString())) {
- mAccountJid.setError(getString(R.string.invalid_jid));
- mAccountJid.requestFocus();
- return;
- }
- boolean registerNewAccount = mRegisterNew.isChecked();
+ final boolean registerNewAccount = mRegisterNew.isChecked();
final Jid jid;
try {
jid = Jid.fromString(mAccountJid.getText().toString());
@@ -82,37 +81,41 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
mAccountJid.requestFocus();
return;
}
- String password = mPassword.getText().toString();
- String passwordConfirm = mPasswordConfirm.getText().toString();
+ if (jid.isDomainJid()) {
+ mAccountJid.setError(getString(R.string.invalid_jid));
+ mAccountJid.requestFocus();
+ return;
+ }
+ final String password = mPassword.getText().toString();
+ final String passwordConfirm = mPasswordConfirm.getText().toString();
if (registerNewAccount) {
if (!password.equals(passwordConfirm)) {
- mPasswordConfirm
- .setError(getString(R.string.passwords_do_not_match));
+ mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
mPasswordConfirm.requestFocus();
return;
}
}
if (mAccount != null) {
+ try {
+ mAccount.setUsername(jid.hasLocalpart() ? jid.getLocalpart() : "");
+ mAccount.setServer(jid.getDomainpart());
+ } catch (final InvalidJidException ignored) {
+ return;
+ }
mAccount.setPassword(password);
- try {
- mAccount.setUsername(jid.hasLocalpart() ? jid.getLocalpart() : "");
- mAccount.setServer(jid.getDomainpart());
- } catch (final InvalidJidException ignored) {
- }
mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
xmppConnectionService.updateAccount(mAccount);
} else {
- try {
- if (xmppConnectionService.findAccountByJid(Jid.fromString(mAccountJid.getText().toString())) != null) {
- mAccountJid
- .setError(getString(R.string.account_already_exists));
- mAccountJid.requestFocus();
- return;
- }
- } catch (InvalidJidException e) {
- return;
- }
- mAccount = new Account(jid.toBareJid(), password);
+ try {
+ if (xmppConnectionService.findAccountByJid(Jid.fromString(mAccountJid.getText().toString())) != null) {
+ mAccountJid.setError(getString(R.string.account_already_exists));
+ mAccountJid.requestFocus();
+ return;
+ }
+ } catch (final InvalidJidException e) {
+ return;
+ }
+ mAccount = new Account(jid.toBareJid(), password);
mAccount.setOption(Account.OPTION_USETLS, true);
mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
@@ -127,82 +130,81 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
};
- private OnClickListener mCancelButtonClickListener = new OnClickListener() {
+ private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
@Override
- public void onClick(View v) {
+ public void onClick(final View v) {
finish();
}
};
- @Override
- public void onAccountUpdate() {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- if (mAccount != null
- && mAccount.getStatus() != Account.State.ONLINE
- && mFetchingAvatar) {
- startActivity(new Intent(getApplicationContext(),
+ @Override
+ public void onAccountUpdate() {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ invalidateOptionsMenu();
+ if (mAccount != null
+ && mAccount.getStatus() != Account.State.ONLINE
+ && mFetchingAvatar) {
+ startActivity(new Intent(getApplicationContext(),
ManageAccountActivity.class));
- finish();
- } else if (jidToEdit == null && mAccount != null
- && mAccount.getStatus() == Account.State.ONLINE) {
- if (!mFetchingAvatar) {
- mFetchingAvatar = true;
- xmppConnectionService.checkForAvatar(mAccount,
- mAvatarFetchCallback);
- }
- } else {
- updateSaveButton();
- }
- if (mAccount != null) {
- updateAccountInformation();
+ finish();
+ } else if (jidToEdit == null && mAccount != null
+ && mAccount.getStatus() == Account.State.ONLINE) {
+ if (!mFetchingAvatar) {
+ mFetchingAvatar = true;
+ xmppConnectionService.checkForAvatar(mAccount,
+ mAvatarFetchCallback);
}
+ } else {
+ updateSaveButton();
}
- });
- }
- private UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
+ if (mAccount != null) {
+ updateAccountInformation();
+ }
+ }
+ });
+ }
+ private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
@Override
- public void userInputRequried(PendingIntent pi, Avatar avatar) {
+ public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
finishInitialSetup(avatar);
}
@Override
- public void success(Avatar avatar) {
+ public void success(final Avatar avatar) {
finishInitialSetup(avatar);
}
@Override
- public void error(int errorCode, Avatar avatar) {
+ public void error(final int errorCode, final Avatar avatar) {
finishInitialSetup(avatar);
}
};
- private TextWatcher mTextWatcher = new TextWatcher() {
+ private final TextWatcher mTextWatcher = new TextWatcher() {
@Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
+ public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
updateSaveButton();
}
@Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
-
+ public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
}
@Override
- public void afterTextChanged(Editable s) {
+ public void afterTextChanged(final Editable s) {
}
};
- private OnClickListener mAvatarClickListener = new OnClickListener() {
+
+ private final OnClickListener mAvatarClickListener = new OnClickListener() {
@Override
- public void onClick(View view) {
- if (mAccount!=null) {
- Intent intent = new Intent(getApplicationContext(),
+ public void onClick(final View view) {
+ if (mAccount != null) {
+ final Intent intent = new Intent(getApplicationContext(),
PublishProfilePictureActivity.class);
intent.putExtra("account", mAccount.getJid().toBareJid().toString());
startActivity(intent);
@@ -215,7 +217,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override
public void run() {
- Intent intent;
+ final Intent intent;
if (avatar != null) {
intent = new Intent(getApplicationContext(),
StartConversationActivity.class);
@@ -232,13 +234,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
protected void updateSaveButton() {
- if (mAccount != null
- && mAccount.getStatus() == Account.State.CONNECTING) {
+ if (mAccount != null && mAccount.getStatus() == Account.State.CONNECTING) {
this.mSaveButton.setEnabled(false);
this.mSaveButton.setTextColor(getSecondaryTextColor());
this.mSaveButton.setText(R.string.account_status_connecting);
- } else if (mAccount != null
- && mAccount.getStatus() == Account.State.DISABLED) {
+ } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED) {
this.mSaveButton.setEnabled(true);
this.mSaveButton.setTextColor(getPrimaryTextColor());
this.mSaveButton.setText(R.string.enable);
@@ -246,8 +246,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mSaveButton.setEnabled(true);
this.mSaveButton.setTextColor(getPrimaryTextColor());
if (jidToEdit != null) {
- if (mAccount != null
- && mAccount.getStatus() == Account.State.ONLINE) {
+ if (mAccount != null && mAccount.isOnlineAndConnected()) {
this.mSaveButton.setText(R.string.save);
if (!accountInfoEdited()) {
this.mSaveButton.setEnabled(false);
@@ -263,10 +262,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
protected boolean accountInfoEdited() {
- return (!this.mAccount.getJid().toBareJid().equals(
- this.mAccountJid.getText().toString()))
- || (!this.mAccount.getPassword().equals(
- this.mPassword.getText().toString()));
+ return (!this.mAccount.getJid().toBareJid().toString().equals(
+ this.mAccountJid.getText().toString()))
+ || (!this.mAccount.getPassword().equals(
+ this.mPassword.getText().toString()));
}
@Override
@@ -279,7 +278,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_account);
this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
@@ -292,7 +291,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
this.mStats = (LinearLayout) findViewById(R.id.stats);
this.mSessionEst = (TextView) findViewById(R.id.session_est);
+ this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
+ this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
+ this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
+ this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
@@ -302,29 +305,41 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mCancelButton = (Button) findViewById(R.id.cancel_button);
this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
- this.mRegisterNew
- .setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- mPasswordConfirm.setVisibility(View.VISIBLE);
- } else {
- mPasswordConfirm.setVisibility(View.GONE);
- }
- updateSaveButton();
- }
- });
+ this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
+ final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(final CompoundButton buttonView,
+ final boolean isChecked) {
+ if (isChecked) {
+ mPasswordConfirm.setVisibility(View.VISIBLE);
+ } else {
+ mPasswordConfirm.setVisibility(View.GONE);
+ }
+ updateSaveButton();
+ }
+ };
+ this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ public boolean onCreateOptionsMenu(final Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.editaccount, menu);
- MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
+ final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
+ final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
+ final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
+ final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
if (mAccount == null) {
showQrCode.setVisible(false);
+ showBlocklist.setVisible(false);
+ showMoreInfo.setVisible(false);
+ changePassword.setVisible(false);
+ } else if (mAccount.getStatus() != Account.State.ONLINE) {
+ showBlocklist.setVisible(false);
+ showMoreInfo.setVisible(false);
+ changePassword.setVisible(false);
+ } else if (!mAccount.getXmppConnection().getFeatures().blocking()) {
+ showBlocklist.setVisible(false);
}
return true;
}
@@ -333,32 +348,38 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
protected void onStart() {
super.onStart();
if (getIntent() != null) {
- try {
- this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
- } catch (final InvalidJidException | NullPointerException ignored) {
- this.jidToEdit = null;
- }
- if (this.jidToEdit != null) {
+ try {
+ this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
+ } catch (final InvalidJidException | NullPointerException ignored) {
+ this.jidToEdit = null;
+ }
+ if (this.jidToEdit != null) {
this.mRegisterNew.setVisibility(View.GONE);
- getActionBar().setTitle(getString(R.string.account_details));
+ if (getActionBar() != null) {
+ getActionBar().setTitle(getString(R.string.account_details));
+ }
} else {
this.mAvatar.setVisibility(View.GONE);
- getActionBar().setTitle(R.string.action_add_account);
+ if (getActionBar() != null) {
+ getActionBar().setTitle(R.string.action_add_account);
+ }
}
}
}
@Override
protected void onBackendConnected() {
- KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
- android.R.layout.simple_list_item_1,
- xmppConnectionService.getKnownHosts());
+ final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
+ android.R.layout.simple_list_item_1,
+ xmppConnectionService.getKnownHosts());
if (this.jidToEdit != null) {
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
updateAccountInformation();
} else if (this.xmppConnectionService.getAccounts().size() == 0) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setDisplayShowHomeEnabled(false);
+ if (getActionBar() != null) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setDisplayShowHomeEnabled(false);
+ }
this.mCancelButton.setEnabled(false);
this.mCancelButton.setTextColor(getSecondaryTextColor());
}
@@ -366,6 +387,27 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
updateSaveButton();
}
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_show_block_list:
+ final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
+ showBlocklistIntent.putExtra("account", mAccount.getJid().toString());
+ startActivity(showBlocklistIntent);
+ break;
+ case R.id.action_server_info_show_more:
+ mMoreTable.setVisibility(item.isChecked() ? View.GONE : View.VISIBLE);
+ item.setChecked(!item.isChecked());
+ break;
+ case R.id.action_change_password_on_server:
+ final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
+ changePasswordIntent.putExtra("account", mAccount.getJid().toString());
+ startActivity(changePasswordIntent);
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
private void updateAccountInformation() {
this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString());
this.mPassword.setText(this.mAccount.getPassword());
@@ -381,18 +423,36 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mRegisterNew.setVisibility(View.GONE);
this.mRegisterNew.setChecked(false);
}
- if (this.mAccount.getStatus() == Account.State.ONLINE
- && !this.mFetchingAvatar) {
+ if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
this.mStats.setVisibility(View.VISIBLE);
- this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(
- getApplicationContext(), this.mAccount.getXmppConnection()
- .getLastSessionEstablished()));
+ this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
+ .getLastSessionEstablished()));
Features features = this.mAccount.getXmppConnection().getFeatures();
+ if (features.rosterVersioning()) {
+ this.mServerInfoRosterVersion.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
+ }
if (features.carbons()) {
this.mServerInfoCarbons.setText(R.string.server_info_available);
} else {
this.mServerInfoCarbons
- .setText(R.string.server_info_unavailable);
+ .setText(R.string.server_info_unavailable);
+ }
+ if (features.mam()) {
+ this.mServerInfoMam.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoMam.setText(R.string.server_info_unavailable);
+ }
+ if (features.csi()) {
+ this.mServerInfoCSI.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoCSI.setText(R.string.server_info_unavailable);
+ }
+ if (features.blocking()) {
+ this.mServerInfoBlocking.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
}
if (features.sm()) {
this.mServerInfoSm.setText(R.string.server_info_available);
@@ -409,21 +469,21 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(fingerprint));
this.mOtrFingerprintToClipboardButton
- .setVisibility(View.VISIBLE);
+ .setVisibility(View.VISIBLE);
this.mOtrFingerprintToClipboardButton
- .setOnClickListener(new View.OnClickListener() {
+ .setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
+ @Override
+ public void onClick(final View v) {
- if (copyTextToClipboard(fingerprint, R.string.otr_fingerprint)) {
- Toast.makeText(
- EditAccountActivity.this,
- R.string.toast_message_otr_fingerprint,
- Toast.LENGTH_SHORT).show();
- }
+ if (copyTextToClipboard(fingerprint, R.string.otr_fingerprint)) {
+ Toast.makeText(
+ EditAccountActivity.this,
+ R.string.toast_message_otr_fingerprint,
+ Toast.LENGTH_SHORT).show();
}
- });
+ }
+ });
} else {
this.mOtrFingerprintBox.setVisibility(View.GONE);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
index 906a16cc..b3ab5ee6 100644
--- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -26,21 +26,25 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
protected Account selectedAccount = null;
- protected List<Account> accountList = new ArrayList<Account>();
+ protected final List<Account> accountList = new ArrayList<>();
protected ListView accountListView;
protected AccountAdapter mAccountAdapter;
- @Override
- public void onAccountUpdate() {
+
+ @Override
+ public void onAccountUpdate() {
+ synchronized (this.accountList) {
accountList.clear();
accountList.addAll(xmppConnectionService.getAccounts());
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- mAccountAdapter.notifyDataSetChanged();
- }
- });
}
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ invalidateOptionsMenu();
+ mAccountAdapter.notifyDataSetChanged();
+ }
+ });
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -91,6 +95,14 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.manageaccounts, menu);
+ MenuItem enableAll = menu.findItem(R.id.action_enable_all);
+ if (!accountsLeftToEnable()) {
+ enableAll.setVisible(false);
+ }
+ MenuItem disableAll = menu.findItem(R.id.action_disable_all);
+ if (!accountsLeftToDisable()) {
+ disableAll.setVisible(false);
+ }
return true;
}
@@ -120,12 +132,18 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.action_add_account:
- startActivity(new Intent(getApplicationContext(),
- EditAccountActivity.class));
- break;
- default:
- break;
+ case R.id.action_add_account:
+ startActivity(new Intent(getApplicationContext(),
+ EditAccountActivity.class));
+ break;
+ case R.id.action_disable_all:
+ disableAllAccounts();
+ break;
+ case R.id.action_enable_all:
+ enableAllAccounts();
+ break;
+ default:
+ break;
}
return super.onOptionsItemSelected(item);
}
@@ -158,6 +176,56 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
startActivity(intent);
}
+ private void disableAllAccounts() {
+ List<Account> list = new ArrayList<>();
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ list.add(account);
+ }
+ }
+ }
+ for(Account account : list) {
+ disableAccount(account);
+ }
+ }
+
+ private boolean accountsLeftToDisable() {
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private boolean accountsLeftToEnable() {
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (account.isOptionSet(Account.OPTION_DISABLED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private void enableAllAccounts() {
+ List<Account> list = new ArrayList<>();
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (account.isOptionSet(Account.OPTION_DISABLED)) {
+ list.add(account);
+ }
+ }
+ }
+ for(Account account : list) {
+ enableAccount(account);
+ }
+ }
+
private void disableAccount(Account account) {
account.setOption(Account.OPTION_DISABLED, true);
xmppConnectionService.updateAccount(account);
diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
index 609dc280..5e770376 100644
--- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
@@ -1,17 +1,5 @@
package eu.siacs.conversations.ui;
-import java.util.ArrayList;
-import java.util.List;
-
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.ui.adapter.ConversationAdapter;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
-import eu.siacs.conversations.xmpp.jid.Jid;
-
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
@@ -25,10 +13,27 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
+import java.io.UnsupportedEncodingException;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.ui.adapter.ConversationAdapter;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
public class ShareWithActivity extends XmppActivity {
private class Share {
public Uri uri;
+ public boolean image;
public String account;
public String contact;
public String text;
@@ -38,9 +43,9 @@ public class ShareWithActivity extends XmppActivity {
private static final int REQUEST_START_NEW_CONVERSATION = 0x0501;
private ListView mListView;
- private List<Conversation> mConversations = new ArrayList<Conversation>();
+ private List<Conversation> mConversations = new ArrayList<>();
- private UiCallback<Message> attachImageCallback = new UiCallback<Message>() {
+ private UiCallback<Message> attachFileCallback = new UiCallback<Message>() {
@Override
public void userInputRequried(PendingIntent pi, Message object) {
@@ -78,11 +83,12 @@ public class ShareWithActivity extends XmppActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
+ if (getActionBar() != null) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
+ }
setContentView(R.layout.share_with);
setTitle(getString(R.string.title_activity_sharewith));
@@ -114,10 +120,10 @@ public class ShareWithActivity extends XmppActivity {
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add:
- Intent intent = new Intent(getApplicationContext(),
+ final Intent intent = new Intent(getApplicationContext(),
ChooseContactActivity.class);
startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION);
return true;
@@ -127,16 +133,20 @@ public class ShareWithActivity extends XmppActivity {
@Override
public void onStart() {
- if (getIntent().getType() != null
- && getIntent().getType().startsWith("image/")) {
- this.share.uri = (Uri) getIntent().getParcelableExtra(
- Intent.EXTRA_STREAM);
+ final String type = getIntent().getType();
+ if (type != null && !type.startsWith("text/")) {
+ this.share.uri = (Uri) getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
+ try {
+ this.share.image = type.startsWith("image/")
+ || URLConnection.guessContentTypeFromName(this.share.uri.toString()).startsWith("image/");
+ } catch (final StringIndexOutOfBoundsException ignored) {
+ this.share.image = false;
+ }
} else {
this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
}
if (xmppConnectionServiceBound) {
- xmppConnectionService.populateWithOrderedConversations(
- mConversations, this.share.uri == null);
+ xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uri == null);
}
super.onStart();
}
@@ -177,12 +187,21 @@ public class ShareWithActivity extends XmppActivity {
selectPresence(conversation, new OnPresenceSelected() {
@Override
public void onPresenceSelected() {
- Toast.makeText(getApplicationContext(),
- getText(R.string.preparing_image),
- Toast.LENGTH_LONG).show();
- ShareWithActivity.this.xmppConnectionService
+ if (share.image) {
+ Toast.makeText(getApplicationContext(),
+ getText(R.string.preparing_image),
+ Toast.LENGTH_LONG).show();
+ ShareWithActivity.this.xmppConnectionService
.attachImageToConversation(conversation, share.uri,
- attachImageCallback);
+ attachFileCallback);
+ } else {
+ Toast.makeText(getApplicationContext(),
+ getText(R.string.preparing_file),
+ Toast.LENGTH_LONG).show();
+ ShareWithActivity.this.xmppConnectionService
+ .attachFileToConversation(conversation, share.uri,
+ attachFileCallback);
+ }
switchToConversation(conversation, null, true);
finish();
}
@@ -195,4 +214,4 @@ public class ShareWithActivity extends XmppActivity {
}
-} \ No newline at end of file
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
index c8b41821..209c0a7b 100644
--- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -38,6 +38,7 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.CheckBox;
+import android.widget.Checkable;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
@@ -53,19 +54,20 @@ import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.ListItem;
-import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.adapter.ListItemAdapter;
-import eu.siacs.conversations.utils.Validator;
+import eu.siacs.conversations.utils.XmppUri;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
-public class StartConversationActivity extends XmppActivity implements OnRosterUpdate {
+public class StartConversationActivity extends XmppActivity implements OnRosterUpdate, OnUpdateBlocklist {
public int conference_context_id;
public int contact_context_id;
@@ -133,7 +135,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private ViewPager.SimpleOnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
- getActionBar().setSelectedNavigationItem(position);
+ if (getActionBar() != null) {
+ getActionBar().setSelectedNavigationItem(position);
+ }
onTabChanged();
}
};
@@ -146,16 +150,27 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
+ int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
- int count) {
+ int count) {
}
};
private MenuItem mMenuSearchView;
private String mInitialJid;
+ private ListItemAdapter.OnTagClickedListener mOnTagClickedListener = new ListItemAdapter.OnTagClickedListener() {
+ @Override
+ public void onTagClicked(String tag) {
+ if (mMenuSearchView != null) {
+ mMenuSearchView.expandActionView();
+ mSearchEditText.setText("");
+ mSearchEditText.append(tag);
+ filter(tag);
+ }
+ }
+ };
@Override
public void onRosterUpdate() {
@@ -179,9 +194,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mContactsTab = actionBar.newTab().setText(R.string.contacts)
- .setTabListener(mTabListener);
+ .setTabListener(mTabListener);
mConferencesTab = actionBar.newTab().setText(R.string.conferences)
- .setTabListener(mTabListener);
+ .setTabListener(mTabListener);
actionBar.addTab(mContactsTab);
actionBar.addTab(mConferencesTab);
@@ -207,35 +222,36 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
mConferenceListFragment.setListAdapter(mConferenceAdapter);
mConferenceListFragment.setContextMenu(R.menu.conference_context);
mConferenceListFragment
- .setOnListItemClickListener(new OnItemClickListener() {
+ .setOnListItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> arg0, View arg1,
- int position, long arg3) {
- openConversationForBookmark(position);
- }
- });
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1,
+ int position, long arg3) {
+ openConversationForBookmark(position);
+ }
+ });
mContactsAdapter = new ListItemAdapter(this, contacts);
+ ((ListItemAdapter) mContactsAdapter).setOnTagClickedListener(this.mOnTagClickedListener);
mContactsListFragment.setListAdapter(mContactsAdapter);
mContactsListFragment.setContextMenu(R.menu.contact_context);
mContactsListFragment
- .setOnListItemClickListener(new OnItemClickListener() {
+ .setOnListItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> arg0, View arg1,
- int position, long arg3) {
- openConversationForContact(position);
- }
- });
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1,
+ int position, long arg3) {
+ openConversationForContact(position);
+ }
+ });
}
protected void openConversationForContact(int position) {
Contact contact = (Contact) contacts.get(position);
Conversation conversation = xmppConnectionService
- .findOrCreateConversation(contact.getAccount(),
- contact.getJid(), false);
+ .findOrCreateConversation(contact.getAccount(),
+ contact.getJid(), false);
switchToConversation(conversation);
}
@@ -251,8 +267,8 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
protected void openConversationForBookmark(int position) {
Bookmark bookmark = (Bookmark) conferences.get(position);
Conversation conversation = xmppConnectionService
- .findOrCreateConversation(bookmark.getAccount(),
- bookmark.getJid(), true);
+ .findOrCreateConversation(bookmark.getAccount(),
+ bookmark.getJid(), true);
conversation.setBookmark(bookmark);
if (!conversation.getMucOptions().online()) {
xmppConnectionService.joinMuc(conversation);
@@ -270,14 +286,19 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
switchToContactDetails(contact);
}
+ protected void toggleContactBlock() {
+ final int position = contact_context_id;
+ BlockContactDialog.show(this, xmppConnectionService, (Contact)contacts.get(position));
+ }
+
protected void deleteContact() {
- int position = contact_context_id;
+ final int position = contact_context_id;
final Contact contact = (Contact) contacts.get(position);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.action_delete_contact);
builder.setMessage(getString(R.string.remove_contact_text,
- contact.getJid()));
+ contact.getJid()));
builder.setPositiveButton(R.string.delete, new OnClickListener() {
@Override
@@ -287,7 +308,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
}
});
builder.create().show();
-
}
protected void deleteConference() {
@@ -298,7 +318,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_bookmark);
builder.setMessage(getString(R.string.remove_bookmark_text,
- bookmark.getJid()));
+ bookmark.getJid()));
builder.setPositiveButton(R.string.delete, new OnClickListener() {
@Override
@@ -318,13 +338,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
protected void showCreateContactDialog(final String prefilledJid, final String fingerprint) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.create_contact);
- View dialogView = getLayoutInflater().inflate(
- R.layout.create_contact_dialog, null);
+ View dialogView = getLayoutInflater().inflate(R.layout.create_contact_dialog, null);
final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
- final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
- .findViewById(R.id.jid);
- jid.setAdapter(new KnownHostsAdapter(this,
- android.R.layout.simple_list_item_1, mKnownHosts));
+ final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
+ jid.setAdapter(new KnownHostsAdapter(this,android.R.layout.simple_list_item_1, mKnownHosts));
if (prefilledJid != null) {
jid.append(prefilledJid);
if (fingerprint!=null) {
@@ -344,41 +361,37 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
new View.OnClickListener() {
@Override
- public void onClick(View v) {
+ public void onClick(final View v) {
if (!xmppConnectionServiceBound) {
return;
}
- if (Validator.isValidJid(jid.getText().toString())) {
- final Jid accountJid;
- try {
- accountJid = Jid.fromString((String) spinner
- .getSelectedItem());
- } catch (final InvalidJidException e) {
- return;
- }
- final Jid contactJid;
- try {
- contactJid = Jid.fromString(jid.getText().toString());
- } catch (final InvalidJidException e) {
- return;
- }
- Account account = xmppConnectionService
- .findAccountByJid(accountJid);
- if (account == null) {
- dialog.dismiss();
- return;
- }
- Contact contact = account.getRoster().getContact(contactJid);
- if (contact.showInRoster()) {
- jid.setError(getString(R.string.contact_already_exists));
- } else {
- contact.addOtrFingerprint(fingerprint);
- xmppConnectionService.createContact(contact);
- dialog.dismiss();
- switchToConversation(contact);
- }
- } else {
+ final Jid accountJid;
+ try {
+ accountJid = Jid.fromString((String) spinner.getSelectedItem());
+ } catch (final InvalidJidException e) {
+ return;
+ }
+ final Jid contactJid;
+ try {
+ contactJid = Jid.fromString(jid.getText().toString());
+ } catch (final InvalidJidException e) {
jid.setError(getString(R.string.invalid_jid));
+ return;
+ }
+ final Account account = xmppConnectionService
+ .findAccountByJid(accountJid);
+ if (account == null) {
+ dialog.dismiss();
+ return;
+ }
+ final Contact contact = account.getRoster().getContact(contactJid);
+ if (contact.showInRoster()) {
+ jid.setError(getString(R.string.contact_already_exists));
+ } else {
+ contact.addOtrFingerprint(fingerprint);
+ xmppConnectionService.createContact(contact);
+ dialog.dismiss();
+ switchToConversation(contact);
}
}
});
@@ -386,22 +399,19 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
}
@SuppressLint("InflateParams")
- protected void showJoinConferenceDialog(String prefilledJid) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ protected void showJoinConferenceDialog(final String prefilledJid) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.join_conference);
- View dialogView = getLayoutInflater().inflate(
- R.layout.join_conference_dialog, null);
+ final View dialogView = getLayoutInflater().inflate(R.layout.join_conference_dialog, null);
final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
- final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
- .findViewById(R.id.jid);
- jid.setAdapter(new KnownHostsAdapter(this,
- android.R.layout.simple_list_item_1, mKnownConferenceHosts));
+ final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
+ jid.setAdapter(new KnownHostsAdapter(this,android.R.layout.simple_list_item_1, mKnownConferenceHosts));
if (prefilledJid != null) {
jid.append(prefilledJid);
}
populateAccountSpinner(spinner);
- final CheckBox bookmarkCheckBox = (CheckBox) dialogView
- .findViewById(R.id.bookmark);
+ final Checkable bookmarkCheckBox = (CheckBox) dialogView
+ .findViewById(R.id.bookmark);
builder.setView(dialogView);
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.join, null);
@@ -411,62 +421,59 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
new View.OnClickListener() {
@Override
- public void onClick(View v) {
+ public void onClick(final View v) {
if (!xmppConnectionServiceBound) {
return;
}
- if (Validator.isValidJid(jid.getText().toString())) {
- final Jid accountJid;
- try {
- accountJid = Jid.fromString((String) spinner.getSelectedItem());
- } catch (final InvalidJidException e) {
- return;
- }
- final Jid conferenceJid;
- try {
- conferenceJid = Jid.fromString(jid.getText().toString());
- } catch (final InvalidJidException e) {
- return; // TODO: Do some error handling...
- }
- Account account = xmppConnectionService
- .findAccountByJid(accountJid);
- if (account == null) {
- dialog.dismiss();
- return;
- }
- if (bookmarkCheckBox.isChecked()) {
- if (account.hasBookmarkFor(conferenceJid)) {
- jid.setError(getString(R.string.bookmark_already_exists));
- } else {
- Bookmark bookmark = new Bookmark(account,
- conferenceJid);
- bookmark.setAutojoin(true);
- account.getBookmarks().add(bookmark);
- xmppConnectionService
- .pushBookmarks(account);
- Conversation conversation = xmppConnectionService
- .findOrCreateConversation(account,
- conferenceJid, true);
- conversation.setBookmark(bookmark);
- if (!conversation.getMucOptions().online()) {
- xmppConnectionService
- .joinMuc(conversation);
- }
- dialog.dismiss();
- switchToConversation(conversation);
- }
+ final Jid accountJid;
+ try {
+ accountJid = Jid.fromString((String) spinner.getSelectedItem());
+ } catch (final InvalidJidException e) {
+ return;
+ }
+ final Jid conferenceJid;
+ try {
+ conferenceJid = Jid.fromString(jid.getText().toString());
+ } catch (final InvalidJidException e) {
+ jid.setError(getString(R.string.invalid_jid));
+ return;
+ }
+ final Account account = xmppConnectionService
+ .findAccountByJid(accountJid);
+ if (account == null) {
+ dialog.dismiss();
+ return;
+ }
+ if (bookmarkCheckBox.isChecked()) {
+ if (account.hasBookmarkFor(conferenceJid)) {
+ jid.setError(getString(R.string.bookmark_already_exists));
} else {
- Conversation conversation = xmppConnectionService
- .findOrCreateConversation(account,
- conferenceJid, true);
+ final Bookmark bookmark = new Bookmark(account,
+ conferenceJid);
+ bookmark.setAutojoin(true);
+ account.getBookmarks().add(bookmark);
+ xmppConnectionService
+ .pushBookmarks(account);
+ final Conversation conversation = xmppConnectionService
+ .findOrCreateConversation(account,
+ conferenceJid, true);
+ conversation.setBookmark(bookmark);
if (!conversation.getMucOptions().online()) {
- xmppConnectionService.joinMuc(conversation);
+ xmppConnectionService
+ .joinMuc(conversation);
}
dialog.dismiss();
switchToConversation(conversation);
}
} else {
- jid.setError(getString(R.string.invalid_jid));
+ final Conversation conversation = xmppConnectionService
+ .findOrCreateConversation(account,
+ conferenceJid, true);
+ if (!conversation.getMucOptions().online()) {
+ xmppConnectionService.joinMuc(conversation);
+ }
+ dialog.dismiss();
+ switchToConversation(conversation);
}
}
});
@@ -474,8 +481,8 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
protected void switchToConversation(Contact contact) {
Conversation conversation = xmppConnectionService
- .findOrCreateConversation(contact.getAccount(),
- contact.getJid(), false);
+ .findOrCreateConversation(contact.getAccount(),
+ contact.getJid(), false);
switchToConversation(conversation);
}
@@ -491,14 +498,14 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
this.mOptionsMenu = menu;
getMenuInflater().inflate(R.menu.start_conversation, menu);
MenuItem menuCreateContact = menu
- .findItem(R.id.action_create_contact);
+ .findItem(R.id.action_create_contact);
MenuItem menuCreateConference = menu
- .findItem(R.id.action_join_conference);
+ .findItem(R.id.action_join_conference);
mMenuSearchView = menu.findItem(R.id.action_search);
mMenuSearchView.setOnActionExpandListener(mOnActionExpandListener);
View mSearchView = mMenuSearchView.getActionView();
mSearchEditText = (EditText) mSearchView
- .findViewById(R.id.search_field);
+ .findViewById(R.id.search_field);
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
if (getActionBar().getSelectedNavigationIndex() == 0) {
menuCreateConference.setVisible(false);
@@ -567,7 +574,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
}
this.mKnownHosts = xmppConnectionService.getKnownHosts();
this.mKnownConferenceHosts = xmppConnectionService
- .getKnownConferenceHosts();
+ .getKnownConferenceHosts();
if (this.mPendingInvite != null) {
mPendingInvite.invite();
this.mPendingInvite = null;
@@ -609,7 +616,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
byte[] payload = record.getPayload();
if (payload[0] == 0) {
return new Invite(Uri.parse(new String(Arrays.copyOfRange(
- payload, 1, payload.length)))).invite();
+ payload, 1, payload.length)))).invite();
}
}
}
@@ -690,16 +697,29 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
invalidateOptionsMenu();
}
+ @Override
+ public void OnUpdateBlocklist(final Status status) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mSearchEditText != null) {
+ filter(mSearchEditText.getText().toString());
+ }
+ }
+ });
+ }
+
public static class MyListFragment extends ListFragment {
private AdapterView.OnItemClickListener mOnItemClickListener;
private int mResContextMenu;
- public void setContextMenu(int res) {
+ public void setContextMenu(final int res) {
this.mResContextMenu = res;
}
@Override
- public void onListItemClick(ListView l, View v, int position, long id) {
+ public void onListItemClick(final ListView l, final View v, final int position, final long id) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(l, v, position, id);
}
@@ -710,28 +730,38 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
}
@Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
+ public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
registerForContextMenu(getListView());
getListView().setFastScrollEnabled(true);
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
+ public void onCreateContextMenu(final ContextMenu menu, final View v,
+ final ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- StartConversationActivity activity = (StartConversationActivity) getActivity();
+ final StartConversationActivity activity = (StartConversationActivity) getActivity();
activity.getMenuInflater().inflate(mResContextMenu, menu);
- AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ final AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
if (mResContextMenu == R.menu.conference_context) {
activity.conference_context_id = acmi.position;
} else {
activity.contact_context_id = acmi.position;
+ final Blockable contact = (Contact) activity.contacts.get(acmi.position);
+
+ final MenuItem blockUnblockItem = menu.findItem(R.id.context_contact_block_unblock);
+ if (blockUnblockItem != null) {
+ if (contact.isBlocked()) {
+ blockUnblockItem.setTitle(R.string.unblock_contact);
+ } else {
+ blockUnblockItem.setTitle(R.string.block_contact);
+ }
+ }
}
}
@Override
- public boolean onContextItemSelected(MenuItem item) {
+ public boolean onContextItemSelected(final MenuItem item) {
StartConversationActivity activity = (StartConversationActivity) getActivity();
switch (item.getItemId()) {
case R.id.context_start_conversation:
@@ -740,6 +770,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
case R.id.context_contact_details:
activity.openDetailsForContact();
break;
+ case R.id.context_contact_block_unblock:
+ activity.toggleContactBlock();
+ break;
case R.id.context_delete_contact:
activity.deleteContact();
break;
@@ -755,11 +788,11 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private class Invite extends XmppUri {
- public Invite(Uri uri) {
+ public Invite(final Uri uri) {
super(uri);
}
- public Invite(String uri) {
+ public Invite(final String uri) {
super(uri);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/TimePreference.java b/src/main/java/eu/siacs/conversations/ui/TimePreference.java
new file mode 100644
index 00000000..e32b068c
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/TimePreference.java
@@ -0,0 +1,105 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.preference.DialogPreference;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TimePicker;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+public class TimePreference extends DialogPreference implements Preference.OnPreferenceChangeListener {
+ private TimePicker picker = null;
+ public final static long DEFAULT_VALUE = 0;
+
+ public TimePreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs, 0);
+ this.setOnPreferenceChangeListener(this);
+ }
+
+ protected void setTime(final long time) {
+ persistLong(time);
+ notifyDependencyChange(shouldDisableDependents());
+ notifyChanged();
+ }
+
+ protected void updateSummary(final long time) {
+ final DateFormat dateFormat = android.text.format.DateFormat.getTimeFormat(getContext());
+ final Date date = new Date(time);
+ setSummary(dateFormat.format(date.getTime()));
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ picker = new TimePicker(getContext());
+ picker.setIs24HourView(android.text.format.DateFormat.is24HourFormat(getContext()));
+ return picker;
+ }
+
+ protected Calendar getPersistedTime() {
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(getPersistedLong(DEFAULT_VALUE));
+
+ return c;
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ protected void onBindDialogView(final View v) {
+ super.onBindDialogView(v);
+ final Calendar c = getPersistedTime();
+
+ picker.setCurrentHour(c.get(Calendar.HOUR_OF_DAY));
+ picker.setCurrentMinute(c.get(Calendar.MINUTE));
+ }
+
+ @Override
+ protected void onDialogClosed(final boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult) {
+ final Calendar c = Calendar.getInstance();
+ c.set(Calendar.MINUTE, picker.getCurrentMinute());
+ c.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
+
+
+ if (!callChangeListener(c.getTimeInMillis())) {
+ return;
+ }
+
+ setTime(c.getTimeInMillis());
+ }
+ }
+
+ @Override
+ protected Object onGetDefaultValue(final TypedArray a, final int index) {
+ return a.getInteger(index, 0);
+ }
+
+ @Override
+ protected void onSetInitialValue(final boolean restorePersistedValue, final Object defaultValue) {
+ long time;
+ if (defaultValue == null) {
+ time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
+ } else if (defaultValue instanceof Long) {
+ time = restorePersistedValue ? getPersistedLong((Long) defaultValue) : (Long) defaultValue;
+ } else if (defaultValue instanceof Calendar) {
+ time = restorePersistedValue ? getPersistedLong(((Calendar)defaultValue).getTimeInMillis()) : ((Calendar)defaultValue).getTimeInMillis();
+ } else {
+ time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
+ }
+
+ setTime(time);
+ updateSummary(time);
+ }
+
+ @Override
+ public boolean onPreferenceChange(final Preference preference, final Object newValue) {
+ ((TimePreference) preference).updateSummary((Long)newValue);
+ return true;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java
index e5775ab0..c33decd8 100644
--- a/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java
@@ -1,15 +1,15 @@
package eu.siacs.conversations.ui;
+import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
@@ -32,57 +32,56 @@ import eu.siacs.conversations.xmpp.jid.Jid;
public class VerifyOTRActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
public static final String ACTION_VERIFY_CONTACT = "verify_contact";
+ public static final int MODE_SCAN_FINGERPRINT = - 0x0502;
+ public static final int MODE_ASK_QUESTION = 0x0503;
+ public static final int MODE_ANSWER_QUESTION = 0x0504;
+ public static final int MODE_MANUAL_VERIFICATION = 0x0505;
- private RelativeLayout mVerificationAreaOne;
- private RelativeLayout mVerificationAreaTwo;
- private TextView mErrorNoSession;
- private TextView mRemoteJid;
+ private LinearLayout mManualVerificationArea;
+ private LinearLayout mSmpVerificationArea;
private TextView mRemoteFingerprint;
private TextView mYourFingerprint;
- private EditText mSharedSecretHint;
- private EditText mSharedSecretSecret;
- private Button mButtonScanQrCode;
- private Button mButtonShowQrCode;
- private Button mButtonSharedSecretPositive;
- private Button mButtonSharedSecretNegative;
+ private TextView mVerificationExplain;
private TextView mStatusMessage;
+ private TextView mSharedSecretHint;
+ private EditText mSharedSecretHintEditable;
+ private EditText mSharedSecretSecret;
+ private Button mLeftButton;
+ private Button mRightButton;
private Account mAccount;
private Conversation mConversation;
+ private int mode = MODE_MANUAL_VERIFICATION;
+ private XmppUri mPendingUri = null;
private DialogInterface.OnClickListener mVerifyFingerprintListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int click) {
mConversation.verifyOtrFingerprint();
- updateView();
xmppConnectionService.syncRosterToDisk(mConversation.getAccount());
+ Toast.makeText(VerifyOTRActivity.this,R.string.verified,Toast.LENGTH_SHORT).show();
+ finish();
}
};
- private View.OnClickListener mShowQrCodeListener = new View.OnClickListener() {
- @Override
- public void onClick(final View view) {
- showQrCode();
- }
- };
-
- private View.OnClickListener mScanQrCodeListener = new View.OnClickListener() {
-
- @Override
- public void onClick(View view) {
- new IntentIntegrator(VerifyOTRActivity.this).initiateScan();
- }
-
- };
-
private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
@Override
public void onClick(final View view) {
if (isAccountOnline()) {
- final String question = mSharedSecretHint.getText().toString();
+ final String question = mSharedSecretHintEditable.getText().toString();
final String secret = mSharedSecretSecret.getText().toString();
- initSmp(question, secret);
- updateView();
+ if (question.trim().isEmpty()) {
+ mSharedSecretHintEditable.requestFocus();
+ mSharedSecretHintEditable.setError(getString(R.string.shared_secret_hint_should_not_be_empty));
+ } else if (secret.trim().isEmpty()) {
+ mSharedSecretSecret.requestFocus();
+ mSharedSecretSecret.setError(getString(R.string.shared_secret_can_not_be_empty));
+ } else {
+ mSharedSecretSecret.setError(null);
+ mSharedSecretHintEditable.setError(null);
+ initSmp(question, secret);
+ updateView();
+ }
}
}
};
@@ -100,7 +99,7 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
@Override
public void onClick(View view) {
if (isAccountOnline()) {
- final String question = mSharedSecretHint.getText().toString();
+ final String question = mSharedSecretHintEditable.getText().toString();
final String secret = mSharedSecretSecret.getText().toString();
respondSmp(question, secret);
updateView();
@@ -124,14 +123,14 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
}
};
- private XmppUri mPendingUri = null;
-
protected boolean initSmp(final String question, final String secret) {
final Session session = mConversation.getOtrSession();
if (session!=null) {
try {
session.initSmp(question, secret);
mConversation.smp().status = Conversation.Smp.STATUS_WE_REQUESTED;
+ mConversation.smp().secret = secret;
+ mConversation.smp().hint = question;
return true;
} catch (OtrException e) {
return false;
@@ -172,15 +171,17 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
}
}
- protected void verifyWithUri(XmppUri uri) {
+ protected boolean verifyWithUri(XmppUri uri) {
Contact contact = mConversation.getContact();
if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.getFingerprint() != null) {
contact.addOtrFingerprint(uri.getFingerprint());
Toast.makeText(this,R.string.verified,Toast.LENGTH_SHORT).show();
updateView();
xmppConnectionService.syncRosterToDisk(contact.getAccount());
+ return true;
} else {
Toast.makeText(this,R.string.could_not_verify_fingerprint,Toast.LENGTH_SHORT).show();
+ return false;
}
}
@@ -194,7 +195,7 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
}
protected boolean handleIntent(Intent intent) {
- if (intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
+ if (intent != null && intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
try {
this.mAccount = this.xmppConnectionService.findAccountByJid(Jid.fromString(intent.getExtras().getString("account")));
} catch (final InvalidJidException ignored) {
@@ -208,6 +209,11 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
} catch (final InvalidJidException ignored) {
return false;
}
+ this.mode = intent.getIntExtra("mode", MODE_MANUAL_VERIFICATION);
+ if (this.mode == MODE_SCAN_FINGERPRINT) {
+ new IntentIntegrator(this).initiateScan();
+ return false;
+ }
return true;
} else {
return false;
@@ -223,9 +229,12 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
XmppUri uri = new XmppUri(data);
if (xmppConnectionServiceBound) {
verifyWithUri(uri);
+ finish();
} else {
this.mPendingUri = uri;
}
+ } else {
+ finish();
}
}
super.onActivityResult(requestCode, requestCode, intent);
@@ -234,84 +243,139 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
@Override
protected void onBackendConnected() {
if (handleIntent(getIntent())) {
- if (mPendingUri!=null) {
- verifyWithUri(mPendingUri);
- mPendingUri = null;
- }
updateView();
+ } else if (mPendingUri!=null) {
+ verifyWithUri(mPendingUri);
+ finish();
+ mPendingUri = null;
}
+ setIntent(null);
}
protected void updateView() {
if (this.mConversation.hasValidOtrSession()) {
+ final ActionBar actionBar = getActionBar();
+ this.mVerificationExplain.setText(R.string.no_otr_session_found);
invalidateOptionsMenu();
- this.mVerificationAreaOne.setVisibility(View.VISIBLE);
- this.mVerificationAreaTwo.setVisibility(View.VISIBLE);
- this.mErrorNoSession.setVisibility(View.GONE);
- this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
- this.mRemoteFingerprint.setText(this.mConversation.getOtrFingerprint());
- this.mRemoteJid.setText(this.mConversation.getContact().getJid().toBareJid().toString());
- Conversation.Smp smp = mConversation.smp();
- Session session = mConversation.getOtrSession();
- if (mConversation.isOtrFingerprintVerified()) {
- deactivateButton(mButtonScanQrCode, R.string.verified);
- } else {
- activateButton(mButtonScanQrCode, R.string.scan_qr_code, mScanQrCodeListener);
+ switch(this.mode) {
+ case MODE_ASK_QUESTION:
+ if (actionBar != null ) {
+ actionBar.setTitle(R.string.ask_question);
+ }
+ this.updateViewAskQuestion();
+ break;
+ case MODE_ANSWER_QUESTION:
+ if (actionBar != null ) {
+ actionBar.setTitle(R.string.smp_requested);
+ }
+ this.updateViewAnswerQuestion();
+ break;
+ case MODE_MANUAL_VERIFICATION:
+ default:
+ if (actionBar != null ) {
+ actionBar.setTitle(R.string.manually_verify);
+ }
+ this.updateViewManualVerification();
+ break;
}
- if (smp.status == Conversation.Smp.STATUS_NONE) {
- activateButton(mButtonSharedSecretPositive, R.string.create, mCreateSharedSecretListener);
- deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
- this.mSharedSecretHint.setFocusableInTouchMode(true);
- this.mSharedSecretSecret.setFocusableInTouchMode(true);
- this.mSharedSecretSecret.setText("");
- this.mSharedSecretHint.setText("");
- this.mSharedSecretHint.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ } else {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.GONE);
+ }
+ }
+
+ protected void updateViewManualVerification() {
+ this.mVerificationExplain.setText(R.string.manual_verification_explanation);
+ this.mManualVerificationArea.setVisibility(View.VISIBLE);
+ this.mSmpVerificationArea.setVisibility(View.GONE);
+ this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
+ this.mRemoteFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mConversation.getOtrFingerprint()));
+ if (this.mConversation.isOtrFingerprintVerified()) {
+ deactivateButton(this.mRightButton,R.string.verified);
+ activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
+ } else {
+ activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
+ activateButton(this.mRightButton,R.string.verify, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ showManuallyVerifyDialog();
+ }
+ });
+ }
+ }
+
+ protected void updateViewAskQuestion() {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.VISIBLE);
+ this.mVerificationExplain.setText(R.string.smp_explain_question);
+ final int smpStatus = this.mConversation.smp().status;
+ switch (smpStatus) {
+ case Conversation.Smp.STATUS_WE_REQUESTED:
this.mStatusMessage.setVisibility(View.GONE);
- } else if (smp.status == Conversation.Smp.STATUS_CONTACT_REQUESTED) {
- this.mSharedSecretHint.setFocusable(false);
- this.mSharedSecretHint.setText(smp.hint);
- this.mSharedSecretSecret.setFocusableInTouchMode(true);
- this.mSharedSecretHint.setVisibility(View.VISIBLE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mSharedSecretHintEditable.setText(this.mConversation.smp().hint);
+ this.mSharedSecretSecret.setText(this.mConversation.smp().secret);
+ this.activateButton(this.mLeftButton, R.string.cancel, this.mCancelSharedSecretListener);
+ this.deactivateButton(this.mRightButton, R.string.in_progress);
+ break;
+ case Conversation.Smp.STATUS_FAILED:
this.mStatusMessage.setVisibility(View.GONE);
- deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
- activateButton(mButtonSharedSecretPositive, R.string.respond, mRespondSharedSecretListener);
- } else if (smp.status == Conversation.Smp.STATUS_FAILED) {
- activateButton(mButtonSharedSecretNegative, R.string.cancel, mFinishListener);
- activateButton(mButtonSharedSecretPositive, R.string.try_again, mRetrySharedSecretListener);
- this.mSharedSecretHint.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.requestFocus();
+ this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ this.activateButton(this.mRightButton, R.string.try_again, this.mRetrySharedSecretListener);
+ break;
+ case Conversation.Smp.STATUS_VERIFIED:
+ this.mSharedSecretHintEditable.setText("");
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretSecret.setText("");
this.mSharedSecretSecret.setVisibility(View.GONE);
this.mStatusMessage.setVisibility(View.VISIBLE);
- this.mStatusMessage.setText(R.string.secrets_do_not_match);
- this.mStatusMessage.setTextColor(getWarningTextColor());
- } else if (smp.status == Conversation.Smp.STATUS_FINISHED) {
- this.mSharedSecretHint.setText("");
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ default:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
+ this.activateButton(this.mRightButton, R.string.ask_question, this.mCreateSharedSecretListener);
+ break;
+ }
+ }
+
+ protected void updateViewAnswerQuestion() {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.VISIBLE);
+ this.mVerificationExplain.setText(R.string.smp_explain_answer);
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretHint.setVisibility(View.VISIBLE);
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ final int smpStatus = this.mConversation.smp().status;
+ switch (smpStatus) {
+ case Conversation.Smp.STATUS_CONTACT_REQUESTED:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHint.setText(this.mConversation.smp().hint);
+ this.activateButton(this.mRightButton,R.string.respond,this.mRespondSharedSecretListener);
+ break;
+ case Conversation.Smp.STATUS_VERIFIED:
+ this.mSharedSecretHintEditable.setText("");
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
this.mSharedSecretHint.setVisibility(View.GONE);
this.mSharedSecretSecret.setText("");
this.mSharedSecretSecret.setVisibility(View.GONE);
this.mStatusMessage.setVisibility(View.VISIBLE);
- this.mStatusMessage.setTextColor(getPrimaryColor());
- deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
- if (mConversation.isOtrFingerprintVerified()) {
- activateButton(mButtonSharedSecretPositive, R.string.finish, mFinishListener);
- this.mStatusMessage.setText(R.string.verified);
- } else {
- activateButton(mButtonSharedSecretPositive,R.string.reset,mRetrySharedSecretListener);
- this.mStatusMessage.setText(R.string.secret_accepted);
- }
- } else if (session != null && session.isSmpInProgress()) {
- deactivateButton(mButtonSharedSecretPositive, R.string.in_progress);
- activateButton(mButtonSharedSecretNegative, R.string.cancel, mCancelSharedSecretListener);
- this.mSharedSecretHint.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
- this.mSharedSecretHint.setFocusable(false);
- this.mSharedSecretSecret.setFocusable(false);
- }
- } else {
- this.mVerificationAreaOne.setVisibility(View.GONE);
- this.mVerificationAreaTwo.setVisibility(View.GONE);
- this.mErrorNoSession.setVisibility(View.VISIBLE);
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ case Conversation.Smp.STATUS_FAILED:
+ default:
+ this.mSharedSecretSecret.requestFocus();
+ this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
+ this.activateButton(this.mRightButton,R.string.finish,this.mFinishListener);
+ break;
}
}
@@ -334,41 +398,25 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verify_otr);
this.mRemoteFingerprint = (TextView) findViewById(R.id.remote_fingerprint);
- this.mRemoteJid = (TextView) findViewById(R.id.remote_jid);
this.mYourFingerprint = (TextView) findViewById(R.id.your_fingerprint);
- this.mButtonSharedSecretNegative = (Button) findViewById(R.id.button_shared_secret_negative);
- this.mButtonSharedSecretPositive = (Button) findViewById(R.id.button_shared_secret_positive);
- this.mButtonScanQrCode = (Button) findViewById(R.id.button_scan_qr_code);
- this.mButtonShowQrCode = (Button) findViewById(R.id.button_show_qr_code);
- this.mButtonShowQrCode.setOnClickListener(this.mShowQrCodeListener);
+ this.mLeftButton = (Button) findViewById(R.id.left_button);
+ this.mRightButton = (Button) findViewById(R.id.right_button);
+ this.mVerificationExplain = (TextView) findViewById(R.id.verification_explanation);
+ this.mStatusMessage = (TextView) findViewById(R.id.status_message);
this.mSharedSecretSecret = (EditText) findViewById(R.id.shared_secret_secret);
- this.mSharedSecretHint = (EditText) findViewById(R.id.shared_secret_hint);
- this.mStatusMessage= (TextView) findViewById(R.id.status_message);
- this.mVerificationAreaOne = (RelativeLayout) findViewById(R.id.verification_area_one);
- this.mVerificationAreaTwo = (RelativeLayout) findViewById(R.id.verification_area_two);
- this.mErrorNoSession = (TextView) findViewById(R.id.error_no_session);
+ this.mSharedSecretHintEditable = (EditText) findViewById(R.id.shared_secret_hint_editable);
+ this.mSharedSecretHint = (TextView) findViewById(R.id.shared_secret_hint);
+ this.mManualVerificationArea = (LinearLayout) findViewById(R.id.manual_verification_area);
+ this.mSmpVerificationArea = (LinearLayout) findViewById(R.id.smp_verification_area);
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.verify_otr, menu);
- if (mConversation != null && mConversation.isOtrFingerprintVerified()) {
- MenuItem manuallyVerifyItem = menu.findItem(R.id.manually_verify);
- manuallyVerifyItem.setVisible(false);
- }
return true;
}
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- if (menuItem.getItemId() == R.id.manually_verify) {
- showManuallyVerifyDialog();
- return true;
- } else {
- return super.onOptionsItemSelected(menuItem);
- }
- }
-
private void showManuallyVerifyDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.manually_verify);
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 5fba1664..69dd47e7 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -71,6 +71,7 @@ import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.utils.ExceptionHelper;
+import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -94,6 +95,7 @@ public abstract class XmppActivity extends Activity {
protected boolean mUseSubject = true;
private DisplayMetrics metrics;
+ protected int mTheme;
protected interface OnValueEdited {
public void onValueEdited(String value);
@@ -198,7 +200,7 @@ public abstract class XmppActivity extends Activity {
xmppConnectionServiceBound = false;
}
stopService(new Intent(XmppActivity.this,
- XmppConnectionService.class));
+ XmppConnectionService.class));
finish();
}
});
@@ -208,13 +210,13 @@ public abstract class XmppActivity extends Activity {
@Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri
- .parse("market://details?id=org.sufficientlysecure.keychain");
+ .parse("market://details?id=org.sufficientlysecure.keychain");
Intent marketIntent = new Intent(Intent.ACTION_VIEW,
uri);
PackageManager manager = getApplicationContext()
- .getPackageManager();
+ .getPackageManager();
List<ResolveInfo> infos = manager
- .queryIntentActivities(marketIntent, 0);
+ .queryIntentActivities(marketIntent, 0);
if (infos.size() > 0) {
startActivity(marketIntent);
} else {
@@ -244,6 +246,9 @@ public abstract class XmppActivity extends Activity {
if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
this.xmppConnectionService.setOnMucRosterUpdateListener((XmppConnectionService.OnMucRosterUpdate) this);
}
+ if (this instanceof OnUpdateBlocklist) {
+ this.xmppConnectionService.setOnUpdateBlocklistListener((OnUpdateBlocklist) this);
+ }
}
protected void unregisterListeners() {
@@ -259,9 +264,13 @@ public abstract class XmppActivity extends Activity {
if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
this.xmppConnectionService.removeOnMucRosterUpdateListener();
}
+ if (this instanceof OnUpdateBlocklist) {
+ this.xmppConnectionService.removeOnUpdateBlocklistListener();
+ }
}
- public boolean onOptionsItemSelected(MenuItem item) {
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
startActivity(new Intent(this, SettingsActivity.class));
@@ -292,15 +301,14 @@ public abstract class XmppActivity extends Activity {
mPrimaryColor = getResources().getColor(R.color.primary);
mSecondaryBackgroundColor = getResources().getColor(
R.color.secondarybackground);
- if (getPreferences().getBoolean("use_larger_font", false)) {
- setTheme(R.style.ConversationsTheme_LargerText);
- }
+ this.mTheme = findTheme();
+ setTheme(this.mTheme);
mUseSubject = getPreferences().getBoolean("use_subject", true);
}
protected SharedPreferences getPreferences() {
return PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
+ .getDefaultSharedPreferences(getApplicationContext());
}
public boolean useSubjectToIdentifyConference() {
@@ -312,7 +320,7 @@ public abstract class XmppActivity extends Activity {
}
public void switchToConversation(Conversation conversation, String text,
- boolean newTask) {
+ boolean newTask) {
switchToConversation(conversation,text,null,newTask);
}
@@ -372,7 +380,7 @@ public abstract class XmppActivity extends Activity {
@Override
public void userInputRequried(PendingIntent pi,
- Account account) {
+ Account account) {
try {
startIntentSenderForResult(pi.getIntentSender(),
REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
@@ -383,15 +391,15 @@ public abstract class XmppActivity extends Activity {
@Override
public void success(Account account) {
xmppConnectionService.databaseBackend
- .updateAccount(account);
+ .updateAccount(account);
xmppConnectionService.sendPresencePacket(account,
xmppConnectionService.getPresenceGenerator()
- .sendPresence(account));
+ .sendPresence(account));
if (conversation != null) {
conversation
- .setNextEncryption(Message.ENCRYPTION_PGP);
+ .setNextEncryption(Message.ENCRYPTION_PGP);
xmppConnectionService.databaseBackend
- .updateConversation(conversation);
+ .updateConversation(conversation);
}
}
@@ -420,7 +428,7 @@ public abstract class XmppActivity extends Activity {
}
protected void showAddToRosterDialog(final Conversation conversation) {
- final Jid jid = conversation.getContactJid();
+ final Jid jid = conversation.getJid();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(jid.toString());
builder.setMessage(getString(R.string.not_in_roster));
@@ -430,7 +438,7 @@ public abstract class XmppActivity extends Activity {
@Override
public void onClick(DialogInterface dialog, int which) {
- final Jid jid = conversation.getContactJid();
+ final Jid jid = conversation.getJid();
Account account = conversation.getAccount();
Contact contact = account.getRoster().getContact(jid);
xmppConnectionService.createContact(contact);
@@ -462,7 +470,7 @@ public abstract class XmppActivity extends Activity {
}
private void warnMutalPresenceSubscription(final Conversation conversation,
- final OnPresenceSelected listener) {
+ final OnPresenceSelected listener) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(conversation.getContact().getJid().toString());
builder.setMessage(R.string.without_mutual_presence_updates);
@@ -485,13 +493,13 @@ public abstract class XmppActivity extends Activity {
}
protected void quickPasswordEdit(String previousValue,
- OnValueEdited callback) {
+ OnValueEdited callback) {
quickEdit(previousValue, callback, true);
}
@SuppressLint("InflateParams")
private void quickEdit(final String previousValue,
- final OnValueEdited callback, boolean password) {
+ final OnValueEdited callback, boolean password) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = getLayoutInflater().inflate(R.layout.quickedit, null);
final EditText editor = (EditText) view.findViewById(R.id.editor);
@@ -521,7 +529,7 @@ public abstract class XmppActivity extends Activity {
}
public void selectPresence(final Conversation conversation,
- final OnPresenceSelected listener) {
+ final OnPresenceSelected listener) {
final Contact contact = conversation.getContact();
if (conversation.hasValidOtrSession()) {
SessionID id = conversation.getOtrSession().getSessionID();
@@ -576,7 +584,7 @@ public abstract class XmppActivity extends Activity {
@Override
public void onClick(DialogInterface dialog,
- int which) {
+ int which) {
presence.delete(0, presence.length());
presence.append(presencesArray[which]);
}
@@ -600,7 +608,7 @@ public abstract class XmppActivity extends Activity {
}
protected void onActivityResult(int requestCode, int resultCode,
- final Intent data) {
+ final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_INVITE_TO_CONVERSATION
&& resultCode == RESULT_OK) {
@@ -608,19 +616,19 @@ public abstract class XmppActivity extends Activity {
Jid jid = Jid.fromString(data.getStringExtra("contact"));
String conversationUuid = data.getStringExtra("conversation");
Conversation conversation = xmppConnectionService
- .findConversationByUuid(conversationUuid);
+ .findConversationByUuid(conversationUuid);
if (conversation.getMode() == Conversation.MODE_MULTI) {
xmppConnectionService.invite(conversation, jid);
} else {
List<Jid> jids = new ArrayList<Jid>();
- jids.add(conversation.getContactJid().toBareJid());
+ jids.add(conversation.getJid().toBareJid());
jids.add(jid);
xmppConnectionService.createAdhocConference(conversation.getAccount(), jids, adhocCallback);
}
} catch (final InvalidJidException ignored) {
}
- }
+ }
}
private UiCallback<Conversation> adhocCallback = new UiCallback<Conversation>() {
@@ -688,18 +696,18 @@ public abstract class XmppActivity extends Activity {
}
protected void registerNdefPushMessageCallback() {
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
- if (nfcAdapter != null && nfcAdapter.isEnabled()) {
- nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
- @Override
- public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
- return new NdefMessage(new NdefRecord[]{
- NdefRecord.createUri(getShareableUri()),
- NdefRecord.createApplicationRecord("eu.siacs.conversations")
- });
- }
- }, this);
- }
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (nfcAdapter != null && nfcAdapter.isEnabled()) {
+ nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
+ return new NdefMessage(new NdefRecord[]{
+ NdefRecord.createUri(getShareableUri()),
+ NdefRecord.createApplicationRecord("eu.siacs.conversations")
+ });
+ }
+ }, this);
+ }
}
protected void unregisterNdefPushMessageCallback() {
@@ -721,6 +729,14 @@ public abstract class XmppActivity extends Activity {
}
}
+ protected int findTheme() {
+ if (getPreferences().getBoolean("use_larger_font", false)) {
+ return R.style.ConversationsTheme_LargerText;
+ } else {
+ return R.style.ConversationsTheme;
+ }
+ }
+
@Override
public void onPause() {
super.onPause();
@@ -823,13 +839,13 @@ public abstract class XmppActivity extends Activity {
try {
task.execute(message);
} catch (final RejectedExecutionException ignored) {
- }
+ }
}
}
}
public static boolean cancelPotentialWork(Message message,
- ImageView imageView) {
+ ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
@@ -858,7 +874,7 @@ public abstract class XmppActivity extends Activity {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap,
- BitmapWorkerTask bitmapWorkerTask) {
+ BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<>(
bitmapWorkerTask);
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
index c5ee9ba6..32f95431 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -2,7 +2,6 @@ package eu.siacs.conversations.ui.adapter;
import java.util.List;
-import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
@@ -35,7 +34,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.conversation_list_row,
parent, false);
}
@@ -54,19 +53,19 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
}
}
TextView convName = (TextView) view
- .findViewById(R.id.conversation_name);
+ .findViewById(R.id.conversation_name);
if (conversation.getMode() == Conversation.MODE_SINGLE
|| activity.useSubjectToIdentifyConference()) {
convName.setText(conversation.getName());
} else {
- convName.setText(conversation.getContactJid().toBareJid().toString());
+ convName.setText(conversation.getJid().toBareJid().toString());
}
TextView mLastMessage = (TextView) view
- .findViewById(R.id.conversation_lastmsg);
+ .findViewById(R.id.conversation_lastmsg);
TextView mTimestamp = (TextView) view
- .findViewById(R.id.conversation_lastupdate);
+ .findViewById(R.id.conversation_lastupdate);
ImageView imagePreview = (ImageView) view
- .findViewById(R.id.conversation_lastimage);
+ .findViewById(R.id.conversation_lastimage);
Message message = conversation.getLatestMessage();
@@ -154,12 +153,12 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
imagePreview.setVisibility(View.GONE);
}
mTimestamp.setText(UIHelper.readableTimeDifference(getContext(),
- conversation.getLatestMessage().getTimeSent()));
+ conversation.getLatestMessage().getTimeSent()));
ImageView profilePicture = (ImageView) view
- .findViewById(R.id.conversation_image);
+ .findViewById(R.id.conversation_image);
profilePicture.setImageBitmap(activity.avatarService().get(
- conversation, activity.getPixel(56)));
+ conversation, activity.getPixel(56)));
return view;
}
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
index 0865d1aa..91fb021c 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.ui.adapter;
import java.util.List;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.ui.XmppActivity;
@@ -10,6 +11,7 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -22,6 +24,17 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
protected XmppActivity activity;
protected boolean showDynamicTags = false;
+ private View.OnClickListener onTagTvClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (view instanceof TextView && mOnTagClickedListener != null) {
+ TextView tv = (TextView) view;
+ final String tag = tv.getText().toString();
+ mOnTagClickedListener.onTagClicked(tag);
+ }
+ }
+ };
+ private OnTagClickedListener mOnTagClickedListener = null;
public ListItemAdapter(XmppActivity activity, List<ListItem> objects) {
super(activity, 0, objects);
@@ -53,6 +66,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tagLayout,false);
tv.setText(tag.getName());
tv.setBackgroundColor(tag.getColor());
+ tv.setOnClickListener(this.onTagTvClick);
tagLayout.addView(tv);
}
}
@@ -68,4 +82,12 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
return view;
}
+ public void setOnTagClickedListener(OnTagClickedListener listener) {
+ this.mOnTagClickedListener = listener;
+ }
+
+ public interface OnTagClickedListener {
+ public void onTagClicked(String tag);
+ }
+
}
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 00000e2b..83b4e41b 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -23,7 +23,6 @@ import android.widget.Toast;
import java.util.List;
-import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
@@ -271,7 +270,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public void onClick(View v) {
- startDonwloadable(message);
+ startDownloadable(message);
}
});
viewHolder.download_button.setOnLongClickListener(openContextMenu);
@@ -287,7 +286,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public void onClick(View v) {
- openDonwloadable(file);
+ openDownloadable(file);
}
});
viewHolder.download_button.setOnLongClickListener(openContextMenu);
@@ -441,6 +440,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
view.setLayoutParams(view.getLayoutParams());
return view;
+ } else if (viewHolder.messageBody == null || viewHolder.image == null) {
+ return view; //avoiding weird platform bugs
} else if (type == RECEIVED) {
Contact contact = message.getContact();
if (contact != null) {
@@ -449,38 +450,36 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(getDisplayedMucCounterpart(message.getCounterpart()),
activity.getPixel(48)));
}
- } else if (type == SENT && viewHolder.contact_picture != null) {
+ } else if (type == SENT) {
viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
}
- if (viewHolder != null && viewHolder.contact_picture != null) {
- viewHolder.contact_picture
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
- MessageAdapter.this.mOnContactPictureClickedListener
- .onContactPictureClicked(message);
- }
+ viewHolder.contact_picture
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureClickedListener
+ .onContactPictureClicked(message);
}
- });
- viewHolder.contact_picture
- .setOnLongClickListener(new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
- MessageAdapter.this.mOnContactPictureLongClickedListener
- .onContactPictureLongClicked(message);
- return true;
- } else {
- return false;
- }
+
+ }
+ });
+ viewHolder.contact_picture
+ .setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureLongClickedListener
+ .onContactPictureLongClicked(message);
+ return true;
+ } else {
+ return false;
}
- });
- }
+ }
+ });
if (message.getDownloadable() != null && message.getDownloadable().getStatus() != Downloadable.STATUS_UPLOADING) {
Downloadable d = message.getDownloadable();
@@ -549,7 +548,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
return view;
}
- public void startDonwloadable(Message message) {
+ public void startDownloadable(Message message) {
Downloadable downloadable = message.getDownloadable();
if (downloadable != null) {
if (!downloadable.start()) {
@@ -559,7 +558,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
- public void openDonwloadable(DownloadableFile file) {
+ public void openDownloadable(DownloadableFile file) {
if (!file.exists()) {
Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
return;
diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
index 8c1a8dea..a09b4d0f 100644
--- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
@@ -140,15 +140,18 @@ public class DNSHelper {
}
ArrayList<Bundle> values = new ArrayList<>();
for (SRV srv : result) {
- Bundle namePort = new Bundle();
- namePort.putString("name", srv.getName());
- namePort.putInt("port", srv.getPort());
+ boolean added = false;
+ if (ips6.containsKey(srv.getName())) {
+ values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6));
+ added = true;
+ }
if (ips4.containsKey(srv.getName())) {
- ArrayList<String> ip = ips4.get(srv.getName());
- Collections.shuffle(ip, rnd);
- namePort.putString("ipv4", ip.get(0));
+ values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4));
+ added = true;
+ }
+ if (!added) {
+ values.add(createNamePortBundle(srv.getName(),srv.getPort(),null));
}
- values.add(namePort);
}
bundle.putParcelableArrayList("values", values);
} catch (SocketTimeoutException e) {
@@ -159,6 +162,18 @@ public class DNSHelper {
return bundle;
}
+ private static Bundle createNamePortBundle(String name, int port, TreeMap<String, ArrayList<String>> ips) {
+ Bundle namePort = new Bundle();
+ namePort.putString("name", name);
+ namePort.putInt("port", port);
+ if (ips!=null) {
+ ArrayList<String> ip = ips.get(name);
+ Collections.shuffle(ip, new Random());
+ namePort.putString("ip", ip.get(0));
+ }
+ return namePort;
+ }
+
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
diff --git a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
index 87973159..9a5cbaaf 100644
--- a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
@@ -16,10 +16,7 @@ import android.provider.ContactsContract.Profile;
public class PhoneHelper {
- public static void loadPhoneContacts(Context context,
- final OnPhoneContactsLoadedListener listener) {
- final List<Bundle> phoneContacts = new ArrayList<Bundle>();
-
+ public static void loadPhoneContacts(Context context,final List<Bundle> phoneContacts, final OnPhoneContactsLoadedListener listener) {
final String[] PROJECTION = new String[] { ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.PHOTO_URI,
diff --git a/src/main/java/eu/siacs/conversations/utils/Validator.java b/src/main/java/eu/siacs/conversations/utils/Validator.java
deleted file mode 100644
index 00130fa2..00000000
--- a/src/main/java/eu/siacs/conversations/utils/Validator.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package eu.siacs.conversations.utils;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Validator {
- public static final Pattern VALID_JID = Pattern.compile(
- "^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE);
-
- public static boolean isValidJid(String jid) {
- Matcher matcher = VALID_JID.matcher(jid);
- return matcher.find();
- }
-}
diff --git a/src/main/java/eu/siacs/conversations/utils/Xmlns.java b/src/main/java/eu/siacs/conversations/utils/Xmlns.java
new file mode 100644
index 00000000..67de7c79
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/utils/Xmlns.java
@@ -0,0 +1,7 @@
+package eu.siacs.conversations.utils;
+
+public final class Xmlns {
+ public static final String BLOCKING = "urn:xmpp:blocking";
+ public static final String ROSTER = "jabber:iq:roster";
+ public static final String REGISTER = "jabber:iq:register";
+}
diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
index a9b8d1c0..aacb6362 100644
--- a/src/main/java/eu/siacs/conversations/utils/XmppUri.java
+++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
@@ -5,7 +5,6 @@ import android.net.Uri;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
-import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -54,7 +53,7 @@ public class XmppUri {
final String NEEDLE = "otr-fingerprint=";
int index = query.indexOf(NEEDLE);
if (index >= 0 && query.length() >= (NEEDLE.length() + index + 40)) {
- return CryptoHelper.prettifyFingerprint(query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40));
+ return query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40);
} else {
return null;
}
diff --git a/src/main/java/eu/siacs/conversations/utils/zlib/ZLibInputStream.java b/src/main/java/eu/siacs/conversations/utils/zlib/ZLibInputStream.java
deleted file mode 100644
index b777c10c..00000000
--- a/src/main/java/eu/siacs/conversations/utils/zlib/ZLibInputStream.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package eu.siacs.conversations.utils.zlib;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-
-/**
- * ZLibInputStream is a zlib and input stream compatible version of an
- * InflaterInputStream. This class solves the incompatibility between
- * {@link InputStream#available()} and {@link InflaterInputStream#available()}.
- */
-public class ZLibInputStream extends InflaterInputStream {
-
- /**
- * Construct a ZLibInputStream, reading data from the underlying stream.
- *
- * @param is
- * The {@code InputStream} to read data from.
- * @throws IOException
- * If an {@code IOException} occurs.
- */
- public ZLibInputStream(InputStream is) throws IOException {
- super(is, new Inflater(), 512);
- }
-
- /**
- * Provide a more InputStream compatible version of available. A return
- * value of 1 means that it is likly to read one byte without blocking, 0
- * means that the system is known to block for more input.
- *
- * @return 0 if no data is available, 1 otherwise
- * @throws IOException
- */
- @Override
- public int available() throws IOException {
- /*
- * This is one of the funny code blocks. InflaterInputStream.available
- * violates the contract of InputStream.available, which breaks kXML2.
- *
- * I'm not sure who's to blame, oracle/sun for a broken api or the
- * google guys for mixing a sun bug with a xml reader that can't handle
- * it....
- *
- * Anyway, this simple if breaks suns distorted reality, but helps to
- * use the api as intended.
- */
- if (inf.needsInput()) {
- return 0;
- }
- return super.available();
- }
-
-}
diff --git a/src/main/java/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java b/src/main/java/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java
deleted file mode 100644
index 8b3f5e68..00000000
--- a/src/main/java/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package eu.siacs.conversations.utils.zlib;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.security.NoSuchAlgorithmException;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-
-/**
- * <p>
- * Android 2.2 includes Java7 FLUSH_SYNC option, which will be used by this
- * Implementation, preferable via reflection. The @hide was remove in API level
- * 19. This class might thus go away in the future.
- * </p>
- * <p>
- * Please use {@link ZLibOutputStream#SUPPORTED} to check for flush
- * compatibility.
- * </p>
- */
-public class ZLibOutputStream extends DeflaterOutputStream {
-
- /**
- * The reflection based flush method.
- */
-
- private final static Method method;
- /**
- * SUPPORTED is true if a flush compatible method exists.
- */
- public final static boolean SUPPORTED;
-
- /**
- * Static block to initialize {@link #SUPPORTED} and {@link #method}.
- */
- static {
- Method m = null;
- try {
- m = Deflater.class.getMethod("deflate", byte[].class, int.class,
- int.class, int.class);
- } catch (SecurityException e) {
- } catch (NoSuchMethodException e) {
- }
- method = m;
- SUPPORTED = (method != null);
- }
-
- /**
- * Create a new ZLib compatible output stream wrapping the given low level
- * stream. ZLib compatiblity means we will send a zlib header.
- *
- * @param os
- * OutputStream The underlying stream.
- * @throws IOException
- * In case of a lowlevel transfer problem.
- * @throws NoSuchAlgorithmException
- * In case of a {@link Deflater} error.
- */
- public ZLibOutputStream(OutputStream os) throws IOException,
- NoSuchAlgorithmException {
- super(os, new Deflater(Deflater.BEST_COMPRESSION));
- }
-
- /**
- * Flush the given stream, preferring Java7 FLUSH_SYNC if available.
- *
- * @throws IOException
- * In case of a lowlevel exception.
- */
- @Override
- public void flush() throws IOException {
- if (!SUPPORTED) {
- super.flush();
- return;
- }
- try {
- int count = 0;
- do {
- count = (Integer) method.invoke(def, buf, 0, buf.length, 3);
- if (count > 0) {
- out.write(buf, 0, count);
- }
- } while (count > 0);
- } catch (IllegalArgumentException e) {
- throw new IOException("Can't flush");
- } catch (IllegalAccessException e) {
- throw new IOException("Can't flush");
- } catch (InvocationTargetException e) {
- throw new IOException("Can't flush");
- }
- super.flush();
- }
-
-}
diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java
index 02c3e695..51708759 100644
--- a/src/main/java/eu/siacs/conversations/xml/Element.java
+++ b/src/main/java/eu/siacs/conversations/xml/Element.java
@@ -1,9 +1,12 @@
package eu.siacs.conversations.xml;
+import android.util.Log;
+
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.XmlHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -59,16 +62,16 @@ public class Element {
if (child.getName().equals(name)
&& (child.getAttribute("xmlns").equals(xmlns))) {
return child;
- }
+ }
}
return null;
}
- public boolean hasChild(String name) {
+ public boolean hasChild(final String name) {
return findChild(name) != null;
}
- public boolean hasChild(String name, String xmlns) {
+ public boolean hasChild(final String name, final String xmlns) {
return findChild(name, xmlns) != null;
}
@@ -107,14 +110,15 @@ public class Element {
public Jid getAttributeAsJid(String name) {
final String jid = this.getAttribute(name);
- if (jid != null && !jid.isEmpty()) {
- try {
- return Jid.fromString(jid);
- } catch (final InvalidJidException e) {
- return null;
- }
- }
- return null;
+ if (jid != null && !jid.isEmpty()) {
+ try {
+ return Jid.fromString(jid);
+ } catch (final InvalidJidException e) {
+ Log.e(Config.LOGTAG, "could not parse jid " + jid);
+ return null;
+ }
+ }
+ return null;
}
public Hashtable<String, String> getAttributes() {
@@ -159,4 +163,9 @@ public class Element {
public void setAttribute(String name, int value) {
this.setAttribute(name, Integer.toString(value));
}
+
+ public boolean getAttributeAsBoolean(String name) {
+ String attr = getAttribute(name);
+ return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java b/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java
new file mode 100644
index 00000000..e45eba73
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java
@@ -0,0 +1,7 @@
+package eu.siacs.conversations.xmpp;
+
+import eu.siacs.conversations.entities.Account;
+
+public interface OnAdvancedStreamFeaturesLoaded {
+ public void onAdvancedStreamFeaturesAvailable(final Account account);
+}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/OnContactStatusChanged.java b/src/main/java/eu/siacs/conversations/xmpp/OnContactStatusChanged.java
index 849e8e76..20b17f02 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/OnContactStatusChanged.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/OnContactStatusChanged.java
@@ -3,5 +3,5 @@ package eu.siacs.conversations.xmpp;
import eu.siacs.conversations.entities.Contact;
public interface OnContactStatusChanged {
- public void onContactStatusChanged(Contact contact, boolean online);
+ public void onContactStatusChanged(final Contact contact, final boolean online);
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/OnUpdateBlocklist.java b/src/main/java/eu/siacs/conversations/xmpp/OnUpdateBlocklist.java
new file mode 100644
index 00000000..92e72cfa
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/xmpp/OnUpdateBlocklist.java
@@ -0,0 +1,13 @@
+package eu.siacs.conversations.xmpp;
+
+public interface OnUpdateBlocklist {
+ // Use an enum instead of a boolean to make sure we don't run into the boolean trap
+ // (`onUpdateBlocklist(true)' doesn't read well, and could be confusing).
+ public static enum Status {
+ BLOCKED,
+ UNBLOCKED
+ }
+
+ @SuppressWarnings("MethodNameSameAsClassName")
+ public void OnUpdateBlocklist(final Status status);
+}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index fb151427..f7f0c346 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -30,10 +30,12 @@ import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import javax.net.ssl.HostnameVerifier;
@@ -48,10 +50,10 @@ import eu.siacs.conversations.crypto.sasl.Plain;
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
import eu.siacs.conversations.crypto.sasl.ScramSha1;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.generator.IqGenerator;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.DNSHelper;
-import eu.siacs.conversations.utils.zlib.ZLibInputStream;
-import eu.siacs.conversations.utils.zlib.ZLibOutputStream;
+import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Tag;
import eu.siacs.conversations.xml.TagWriter;
@@ -78,21 +80,20 @@ public class XmppConnection implements Runnable {
private static final int PACKET_PRESENCE = 2;
private final Context applicationContext;
protected Account account;
- private WakeLock wakeLock;
+ private final WakeLock wakeLock;
private Socket socket;
private XmlReader tagReader;
private TagWriter tagWriter;
- private Features features = new Features(this);
+ private final Features features = new Features(this);
private boolean shouldBind = true;
private boolean shouldAuthenticate = true;
private Element streamFeatures;
- private HashMap<String, List<String>> disco = new HashMap<>();
+ private final HashMap<String, List<String>> disco = new HashMap<>();
private String streamId = null;
private int smVersion = 3;
- private SparseArray<String> messageReceipts = new SparseArray<>();
+ private final SparseArray<String> messageReceipts = new SparseArray<>();
- private boolean enabledCompression = false;
private boolean enabledEncryption = false;
private boolean enabledCarbons = false;
@@ -103,19 +104,20 @@ public class XmppConnection implements Runnable {
private long lastConnect = 0;
private long lastSessionStarted = 0;
private int attempt = 0;
- private Hashtable<String, PacketReceived> packetCallbacks = new Hashtable<>();
+ private final Map<String, PacketReceived> packetCallbacks = new Hashtable<>();
private OnPresencePacketReceived presenceListener = null;
private OnJinglePacketReceived jingleListener = null;
private OnIqPacketReceived unregisteredIqListener = null;
private OnMessagePacketReceived messageListener = null;
private OnStatusChanged statusListener = null;
private OnBindListener bindListener = null;
+ private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
private OnMessageAcknowledged acknowledgedListener = null;
private XmppConnectionService mXmppConnectionService = null;
private SaslMechanism saslMechanism;
- public XmppConnection(Account account, XmppConnectionService service) {
+ public XmppConnection(final Account account, final XmppConnectionService service) {
this.account = account;
this.wakeLock = service.getPowerManager().newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString());
@@ -131,7 +133,7 @@ public class XmppConnection implements Runnable {
&& (account.getStatus() != Account.State.ONLINE)
&& (account.getStatus() != Account.State.DISABLED)) {
return;
- }
+ }
if (nextStatus == Account.State.ONLINE) {
this.attempt = 0;
}
@@ -144,7 +146,6 @@ public class XmppConnection implements Runnable {
protected void connect() {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
- enabledCompression = false;
enabledEncryption = false;
lastConnect = SystemClock.elapsedRealtime();
lastPingSent = SystemClock.elapsedRealtime();
@@ -156,15 +157,15 @@ public class XmppConnection implements Runnable {
tagWriter = new TagWriter();
packetCallbacks.clear();
this.changeStatus(Account.State.CONNECTING);
- Bundle result = DNSHelper.getSRVRecord(account.getServer());
- ArrayList<Parcelable> values = result.getParcelableArrayList("values");
+ final Bundle result = DNSHelper.getSRVRecord(account.getServer());
+ final ArrayList<Parcelable> values = result.getParcelableArrayList("values");
if ("timeout".equals(result.getString("error"))) {
throw new IOException("timeout in dns");
} else if (values != null) {
int i = 0;
boolean socketError = true;
while (socketError && values.size() > i) {
- Bundle namePort = (Bundle) values.get(i);
+ final Bundle namePort = (Bundle) values.get(i);
try {
String srvRecordServer;
try {
@@ -173,9 +174,9 @@ public class XmppConnection implements Runnable {
// TODO: Handle me?`
srvRecordServer = "";
}
- int srvRecordPort = namePort.getInt("port");
- String srvIpServer = namePort.getString("ipv4");
- InetSocketAddress addr;
+ final int srvRecordPort = namePort.getInt("port");
+ final String srvIpServer = namePort.getString("ip");
+ final InetSocketAddress addr;
if (srvIpServer != null) {
addr = new InetSocketAddress(srvIpServer, srvRecordPort);
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
@@ -190,10 +191,10 @@ public class XmppConnection implements Runnable {
socket = new Socket();
socket.connect(addr, 20000);
socketError = false;
- } catch (UnknownHostException e) {
+ } catch (final UnknownHostException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
i++;
- } catch (IOException e) {
+ } catch (final IOException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
i++;
}
@@ -207,9 +208,9 @@ public class XmppConnection implements Runnable {
} else {
throw new IOException("timeout in dns");
}
- OutputStream out = socket.getOutputStream();
+ final OutputStream out = socket.getOutputStream();
tagWriter.setOutputStream(out);
- InputStream in = socket.getInputStream();
+ final InputStream in = socket.getInputStream();
tagReader.setInputStream(in);
tagWriter.beginDocument();
sendStartStream();
@@ -225,14 +226,9 @@ public class XmppConnection implements Runnable {
if (socket.isConnected()) {
socket.close();
}
- } catch (UnknownHostException e) {
+ } catch (final UnknownHostException | ConnectException e) {
this.changeStatus(Account.State.SERVER_NOT_FOUND);
- } catch (final ConnectException e) {
- this.changeStatus(Account.State.SERVER_NOT_FOUND);
- } catch (final IOException | XmlPullParserException e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
- this.changeStatus(Account.State.OFFLINE);
- } catch (NoSuchAlgorithmException e) {
+ } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
this.changeStatus(Account.State.OFFLINE);
} finally {
@@ -261,8 +257,6 @@ public class XmppConnection implements Runnable {
processStreamFeatures(nextTag);
} else if (nextTag.isStart("proceed")) {
switchOverToTls(nextTag);
- } else if (nextTag.isStart("compressed")) {
- switchOverToZLib(nextTag);
} else if (nextTag.isStart("success")) {
final String challenge = tagReader.readElement(nextTag).getContent();
try {
@@ -273,7 +267,7 @@ public class XmppConnection implements Runnable {
}
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
account.setKey(Account.PINNED_MECHANISM_KEY,
- String.valueOf(saslMechanism.getPriority()));
+ String.valueOf(saslMechanism.getPriority()));
tagReader.reset();
sendStartStream();
processStream(tagReader.readTag());
@@ -294,7 +288,7 @@ public class XmppConnection implements Runnable {
}
tagWriter.writeElement(response);
} else if (nextTag.isStart("enabled")) {
- Element enabled = tagReader.readElement(nextTag);
+ final Element enabled = tagReader.readElement(nextTag);
if ("true".equals(enabled.getAttribute("resume"))) {
this.streamId = enabled.getAttribute("id");
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
@@ -306,14 +300,14 @@ public class XmppConnection implements Runnable {
}
this.lastSessionStarted = SystemClock.elapsedRealtime();
this.stanzasReceived = 0;
- RequestPacket r = new RequestPacket(smVersion);
+ final RequestPacket r = new RequestPacket(smVersion);
tagWriter.writeStanzaAsync(r);
} else if (nextTag.isStart("resumed")) {
lastPaketReceived = SystemClock.elapsedRealtime();
- Element resumed = tagReader.readElement(nextTag);
- String h = resumed.getAttribute("h");
+ final Element resumed = tagReader.readElement(nextTag);
+ final String h = resumed.getAttribute("h");
try {
- int serverCount = Integer.parseInt(h);
+ final int serverCount = Integer.parseInt(h);
if (serverCount != stanzasSent) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": session resumed with lost packages");
@@ -332,20 +326,19 @@ public class XmppConnection implements Runnable {
}
messageReceipts.clear();
} catch (final NumberFormatException ignored) {
-
}
sendServiceDiscoveryInfo(account.getServer());
sendServiceDiscoveryItems(account.getServer());
sendInitialPing();
} else if (nextTag.isStart("r")) {
tagReader.readElement(nextTag);
- AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
+ final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
tagWriter.writeStanzaAsync(ack);
} else if (nextTag.isStart("a")) {
- Element ack = tagReader.readElement(nextTag);
+ final Element ack = tagReader.readElement(nextTag);
lastPaketReceived = SystemClock.elapsedRealtime();
- int serverSequence = Integer.parseInt(ack.getAttribute("h"));
- String msgId = this.messageReceipts.get(serverSequence);
+ final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
+ final String msgId = this.messageReceipts.get(serverSequence);
if (msgId != null) {
if (this.acknowledgedListener != null) {
this.acknowledgedListener.onMessageAcknowledged(
@@ -379,13 +372,12 @@ public class XmppConnection implements Runnable {
private void sendInitialPing() {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": sending intial ping");
- IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
iq.setFrom(account.getJid());
iq.addChild("ping", "urn:xmpp:ping");
this.sendIqPacket(iq, new OnIqPacketReceived() {
-
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": online with resource " + account.getResource());
changeStatus(Account.State.ONLINE);
@@ -393,7 +385,7 @@ public class XmppConnection implements Runnable {
});
}
- private Element processPacket(Tag currentTag, int packetType)
+ private Element processPacket(final Tag currentTag, final int packetType)
throws XmlPullParserException, IOException {
Element element;
switch (packetType) {
@@ -416,8 +408,8 @@ public class XmppConnection implements Runnable {
}
while (!nextTag.isEnd(element.getName())) {
if (!nextTag.isNo()) {
- Element child = tagReader.readElement(nextTag);
- String type = currentTag.getAttribute("type");
+ final Element child = tagReader.readElement(nextTag);
+ final String type = currentTag.getAttribute("type");
if (packetType == PACKET_IQ
&& "jingle".equals(child.getName())
&& ("set".equalsIgnoreCase(type) || "get"
@@ -437,9 +429,9 @@ public class XmppConnection implements Runnable {
return element;
}
- private void processIq(Tag currentTag) throws XmlPullParserException,
+ private void processIq(final Tag currentTag) throws XmlPullParserException,
IOException {
- IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
+ final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
if (packet.getId() == null) {
return; // an iq packet without id is definitely invalid
@@ -466,11 +458,11 @@ public class XmppConnection implements Runnable {
}
}
- private void processMessage(Tag currentTag) throws XmlPullParserException,
+ private void processMessage(final Tag currentTag) throws XmlPullParserException,
IOException {
- MessagePacket packet = (MessagePacket) processPacket(currentTag,
+ final MessagePacket packet = (MessagePacket) processPacket(currentTag,
PACKET_MESSAGE);
- String id = packet.getAttribute("id");
+ final String id = packet.getAttribute("id");
if ((id != null) && (packetCallbacks.containsKey(id))) {
if (packetCallbacks.get(id) instanceof OnMessagePacketReceived) {
((OnMessagePacketReceived) packetCallbacks.get(id))
@@ -482,11 +474,11 @@ public class XmppConnection implements Runnable {
}
}
- private void processPresence(Tag currentTag) throws XmlPullParserException,
+ private void processPresence(final Tag currentTag) throws XmlPullParserException,
IOException {
PresencePacket packet = (PresencePacket) processPacket(currentTag,
PACKET_PRESENCE);
- String id = packet.getAttribute("id");
+ final String id = packet.getAttribute("id");
if ((id != null) && (packetCallbacks.containsKey(id))) {
if (packetCallbacks.get(id) instanceof OnPresencePacketReceived) {
((OnPresencePacketReceived) packetCallbacks.get(id))
@@ -498,30 +490,8 @@ public class XmppConnection implements Runnable {
}
}
- private void sendCompressionZlib() throws IOException {
- Element compress = new Element("compress");
- compress.setAttribute("xmlns", "http://jabber.org/protocol/compress");
- compress.addChild("method").setContent("zlib");
- tagWriter.writeElement(compress);
- }
-
- private void switchOverToZLib(final Tag currentTag)
- throws XmlPullParserException, IOException,
- NoSuchAlgorithmException {
- tagReader.readTag(); // read tag close
- tagWriter.setOutputStream(new ZLibOutputStream(tagWriter
- .getOutputStream()));
- tagReader
- .setInputStream(new ZLibInputStream(tagReader.getInputStream()));
-
- sendStartStream();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": compression enabled");
- enabledCompression = true;
- processStream(tagReader.readTag());
- }
-
private void sendStartTLS() throws IOException {
- Tag startTLS = Tag.empty("starttls");
+ final Tag startTLS = Tag.empty("starttls");
startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
tagWriter.writeTag(startTLS);
}
@@ -535,74 +505,61 @@ public class XmppConnection implements Runnable {
return getPreferences().getBoolean("enable_legacy_ssl", false);
}
- private void switchOverToTls(final Tag currentTag) throws XmlPullParserException,
- IOException {
- tagReader.readTag();
- try {
- SSLContext sc = SSLContext.getInstance("TLS");
- sc.init(null,
- new X509TrustManager[]{this.mXmppConnectionService.getMemorizingTrustManager()},
- mXmppConnectionService.getRNG());
- SSLSocketFactory factory = sc.getSocketFactory();
-
- if (factory == null) {
- throw new IOException("SSLSocketFactory was null");
- }
-
- final HostnameVerifier verifier = this.mXmppConnectionService.getMemorizingTrustManager().wrapHostnameVerifier(new StrictHostnameVerifier());
+ private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
+ tagReader.readTag();
+ try {
+ final SSLContext sc = SSLContext.getInstance("TLS");
+ sc.init(null,new X509TrustManager[]{this.mXmppConnectionService.getMemorizingTrustManager()},mXmppConnectionService.getRNG());
+ final SSLSocketFactory factory = sc.getSocketFactory();
+ final HostnameVerifier verifier = this.mXmppConnectionService.getMemorizingTrustManager().wrapHostnameVerifier(new StrictHostnameVerifier());
+ final InetAddress address = socket == null ? null : socket.getInetAddress();
+
+ if (factory == null || address == null || verifier == null) {
+ throw new IOException("could not setup ssl");
+ }
- if (socket == null || socket.isClosed()) {
- throw new IOException("socket null or closed");
- }
- final InetAddress address = socket.getInetAddress();
- if (address == null) {
- throw new IOException("socket address was null");
- }
+ final SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,address.getHostAddress(), socket.getPort(),true);
- final SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,address.getHostAddress(), socket.getPort(),true);
+ if (sslSocket == null) {
+ throw new IOException("could not initialize ssl socket");
+ }
- // Support all protocols except legacy SSL.
- // The min SDK version prevents us having to worry about SSLv2. In
- // future, this may be true of SSLv3 as well.
- final String[] supportProtocols;
- if (enableLegacySSL()) {
- supportProtocols = sslSocket.getSupportedProtocols();
- } else {
- final List<String> supportedProtocols = new LinkedList<>(
- Arrays.asList(sslSocket.getSupportedProtocols()));
- supportedProtocols.remove("SSLv3");
- supportProtocols = new String[supportedProtocols.size()];
- supportedProtocols.toArray(supportProtocols);
- }
- sslSocket.setEnabledProtocols(supportProtocols);
-
- if (verifier != null
- && !verifier.verify(account.getServer().getDomainpart(),
- sslSocket.getSession())) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
- disconnect(true);
- changeStatus(Account.State.SECURITY_ERROR);
- }
- tagReader.setInputStream(sslSocket.getInputStream());
- tagWriter.setOutputStream(sslSocket.getOutputStream());
- sendStartStream();
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": TLS connection established");
- enabledEncryption = true;
- processStream(tagReader.readTag());
- sslSocket.close();
- } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
- e1.printStackTrace();
- }
+ final String[] supportProtocols;
+ if (enableLegacySSL()) {
+ supportProtocols = sslSocket.getSupportedProtocols();
+ } else {
+ final Collection<String> supportedProtocols = new LinkedList<>(
+ Arrays.asList(sslSocket.getSupportedProtocols()));
+ supportedProtocols.remove("SSLv3");
+ supportProtocols = new String[supportedProtocols.size()];
+ supportedProtocols.toArray(supportProtocols);
+ }
+ sslSocket.setEnabledProtocols(supportProtocols);
+
+ if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
+ disconnect(true);
+ changeStatus(Account.State.SECURITY_ERROR);
+ }
+ tagReader.setInputStream(sslSocket.getInputStream());
+ tagWriter.setOutputStream(sslSocket.getOutputStream());
+ sendStartStream();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": TLS connection established");
+ enabledEncryption = true;
+ processStream(tagReader.readTag());
+ sslSocket.close();
+ } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
+ disconnect(true);
+ changeStatus(Account.State.SECURITY_ERROR);
+ }
}
- private void processStreamFeatures(Tag currentTag)
+ private void processStreamFeatures(final Tag currentTag)
throws XmlPullParserException, IOException {
this.streamFeatures = tagReader.readElement(currentTag);
if (this.streamFeatures.hasChild("starttls") && !enabledEncryption) {
sendStartTLS();
- } else if (compressionAvailable()) {
- sendCompressionZlib();
} else if (this.streamFeatures.hasChild("register")
&& account.isOptionSet(Account.OPTION_REGISTER)
&& enabledEncryption) {
@@ -619,10 +576,10 @@ public class XmppConnection implements Runnable {
auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
if (mechanisms.contains("SCRAM-SHA-1")) {
saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("DIGEST-MD5")) {
- saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
} else if (mechanisms.contains("PLAIN")) {
saslMechanism = new Plain(tagWriter, account);
+ } else if (mechanisms.contains("DIGEST-MD5")) {
+ saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
}
final JSONObject keys = account.getKeys();
try {
@@ -634,7 +591,7 @@ public class XmppConnection implements Runnable {
"). Possible downgrade attack?");
disconnect(true);
changeStatus(Account.State.SECURITY_ERROR);
- }
+ }
} catch (final JSONException e) {
Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
}
@@ -647,7 +604,7 @@ public class XmppConnection implements Runnable {
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
+ smVersion)
&& streamId != null) {
- ResumePacket resume = new ResumePacket(this.streamId,
+ final ResumePacket resume = new ResumePacket(this.streamId,
stanzasReceived, smVersion);
this.tagWriter.writeStanzaAsync(resume);
} else if (this.streamFeatures.hasChild("bind") && shouldBind) {
@@ -658,67 +615,44 @@ public class XmppConnection implements Runnable {
}
}
- private boolean compressionAvailable() {
- if (!this.streamFeatures.hasChild("compression",
- "http://jabber.org/features/compress"))
- return false;
- if (!ZLibOutputStream.SUPPORTED)
- return false;
- if (!account.isOptionSet(Account.OPTION_USECOMPRESSION))
- return false;
-
- Element compression = this.streamFeatures.findChild("compression",
- "http://jabber.org/features/compress");
- for (Element child : compression.getChildren()) {
- if (!"method".equals(child.getName()))
- continue;
-
- if ("zlib".equalsIgnoreCase(child.getContent())) {
- return true;
- }
- }
- return false;
- }
-
- private List<String> extractMechanisms(Element stream) {
- ArrayList<String> mechanisms = new ArrayList<>(stream
+ private List<String> extractMechanisms(final Element stream) {
+ final ArrayList<String> mechanisms = new ArrayList<>(stream
.getChildren().size());
- for (Element child : stream.getChildren()) {
+ for (final Element child : stream.getChildren()) {
mechanisms.add(child.getContent());
}
return mechanisms;
}
private void sendRegistryRequest() {
- IqPacket register = new IqPacket(IqPacket.TYPE_GET);
+ final IqPacket register = new IqPacket(IqPacket.TYPE_GET);
register.query("jabber:iq:register");
register.setTo(account.getServer());
sendIqPacket(register, new OnIqPacketReceived() {
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element instructions = packet.query().findChild("instructions");
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ final Element instructions = packet.query().findChild("instructions");
if (packet.query().hasChild("username")
&& (packet.query().hasChild("password"))) {
- IqPacket register = new IqPacket(IqPacket.TYPE_SET);
- Element username = new Element("username")
- .setContent(account.getUsername());
- Element password = new Element("password")
- .setContent(account.getPassword());
+ final IqPacket register = new IqPacket(IqPacket.TYPE_SET);
+ final Element username = new Element("username")
+ .setContent(account.getUsername());
+ final Element password = new Element("password")
+ .setContent(account.getPassword());
register.query("jabber:iq:register").addChild(username);
register.query().addChild(password);
sendIqPacket(register, new OnIqPacketReceived() {
@Override
- public void onIqPacketReceived(Account account,
- IqPacket packet) {
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_RESULT) {
account.setOption(Account.OPTION_REGISTER,
false);
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
} else if (packet.hasChild("error")
&& (packet.findChild("error")
- .hasChild("conflict"))) {
+ .hasChild("conflict"))) {
changeStatus(Account.State.REGISTRATION_CONFLICT);
} else {
changeStatus(Account.State.REGISTRATION_FAILED);
@@ -738,14 +672,14 @@ public class XmppConnection implements Runnable {
});
}
- private void sendBindRequest() throws IOException {
- IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+ private void sendBindRequest() {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
.addChild("resource").setContent(account.getResource());
this.sendUnboundIqPacket(iq, new OnIqPacketReceived() {
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element bind = packet.findChild("bind");
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ final Element bind = packet.findChild("bind");
if (bind != null) {
final Element jid = bind.findChild("jid");
if (jid != null && jid.getContent() != null) {
@@ -756,14 +690,14 @@ public class XmppConnection implements Runnable {
}
if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
smVersion = 3;
- EnablePacket enable = new EnablePacket(smVersion);
+ final EnablePacket enable = new EnablePacket(smVersion);
tagWriter.writeStanzaAsync(enable);
stanzasSent = 0;
messageReceipts.clear();
} else if (streamFeatures.hasChild("sm",
- "urn:xmpp:sm:2")) {
+ "urn:xmpp:sm:2")) {
smVersion = 2;
- EnablePacket enable = new EnablePacket(smVersion);
+ final EnablePacket enable = new EnablePacket(smVersion);
tagWriter.writeStanzaAsync(enable);
stanzasSent = 0;
messageReceipts.clear();
@@ -787,7 +721,7 @@ public class XmppConnection implements Runnable {
if (this.streamFeatures.hasChild("session")) {
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": sending deprecated session");
- IqPacket startSession = new IqPacket(IqPacket.TYPE_SET);
+ final IqPacket startSession = new IqPacket(IqPacket.TYPE_SET);
startSession.addChild("session",
"urn:ietf:params:xml:ns:xmpp-session");
this.sendUnboundIqPacket(startSession, null);
@@ -806,10 +740,10 @@ public class XmppConnection implements Runnable {
this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
final List<Element> elements = packet.query().getChildren();
final List<String> features = new ArrayList<>();
- for (Element element : elements) {
+ for (final Element element : elements) {
if (element.getName().equals("identity")) {
if ("irc".equals(element.getAttribute("type"))) {
//add fake feature to not confuse irc and real muc
@@ -823,6 +757,9 @@ public class XmppConnection implements Runnable {
if (account.getServer().equals(server.toDomainJid())) {
enableAdvancedStreamFeatures();
+ for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
+ listener.onAdvancedStreamFeaturesAvailable(account);
+ }
}
}
});
@@ -835,6 +772,10 @@ public class XmppConnection implements Runnable {
sendEnableCarbons();
}
}
+ if (getFeatures().blocking()) {
+ Log.d(Config.LOGTAG, "Requesting block list");
+ this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
+ }
}
private void sendServiceDiscoveryItems(final Jid server) {
@@ -844,9 +785,9 @@ public class XmppConnection implements Runnable {
this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- List<Element> elements = packet.query().getChildren();
- for (Element element : elements) {
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ final List<Element> elements = packet.query().getChildren();
+ for (final Element element : elements) {
if (element.getName().equals("item")) {
final Jid jid = element.getAttributeAsJid("jid");
if (jid != null && !jid.equals(account.getServer())) {
@@ -859,12 +800,12 @@ public class XmppConnection implements Runnable {
}
private void sendEnableCarbons() {
- IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
iq.addChild("enable", "urn:xmpp:carbons:2");
this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (!packet.hasChild("error")) {
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": successfully enabled carbons");
@@ -877,20 +818,20 @@ public class XmppConnection implements Runnable {
});
}
- private void processStreamError(Tag currentTag)
+ private void processStreamError(final Tag currentTag)
throws XmlPullParserException, IOException {
- Element streamError = tagReader.readElement(currentTag);
+ final Element streamError = tagReader.readElement(currentTag);
if (streamError != null && streamError.hasChild("conflict")) {
final String resource = account.getResource().split("\\.")[0];
account.setResource(resource + "." + nextRandomId());
Log.d(Config.LOGTAG,
account.getJid().toBareJid() + ": switching resource due to conflict ("
- + account.getResource() + ")");
+ + account.getResource() + ")");
}
}
private void sendStartStream() throws IOException {
- Tag stream = Tag.start("stream:stream");
+ final Tag stream = Tag.start("stream:stream");
stream.setAttribute("from", account.getJid().toBareJid().toString());
stream.setAttribute("to", account.getServer().toString());
stream.setAttribute("version", "1.0");
@@ -904,33 +845,32 @@ public class XmppConnection implements Runnable {
return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
}
- public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) {
+ public void sendIqPacket(final IqPacket packet, final PacketReceived callback) {
if (packet.getId() == null) {
- String id = nextRandomId();
+ final String id = nextRandomId();
packet.setAttribute("id", id);
}
packet.setFrom(account.getJid());
this.sendPacket(packet, callback);
}
- public void sendUnboundIqPacket(IqPacket packet, OnIqPacketReceived callback) {
+ public void sendUnboundIqPacket(final IqPacket packet, final PacketReceived callback) {
if (packet.getId() == null) {
- String id = nextRandomId();
+ final String id = nextRandomId();
packet.setAttribute("id", id);
}
this.sendPacket(packet, callback);
}
- public void sendMessagePacket(MessagePacket packet) {
+ public void sendMessagePacket(final MessagePacket packet) {
this.sendPacket(packet, null);
}
- public void sendPresencePacket(PresencePacket packet) {
+ public void sendPresencePacket(final PresencePacket packet) {
this.sendPacket(packet, null);
}
- private synchronized void sendPacket(final AbstractStanza packet,
- PacketReceived callback) {
+ private synchronized void sendPacket(final AbstractStanza packet, final PacketReceived callback) {
if (packet.getName().equals("iq") || packet.getName().equals("message")
|| packet.getName().equals("presence")) {
++stanzasSent;
@@ -955,7 +895,7 @@ public class XmppConnection implements Runnable {
if (streamFeatures.hasChild("sm")) {
tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
} else {
- IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
+ final IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
iq.setFrom(account.getJid());
iq.addChild("ping", "urn:xmpp:ping");
this.sendIqPacket(iq, null);
@@ -964,38 +904,44 @@ public class XmppConnection implements Runnable {
}
public void setOnMessagePacketReceivedListener(
- OnMessagePacketReceived listener) {
+ final OnMessagePacketReceived listener) {
this.messageListener = listener;
}
public void setOnUnregisteredIqPacketReceivedListener(
- OnIqPacketReceived listener) {
+ final OnIqPacketReceived listener) {
this.unregisteredIqListener = listener;
}
public void setOnPresencePacketReceivedListener(
- OnPresencePacketReceived listener) {
+ final OnPresencePacketReceived listener) {
this.presenceListener = listener;
}
public void setOnJinglePacketReceivedListener(
- OnJinglePacketReceived listener) {
+ final OnJinglePacketReceived listener) {
this.jingleListener = listener;
}
- public void setOnStatusChangedListener(OnStatusChanged listener) {
+ public void setOnStatusChangedListener(final OnStatusChanged listener) {
this.statusListener = listener;
}
- public void setOnBindListener(OnBindListener listener) {
+ public void setOnBindListener(final OnBindListener listener) {
this.bindListener = listener;
}
- public void setOnMessageAcknowledgeListener(OnMessageAcknowledged listener) {
+ public void setOnMessageAcknowledgeListener(final OnMessageAcknowledged listener) {
this.acknowledgedListener = listener;
}
- public void disconnect(boolean force) {
+ public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) {
+ if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
+ this.advancedStreamFeaturesLoadedListeners.add(listener);
+ }
+ }
+
+ public void disconnect(final boolean force) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting");
try {
if (force) {
@@ -1015,23 +961,23 @@ public class XmppConnection implements Runnable {
}
tagWriter.writeTag(Tag.end("stream:stream"));
socket.close();
- } catch (IOException e) {
+ } catch (final IOException e) {
Log.d(Config.LOGTAG,
"io exception during disconnect");
- } catch (InterruptedException e) {
+ } catch (final InterruptedException e) {
Log.d(Config.LOGTAG, "interrupted");
}
}
}
}).start();
- } catch (IOException e) {
+ } catch (final IOException e) {
Log.d(Config.LOGTAG, "io exception during disconnect");
}
}
- public List<String> findDiscoItemsByFeature(String feature) {
+ public List<String> findDiscoItemsByFeature(final String feature) {
final List<String> items = new ArrayList<>();
- for (Entry<String, List<String>> cursor : disco.entrySet()) {
+ for (final Entry<String, List<String>> cursor : disco.entrySet()) {
if (cursor.getValue().contains(feature)) {
items.add(cursor.getKey());
}
@@ -1039,8 +985,8 @@ public class XmppConnection implements Runnable {
return items;
}
- public String findDiscoItemByFeature(String feature) {
- List<String> items = findDiscoItemsByFeature(feature);
+ public String findDiscoItemByFeature(final String feature) {
+ final List<String> items = findDiscoItemsByFeature(feature);
if (items.size() >= 1) {
return items.get(0);
}
@@ -1052,8 +998,7 @@ public class XmppConnection implements Runnable {
}
public String getMucServer() {
- final List<String> items = new ArrayList<>();
- for (Entry<String, List<String>> cursor : disco.entrySet()) {
+ for (final Entry<String, List<String>> cursor : disco.entrySet()) {
final List<String> value = cursor.getValue();
if (value.contains("http://jabber.org/protocol/muc") && !value.contains("jabber:iq:gateway") && !value.contains("siacs:no:muc")) {
return cursor.getKey();
@@ -1063,8 +1008,8 @@ public class XmppConnection implements Runnable {
}
public int getTimeToNextAttempt() {
- int interval = (int) (25 * Math.pow(1.5, attempt));
- int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
+ final int interval = (int) (25 * Math.pow(1.5, attempt));
+ final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
return interval - secondsSinceLast;
}
@@ -1077,7 +1022,7 @@ public class XmppConnection implements Runnable {
}
public long getLastSessionEstablished() {
- long diff;
+ final long diff;
if (this.lastSessionStarted == 0) {
diff = SystemClock.elapsedRealtime() - this.lastConnect;
} else {
@@ -1109,7 +1054,7 @@ public class XmppConnection implements Runnable {
public class Features {
XmppConnection connection;
- public Features(XmppConnection connection) {
+ public Features(final XmppConnection connection) {
this.connection = connection;
}
@@ -1122,6 +1067,14 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
}
+ public boolean blocking() {
+ return hasDiscoFeature(account.getServer(), Xmlns.BLOCKING);
+ }
+
+ public boolean register() {
+ return hasDiscoFeature(account.getServer(), Xmlns.REGISTER);
+ }
+
public boolean sm() {
return streamId != null;
}
@@ -1139,6 +1092,10 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
}
+ public boolean advancedStreamFeaturesLoaded() {
+ return disco.containsKey(account.getServer().toString());
+ }
+
public boolean rosterVersioning() {
return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver");
}
@@ -1147,9 +1104,9 @@ public class XmppConnection implements Runnable {
return connection
.findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null;
}
+ }
- public boolean compression() {
- return connection.enabledCompression;
- }
+ private IqGenerator getIqGenerator() {
+ return mXmppConnectionService.getIqGenerator();
}
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
index ff9acb3f..44794c80 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
@@ -37,6 +37,7 @@ public class Data extends Element {
Field field = getFieldByName(name);
if (field == null) {
field = new Field(name);
+ this.addChild(field);
}
field.setValue(value);
}
@@ -45,6 +46,7 @@ public class Data extends Element {
Field field = getFieldByName(name);
if (field == null) {
field = new Field(name);
+ this.addChild(field);
}
field.setValues(values);
}
@@ -72,4 +74,12 @@ public class Data extends Element {
data.setChildren(element.getChildren());
return data;
}
+
+ public void setFormType(String formType) {
+ this.put("FORM_TYPE",formType);
+ }
+
+ public String getFormType() {
+ return this.getAttribute("FORM_TYPE");
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
index c40fa0b6..a35ea37c 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
@@ -32,7 +32,7 @@ public final class Jid {
return resourcepart;
}
- public static Jid fromSessionID(SessionID id) throws InvalidJidException{
+ public static Jid fromSessionID(final SessionID id) throws InvalidJidException{
if (id.getUserID().isEmpty()) {
return Jid.fromString(id.getAccountID());
} else {
@@ -190,4 +190,8 @@ public final class Jid {
public boolean isBareJid() {
return this.resourcepart.isEmpty();
}
+
+ public boolean isDomainJid() {
+ return !this.hasLocalpart();
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 3a1ba778..d578ca38 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -191,10 +191,10 @@ public class JingleConnection implements Downloadable {
}
IqPacket response;
if (returnResult) {
- response = packet.generateRespone(IqPacket.TYPE_RESULT);
+ response = packet.generateResponse(IqPacket.TYPE_RESULT);
} else {
- response = packet.generateRespone(IqPacket.TYPE_ERROR);
+ response = packet.generateResponse(IqPacket.TYPE_ERROR);
}
account.getXmppConnection().sendIqPacket(response, null);
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 72c960d8..b0a730b1 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -46,7 +46,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
return;
}
}
- IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR);
+ IqPacket response = packet.generateResponse(IqPacket.TYPE_ERROR);
Element error = response.addChild("error");
error.setAttribute("type", "cancel");
error.addChild("item-not-found",
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
index 04b225d0..e25f7e65 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
@@ -208,15 +208,15 @@ public class JingleInbandTransport extends JingleTransport {
established = true;
connected = true;
this.account.getXmppConnection().sendIqPacket(
- packet.generateRespone(IqPacket.TYPE_RESULT), null);
+ packet.generateResponse(IqPacket.TYPE_RESULT), null);
} else {
this.account.getXmppConnection().sendIqPacket(
- packet.generateRespone(IqPacket.TYPE_ERROR), null);
+ packet.generateResponse(IqPacket.TYPE_ERROR), null);
}
} else if (connected && payload.getName().equals("data")) {
this.receiveNextBlock(payload.getContent());
this.account.getXmppConnection().sendIqPacket(
- packet.generateRespone(IqPacket.TYPE_RESULT), null);
+ packet.generateResponse(IqPacket.TYPE_RESULT), null);
} else {
// TODO some sort of exception
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java
index 9e051472..1a49b45e 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java
@@ -1,7 +1,6 @@
package eu.siacs.conversations.xmpp.stanzas;
import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
public class AbstractStanza extends Element {
@@ -11,24 +10,11 @@ public class AbstractStanza extends Element {
}
public Jid getTo() {
- try {
- return Jid.fromString(getAttribute("to"));
- } catch (final InvalidJidException e) {
- return null;
- }
+ return getAttributeAsJid("to");
}
public Jid getFrom() {
- String from = getAttribute("from");
- if (from == null) {
- return null;
- } else {
- try {
- return Jid.fromString(from);
- } catch (final InvalidJidException e) {
- return null;
- }
- }
+ return getAttributeAsJid("from");
}
public String getId() {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java
index 9df05e67..2481112b 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java
@@ -9,27 +9,27 @@ public class IqPacket extends AbstractStanza {
public static final int TYPE_RESULT = 1;
public static final int TYPE_GET = 2;
- private IqPacket(String name) {
+ private IqPacket(final String name) {
super(name);
}
- public IqPacket(int type) {
+ public IqPacket(final int type) {
super("iq");
switch (type) {
- case TYPE_SET:
- this.setAttribute("type", "set");
- break;
- case TYPE_GET:
- this.setAttribute("type", "get");
- break;
- case TYPE_RESULT:
- this.setAttribute("type", "result");
- break;
- case TYPE_ERROR:
- this.setAttribute("type", "error");
- break;
- default:
- break;
+ case TYPE_SET:
+ this.setAttribute("type", "set");
+ break;
+ case TYPE_GET:
+ this.setAttribute("type", "get");
+ break;
+ case TYPE_RESULT:
+ this.setAttribute("type", "result");
+ break;
+ case TYPE_ERROR:
+ this.setAttribute("type", "error");
+ break;
+ default:
+ break;
}
}
@@ -45,29 +45,30 @@ public class IqPacket extends AbstractStanza {
return query;
}
- public Element query(String xmlns) {
- Element query = query();
+ public Element query(final String xmlns) {
+ final Element query = query();
query.setAttribute("xmlns", xmlns);
return query();
}
public int getType() {
- String type = getAttribute("type");
- if ("error".equals(type)) {
- return TYPE_ERROR;
- } else if ("result".equals(type)) {
- return TYPE_RESULT;
- } else if ("set".equals(type)) {
- return TYPE_SET;
- } else if ("get".equals(type)) {
- return TYPE_GET;
- } else {
- return 1000;
+ final String type = getAttribute("type");
+ switch (type) {
+ case "error":
+ return TYPE_ERROR;
+ case "result":
+ return TYPE_RESULT;
+ case "set":
+ return TYPE_SET;
+ case "get":
+ return TYPE_GET;
+ default:
+ return 1000;
}
}
- public IqPacket generateRespone(int type) {
- IqPacket packet = new IqPacket(type);
+ public IqPacket generateResponse(final int type) {
+ final IqPacket packet = new IqPacket(type);
packet.setTo(this.getFrom());
packet.setId(this.getId());
return packet;