aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/OtrService.java40
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java12
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java29
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java8
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Account.java100
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java4
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java7
-rw-r--r--src/main/java/eu/siacs/conversations/entities/MucOptions.java1
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java8
-rw-r--r--src/main/java/eu/siacs/conversations/generator/MessageGenerator.java4
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java4
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java4
-rw-r--r--src/main/java/eu/siacs/conversations/parser/IqParser.java8
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java47
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java3
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/FileBackend.java35
-rw-r--r--src/main/java/eu/siacs/conversations/services/NotificationService.java51
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java104
-rw-r--r--src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java33
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java9
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java11
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java28
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java7
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java25
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ExportLogsPreference.java36
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java9
-rw-r--r--src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java16
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java72
-rw-r--r--src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java7
-rw-r--r--src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java6
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java3
-rw-r--r--src/main/java/eu/siacs/conversations/utils/CryptoHelper.java10
-rw-r--r--src/main/java/eu/siacs/conversations/utils/PhoneHelper.java4
-rw-r--r--src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java4
-rw-r--r--src/main/java/eu/siacs/conversations/utils/UIHelper.java4
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java37
36 files changed, 512 insertions, 278 deletions
diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrService.java b/src/main/java/eu/siacs/conversations/crypto/OtrService.java
index af11756f..3663cd3b 100644
--- a/src/main/java/eu/siacs/conversations/crypto/OtrService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/OtrService.java
@@ -53,28 +53,30 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
this.mXmppConnectionService = service;
}
- private KeyPair loadKey(JSONObject keys) {
+ private KeyPair loadKey(final JSONObject keys) {
if (keys == null) {
return null;
}
- try {
- BigInteger x = new BigInteger(keys.getString("otr_x"), 16);
- BigInteger y = new BigInteger(keys.getString("otr_y"), 16);
- BigInteger p = new BigInteger(keys.getString("otr_p"), 16);
- BigInteger q = new BigInteger(keys.getString("otr_q"), 16);
- BigInteger g = new BigInteger(keys.getString("otr_g"), 16);
- KeyFactory keyFactory = KeyFactory.getInstance("DSA");
- DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g);
- DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g);
- PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
- PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
- return new KeyPair(publicKey, privateKey);
- } catch (JSONException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (InvalidKeySpecException e) {
- return null;
+ synchronized (keys) {
+ try {
+ BigInteger x = new BigInteger(keys.getString("otr_x"), 16);
+ BigInteger y = new BigInteger(keys.getString("otr_y"), 16);
+ BigInteger p = new BigInteger(keys.getString("otr_p"), 16);
+ BigInteger q = new BigInteger(keys.getString("otr_q"), 16);
+ BigInteger g = new BigInteger(keys.getString("otr_g"), 16);
+ KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+ DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g);
+ DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g);
+ PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
+ PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
+ return new KeyPair(publicKey, privateKey);
+ } catch (JSONException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (InvalidKeySpecException e) {
+ return null;
+ }
}
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java
index 53768714..87b58613 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java
@@ -115,7 +115,11 @@ public class PgpDecryptionService {
case OpenPgpApi.RESULT_CODE_SUCCESS:
try {
os.flush();
- message.setBody(os.toString());
+ final String body = os.toString();
+ if (body == null) {
+ throw new IOException("body was null");
+ }
+ message.setBody(body);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager();
if (message.trusted()
@@ -130,9 +134,10 @@ public class PgpDecryptionService {
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
synchronized (PgpDecryptionService.this) {
+ PendingIntent pendingIntent = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
messages.addFirst(message);
currentMessage = null;
- storePendingIntent((PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
+ storePendingIntent(pendingIntent);
}
break;
case OpenPgpApi.RESULT_CODE_ERROR:
@@ -160,9 +165,10 @@ public class PgpDecryptionService {
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
synchronized (PgpDecryptionService.this) {
+ PendingIntent pendingIntent = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
messages.addFirst(message);
currentMessage = null;
- storePendingIntent((PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
+ storePendingIntent(pendingIntent);
}
break;
case OpenPgpApi.RESULT_CODE_ERROR:
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
index 28085085..1bc5fa83 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
@@ -660,24 +660,25 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
public Pair<AxolotlCapability,Jid> isConversationAxolotlCapableDetailed(Conversation conversation) {
- final List<Jid> jids = getCryptoTargets(conversation);
- for(Jid jid : jids) {
- if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
- if (conversation.getAccount().getRoster().getContact(jid).trusted()) {
- return new Pair<>(AxolotlCapability.MISSING_KEYS,jid);
- } else {
- return new Pair<>(AxolotlCapability.MISSING_PRESENCE,jid);
+ if (conversation.getMode() == Conversation.MODE_SINGLE
+ || (conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous())) {
+ final List<Jid> jids = getCryptoTargets(conversation);
+ for(Jid jid : jids) {
+ if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
+ if (conversation.getAccount().getRoster().getContact(jid).mutualPresenceSubscription()) {
+ return new Pair<>(AxolotlCapability.MISSING_KEYS,jid);
+ } else {
+ return new Pair<>(AxolotlCapability.MISSING_PRESENCE,jid);
+ }
}
}
- }
- if (jids.size() > 0) {
- return new Pair<>(AxolotlCapability.FULL, null);
- } else {
- if (conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous()) {
- return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
+ if (jids.size() > 0) {
+ return new Pair<>(AxolotlCapability.FULL, null);
} else {
- return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
+ return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
}
+ } else {
+ return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
}
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
index e8ec5426..0b3164f8 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
@@ -91,7 +91,11 @@ public class XmppAxolotlMessage {
private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
this.from = from;
Element header = axolotlMessage.findChild(HEADER);
- this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
+ try {
+ this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid source id");
+ }
List<Element> keyElements = header.getChildren();
this.keys = new HashMap<>(keyElements.size());
for (Element keyElement : keyElements) {
@@ -102,7 +106,7 @@ public class XmppAxolotlMessage {
byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
this.keys.put(recipientId, key);
} catch (NumberFormatException e) {
- throw new IllegalArgumentException(e);
+ throw new IllegalArgumentException("invalid remote id");
}
break;
case IVTAG:
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 38b5a999..7d60dcf7 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -91,6 +91,17 @@ public class Account extends AbstractEntity {
return pgpDecryptionService != null && pgpDecryptionService.isConnected();
}
+ public boolean setShowErrorNotification(boolean newValue) {
+ boolean oldValue = showErrorNotification();
+ setKey("show_error",Boolean.toString(newValue));
+ return newValue != oldValue;
+ }
+
+ public boolean showErrorNotification() {
+ String key = getKey("show_error");
+ return key == null || Boolean.parseBoolean(key);
+ }
+
public enum State {
DISABLED,
OFFLINE,
@@ -192,7 +203,7 @@ public class Account extends AbstractEntity {
protected int options = 0;
protected String rosterVersion;
protected State status = State.OFFLINE;
- protected JSONObject keys = new JSONObject();
+ protected final JSONObject keys;
protected String avatar;
protected String displayName = null;
protected String hostname = null;
@@ -227,11 +238,13 @@ public class Account extends AbstractEntity {
this.password = password;
this.options = options;
this.rosterVersion = rosterVersion;
+ JSONObject tmp;
try {
- this.keys = new JSONObject(keys);
- } catch (final JSONException ignored) {
- this.keys = new JSONObject();
+ tmp = new JSONObject(keys);
+ } catch(JSONException e) {
+ tmp = new JSONObject();
}
+ this.keys = tmp;
this.avatar = avatar;
this.displayName = displayName;
this.hostname = hostname;
@@ -380,15 +393,28 @@ public class Account extends AbstractEntity {
}
public String getKey(final String name) {
- return this.keys.optString(name, null);
+ synchronized (this.keys) {
+ return this.keys.optString(name, null);
+ }
}
- public boolean setKey(final String keyName, final String keyValue) {
+ public int getKeyAsInt(final String name, int defaultValue) {
+ String key = getKey(name);
try {
- this.keys.put(keyName, keyValue);
- return true;
- } catch (final JSONException e) {
- return false;
+ return key == null ? defaultValue : Integer.parseInt(key);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ public boolean setKey(final String keyName, final String keyValue) {
+ synchronized (this.keys) {
+ try {
+ this.keys.put(keyName, keyValue);
+ return true;
+ } catch (final JSONException e) {
+ return false;
+ }
}
}
@@ -408,7 +434,9 @@ public class Account extends AbstractEntity {
values.put(SERVER, jid.getDomainpart());
values.put(PASSWORD, password);
values.put(OPTIONS, options);
- values.put(KEYS, this.keys.toString());
+ synchronized (this.keys) {
+ values.put(KEYS, this.keys.toString());
+ }
values.put(ROSTERVERSION, rosterVersion);
values.put(AVATAR, avatar);
values.put(DISPLAY_NAME, displayName);
@@ -485,54 +513,42 @@ public class Account extends AbstractEntity {
}
public String getPgpSignature() {
- try {
- if (keys.has(KEY_PGP_SIGNATURE) && !"null".equals(keys.getString(KEY_PGP_SIGNATURE))) {
- return keys.getString(KEY_PGP_SIGNATURE);
- } else {
- return null;
- }
- } catch (final JSONException e) {
- return null;
- }
+ return getKey(KEY_PGP_SIGNATURE);
}
public boolean setPgpSignature(String signature) {
- try {
- keys.put(KEY_PGP_SIGNATURE, signature);
- } catch (JSONException e) {
- return false;
- }
- return true;
+ return setKey(KEY_PGP_SIGNATURE, signature);
}
public boolean unsetPgpSignature() {
- try {
- keys.put(KEY_PGP_SIGNATURE, JSONObject.NULL);
- } catch (JSONException e) {
- return false;
+ synchronized (this.keys) {
+ return keys.remove(KEY_PGP_SIGNATURE) != null;
}
- return true;
}
public long getPgpId() {
- if (keys.has(KEY_PGP_ID)) {
- try {
- return keys.getLong(KEY_PGP_ID);
- } catch (JSONException e) {
+ synchronized (this.keys) {
+ if (keys.has(KEY_PGP_ID)) {
+ try {
+ return keys.getLong(KEY_PGP_ID);
+ } catch (JSONException e) {
+ return 0;
+ }
+ } else {
return 0;
}
- } else {
- return 0;
}
}
public boolean setPgpSignId(long pgpID) {
- try {
- keys.put(KEY_PGP_ID, pgpID);
- } catch (JSONException e) {
- return false;
+ synchronized (this.keys) {
+ try {
+ keys.put(KEY_PGP_ID, pgpID);
+ } catch (JSONException e) {
+ return false;
+ }
+ return true;
}
- return true;
}
public Roster getRoster() {
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index e512b586..70af45d4 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -118,7 +118,7 @@ public class Contact implements ListItem, Blockable {
return this.systemName;
} else if (this.serverName != null) {
return this.serverName;
- } else if (this.presenceName != null && trusted()) {
+ } else if (this.presenceName != null && mutualPresenceSubscription()) {
return this.presenceName;
} else if (jid.hasLocalpart()) {
return jid.getLocalpart();
@@ -487,7 +487,7 @@ public class Contact implements ListItem, Blockable {
}
}
- public boolean trusted() {
+ public boolean mutualPresenceSubscription() {
return getOption(Options.FROM) && getOption(Options.TO);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 1b38d349..e3577b06 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -124,7 +124,7 @@ public class Message extends AbstractEntity {
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
this.trueCounterpart = trueCounterpart;
- this.body = body;
+ this.body = body == null ? "" : body;
this.timeSent = timeSent;
this.encryption = encryption;
this.status = status;
@@ -266,6 +266,9 @@ public class Message extends AbstractEntity {
}
public void setBody(String body) {
+ if (body == null) {
+ throw new Error("You should not set the message body to null");
+ }
this.body = body;
}
@@ -539,7 +542,7 @@ public class Message extends AbstractEntity {
public boolean trusted() {
Contact contact = this.getContact();
- return (status > STATUS_RECEIVED || (contact != null && contact.trusted()));
+ return (status > STATUS_RECEIVED || (contact != null && contact.mutualPresenceSubscription()));
}
public boolean fixCounterpart() {
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 9d54a86d..fa6afcfa 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -127,7 +127,6 @@ public class MucOptions {
UNKNOWN
}
- public static final String STATUS_CODE_ROOM_CONFIG_CHANGED = "104";
public static final String STATUS_CODE_SELF_PRESENCE = "110";
public static final String STATUS_CODE_ROOM_CREATED = "201";
public static final String STATUS_CODE_BANNED = "301";
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 884bc4af..cb9ffd96 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -254,10 +254,14 @@ public class IqGenerator extends AbstractGenerator {
return iq;
}
- public IqPacket generateSetBlockRequest(final Jid jid) {
+ public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) {
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
final Element block = iq.addChild("block", Xmlns.BLOCKING);
- block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ final Element item = block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ if (reportSpam) {
+ item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
+ }
+ Log.d(Config.LOGTAG,iq.toString());
return iq;
}
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index 5ef47a33..f5c701cc 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -183,6 +183,10 @@ public class MessageGenerator extends AbstractGenerator {
packet.setFrom(conversation.getAccount().getJid());
Element x = packet.addChild("x", "jabber:x:conference");
x.setAttribute("jid", conversation.getJid().toBareJid().toString());
+ String password = conversation.getMucOptions().getPassword();
+ if (password != null) {
+ x.setAttribute("password",password);
+ }
return packet;
}
diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
index 741ea98d..a9bffe3e 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
@@ -212,6 +212,8 @@ public class HttpDownloadConnection implements Transferable {
if (connection instanceof HttpsURLConnection) {
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
}
+ connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
+ connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.connect();
String contentLength = connection.getHeaderField("Content-Length");
connection.disconnect();
@@ -279,6 +281,8 @@ public class HttpDownloadConnection implements Transferable {
long size = file.getSize();
connection.setRequestProperty("Range", "bytes="+size+"-");
}
+ connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
+ connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.connect();
is = new BufferedInputStream(connection.getInputStream());
boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges"));
diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
index 1bb70690..1bd6a8e4 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
@@ -171,10 +171,12 @@ public class HttpUploadConnection implements Transferable {
connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime);
connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName());
connection.setDoOutput(true);
+ connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
+ connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.connect();
os = connection.getOutputStream();
transmitted = 0;
- int count = -1;
+ int count;
byte[] buffer = new byte[4096];
while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
transmitted += count;
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index a679d00c..49b0db21 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -139,7 +139,11 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
if(signedPreKeyPublic == null) {
return null;
}
- return Integer.valueOf(signedPreKeyPublic.getAttribute("signedPreKeyId"));
+ try {
+ return Integer.valueOf(signedPreKeyPublic.getAttribute("signedPreKeyId"));
+ } catch (NumberFormatException e) {
+ return null;
+ }
}
public ECPublicKey signedPreKeyPublic(final Element bundle) {
@@ -255,7 +259,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
Integer signedPreKeyId = signedPreKeyId(bundleElement);
byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
IdentityKey identityKey = identityKey(bundleElement);
- if(signedPreKeyPublic == null || identityKey == null) {
+ if(signedPreKeyId == null || signedPreKeyPublic == null || identityKey == null) {
return null;
}
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index f4fe80b5..81b68ed9 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -333,7 +333,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
}
if (timestamp == null) {
- timestamp = AbstractParser.parseTimestamp(packet);
+ timestamp = AbstractParser.parseTimestamp(original,AbstractParser.parseTimestamp(packet));
}
final String body = packet.getBody();
final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
@@ -539,7 +539,11 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
mXmppConnectionService.updateConversationUi();
}
- if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
+ if (mXmppConnectionService.confirmMessages()
+ && message.trusted()
+ && remoteMsgId != null
+ && !isForwarded
+ && !isTypeGroupChat) {
sendMessageReceipts(account, packet);
}
@@ -583,26 +587,27 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
}
}
if (conversation != null && mucUserElement != null && from.isBareJid()) {
- if (mucUserElement.hasChild("status")) {
- for (Element child : mucUserElement.getChildren()) {
- if (child.getName().equals("status")
- && MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED.equals(child.getAttribute("code"))) {
- mXmppConnectionService.fetchConferenceConfiguration(conversation);
- }
- }
- } else if (mucUserElement.hasChild("item")) {
- for(Element child : mucUserElement.getChildren()) {
- if ("item".equals(child.getName())) {
- MucOptions.User user = AbstractParser.parseItem(conversation,child);
- Log.d(Config.LOGTAG,account.getJid()+": changing affiliation for "
- +user.getRealJid()+" to "+user.getAffiliation()+" in "
- +conversation.getJid().toBareJid());
- if (!user.realJidMatchesAccount()) {
- conversation.getMucOptions().addUser(user);
- mXmppConnectionService.getAvatarService().clear(conversation);
- mXmppConnectionService.updateMucRosterUi();
- mXmppConnectionService.updateConversationUi();
+ for (Element child : mucUserElement.getChildren()) {
+ if ("status".equals(child.getName())) {
+ try {
+ int code = Integer.parseInt(child.getAttribute("code"));
+ if ((code >= 170 && code <= 174) || (code >= 102 && code <= 104)) {
+ mXmppConnectionService.fetchConferenceConfiguration(conversation);
+ break;
}
+ } catch (Exception e) {
+ //ignored
+ }
+ } else if ("item".equals(child.getName())) {
+ MucOptions.User user = AbstractParser.parseItem(conversation,child);
+ Log.d(Config.LOGTAG,account.getJid()+": changing affiliation for "
+ +user.getRealJid()+" to "+user.getAffiliation()+" in "
+ +conversation.getJid().toBareJid());
+ if (!user.realJidMatchesAccount()) {
+ conversation.getMucOptions().addUser(user);
+ mXmppConnectionService.getAvatarService().clear(conversation);
+ mXmppConnectionService.updateMucRosterUi();
+ mXmppConnectionService.updateConversationUi();
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 484b7b15..d8b6b4e1 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -1184,9 +1184,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
storeIdentityKey(account, account.getJid().toBareJid().toString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), XmppAxolotlSession.Trust.TRUSTED);
}
- public void recreateAxolotlDb() {
- recreateAxolotlDb(getWritableDatabase());
- }
public void recreateAxolotlDb(SQLiteDatabase db) {
Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<");
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 8fcda7c2..bc919788 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -14,7 +14,6 @@ import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
-import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
@@ -58,7 +57,9 @@ import eu.siacs.conversations.utils.FileUtils;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class FileBackend {
- private final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ private static final SimpleDateFormat IMAGE_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
+
+ public static final String CONVERSATIONS_FILE_PROVIDER = "eu.siacs.conversations.files";
private XmppConnectionService mXmppConnectionService;
@@ -226,6 +227,7 @@ public class FileBackend {
}
public void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
+ Log.d(Config.LOGTAG,"copy file ("+uri.toString()+") to private storage "+file.getAbsolutePath());
file.getParentFile().mkdirs();
OutputStream os = null;
InputStream is = null;
@@ -248,7 +250,6 @@ public class FileBackend {
close(os);
close(is);
}
- Log.d(Config.LOGTAG, "output file name " + file.getAbsolutePath());
}
public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
@@ -338,6 +339,7 @@ public class FileBackend {
}
public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException {
+ Log.d(Config.LOGTAG,"copy image ("+image.toString()+") to private storage "+file.getAbsolutePath());
copyImageToPrivateStorage(file, image, 0);
}
@@ -431,16 +433,27 @@ public class FileBackend {
return frame;
}
+ private static String getTakePhotoPath() {
+ return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)+"/Camera/";
+ }
+
public Uri getTakePhotoUri() {
- StringBuilder pathBuilder = new StringBuilder();
- pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
- pathBuilder.append('/');
- pathBuilder.append("Camera");
- pathBuilder.append('/');
- pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date()) + ".jpg");
- File file = new File(pathBuilder.toString());
+ File file = new File(getTakePhotoPath()+"IMG_" + this.IMAGE_DATE_FORMAT.format(new Date()) + ".jpg");
file.getParentFile().mkdirs();
- return FileProvider.getUriForFile(mXmppConnectionService,"eu.siacs.conversations.files",file);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return FileProvider.getUriForFile(mXmppConnectionService, CONVERSATIONS_FILE_PROVIDER, file);
+ } else {
+ return Uri.fromFile(file);
+ }
+ }
+
+ public static Uri getIndexableTakePhotoUri(Uri original) {
+ if ("file".equals(original.getScheme())) {
+ return original;
+ } else {
+ List<String> segments = original.getPathSegments();
+ return Uri.parse("file://"+getTakePhotoPath()+segments.get(segments.size() - 1));
+ }
}
public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index 7cfe2433..6e1d6c4b 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -2,7 +2,6 @@ package eu.siacs.conversations.services;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
@@ -14,18 +13,13 @@ import android.support.v4.app.NotificationCompat.BigPictureStyle;
import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.app.RemoteInput;
-import android.support.v4.app.TaskStackBuilder;
import android.text.Html;
import android.util.DisplayMetrics;
import android.util.Log;
-import org.json.JSONArray;
-import org.json.JSONObject;
-
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Calendar;
-import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -297,11 +291,11 @@ public class NotificationService {
modifyForTextOnly(mBuilder, messages);
}
RemoteInput remoteInput = new RemoteInput.Builder("text_reply").setLabel(UIHelper.getMessageHint(mXmppConnectionService, conversation)).build();
- NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_send_text_offline, "Reply", createReplyIntent(conversation)).addRemoteInput(remoteInput).build();
+ NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(R.drawable.ic_send_text_offline, "Reply", createReplyIntent(conversation, false)).addRemoteInput(remoteInput).build();
+ NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_send_text_offline, "Reply", createReplyIntent(conversation, true)).addRemoteInput(remoteInput).build();
+ mBuilder.extend(new NotificationCompat.WearableExtender().addAction(wearReplyAction));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- mBuilder.addAction(action);
- } else {
- mBuilder.extend(new NotificationCompat.WearableExtender().addAction(action));
+ mBuilder.addAction(replyAction);
}
if ((message = getFirstDownloadableMessage(messages)) != null) {
mBuilder.addAction(
@@ -474,11 +468,13 @@ public class NotificationService {
return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
}
- private PendingIntent createReplyIntent(Conversation conversation) {
+ private PendingIntent createReplyIntent(Conversation conversation, boolean dismissAfterReply) {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION);
intent.putExtra("uuid",conversation.getUuid());
- return PendingIntent.getService(mXmppConnectionService, conversation.getUuid().hashCode() % 402361, intent, 0);
+ intent.putExtra("dismiss_notification",dismissAfterReply);
+ int id = conversation.getUuid().hashCode() % (dismissAfterReply ? 402359 : 426583);
+ return PendingIntent.getService(mXmppConnectionService, id, intent, 0);
}
private PendingIntent createDisableForeground() {
@@ -494,11 +490,10 @@ public class NotificationService {
return PendingIntent.getService(mXmppConnectionService, 45, intent, 0);
}
- private PendingIntent createDisableAccountIntent(final Account account) {
+ private PendingIntent createDismissErrorIntent() {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
- intent.setAction(XmppConnectionService.ACTION_DISABLE_ACCOUNT);
- intent.putExtra("account", account.getJid().toBareJid().toString());
- return PendingIntent.getService(mXmppConnectionService, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ intent.setAction(XmppConnectionService.ACTION_DISMISS_ERROR_NOTIFICATIONS);
+ return PendingIntent.getService(mXmppConnectionService, 69, intent, 0);
}
private boolean wasHighlightedOrPrivate(final Message message) {
@@ -592,7 +587,7 @@ public class NotificationService {
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService);
final List<Account> errors = new ArrayList<>();
for (final Account account : mXmppConnectionService.getAccounts()) {
- if (account.hasErrorStatus()) {
+ if (account.hasErrorStatus() && account.showErrorNotification()) {
errors.add(account);
}
}
@@ -613,27 +608,17 @@ public class NotificationService {
mBuilder.addAction(R.drawable.ic_autorenew_white_24dp,
mXmppConnectionService.getString(R.string.try_again),
createTryAgainIntent());
- if (errors.size() == 1) {
- mBuilder.addAction(R.drawable.ic_block_white_24dp,
- mXmppConnectionService.getString(R.string.disable_account),
- createDisableAccountIntent(errors.get(0)));
- }
- mBuilder.setOngoing(true);
- //mBuilder.setLights(0xffffffff, 2000, 4000);
+ mBuilder.setDeleteIntent(createDismissErrorIntent());
+ mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp);
} else {
mBuilder.setSmallIcon(R.drawable.ic_stat_alert_warning);
}
- final TaskStackBuilder stackBuilder = TaskStackBuilder.create(mXmppConnectionService);
- stackBuilder.addParentStack(ConversationActivity.class);
-
- final Intent manageAccountsIntent = new Intent(mXmppConnectionService, ManageAccountActivity.class);
- stackBuilder.addNextIntent(manageAccountsIntent);
-
- final PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
-
- mBuilder.setContentIntent(resultPendingIntent);
+ mBuilder.setContentIntent(PendingIntent.getActivity(mXmppConnectionService,
+ 145,
+ new Intent(mXmppConnectionService,ManageAccountActivity.class),
+ PendingIntent.FLAG_UPDATE_CURRENT));
notificationManager.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 3bf1e745..774842a7 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -132,8 +132,8 @@ public class XmppConnectionService extends Service {
public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations";
public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
+ public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
public static final String ACTION_TRY_AGAIN = "try_again";
- public static final String ACTION_DISABLE_ACCOUNT = "disable_account";
public static final String ACTION_IDLE_PING = "idle_ping";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh";
@@ -294,6 +294,9 @@ public class XmppConnectionService extends Service {
mOnAccountUpdate.onAccountUpdate();
}
if (account.getStatus() == Account.State.ONLINE) {
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ }
mMessageArchiveService.executePendingQueries(account);
if (connection != null && connection.getFeatures().csi()) {
if (checkListeners()) {
@@ -323,7 +326,7 @@ public class XmppConnectionService extends Service {
}
account.pendingConferenceJoins.clear();
scheduleWakeUpCall(Config.PUSH_MODE ? Config.PING_MIN_INTERVAL : Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
- } else if (account.getStatus() == Account.State.OFFLINE) {
+ } else if (account.getStatus() == Account.State.OFFLINE || account.getStatus() == Account.State.DISABLED) {
resetSendingToWaiting(account);
final boolean disabled = account.isOptionSet(Account.OPTION_DISABLED);
final boolean listeners = checkListeners();
@@ -340,6 +343,7 @@ public class XmppConnectionService extends Service {
reconnectAccount(account, true, false);
} else if ((account.getStatus() != Account.State.CONNECTING)
&& (account.getStatus() != Account.State.NO_INTERNET)) {
+ resetSendingToWaiting(account);
if (connection != null) {
int next = connection.getTimeToNextAttempt();
Log.d(Config.LOGTAG, account.getJid().toBareJid()
@@ -526,6 +530,7 @@ public class XmppConnectionService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final String action = intent == null ? null : intent.getAction();
+ String pushedAccountHash = null;
boolean interactive = false;
if (action != null) {
final Conversation c = findConversationByUuid(intent.getStringExtra("uuid"));
@@ -554,28 +559,20 @@ public class XmppConnectionService extends Service {
getPreferences().edit().putBoolean("keep_foreground_service", false).commit();
toggleForegroundService();
break;
+ case ACTION_DISMISS_ERROR_NOTIFICATIONS:
+ dismissErrorNotifications();
+ break;
case ACTION_TRY_AGAIN:
resetAllAttemptCounts(false);
interactive = true;
break;
- case ACTION_DISABLE_ACCOUNT:
- try {
- String jid = intent.getStringExtra("account");
- Account account = jid == null ? null : findAccountByJid(Jid.fromString(jid));
- if (account != null) {
- account.setOption(Account.OPTION_DISABLED, true);
- updateAccount(account);
- }
- } catch (final InvalidJidException ignored) {
- break;
- }
- break;
case ACTION_REPLY_TO_CONVERSATION:
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null && c != null) {
-
- String body = remoteInput.getString("text_reply");
- directReply(c,body);
+ final CharSequence body = remoteInput.getCharSequence("text_reply");
+ if (body != null && body.length() > 0) {
+ directReply(c, body.toString(),intent.getBooleanExtra("dismiss_notification",false));
+ }
}
break;
case AudioManager.RINGER_MODE_CHANGED_ACTION:
@@ -601,6 +598,7 @@ public class XmppConnectionService extends Service {
break;
case ACTION_GCM_MESSAGE_RECEIVED:
Log.d(Config.LOGTAG,"gcm push message arrived in service. extras="+intent.getExtras());
+ pushedAccountHash = intent.getStringExtra("account");
break;
}
}
@@ -639,7 +637,7 @@ public class XmppConnectionService extends Service {
}
} else {
pingCandidates.add(account);
- if (msToNextPing <= 0) {
+ if (msToNextPing <= 0 || CryptoHelper.getAccountFingerprint(account).equals(pushedAccountHash)) {
pingNow = true;
} else {
this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
@@ -707,7 +705,7 @@ public class XmppConnectionService extends Service {
}
}
- private void directReply(Conversation conversation, String body) {
+ private void directReply(Conversation conversation, String body, final boolean dismissAfterReply) {
Message message = new Message(conversation,body,conversation.getNextEncryption());
message.markUnread();
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
@@ -716,7 +714,11 @@ public class XmppConnectionService extends Service {
public void success(Message message) {
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
sendMessage(message);
- mNotificationService.pushFromDirectReply(message);
+ if (dismissAfterReply) {
+ markRead(message.getConversation(),true);
+ } else {
+ mNotificationService.pushFromDirectReply(message);
+ }
}
@Override
@@ -731,7 +733,11 @@ public class XmppConnectionService extends Service {
});
} else {
sendMessage(message);
- mNotificationService.pushFromDirectReply(message);
+ if (dismissAfterReply) {
+ markRead(conversation,true);
+ } else {
+ mNotificationService.pushFromDirectReply(message);
+ }
}
}
@@ -802,6 +808,21 @@ public class XmppConnectionService extends Service {
connection.resetAttemptCount();
}
}
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ }
+ }
+ mNotificationService.updateErrorNotification();
+ }
+
+ private void dismissErrorNotifications() {
+ for (final Account account : this.accounts) {
+ if (account.hasErrorStatus()) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": dismissing error notification");
+ if (account.setShowErrorNotification(false)) {
+ databaseBackend.updateAccount(account);
+ }
+ }
}
}
@@ -976,8 +997,16 @@ public class XmppConnectionService extends Service {
public XmppConnection createConnection(final Account account) {
final SharedPreferences sharedPref = getPreferences();
- account.setResource(sharedPref.getString("resource", getString(R.string.default_resource))
- .toLowerCase(Locale.getDefault()));
+ String resource;
+ try {
+ resource = sharedPref.getString("resource", getString(R.string.default_resource)).toLowerCase(Locale.ENGLISH);
+ if (resource.trim().isEmpty()) {
+ throw new Exception();
+ }
+ } catch (Exception e) {
+ resource = "conversations";
+ }
+ account.setResource(resource);
final XmppConnection connection = new XmppConnection(account, this);
connection.setOnMessagePacketReceivedListener(this.mMessageParser);
connection.setOnStatusChangedListener(this.statusListener);
@@ -1018,6 +1047,10 @@ public class XmppConnectionService extends Service {
private void sendMessage(final Message message, final boolean resend, final boolean delay) {
final Account account = message.getConversation().getAccount();
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ mNotificationService.updateErrorNotification();
+ }
final Conversation conversation = message.getConversation();
account.deactivateGracePeriod();
MessagePacket packet = null;
@@ -1110,7 +1143,8 @@ public class XmppConnectionService extends Service {
}
if (packet != null) {
- if (account.getXmppConnection().getFeatures().sm() || conversation.getMode() == Conversation.MODE_MULTI) {
+ if (account.getXmppConnection().getFeatures().sm()
+ || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
message.setStatus(Message.STATUS_UNSEND);
} else {
message.setStatus(Message.STATUS_SEND);
@@ -1152,7 +1186,8 @@ public class XmppConnectionService extends Service {
if (resend) {
if (packet != null && addToConversation) {
- if (account.getXmppConnection().getFeatures().sm() || conversation.getMode() == Conversation.MODE_MULTI) {
+ if (account.getXmppConnection().getFeatures().sm()
+ || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
markMessage(message, Message.STATUS_UNSEND);
} else {
markMessage(message, Message.STATUS_SEND);
@@ -1288,6 +1323,13 @@ public class XmppConnectionService extends Service {
for (Conversation conversation : conversations) {
conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
checkDeletedFiles(conversation);
+ conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message, Message.STATUS_WAITING);
+ }
+ });
conversation.findUnreadMessages(new Conversation.OnMessageFound() {
@Override
public void onMessageFound(Message message) {
@@ -1508,7 +1550,6 @@ public class XmppConnectionService extends Service {
conversation.setMode(Conversation.MODE_SINGLE);
conversation.setContactJid(jid.toBareJid());
}
- conversation.setNextEncryption(-1);
conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
this.databaseBackend.updateConversation(conversation);
} else {
@@ -1549,7 +1590,6 @@ public class XmppConnectionService extends Service {
public void archiveConversation(Conversation conversation) {
getNotificationService().clear(conversation);
conversation.setStatus(Conversation.STATUS_ARCHIVED);
- conversation.setNextEncryption(-1);
synchronized (this.conversations) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
@@ -1644,6 +1684,7 @@ public class XmppConnectionService extends Service {
public boolean updateAccount(final Account account) {
if (databaseBackend.updateAccount(account)) {
+ account.setShowErrorNotification(true);
this.statusListener.onStatusChanged(account);
databaseBackend.updateAccount(account);
reconnectAccountInBackground(account);
@@ -3091,7 +3132,10 @@ public class XmppConnectionService extends Service {
if (this.markRead(conversation)) {
updateConversationUi();
}
- if (confirmMessages() && markable != null && markable.getRemoteMsgId() != null) {
+ if (confirmMessages()
+ && markable != null
+ && markable.trusted()
+ && 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();
@@ -3346,10 +3390,10 @@ public class XmppConnectionService extends Service {
mDatabaseExecutor.execute(runnable);
}
- public void sendBlockRequest(final Blockable blockable) {
+ public void sendBlockRequest(final Blockable blockable, boolean reportSpam) {
if (blockable != null && blockable.getBlockedJid() != null) {
final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid), new OnIqPacketReceived() {
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
diff --git a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
index 9cf7e9f8..91ce34a9 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
@@ -3,6 +3,14 @@ package eu.siacs.conversations.ui;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.TypefaceSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Blockable;
@@ -15,16 +23,31 @@ public final class BlockContactDialog {
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final boolean isBlocked = blockable.isBlocked();
builder.setNegativeButton(R.string.cancel, null);
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LinearLayout view = (LinearLayout) inflater.inflate(R.layout.dialog_block_contact,null);
+ TextView message = (TextView) view.findViewById(R.id.text);
+ final CheckBox report = (CheckBox) view.findViewById(R.id.report_spam);
+ final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
+ report.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE);
+ builder.setView(view);
+ String value;
+ SpannableString spannable;
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()));
+ value = blockable.getJid().toDomainJid().toString();
+ spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text, value));
+ message.setText(spannable);
} 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()));
+ value = blockable.getJid().toBareJid().toString();
+ spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text, value));
}
+ int start = spannable.toString().indexOf(value);
+ if (start >= 0) {
+ spannable.setSpan(new TypefaceSpan("monospace"),start,start + value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ message.setText(spannable);
builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, new DialogInterface.OnClickListener() {
@Override
@@ -32,7 +55,7 @@ public final class BlockContactDialog {
if (isBlocked) {
xmppConnectionService.sendUnblockRequest(blockable);
} else {
- xmppConnectionService.sendBlockRequest(blockable);
+ xmppConnectionService.sendBlockRequest(blockable, report.isChecked());
}
}
});
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index e6f7fc5d..7791372a 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -271,6 +271,15 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
@Override
+ protected void onStart() {
+ super.onStart();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ }
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case android.R.id.home:
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index 836e345b..08128094 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -227,9 +227,14 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
@Override
public void onStart() {
super.onStart();
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
- this.showLastSeen = preferences.getBoolean("last_activity", false);
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ } else {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ this.showDynamicTags = preferences.getBoolean("show_dynamic_tags", false);
+ this.showLastSeen = preferences.getBoolean("last_activity", false);
+ }
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index 2c8b27b0..82b59dfc 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -153,11 +153,7 @@ public class ConversationActivity extends XmppActivity
}
public boolean isConversationsOverviewHideable() {
- if (mContentView instanceof SlidingPaneLayout) {
- return true;
- } else {
- return false;
- }
+ return mContentView instanceof SlidingPaneLayout;
}
public boolean isConversationsOverviewVisable() {
@@ -185,6 +181,7 @@ public class ConversationActivity extends XmppActivity
}
String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
if (pending != null) {
+ Log.d(Config.LOGTAG,"ConversationsActivity.onCreate() - restoring pending image uri");
mPendingImageUris.clear();
mPendingImageUris.add(Uri.parse(pending));
}
@@ -454,14 +451,15 @@ public class ConversationActivity extends XmppActivity
private boolean quickOmemoDebugger(Conversation c) {
if (c != null) {
+ boolean single = c.getMode() == Conversation.MODE_SINGLE;
AxolotlService axolotlService = c.getAccount().getAxolotlService();
Pair<AxolotlService.AxolotlCapability,Jid> capabilityJidPair = axolotlService.isConversationAxolotlCapableDetailed(c);
switch (capabilityJidPair.first) {
case MISSING_PRESENCE:
- Toast.makeText(ConversationActivity.this,getString(R.string.missing_presence_subscription_with_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
+ Toast.makeText(ConversationActivity.this,single ? getString(R.string.missing_presence_subscription) : getString(R.string.missing_presence_subscription_with_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
return true;
case MISSING_KEYS:
- Toast.makeText(ConversationActivity.this,getString(R.string.missing_keys_from_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
+ Toast.makeText(ConversationActivity.this,single ? getString(R.string.missing_omemo_keys) : getString(R.string.missing_keys_from_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
return true;
case WRONG_CONFIGURATION:
Toast.makeText(ConversationActivity.this,R.string.wrong_conference_configuration, Toast.LENGTH_SHORT).show();
@@ -1150,6 +1148,7 @@ public class ConversationActivity extends XmppActivity
}
savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
if (this.mPendingImageUris.size() >= 1) {
+ Log.d(Config.LOGTAG,"ConversationsActivity.onSaveInstanceState() - saving pending image uri");
savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString());
} else {
savedInstanceState.remove(STATE_PENDING_URI);
@@ -1237,13 +1236,22 @@ public class ConversationActivity extends XmppActivity
this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
}
+ final boolean stopping;
+ if (Build.VERSION.SDK_INT >= 17) {
+ stopping = isFinishing() || isDestroyed();
+ } else {
+ stopping = isFinishing();
+ }
+
if (!forbidProcessingPendings) {
for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
Uri foo = i.next();
+ Log.d(Config.LOGTAG,"ConversationsActivity.onBackendConnected() - attaching image to conversations. stopping="+Boolean.toString(stopping));
attachImageToConversation(getSelectedConversation(), foo);
}
for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
+ Log.d(Config.LOGTAG,"ConversationsActivity.onBackendConnected() - attaching file to conversations. stopping="+Boolean.toString(stopping));
attachFileToConversation(getSelectedConversation(), i.next());
}
@@ -1368,6 +1376,7 @@ public class ConversationActivity extends XmppActivity
mPendingImageUris.addAll(extractUriFromIntent(data));
if (xmppConnectionServiceBound) {
for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
+ Log.d(Config.LOGTAG,"ConversationsActivity.onActivityResult() - attaching image to conversations. CHOOSE_IMAGE");
attachImageToConversation(getSelectedConversation(), i.next());
}
}
@@ -1381,6 +1390,7 @@ public class ConversationActivity extends XmppActivity
mPendingFileUris.addAll(uris);
if (xmppConnectionServiceBound) {
for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
+ Log.d(Config.LOGTAG,"ConversationsActivity.onActivityResult() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE");
attachFileToConversation(c, i.next());
}
}
@@ -1395,8 +1405,10 @@ public class ConversationActivity extends XmppActivity
}
} else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
if (mPendingImageUris.size() == 1) {
- Uri uri = mPendingImageUris.get(0);
+ Uri uri = FileBackend.getIndexableTakePhotoUri(mPendingImageUris.get(0));
+ mPendingImageUris.set(0, uri);
if (xmppConnectionServiceBound) {
+ Log.d(Config.LOGTAG,"ConversationsActivity.onActivityResult() - attaching image to conversations. TAKE_PHOTO");
attachImageToConversation(getSelectedConversation(), uri);
mPendingImageUris.clear();
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 14469355..df01e87c 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -576,9 +576,10 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
downloadFile.setVisible(true);
downloadFile.setTitle(activity.getString(R.string.download_x_file,UIHelper.getFileDescriptionString(activity, m)));
}
- if ((t != null && !(t instanceof TransferablePlaceholder))
- || (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING
- || m.getStatus() == Message.STATUS_OFFERED))) {
+ boolean waitingOfferedSending = m.getStatus() == Message.STATUS_WAITING
+ || m.getStatus() == Message.STATUS_UNSEND
+ || m.getStatus() == Message.STATUS_OFFERED;
+ if ((t != null && !(t instanceof TransferablePlaceholder)) || waitingOfferedSending && m.needsUploading()) {
cancelTransmission.setVisible(true);
}
if (treatAsFile) {
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index c10d2741..cc178179 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -519,6 +519,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
+ if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) {
+ changeMoreTableVisibility(true);
+ }
final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView,
@@ -585,9 +588,21 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
@Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
+ if (showMoreInfo.isVisible()) {
+ showMoreInfo.setChecked(mMoreTable.getVisibility() == View.VISIBLE);
+ }
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
protected void onStart() {
super.onStart();
- if (getIntent() != null) {
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ } else if (getIntent() != null) {
try {
this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
} catch (final InvalidJidException | NullPointerException ignored) {
@@ -625,6 +640,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (mAccount != null) {
savedInstanceState.putString("account", mAccount.getJid().toBareJid().toString());
savedInstanceState.putBoolean("initMode", mInitMode);
+ savedInstanceState.putBoolean("showMoreTable", mMoreTable.getVisibility() == View.VISIBLE);
}
super.onSaveInstanceState(savedInstanceState);
}
@@ -692,8 +708,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
startActivity(showBlocklistIntent);
break;
case R.id.action_server_info_show_more:
- mMoreTable.setVisibility(item.isChecked() ? View.GONE : View.VISIBLE);
- item.setChecked(!item.isChecked());
+ changeMoreTableVisibility(!item.isChecked());
break;
case R.id.action_change_password_on_server:
gotoChangePassword(null);
@@ -717,6 +732,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
return super.onOptionsItemSelected(item);
}
+ private void changeMoreTableVisibility(boolean visible) {
+ mMoreTable.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
private void gotoChangePassword(String newPassword) {
final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
diff --git a/src/main/java/eu/siacs/conversations/ui/ExportLogsPreference.java b/src/main/java/eu/siacs/conversations/ui/ExportLogsPreference.java
deleted file mode 100644
index bedb4172..00000000
--- a/src/main/java/eu/siacs/conversations/ui/ExportLogsPreference.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package eu.siacs.conversations.ui;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.preference.Preference;
-import android.util.AttributeSet;
-
-import eu.siacs.conversations.services.ExportLogsService;
-
-public class ExportLogsPreference extends Preference {
-
- public ExportLogsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public ExportLogsPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ExportLogsPreference(Context context) {
- super(context);
- }
-
- protected void onClick() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && getContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- return;
- }
- final Intent startIntent = new Intent(getContext(), ExportLogsService.class);
- getContext().startService(startIntent);
- super.onClick();
- }
-} \ No newline at end of file
diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
index f52ccb19..0f6b58ef 100644
--- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -103,6 +103,15 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
}
@Override
+ protected void onStart() {
+ super.onStart();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ }
+ }
+
+ @Override
public void onSaveInstanceState(final Bundle savedInstanceState) {
if (selectedAccount != null) {
savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().toBareJid().toString());
diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
index 0752ae32..9a699b51 100644
--- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
@@ -141,7 +141,7 @@ public class PublishProfilePictureActivity extends XmppActivity {
}
});
- this.defaultUri = PhoneHelper.getSefliUri(getApplicationContext());
+ this.defaultUri = PhoneHelper.getSelfiUri(getApplicationContext());
}
private void chooseAvatar(boolean crop) {
@@ -250,8 +250,11 @@ public class PublishProfilePictureActivity extends XmppActivity {
if (!support) {
this.hintOrWarning
.setTextColor(getWarningTextColor());
- this.hintOrWarning
- .setText(R.string.error_publish_avatar_no_server_support);
+ if (account.getStatus() == Account.State.ONLINE) {
+ this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
+ } else {
+ this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
+ }
}
} else {
this.avatarUri = this.defaultUri;
@@ -306,8 +309,11 @@ public class PublishProfilePictureActivity extends XmppActivity {
} else {
disablePublishButton();
this.hintOrWarning.setTextColor(getWarningTextColor());
- this.hintOrWarning
- .setText(R.string.error_publish_avatar_no_server_support);
+ if (account.getStatus() == Account.State.ONLINE) {
+ this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
+ } else {
+ this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
+ }
}
if (this.defaultUri != null && uri.equals(this.defaultUri)) {
this.secondaryHint.setVisibility(View.INVISIBLE);
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index 17ade702..c2bf20ac 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -29,6 +29,8 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.ExportLogsService;
import eu.siacs.conversations.xmpp.XmppConnection;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
public class SettingsActivity extends XmppActivity implements
OnSharedPreferenceChangeListener {
@@ -91,7 +93,7 @@ public class SettingsActivity extends XmppActivity implements
displayToast(getString(R.string.toast_no_trusted_certs));
return true;
}
- final ArrayList selectedItems = new ArrayList<Integer>();
+ final ArrayList selectedItems = new ArrayList<>();
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
@@ -147,12 +149,72 @@ public class SettingsActivity extends XmppActivity implements
exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
- hasStoragePermission(REQUEST_WRITE_LOGS);
+ if (hasStoragePermission(REQUEST_WRITE_LOGS)) {
+ startExport();
+ }
+ return true;
+ }
+ });
+
+ final Preference deleteOmemoPreference = mSettingsFragment.findPreference("delete_omemo_identities");
+ deleteOmemoPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ deleteOmemoIdentities();
return true;
}
});
}
+ private void deleteOmemoIdentities() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.pref_delete_omemo_identities);
+ final List<CharSequence> accounts = new ArrayList<>();
+ for(Account account : xmppConnectionService.getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ accounts.add(account.getJid().toBareJid().toString());
+ }
+ }
+ final boolean[] checkedItems = new boolean[accounts.size()];
+ builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ checkedItems[which] = isChecked;
+ final AlertDialog alertDialog = (AlertDialog) dialog;
+ for(boolean item : checkedItems) {
+ if (item) {
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+ return;
+ }
+ }
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel,null);
+ builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ for(int i = 0; i < checkedItems.length; ++i) {
+ if (checkedItems[i]) {
+ try {
+ Jid jid = Jid.fromString(accounts.get(i).toString());
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account != null) {
+ account.getAxolotlService().regenerateKeys(true);
+ }
+ } catch (InvalidJidException e) {
+ //
+ }
+
+ }
+ }
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ }
+
@Override
public void onStop() {
super.onStop();
@@ -213,13 +275,17 @@ public class SettingsActivity extends XmppActivity implements
if (grantResults.length > 0)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_WRITE_LOGS) {
- getApplicationContext().startService(new Intent(getApplicationContext(), ExportLogsService.class));
+ startExport();
}
} else {
Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
}
}
+ private void startExport() {
+ startService(new Intent(getApplicationContext(), ExportLogsService.class));
+ }
+
private void displayToast(final String msg) {
runOnUiThread(new Runnable() {
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
index 768961c1..ec5559ae 100644
--- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -282,7 +282,12 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public void onStart() {
super.onStart();
- askForContactsPermissions();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ } else {
+ askForContactsPermissions();
+ }
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java b/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java
index 3c837b94..59d58db5 100644
--- a/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.ui;
+import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -16,6 +17,11 @@ public class WelcomeActivity extends Activity {
if (getResources().getBoolean(R.bool.portrait_only)) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
+ final ActionBar ab = getActionBar();
+ if (ab != null) {
+ ab.setDisplayShowHomeEnabled(false);
+ ab.setDisplayHomeAsUpEnabled(false);
+ }
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);
final Button createAccount = (Button) findViewById(R.id.create_account);
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 b24c9539..4505e632 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -51,6 +51,7 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Message.FileParams;
import eu.siacs.conversations.entities.Transferable;
+import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.widget.ClickableMovementMethod;
import eu.siacs.conversations.ui.widget.ListSelectionManager;
@@ -710,7 +711,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
try {
- uri = FileProvider.getUriForFile(activity, "eu.siacs.conversations.files", file);
+ uri = FileProvider.getUriForFile(activity, FileBackend.CONVERSATIONS_FILE_PROVIDER, file);
} catch (IllegalArgumentException e) {
Toast.makeText(activity,activity.getString(R.string.no_permission_to_access_x,file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
return;
diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
index 429c349a..38ebced1 100644
--- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
@@ -25,6 +25,7 @@ import java.util.regex.Pattern;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -204,6 +205,15 @@ public final class CryptoHelper {
return prettifyFingerprintCert(bytesToHex(fingerprint));
}
+ public static String getAccountFingerprint(Account account) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ return bytesToHex(md.digest(account.getJid().toBareJid().toString().getBytes("UTF-8")));
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
public static int encryptionTypeToText(int encryption) {
switch (encryption) {
case Message.ENCRYPTION_OTR:
diff --git a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
index e3a24fcf..3242cfcf 100644
--- a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
@@ -93,14 +93,14 @@ public class PhoneHelper {
try {
return (super.loadInBackground());
- } catch (SecurityException e) {
+ } catch (Throwable e) {
return(null);
}
}
}
- public static Uri getSefliUri(Context context) {
+ public static Uri getSelfiUri(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
return null;
diff --git a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java
index 768e9f17..81f93653 100644
--- a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java
+++ b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java
@@ -12,6 +12,8 @@ import eu.siacs.conversations.Config;
public class SocksSocketFactory {
+ private static final byte[] LOCALHOST = new byte[]{127,0,0,1};
+
public static void createSocksConnection(Socket socket, String destination, int port) throws IOException {
InputStream proxyIs = socket.getInputStream();
OutputStream proxyOs = socket.getOutputStream();
@@ -44,7 +46,7 @@ public class SocksSocketFactory {
}
public static Socket createSocketOverTor(String destination, int port) throws IOException {
- return createSocket(new InetSocketAddress(InetAddress.getLocalHost(), 9050), destination, port);
+ return createSocket(new InetSocketAddress(InetAddress.getByAddress(LOCALHOST), 9050), destination, port);
}
static class SocksConnectionException extends IOException {
diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
index bf997bc1..faf4b3a5 100644
--- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
@@ -181,9 +181,7 @@ public class UIHelper {
}
} else {
String body = message.getBody();
- if (body == null) {
- body = "";
- } else if (body.length() > 256) {
+ if (body.length() > 256) {
body = body.substring(0,256);
}
if (body.startsWith(Message.ME_COMMAND)) {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index e3af48e3..383e990d 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -136,7 +136,7 @@ public class XmppConnection implements Runnable {
private SaslMechanism saslMechanism;
- private X509KeyManager mKeyManager = new X509KeyManager() {
+ private class MyKeyManager implements X509KeyManager {
@Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return account.getPrivateKeyAlias();
@@ -149,9 +149,11 @@ public class XmppConnection implements Runnable {
@Override
public X509Certificate[] getCertificateChain(String alias) {
+ Log.d(Config.LOGTAG,"getting certificate chain");
try {
return KeyChain.getCertificateChain(mXmppConnectionService, alias);
} catch (Exception e) {
+ Log.d(Config.LOGTAG,e.getMessage());
return new X509Certificate[0];
}
}
@@ -174,7 +176,8 @@ public class XmppConnection implements Runnable {
return null;
}
}
- };
+ }
+
private Identity mServerIdentity = Identity.UNKNOWN;
public final OnIqPacketReceived registrationResponseListener = new OnIqPacketReceived() {
@@ -241,6 +244,9 @@ public class XmppConnection implements Runnable {
}
protected void connect() {
+ if (mXmppConnectionService.areMessagesInitialized()) {
+ mXmppConnectionService.resetSendingToWaiting(account);
+ }
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
features.encryptionEnabled = false;
this.attempt++;
@@ -458,7 +464,7 @@ public class XmppConnection implements Runnable {
MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
KeyManager[] keyManager;
if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
- keyManager = new KeyManager[]{mKeyManager};
+ keyManager = new KeyManager[]{new MyKeyManager()};
} else {
keyManager = null;
}
@@ -846,18 +852,13 @@ public class XmppConnection implements Runnable {
saslMechanism = new Anonymous(tagWriter, account, mXmppConnectionService.getRNG());
}
if (saslMechanism != null) {
- final JSONObject keys = account.getKeys();
- try {
- if (keys.has(Account.PINNED_MECHANISM_KEY) &&
- keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) {
- Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
- " has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
- ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
- "). Possible downgrade attack?");
- throw new SecurityException();
- }
- } catch (final JSONException e) {
- Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
+ final int pinnedMechanism = account.getKeyAsInt(Account.PINNED_MECHANISM_KEY, -1);
+ if (pinnedMechanism > saslMechanism.getPriority()) {
+ Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
+ " has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
+ ") than pinned priority (" + pinnedMechanism +
+ "). Possible downgrade attack?");
+ throw new SecurityException();
}
Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
auth.setAttribute("mechanism", saslMechanism.getMechanism());
@@ -1072,7 +1073,7 @@ public class XmppConnection implements Runnable {
this.disco.clear();
}
mPendingServiceDiscoveries.set(0);
- mWaitForDisco.set(mServerIdentity != Identity.NIMBUZZ);
+ mWaitForDisco.set(mServerIdentity != Identity.NIMBUZZ && smVersion != 0);
lastDiscoStarted = SystemClock.elapsedRealtime();
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery");
mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
@@ -1600,6 +1601,10 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getServer(), Xmlns.BLOCKING);
}
+ public boolean spamReporting() {
+ return hasDiscoFeature(account.getServer(), "urn:xmpp:reporting:reason:spam:0");
+ }
+
public boolean register() {
return hasDiscoFeature(account.getServer(), Xmlns.REGISTER);
}