aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java1
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpEngine.java9
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java32
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java6
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java2
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java4
-rw-r--r--src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java6
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java16
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java18
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditMessage.java36
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java9
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java19
-rw-r--r--src/main/java/eu/siacs/conversations/ui/widget/CopyTextView.java66
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java17
-rw-r--r--src/main/res/layout/message_received.xml2
-rw-r--r--src/main/res/layout/message_sent.xml2
16 files changed, 198 insertions, 47 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 821de698..d716cfa4 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -54,6 +54,7 @@ public final class Config {
public static final int PING_MAX_INTERVAL = 300;
public static final int IDLE_PING_INTERVAL = 600; //540 is minimum according to docs;
public static final int PING_MIN_INTERVAL = 30;
+ public static final int LOW_PING_TIMEOUT = 1; // used after push received
public static final int PING_TIMEOUT = 15;
public static final int SOCKET_TIMEOUT = 15;
public static final int CONNECT_TIMEOUT = 90;
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
index da315812..1a5367fd 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
@@ -259,8 +259,13 @@ public class PgpEngine {
account);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
- logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.unable_to_connect_to_keychain, account);
+ OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+ if (error != null && "signing subkey not found!".equals(error.getMessage())) {
+ callback.error(0,account);
+ } else {
+ logError(account, error);
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
}
}
});
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 1bc5fa83..e776a78b 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
@@ -185,8 +185,8 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
private void fillMap(SQLiteAxolotlStore store) {
- List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().toBareJid().toString());
- putDevicesForJid(account.getJid().toBareJid().toString(), deviceIds, store);
+ List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().toBareJid().toPreppedString());
+ putDevicesForJid(account.getJid().toBareJid().toPreppedString(), deviceIds, store);
for (Contact contact : account.getRoster().getContacts()) {
Jid bareJid = contact.getJid().toBareJid();
String address = bareJid.toString();
@@ -220,7 +220,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
public void clearErrorFor(Jid jid) {
synchronized (MAP_LOCK) {
- Map<Integer, FetchStatus> devices = this.map.get(jid.toBareJid().toString());
+ Map<Integer, FetchStatus> devices = this.map.get(jid.toBareJid().toPreppedString());
if (devices == null) {
return;
}
@@ -257,28 +257,28 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) {
- return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toString(), trust);
+ return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toPreppedString(), trust);
}
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) {
- return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toString(), trust);
+ return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toPreppedString(), trust);
}
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) {
Set<IdentityKey> keys = new HashSet<>();
for(Jid jid : jids) {
- keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toString(), trust));
+ keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toPreppedString(), trust));
}
return keys;
}
public long getNumTrustedKeys(Jid jid) {
- return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString());
+ return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString());
}
public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) {
for(Jid jid : jids) {
- if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()) == 0) {
+ if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString()) == 0) {
return true;
}
}
@@ -286,7 +286,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
private AxolotlAddress getAddressForJid(Jid jid) {
- return new AxolotlAddress(jid.toString(), 0);
+ return new AxolotlAddress(jid.toPreppedString(), 0);
}
private Set<XmppAxolotlSession> findOwnSessions() {
@@ -359,7 +359,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
final XmppAxolotlSession.Trust from,
final XmppAxolotlSession.Trust to) {
for (Integer deviceId : deviceIds) {
- AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toString(), deviceId);
+ AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
XmppAxolotlSession session = sessions.get(address);
if (session != null && session.getFingerprint() != null
&& session.getTrust() == from) {
@@ -381,13 +381,13 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
publishOwnDeviceId(deviceIds);
}
for (Integer deviceId : deviceIds) {
- AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toString(), deviceId);
+ AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
if (sessions.get(ownDeviceAddress) == null) {
buildSessionFromPEP(ownDeviceAddress);
}
}
}
- Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toString()));
+ Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toPreppedString()));
expiredDevices.removeAll(deviceIds);
setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.TRUSTED,
XmppAxolotlSession.Trust.INACTIVE_TRUSTED);
@@ -759,7 +759,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
private void finishBuildingSessionsFromPEP(final AxolotlAddress address) {
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
&& !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) {
FetchStatus report = null;
@@ -873,7 +873,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
if (deviceIds.get(account.getJid().toBareJid()) != null) {
for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) {
- AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toString(), ownId);
+ AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownId);
if (sessions.get(address) == null) {
IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
if (identityKey != null) {
@@ -933,12 +933,12 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
public boolean hasPendingKeyFetches(Account account, List<Jid> jids) {
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) {
return true;
}
for(Jid jid : jids) {
- AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toString(), 0);
+ AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), 0);
if (fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING)) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index 70af45d4..55f5443a 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -196,7 +196,7 @@ public class Contact implements ListItem, Blockable {
values.put(ACCOUNT, accountUuid);
values.put(SYSTEMNAME, systemName);
values.put(SERVERNAME, serverName);
- values.put(JID, jid.toString());
+ values.put(JID, jid.toPreppedString());
values.put(OPTIONS, subscription);
values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri);
@@ -209,10 +209,6 @@ public class Contact implements ListItem, Blockable {
}
}
- public int getSubscription() {
- return this.subscription;
- }
-
public Account getAccount() {
return this.account;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index d9a03fc9..d94e1fec 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -506,7 +506,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
values.put(NAME, name);
values.put(CONTACT, contactUuid);
values.put(ACCOUNT, accountUuid);
- values.put(CONTACTJID, contactJid.toString());
+ values.put(CONTACTJID, contactJid.toPreppedString());
values.put(CREATED, created);
values.put(STATUS, status);
values.put(MODE, mode);
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index b00d8ef3..821eea39 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -204,12 +204,12 @@ public class Message extends AbstractEntity {
if (counterpart == null) {
values.putNull(COUNTERPART);
} else {
- values.put(COUNTERPART, counterpart.toString());
+ values.put(COUNTERPART, counterpart.toPreppedString());
}
if (trueCounterpart == null) {
values.putNull(TRUE_COUNTERPART);
} else {
- values.put(TRUE_COUNTERPART, trueCounterpart.toString());
+ values.put(TRUE_COUNTERPART, trueCounterpart.toPreppedString());
}
values.put(BODY, body);
values.put(TIME_SENT, timeSent);
diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
index f9fed914..f8639885 100644
--- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
@@ -38,13 +38,17 @@ public class PresenceGenerator extends AbstractGenerator {
}
public PresencePacket selfPresence(Account account, Presence.Status status) {
+ return selfPresence(account, status, true);
+ }
+
+ public PresencePacket selfPresence(Account account, Presence.Status status, boolean includePgpAnnouncement) {
PresencePacket packet = new PresencePacket();
if(status.toShowString() != null) {
packet.addChild("show").setContent(status.toShowString());
}
packet.setFrom(account.getJid());
String sig = account.getPgpSignature();
- if (sig != null && mXmppConnectionService.getPgpEngine() != null) {
+ if (includePgpAnnouncement && sig != null && mXmppConnectionService.getPgpEngine() != null) {
packet.addChild("x", "jabber:x:signed").setContent(sig);
}
String capHash = getCapHash();
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index d8b6b4e1..c1063762 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -287,7 +287,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
continue;
}
int ownDeviceId = Integer.valueOf(ownDeviceIdString);
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), ownDeviceId);
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownDeviceId);
deleteSession(db, account, ownAddress);
IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
if (identityKeyPair != null) {
@@ -345,7 +345,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
try {
newJid = Jid.fromString(
cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
- ).toString();
+ ).toPreppedString();
} catch (InvalidJidException ignored) {
Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
+ cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
@@ -370,7 +370,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
try {
newJid = Jid.fromString(
cursor.getString(cursor.getColumnIndex(Contact.JID))
- ).toString();
+ ).toPreppedString();
} catch (InvalidJidException ignored) {
Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
+ cursor.getString(cursor.getColumnIndex(Contact.JID))
@@ -578,8 +578,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public Conversation findConversation(final Account account, final Jid contactJid) {
SQLiteDatabase db = this.getReadableDatabase();
String[] selectionArgs = {account.getUuid(),
- contactJid.toBareJid().toString() + "/%",
- contactJid.toBareJid().toString()
+ contactJid.toBareJid().toPreppedString() + "/%",
+ contactJid.toBareJid().toPreppedString()
};
Cursor cursor = db.query(Conversation.TABLENAME, null,
Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
@@ -691,7 +691,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.insert(Contact.TABLENAME, null, contact.getContentValues());
} else {
String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
- String[] whereArgs = {account.getUuid(), contact.getJid().toString()};
+ String[] whereArgs = {account.getUuid(), contact.getJid().toPreppedString()};
db.delete(Contact.TABLENAME, where, whereArgs);
}
}
@@ -1025,7 +1025,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
private IdentityKeyPair loadOwnIdentityKeyPair(SQLiteDatabase db, Account account) {
- String name = account.getJid().toBareJid().toString();
+ String name = account.getJid().toBareJid().toPreppedString();
IdentityKeyPair identityKeyPair = null;
Cursor cursor = getIdentityKeyCursor(db, account, name, true);
if (cursor.getCount() != 0) {
@@ -1181,7 +1181,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {
- storeIdentityKey(account, account.getJid().toBareJid().toString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), XmppAxolotlSession.Trust.TRUSTED);
+ storeIdentityKey(account, account.getJid().toBareJid().toPreppedString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), XmppAxolotlSession.Trust.TRUSTED);
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index e96704f3..08921516 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -145,6 +145,7 @@ public class XmppConnectionService extends Service {
private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
private final IqGenerator mIqGenerator = new IqGenerator(this);
private final List<String> mInProgressAvatarFetches = new ArrayList<>();
+ private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
private long mLastActivity = 0;
@@ -626,7 +627,8 @@ public class XmppConnectionService extends Service {
long lastSent = account.getXmppConnection().getLastPingSent();
long pingInterval = (Config.PUSH_MODE || "ui".equals(action)) ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000;
long msToNextPing = (Math.max(lastReceived, lastSent) + pingInterval) - SystemClock.elapsedRealtime();
- long pingTimeoutIn = (lastSent + Config.PING_TIMEOUT * 1000) - SystemClock.elapsedRealtime();
+ int pingTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid()) ? Config.LOW_PING_TIMEOUT * 1000 : Config.PING_TIMEOUT * 1000;
+ long pingTimeoutIn = (lastSent + pingTimeout) - SystemClock.elapsedRealtime();
if (lastSent > lastReceived) {
if (pingTimeoutIn < 0) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout");
@@ -634,10 +636,18 @@ public class XmppConnectionService extends Service {
} else {
int secs = (int) (pingTimeoutIn / 1000);
this.scheduleWakeUpCall(secs, account.getUuid().hashCode());
+ if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": leaving low ping timeout mode");
+ }
}
} else {
pingCandidates.add(account);
- if (msToNextPing <= 0 || CryptoHelper.getAccountFingerprint(account).equals(pushedAccountHash)) {
+ if (CryptoHelper.getAccountFingerprint(account).equals(pushedAccountHash)) {
+ pingNow = true;
+ if (mLowPingTimeoutMode.add(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": entering low ping timeout mode");
+ }
+ } else if (msToNextPing <= 0) {
pingNow = true;
} else {
this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
@@ -2037,11 +2047,11 @@ public class XmppConnectionService extends Service {
final MucOptions mucOptions = conversation.getMucOptions();
final Jid joinJid = mucOptions.getSelf().getFullJid();
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString());
- PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE);
+ PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous());
packet.setTo(joinJid);
Element x = packet.addChild("x", "http://jabber.org/protocol/muc");
if (conversation.getMucOptions().getPassword() != null) {
- x.addChild("password").setContent(conversation.getMucOptions().getPassword());
+ x.addChild("password").setContent(mucOptions.getPassword());
}
if (mucOptions.mamSupport()) {
diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
index e3841d1d..e609d08e 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditMessage.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
@@ -1,7 +1,11 @@
package eu.siacs.conversations.ui;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.Spanned;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.EditText;
@@ -89,4 +93,36 @@ public class EditMessage extends EditText {
boolean onTabPressed(boolean repeated);
}
+ private static final InputFilter SPAN_FILTER = new InputFilter() {
+
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
+ return source instanceof Spanned ? source.toString() : source;
+ }
+ };
+
+ @Override
+ public boolean onTextContextMenuItem(int id) {
+ if (id == android.R.id.paste) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ return super.onTextContextMenuItem(android.R.id.pasteAsPlainText);
+ } else {
+ Editable editable = getEditableText();
+ InputFilter[] filters = editable.getFilters();
+ InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1];
+ if (filters != null) {
+ System.arraycopy(filters, 0, tempFilters, 1, filters.length);
+ }
+ tempFilters[0] = SPAN_FILTER;
+ editable.setFilters(tempFilters);
+ try {
+ return super.onTextContextMenuItem(id);
+ } finally {
+ editable.setFilters(filters);
+ }
+ }
+ } else {
+ return super.onTextContextMenuItem(id);
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 5172e18d..c2dfa1d8 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -584,7 +584,14 @@ public abstract class XmppActivity extends Activity {
@Override
public void error(int error, Account account) {
- displayErrorDialog(error);
+ if (error == 0 && account != null) {
+ account.setPgpSignId(0);
+ account.unsetPgpSignature();
+ xmppConnectionService.databaseBackend.updateAccount(account);
+ choosePgpSignId(account);
+ } else {
+ displayErrorDialog(error);
+ }
}
});
}
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 336d9eab..c04cb1a5 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -54,12 +54,13 @@ 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.CopyTextView;
import eu.siacs.conversations.ui.widget.ListSelectionManager;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
-public class MessageAdapter extends ArrayAdapter<Message> {
+public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler {
private static final int SENT = 0;
private static final int RECEIVED = 1;
@@ -485,7 +486,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
viewHolder.image = (ImageView) view
.findViewById(R.id.message_image);
- viewHolder.messageBody = (TextView) view
+ viewHolder.messageBody = (CopyTextView) view
.findViewById(R.id.message_body);
viewHolder.time = (TextView) view
.findViewById(R.id.message_time);
@@ -506,7 +507,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
viewHolder.image = (ImageView) view
.findViewById(R.id.message_image);
- viewHolder.messageBody = (TextView) view
+ viewHolder.messageBody = (CopyTextView) view
.findViewById(R.id.message_body);
viewHolder.time = (TextView) view
.findViewById(R.id.message_time);
@@ -524,7 +525,10 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder = null;
break;
}
- if (viewHolder.messageBody != null) listSelectionManager.onCreate(viewHolder.messageBody);
+ if (viewHolder.messageBody != null) {
+ listSelectionManager.onCreate(viewHolder.messageBody);
+ viewHolder.messageBody.setCopyHandler(this);
+ }
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
@@ -682,6 +686,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
listSelectionManager.onAfterNotifyDataSetChanged();
}
+ @Override
+ public String transformTextForCopy(CharSequence text, int start, int end) {
+ return text.toString().substring(start, end);
+ }
+
public void openDownloadable(Message message) {
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
if (!file.exists()) {
@@ -759,7 +768,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
protected ImageView indicator;
protected ImageView indicatorReceived;
protected TextView time;
- protected TextView messageBody;
+ protected CopyTextView messageBody;
protected ImageView contact_picture;
protected TextView status_message;
protected TextView encryption;
diff --git a/src/main/java/eu/siacs/conversations/ui/widget/CopyTextView.java b/src/main/java/eu/siacs/conversations/ui/widget/CopyTextView.java
new file mode 100644
index 00000000..bed56192
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/widget/CopyTextView.java
@@ -0,0 +1,66 @@
+package eu.siacs.conversations.ui.widget;
+
+import android.annotation.TargetApi;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+public class CopyTextView extends TextView {
+
+ public CopyTextView(Context context) {
+ super(context);
+ }
+
+ public CopyTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CopyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @SuppressWarnings("unused")
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public CopyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public interface CopyHandler {
+ public String transformTextForCopy(CharSequence text, int start, int end);
+ }
+
+ private CopyHandler copyHandler;
+
+ public void setCopyHandler(CopyHandler copyHandler) {
+ this.copyHandler = copyHandler;
+ }
+
+ @Override
+ public boolean onTextContextMenuItem(int id) {
+ CharSequence text = getText();
+ int min = 0;
+ int max = text.length();
+ if (isFocused()) {
+ final int selStart = getSelectionStart();
+ final int selEnd = getSelectionEnd();
+ min = Math.max(0, Math.min(selStart, selEnd));
+ max = Math.max(0, Math.max(selStart, selEnd));
+ }
+ String textForCopy = null;
+ if (id == android.R.id.copy && copyHandler != null) {
+ textForCopy = copyHandler.transformTextForCopy(getText(), min, max);
+ }
+ try {
+ return super.onTextContextMenuItem(id);
+ } finally {
+ if (textForCopy != null) {
+ ClipboardManager clipboard = (ClipboardManager) getContext().
+ getSystemService(Context.CLIPBOARD_SERVICE);
+ clipboard.setPrimaryClip(ClipData.newPlainText(null, textForCopy));
+ }
+ }
+ }
+} \ No newline at end of file
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 6430d41e..20f1feb4 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
@@ -21,6 +21,10 @@ public final class Jid {
private final String domainpart;
private final String resourcepart;
+ // It's much more efficient to store the ful JID as well as the parts instead of figuring them
+ // all out every time (since some characters are displayed but aren't used for comparisons).
+ private final String displayjid;
+
public String getLocalpart() {
return localpart;
}
@@ -69,6 +73,7 @@ public final class Jid {
Jid fromCache = Jid.cache.get(jid);
if (fromCache != null) {
+ displayjid = fromCache.displayjid;
localpart = fromCache.localpart;
domainpart = fromCache.domainpart;
resourcepart = fromCache.resourcepart;
@@ -89,6 +94,8 @@ public final class Jid {
throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER);
}
+ String finaljid;
+
final int domainpartStart;
final int atLoc = jid.indexOf("@");
final int slashLoc = jid.indexOf("/");
@@ -96,6 +103,7 @@ public final class Jid {
// or there are one or more "@" signs but they're all in the resourcepart (eg. "example.net/@/rp@"):
if (atCount == 0 || (atCount > 0 && slashLoc != -1 && atLoc > slashLoc)) {
localpart = "";
+ finaljid = "";
domainpartStart = 0;
} else {
final String lp = jid.substring(0, atLoc);
@@ -108,6 +116,7 @@ public final class Jid {
throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
}
domainpartStart = atLoc + 1;
+ finaljid = lp + "@";
}
final String dp;
@@ -126,6 +135,7 @@ public final class Jid {
} catch (final StringprepException e) {
throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
}
+ finaljid = finaljid + dp + "/" + rp;
} else {
resourcepart = "";
try{
@@ -133,6 +143,7 @@ public final class Jid {
} catch (final StringprepException e) {
throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
}
+ finaljid = finaljid + dp;
}
// Remove trailing "." before storing the domain part.
@@ -156,6 +167,8 @@ public final class Jid {
}
Jid.cache.put(jid, this);
+
+ this.displayjid = finaljid;
}
public Jid toBareJid() {
@@ -178,6 +191,10 @@ public final class Jid {
@Override
public String toString() {
+ return displayjid;
+ }
+
+ public String toPreppedString() {
String out;
if (hasLocalpart()) {
out = localpart + '@' + domainpart;
diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml
index 18f753af..ea098b2d 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -49,7 +49,7 @@
android:background="@color/black87"
android:scaleType="centerCrop" />
- <TextView
+ <eu.siacs.conversations.ui.widget.CopyTextView
android:id="@+id/message_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 5def72dc..0f81c3f7 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -50,7 +50,7 @@
android:longClickable="true"
android:scaleType="centerCrop" />
- <TextView
+ <eu.siacs.conversations.ui.widget.CopyTextView
android:id="@+id/message_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"