aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu/siacs')
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java14
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpEngine.java4
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java30
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java1
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java8
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java14
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Account.java54
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Bookmark.java16
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java33
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java39
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ListItem.java6
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java9
-rw-r--r--src/main/java/eu/siacs/conversations/entities/MucOptions.java261
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Presence.java59
-rw-r--r--src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java76
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Presences.java38
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java57
-rw-r--r--src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java2
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java6
-rw-r--r--src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java2
-rw-r--r--src/main/java/eu/siacs/conversations/parser/AbstractParser.java31
-rw-r--r--src/main/java/eu/siacs/conversations/parser/IqParser.java14
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java81
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java13
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java72
-rw-r--r--src/main/java/eu/siacs/conversations/services/AvatarService.java2
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java114
-rw-r--r--src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java25
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java69
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java24
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java29
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java46
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java245
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java21
-rw-r--r--src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java116
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java20
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java239
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java19
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java5
-rw-r--r--src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java83
-rw-r--r--src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java36
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java83
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java7
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java5
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java3
-rw-r--r--src/main/java/eu/siacs/conversations/utils/DNSHelper.java21
-rw-r--r--src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java14
-rw-r--r--src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java12
-rw-r--r--src/main/java/eu/siacs/conversations/utils/GeoHelper.java18
-rw-r--r--src/main/java/eu/siacs/conversations/utils/UIHelper.java15
-rw-r--r--src/main/java/eu/siacs/conversations/xml/XmlReader.java13
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java92
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/forms/Data.java2
55 files changed, 1760 insertions, 562 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index a9297c81..cfc0c86c 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -38,15 +38,13 @@ public final class Config {
public static final String LOGTAG = BuildConfig.LOGTAG;
public static final String DOMAIN_LOCK = BuildConfig.LOCKED_IN_DOMAIN; //only allow account creation for this domain
- public static final String CONFERENCE_DOMAIN_LOCK = BuildConfig.LOCKED_IN_DOMAIN_CONFERENCES; //only allow conference creation for this domain
- public static final boolean LOCK_DOMAINS_IN_CONVERSATIONS = BuildConfig.CONTACTS_CONFERENCES_LOCKED_TO_DOMAIN; //only add contacts and conferences for own domains
-
- public static final boolean LOCK_SETTINGS = BuildConfig.ACCOUNT_SETTINGS_LOCKED; //set to true to disallow account and settings editing
+ public static final String MAGIC_CREATE_DOMAIN = "conversations.im";
public static final boolean DISALLOW_REGISTRATION_IN_UI = BuildConfig.DISALLOW_REGISTRATION_IN_UI; //hide the register checkbox
public static final boolean ALLOW_NON_TLS_CONNECTIONS = BuildConfig.ALLOW_NON_TLS_CONNECTIONS; //very dangerous. you should have a good reason to set this to true
public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = BuildConfig.HIDE_MESSAGE_TEXT_IN_NOTIFICATION;
public static final boolean SHOW_CONNECTED_ACCOUNTS = BuildConfig.SHOW_CONNECTED_ACCOUNTS_IN_FOREGROUND_NOTIFICATION; //show number of connected accounts in foreground notification
+ public static final boolean SHOW_DISABLE_FOREGROUND = true; //if set to true the foreground notification has a button to disable it
public static final int PING_MAX_INTERVAL = BuildConfig.PING_MAX_INTERVAL;
public static final int PING_MIN_INTERVAL = BuildConfig.PING_MIN_INTERVAL;
@@ -62,6 +60,11 @@ public final class Config {
public static final int AVATAR_SIZE = BuildConfig.AVATAR_SIZE;
public static final Bitmap.CompressFormat AVATAR_FORMAT = BuildConfig.AVATAR_FORMAT;
+ public static final int IMAGE_SIZE = 1920;
+ public static final Bitmap.CompressFormat IMAGE_FORMAT = Bitmap.CompressFormat.JPEG;
+ public static final int IMAGE_QUALITY = 75;
+ public static final int IMAGE_MAX_SIZE = 524288; //512KiB
+
public static final int PAGE_SIZE = BuildConfig.PAGE_SIZE;
public static final int MAX_NUM_PAGES = BuildConfig.MAX_NUM_PAGES;
@@ -71,6 +74,7 @@ public final class Config {
public static final boolean DISABLE_HTTP_UPLOAD = BuildConfig.DISABLE_HTTP_UPLOAD;
public static final boolean DISABLE_STRING_PREP = BuildConfig.DISABLE_STRING_PREP; // setting to true might increase startup performance
public static final boolean EXTENDED_SM_LOGGING = BuildConfig.EXTENDED_SM_LOGGING; // log stanza counts
+ public static final boolean BACKGROUND_STANZA_LOGGING = false;
public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = BuildConfig.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE; //setting to true might increase power consumption
public static final boolean ENCRYPT_ON_HTTP_UPLOADED = BuildConfig.ENCRYPT_ON_HTTP_UPLOADED;
@@ -83,8 +87,6 @@ public final class Config {
public static final boolean IGNORE_ID_REWRITE_IN_MUC = BuildConfig.IGNORE_ID_REWRITE_IN_MUC;
- public static final boolean REQUEST_DISCO = BuildConfig.REQUEST_DISCO;
-
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
public static final int MAM_MAX_MESSAGES = BuildConfig.MAM_MAX_MESSAGES;
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
index 92eb158f..ed8f2857 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.crypto;
import android.app.PendingIntent;
import android.content.Intent;
+import android.util.Log;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -305,7 +306,7 @@ public class PgpEngine {
public void generateSignature(final Account account, String status,
final UiCallback<Account> callback) {
- if (account.getPgpId() == -1) {
+ if (account.getPgpId() == 0) {
return;
}
Intent params = new Intent();
@@ -314,6 +315,7 @@ public class PgpEngine {
params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId());
InputStream is = new ByteArrayInputStream(status.getBytes());
final OutputStream os = new ByteArrayOutputStream();
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": signing status message \""+status+"\"");
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java
index c634d877..526868d0 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java
@@ -90,16 +90,18 @@ public class SQLiteAxolotlStore implements AxolotlStore {
// --------------------------------------
private IdentityKeyPair loadIdentityKeyPair() {
- IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
+ synchronized (mXmppConnectionService) {
+ IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
- if (ownKey != null) {
- return ownKey;
- } else {
+ if (ownKey != null) {
+ return ownKey;
+ } else {
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
- ownKey = generateIdentityKeyPair();
- mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
- }
- return ownKey;
+ ownKey = generateIdentityKeyPair();
+ mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
+ }
+ return ownKey;
+ }
}
private int loadRegistrationId() {
@@ -125,15 +127,15 @@ public class SQLiteAxolotlStore implements AxolotlStore {
}
private int loadCurrentPreKeyId() {
- String regIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
- int reg_id;
- if (regIdString != null) {
- reg_id = Integer.valueOf(regIdString);
+ String prekeyIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
+ int prekey_id;
+ if (prekeyIdString != null) {
+ prekey_id = Integer.valueOf(prekeyIdString);
} else {
Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid());
- reg_id = 0;
+ prekey_id = 0;
}
- return reg_id;
+ return prekey_id;
}
public void regenerate() {
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 cf950d6d..93a73677 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
@@ -158,7 +158,6 @@ public class XmppAxolotlMessage {
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
- this.innerKey = secretKey.getEncoded();
this.ciphertext = cipher.doFinal(plaintext.getBytes());
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
index 93ed32a2..9e1e65e7 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
@@ -168,6 +168,10 @@ public class XmppAxolotlSession {
try {
try {
PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
+ if (!message.getPreKeyId().isPresent()) {
+ Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
+ break;
+ }
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
IdentityKey msgIdentityKey = message.getIdentityKey();
if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
@@ -175,10 +179,8 @@ public class XmppAxolotlSession {
} else {
this.identityKey = msgIdentityKey;
plaintext = cipher.decrypt(message);
- if (message.getPreKeyId().isPresent()) {
- preKeyId = message.getPreKeyId().get();
+ preKeyId = message.getPreKeyId().get();
}
- }
} catch (InvalidMessageException | InvalidVersionException e) {
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "WhisperMessage received");
WhisperMessage message = new WhisperMessage(encryptedKey);
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java
index 3a05446c..f40eec55 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java
@@ -185,13 +185,17 @@ public class ScramSha1 extends SaslMechanism {
state = State.RESPONSE_SENT;
return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP);
case RESPONSE_SENT:
- final String clientCalculatedServerFinalMessage = "v=" +
- Base64.encodeToString(serverSignature, Base64.NO_WRAP);
- if (challenge == null || !clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
+ try {
+ final String clientCalculatedServerFinalMessage = "v=" +
+ Base64.encodeToString(serverSignature, Base64.NO_WRAP);
+ if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
+ throw new Exception();
+ };
+ state = State.VALID_SERVER_RESPONSE;
+ return "";
+ } catch(Exception e) {
throw new AuthenticationException("Server final message does not match calculated final message");
}
- state = State.VALID_SERVER_RESPONSE;
- return "";
default:
throw new InvalidStateException(state);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 4c4a1916..b3a31127 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -46,6 +46,8 @@ public class Account extends AbstractEntity {
public static final String DISPLAY_NAME = "display_name";
public static final String HOSTNAME = "hostname";
public static final String PORT = "port";
+ public static final String STATUS = "status";
+ public static final String STATUS_MESSAGE = "status_message";
public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
@@ -53,6 +55,7 @@ public class Account extends AbstractEntity {
public static final int OPTION_DISABLED = 1;
public static final int OPTION_REGISTER = 2;
public static final int OPTION_USECOMPRESSION = 3;
+ public static final int OPTION_MAGIC_CREATE = 4;
public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
public boolean httpUploadAvailable(long filesize) {
@@ -79,6 +82,10 @@ public class Account extends AbstractEntity {
}
}
+ public Contact getSelfContact() {
+ return getRoster().getContact(jid);
+ }
+
public enum State {
DISABLED,
OFFLINE,
@@ -93,7 +100,9 @@ public class Account extends AbstractEntity {
REGISTRATION_NOT_SUPPORTED(true),
SECURITY_ERROR(true),
INCOMPATIBLE_SERVER(true),
- TOR_NOT_AVAILABLE(true);
+ TOR_NOT_AVAILABLE(true),
+ BIND_FAILURE(true),
+ HOST_UNKNOWN(true);
private final boolean isError;
@@ -139,6 +148,10 @@ public class Account extends AbstractEntity {
return R.string.account_status_incompatible_server;
case TOR_NOT_AVAILABLE:
return R.string.account_status_tor_unavailable;
+ case BIND_FAILURE:
+ return R.string.account_status_bind_failure;
+ case HOST_UNKNOWN:
+ return R.string.account_status_host_unknown;
default:
return R.string.account_status_unknown;
}
@@ -171,6 +184,8 @@ public class Account extends AbstractEntity {
private final Roster roster = new Roster(this);
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
+ private Presence.Status presenceStatus = Presence.Status.ONLINE;
+ private String presenceStatusMessage = null;
public Account() {
this.uuid = "0";
@@ -178,12 +193,13 @@ public class Account extends AbstractEntity {
public Account(final Jid jid, final String password) {
this(java.util.UUID.randomUUID().toString(), jid,
- password, 0, null, "", null, null, null, 5222);
+ password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null);
}
private Account(final String uuid, final Jid jid,
- final String password, final int options, final String rosterVersion, final String keys,
- final String avatar, String displayName, String hostname, int port) {
+ final String password, final int options, final String rosterVersion, final String keys,
+ final String avatar, String displayName, String hostname, int port,
+ final Presence.Status status, String statusMessage) {
this.uuid = uuid;
this.jid = jid;
if (jid.isBareJid()) {
@@ -201,6 +217,8 @@ public class Account extends AbstractEntity {
this.displayName = displayName;
this.hostname = hostname;
this.port = port;
+ this.presenceStatus = status;
+ this.presenceStatusMessage = statusMessage;
}
public static Account fromCursor(final Cursor cursor) {
@@ -219,7 +237,9 @@ public class Account extends AbstractEntity {
cursor.getString(cursor.getColumnIndex(AVATAR)),
cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndex(HOSTNAME)),
- cursor.getInt(cursor.getColumnIndex(PORT)));
+ cursor.getInt(cursor.getColumnIndex(PORT)),
+ Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
+ cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
}
public boolean isOptionSet(final int option) {
@@ -290,6 +310,22 @@ public class Account extends AbstractEntity {
return getXmppConnection() != null && getStatus().isError() && getXmppConnection().getAttempt() >= 3;
}
+ public void setPresenceStatus(Presence.Status status) {
+ this.presenceStatus = status;
+ }
+
+ public Presence.Status getPresenceStatus() {
+ return this.presenceStatus;
+ }
+
+ public void setPresenceStatusMessage(String message) {
+ this.presenceStatusMessage = message;
+ }
+
+ public String getPresenceStatusMessage() {
+ return this.presenceStatusMessage;
+ }
+
public String getResource() {
return jid.getResourcepart();
}
@@ -350,6 +386,8 @@ public class Account extends AbstractEntity {
values.put(DISPLAY_NAME, displayName);
values.put(HOSTNAME, hostname);
values.put(PORT, port);
+ values.put(STATUS, presenceStatus.toShowString());
+ values.put(STATUS_MESSAGE, presenceStatusMessage);
return values;
}
@@ -420,7 +458,7 @@ public class Account extends AbstractEntity {
}
public int countPresences() {
- return this.getRoster().getContact(this.getJid().toBareJid()).getPresences().size();
+ return this.getSelfContact().getPresences().size();
}
public String getPgpSignature() {
@@ -458,10 +496,10 @@ public class Account extends AbstractEntity {
try {
return keys.getLong(KEY_PGP_ID);
} catch (JSONException e) {
- return -1;
+ return 0;
}
} else {
- return -1;
+ return 0;
}
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index fa30443d..428758d6 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -1,6 +1,7 @@
package eu.siacs.conversations.entities;
import android.graphics.Color;
+import android.content.Context;
import java.util.ArrayList;
import java.util.List;
@@ -63,9 +64,7 @@ public class Bookmark extends Element implements ListItem {
@Override
public String getDisplayJid() {
Jid jid = getJid();
- if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && jid != null && jid.getDomainpart().equals(Config.CONFERENCE_DOMAIN_LOCK)) {
- return jid.getLocalpart();
- } else if (jid != null) {
+ if (jid != null) {
return jid.toString();
} else {
return null;
@@ -78,7 +77,7 @@ public class Bookmark extends Element implements ListItem {
}
@Override
- public List<Tag> getTags() {
+ public List<Tag> getTags(Context context) {
ArrayList<Tag> tags = new ArrayList<Tag>();
for (Element element : getChildren()) {
if (element.getName().equals("group") && element.getContent() != null) {
@@ -121,7 +120,8 @@ public class Bookmark extends Element implements ListItem {
}
}
- public boolean match(String needle) {
+ @Override
+ public boolean match(Context context, String needle) {
if (needle == null) {
return true;
}
@@ -129,12 +129,12 @@ public class Bookmark extends Element implements ListItem {
final Jid jid = getJid();
return (jid != null && jid.toString().contains(needle)) ||
getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(needle);
+ matchInTag(context, needle);
}
- private boolean matchInTag(String needle) {
+ private boolean matchInTag(Context context, String needle) {
needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags()) {
+ for (Tag tag : getTags(context)) {
if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
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 9b4be366..60e5d5fc 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -124,9 +124,7 @@ public class Contact implements ListItem, Blockable {
@Override
public String getDisplayJid() {
- if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && jid != null && jid.getDomainpart().equals(Config.DOMAIN_LOCK)) {
- return jid.getLocalpart();
- } else if (jid != null) {
+ if (jid != null) {
return jid.toString();
} else {
return null;
@@ -142,25 +140,14 @@ public class Contact implements ListItem, Blockable {
}
@Override
- public List<Tag> getTags() {
+ public List<Tag> getTags(Context context) {
final ArrayList<Tag> tags = new ArrayList<>();
for (final String group : getGroups()) {
tags.add(new Tag(group, UIHelper.getColorForName(group)));
}
- switch (getMostAvailableStatus()) {
- case CHAT:
- case ONLINE:
- tags.add(new Tag("online", 0xff259b24));
- break;
- case AWAY:
- tags.add(new Tag("away", 0xffff9800));
- break;
- case XA:
- tags.add(new Tag("not available", 0xfff44336));
- break;
- case DND:
- tags.add(new Tag("dnd", 0xfff44336));
- break;
+ Presence.Status status = getMostAvailableStatus();
+ if (status != Presence.Status.OFFLINE) {
+ tags.add(UIHelper.getTagForStatus(context, status));
}
if (isBlocked()) {
tags.add(new Tag("blocked", 0xff2e2f3b));
@@ -173,7 +160,7 @@ public class Contact implements ListItem, Blockable {
return UIHelper.getStatusColor(getMostAvailableStatus());
}
- public boolean match(String needle) {
+ public boolean match(Context context, String needle) {
if (needle == null || needle.isEmpty()) {
return true;
}
@@ -181,7 +168,7 @@ public class Contact implements ListItem, Blockable {
String[] parts = needle.split("\\s+");
if (parts.length > 1) {
for(int i = 0; i < parts.length; ++i) {
- if (!match(parts[i])) {
+ if (!match(context, parts[i])) {
return false;
}
}
@@ -189,13 +176,13 @@ public class Contact implements ListItem, Blockable {
} else {
return jid.toString().contains(needle) ||
getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(needle);
+ matchInTag(context, needle);
}
}
- private boolean matchInTag(String needle) {
+ private boolean matchInTag(Context context, String needle) {
needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags()) {
+ for (Tag tag : getTags(context)) {
if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 7878cecd..fe03daac 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -29,6 +29,7 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
+
public class Conversation extends AbstractEntity implements Blockable {
public static final String TABLENAME = "conversations";
@@ -619,23 +620,6 @@ public class Conversation extends AbstractEntity implements Blockable {
return this.nextCounterpart;
}
- private int getMostRecentlyUsedOutgoingEncryption() {
- synchronized (this.messages) {
- for(int i = this.messages.size() -1; i >= 0; --i) {
- final Message m = this.messages.get(i);
- if (!m.isCarbon() && m.getStatus() != Message.STATUS_RECEIVED) {
- final int e = m.getEncryption();
- if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) {
- return Message.ENCRYPTION_PGP;
- } else {
- return e;
- }
- }
- }
- }
- return Message.ENCRYPTION_NONE;
- }
-
private int getMostRecentlyUsedIncomingEncryption() {
synchronized (this.messages) {
for(int i = this.messages.size() -1; i >= 0; --i) {
@@ -657,24 +641,21 @@ public class Conversation extends AbstractEntity implements Blockable {
final AxolotlService axolotlService = getAccount().getAxolotlService();
int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1);
if (next == -1) {
- if (Config.X509_VERIFICATION) {
- if (axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) {
- return Message.ENCRYPTION_AXOLOTL;
- } else {
- return Message.ENCRYPTION_NONE;
- }
- }
- int outgoing = this.getMostRecentlyUsedOutgoingEncryption();
- if (outgoing == Message.ENCRYPTION_NONE) {
- next = this.getMostRecentlyUsedIncomingEncryption();
+ if (Config.supportOmemo()
+ && axolotlService != null
+ && mode == MODE_SINGLE
+ && axolotlService.isConversationAxolotlCapable(this)
+ && getAccount().getSelfContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY)
+ && getContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY)) {
+ return Message.ENCRYPTION_AXOLOTL;
} else {
- next = outgoing;
+ next = this.getMostRecentlyUsedIncomingEncryption();
}
}
if (!Config.supportUnencrypted() && next <= 0) {
if (Config.supportOmemo()
- && (axolotlService != null && axolotlService.isConversationAxolotlCapable(this) || !Config.multipleEncryptionChoices())) {
+ && ((axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) || !Config.multipleEncryptionChoices())) {
return Message.ENCRYPTION_AXOLOTL;
} else if (Config.supportOtr() && mode == MODE_SINGLE) {
return Message.ENCRYPTION_OTR;
diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java
index 56804fbf..640cb267 100644
--- a/src/main/java/eu/siacs/conversations/entities/ListItem.java
+++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java
@@ -1,5 +1,7 @@
package eu.siacs.conversations.entities;
+import android.content.Context;
+
import java.util.List;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -13,7 +15,7 @@ public interface ListItem extends Comparable<ListItem> {
public int getStatusColor();
- List<Tag> getTags();
+ List<Tag> getTags(Context context);
final class Tag {
private final String name;
@@ -33,5 +35,5 @@ public interface ListItem extends Comparable<ListItem> {
}
}
- boolean match(final String needle);
+ boolean match(Context context, final String needle);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 6faebc65..95ee879d 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -570,13 +570,8 @@ public class Message extends AbstractEntity {
boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}");
if (encrypted) {
- if (MimeUtils.guessMimeTypeFromExtension(extension) != null) {
- mTreatAsDownloadAble = Decision.MUST;
- return mTreatAsDownloadAble;
- } else {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
+ mTreatAsDownloadAble = Decision.MUST;
+ return mTreatAsDownloadAble;
} else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension)
|| Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) {
mTreatAsDownloadAble = Decision.SHOULD;
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 7681a3d4..44d16cf2 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -5,9 +5,7 @@ import android.annotation.SuppressLint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import eu.siacs.conversations.R;
@@ -28,6 +26,19 @@ public class MucOptions {
this.self = user;
}
+ public void changeAffiliation(Jid jid, Affiliation affiliation) {
+ User user = findUserByRealJid(jid);
+ synchronized (users) {
+ if (user != null && user.getRole() == Role.NONE) {
+ users.remove(user);
+ if (affiliation.ranks(Affiliation.MEMBER)) {
+ user.affiliation = affiliation;
+ users.add(user);
+ }
+ }
+ }
+ }
+
public enum Affiliation {
OWNER("owner", 4, R.string.owner),
ADMIN("admin", 3, R.string.admin),
@@ -124,10 +135,10 @@ public class MucOptions {
}
- public static class User {
+ public static class User implements Comparable<User> {
private Role role = Role.NONE;
private Affiliation affiliation = Affiliation.NONE;
- private Jid jid;
+ private Jid realJid;
private Jid fullJid;
private long pgpKeyId = 0;
private Avatar avatar;
@@ -139,15 +150,11 @@ public class MucOptions {
}
public String getName() {
- return this.fullJid.getResourcepart();
+ return fullJid == null ? null : fullJid.getResourcepart();
}
- public void setJid(Jid jid) {
- this.jid = jid;
- }
-
- public Jid getJid() {
- return this.jid;
+ public void setRealJid(Jid jid) {
+ this.realJid = jid != null ? jid.toBareJid() : null;
}
public Role getRole() {
@@ -155,6 +162,10 @@ public class MucOptions {
}
public void setRole(String role) {
+ if (role == null) {
+ this.role = Role.NONE;
+ return;
+ }
role = role.toLowerCase();
switch (role) {
case "moderator":
@@ -172,26 +183,15 @@ public class MucOptions {
}
}
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- } else if (!(other instanceof User)) {
- return false;
- } else {
- User o = (User) other;
- return getName() != null && getName().equals(o.getName())
- && jid != null && jid.equals(o.jid)
- && affiliation == o.affiliation
- && role == o.role;
- }
- }
-
public Affiliation getAffiliation() {
return this.affiliation;
}
public void setAffiliation(String affiliation) {
+ if (affiliation == null) {
+ this.affiliation = Affiliation.NONE;
+ return;
+ }
affiliation = affiliation.toLowerCase();
switch (affiliation) {
case "admin":
@@ -220,7 +220,13 @@ public class MucOptions {
}
public Contact getContact() {
- return getAccount().getRoster().getContactFromRoster(getJid());
+ if (fullJid != null) {
+ return getAccount().getRoster().getContactFromRoster(realJid);
+ } else if (realJid != null){
+ return getAccount().getRoster().getContact(realJid);
+ } else {
+ return null;
+ }
}
public boolean setAvatar(Avatar avatar) {
@@ -243,11 +249,62 @@ public class MucOptions {
public Jid getFullJid() {
return fullJid;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ User user = (User) o;
+
+ if (role != user.role) return false;
+ if (affiliation != user.affiliation) return false;
+ if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
+ return false;
+ return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = role != null ? role.hashCode() : 0;
+ result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
+ result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
+ result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "[fulljid:"+String.valueOf(fullJid)+",realjid:"+String.valueOf(realJid)+",affiliation"+affiliation.toString()+"]";
+ }
+
+ public boolean realJidMatchesAccount() {
+ return realJid != null && realJid.equals(options.account.getJid().toBareJid());
+ }
+
+ @Override
+ public int compareTo(User another) {
+ Contact ourContact = getContact();
+ Contact anotherContact = another.getContact();
+ if (ourContact != null && anotherContact != null) {
+ return ourContact.compareTo(anotherContact);
+ } else if (ourContact == null && anotherContact != null) {
+ return getName().compareToIgnoreCase(anotherContact.getDisplayName());
+ } else if (ourContact != null) {
+ return ourContact.getDisplayName().compareToIgnoreCase(another.getName());
+ } else {
+ return getName().compareToIgnoreCase(another.getName());
+ }
+ }
+
+ public Jid getRealJid() {
+ return realJid;
+ }
}
private Account account;
- private final Map<String, User> users = Collections.synchronizedMap(new LinkedHashMap<String, User>());
- private final Set<Jid> members = Collections.synchronizedSet(new HashSet<Jid>());
+ private final Set<User> users = new HashSet<>();
private final List<String> features = new ArrayList<>();
private Data form = new Data();
private Conversation conversation;
@@ -315,20 +372,82 @@ public class MucOptions {
return hasFeature("muc_moderated");
}
- public User deleteUser(String name) {
- return this.users.remove(name);
+ public User deleteUser(Jid jid) {
+ User user = findUserByFullJid(jid);
+ if (user != null) {
+ synchronized (users) {
+ users.remove(user);
+ if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
+ user.role = Role.NONE;
+ user.avatar = null;
+ user.fullJid = null;
+ users.add(user);
+ }
+ }
+ }
+ return user;
}
public void addUser(User user) {
- this.users.put(user.getName(), user);
+ User old;
+ if (user.fullJid == null && user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ if (old != null) {
+ if (old.fullJid != null) {
+ return; //don't add. user already exists
+ } else {
+ synchronized (users) {
+ users.remove(old);
+ }
+ }
+ }
+ } else if (user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ synchronized (users) {
+ if (old != null && old.fullJid == null) {
+ users.remove(old);
+ }
+ }
+ }
+ old = findUserByFullJid(user.getFullJid());
+ synchronized (this.users) {
+ if (old != null) {
+ users.remove(old);
+ }
+ this.users.add(user);
+ }
}
- public User findUser(String name) {
- return this.users.get(name);
+ public User findUserByFullJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.getFullJid())) {
+ return user;
+ }
+ }
+ }
+ return null;
}
- public boolean isUserInRoom(String name) {
- return findUser(name) != null;
+ public User findUserByRealJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.realJid)) {
+ return user;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean isUserInRoom(Jid jid) {
+ return findUserByFullJid(jid) != null;
}
public void setError(Error error) {
@@ -341,7 +460,23 @@ public class MucOptions {
}
public ArrayList<User> getUsers() {
- return new ArrayList<>(users.values());
+ return getUsers(true);
+ }
+
+ public ArrayList<User> getUsers(boolean includeOffline) {
+ synchronized (users) {
+ if (includeOffline) {
+ return new ArrayList<>(users);
+ } else {
+ ArrayList<User> onlineUsers = new ArrayList<>();
+ for (User user : users) {
+ if (user.getRole().ranks(Role.PARTICIPANT)) {
+ onlineUsers.add(user);
+ }
+ }
+ return onlineUsers;
+ }
+ }
}
public List<User> getUsers(int max) {
@@ -350,7 +485,9 @@ public class MucOptions {
}
public int getUserCount() {
- return this.users.size();
+ synchronized (users) {
+ return users.size();
+ }
}
public String getProposedNick() {
@@ -386,7 +523,9 @@ public class MucOptions {
}
public void setOffline() {
- this.users.clear();
+ synchronized (users) {
+ this.users.clear();
+ }
this.error = Error.NO_RESPONSE;
this.isOnline = false;
}
@@ -404,13 +543,13 @@ public class MucOptions {
}
public String createNameFromParticipants() {
- if (users.size() >= 2) {
+ if (getUserCount() >= 2) {
List<String> names = new ArrayList<>();
for (User user : getUsers(5)) {
Contact contact = user.getContact();
if (contact != null && !contact.getDisplayName().isEmpty()) {
names.add(contact.getDisplayName().split("\\s+")[0]);
- } else {
+ } else if (user.getName() != null){
names.add(user.getName());
}
}
@@ -429,7 +568,7 @@ public class MucOptions {
public long[] getPgpKeyIds() {
List<Long> ids = new ArrayList<>();
- for (User user : this.users.values()) {
+ for (User user : this.users) {
if (user.getPgpKeyId() != 0) {
ids.add(user.getPgpKeyId());
}
@@ -443,18 +582,22 @@ public class MucOptions {
}
public boolean pgpKeysInUse() {
- for (User user : this.users.values()) {
- if (user.getPgpKeyId() != 0) {
- return true;
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() != 0) {
+ return true;
+ }
}
}
return false;
}
public boolean everybodyHasKeys() {
- for (User user : this.users.values()) {
- if (user.getPgpKeyId() == 0) {
- return false;
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() == 0) {
+ return false;
+ }
}
}
return true;
@@ -468,12 +611,12 @@ public class MucOptions {
}
}
- public Jid getTrueCounterpart(String name) {
- if (name.equals(getSelf().getName())) {
+ public Jid getTrueCounterpart(Jid jid) {
+ if (jid.equals(getSelf().getFullJid())) {
return account.getJid().toBareJid();
}
- User user = findUser(name);
- return user == null ? null : user.getJid();
+ User user = findUserByFullJid(jid);
+ return user == null ? null : user.realJid;
}
public String getPassword() {
@@ -499,11 +642,15 @@ public class MucOptions {
return this.conversation;
}
- public void putMember(Jid jid) {
- members.add(jid);
- }
-
public List<Jid> getMembers() {
- return new ArrayList<>(members);
+ ArrayList<Jid> members = new ArrayList<>();
+ synchronized (users) {
+ for (User user : users) {
+ if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
+ members.add(user.realJid);
+ }
+ }
+ }
+ return members;
}
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java
index 442f1bca..e9f6d0d1 100644
--- a/src/main/java/eu/siacs/conversations/entities/Presence.java
+++ b/src/main/java/eu/siacs/conversations/entities/Presence.java
@@ -17,41 +17,46 @@ public class Presence implements Comparable {
case XA: return "xa";
case DND: return "dnd";
}
-
return null;
}
+
+ public static Status fromShowString(String show) {
+ if (show == null) {
+ return ONLINE;
+ } else {
+ switch (show.toLowerCase(Locale.US)) {
+ case "away":
+ return AWAY;
+ case "xa":
+ return XA;
+ case "dnd":
+ return DND;
+ case "chat":
+ return CHAT;
+ default:
+ return ONLINE;
+ }
+ }
+ }
}
- protected final Status status;
- protected ServiceDiscoveryResult disco;
- protected final String ver;
- protected final String hash;
+ private final Status status;
+ private ServiceDiscoveryResult disco;
+ private final String ver;
+ private final String hash;
+ private final String message;
- private Presence(Status status, String ver, String hash) {
+ private Presence(Status status, String ver, String hash, String message) {
this.status = status;
this.ver = ver;
this.hash = hash;
+ this.message = message;
}
- public static Presence parse(String show, Element caps) {
+ public static Presence parse(String show, Element caps, String message) {
final String hash = caps == null ? null : caps.getAttribute("hash");
final String ver = caps == null ? null : caps.getAttribute("ver");
- if (show == null) {
- return new Presence(Status.ONLINE, ver, hash);
- } else {
- switch (show.toLowerCase(Locale.US)) {
- case "away":
- return new Presence(Status.AWAY, ver, hash);
- case "xa":
- return new Presence(Status.XA, ver, hash);
- case "dnd":
- return new Presence(Status.DND, ver, hash);
- case "chat":
- return new Presence(Status.CHAT, ver, hash);
- default:
- return new Presence(Status.ONLINE, ver, hash);
- }
- }
+ return new Presence(Status.fromShowString(show), ver, hash, message);
}
public int compareTo(Object other) {
@@ -74,7 +79,15 @@ public class Presence implements Comparable {
return this.hash;
}
+ public String getMessage() {
+ return this.message;
+ }
+
public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
this.disco = disco;
}
+
+ public ServiceDiscoveryResult getServiceDiscoveryResult() {
+ return disco;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
new file mode 100644
index 00000000..c268b24c
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
@@ -0,0 +1,76 @@
+package eu.siacs.conversations.entities;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+
+public class PresenceTemplate extends AbstractEntity {
+
+ public static final String TABELNAME = "presence_templates";
+ public static final String LAST_USED = "last_used";
+ public static final String MESSAGE = "message";
+ public static final String STATUS = "status";
+
+ private long lastUsed = 0;
+ private String statusMessage;
+ private Presence.Status status = Presence.Status.ONLINE;
+
+ public PresenceTemplate(Presence.Status status, String statusMessage) {
+ this.status = status;
+ this.statusMessage = statusMessage;
+ this.lastUsed = System.currentTimeMillis();
+ this.uuid = java.util.UUID.randomUUID().toString();
+ }
+
+ private PresenceTemplate() {
+
+ }
+
+ @Override
+ public ContentValues getContentValues() {
+ final String show = status.toShowString();
+ ContentValues values = new ContentValues();
+ values.put(LAST_USED, lastUsed);
+ values.put(MESSAGE, statusMessage);
+ values.put(STATUS, show == null ? "" : show);
+ values.put(UUID, uuid);
+ return values;
+ }
+
+ public static PresenceTemplate fromCursor(Cursor cursor) {
+ PresenceTemplate template = new PresenceTemplate();
+ template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
+ template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
+ template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
+ template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
+ return template;
+ }
+
+ public Presence.Status getStatus() {
+ return status;
+ }
+
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PresenceTemplate template = (PresenceTemplate) o;
+
+ if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null)
+ return false;
+ return status == template.status;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = statusMessage != null ? statusMessage.hashCode() : 0;
+ result = 31 * result + status.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java
index 813eda7a..c8078331 100644
--- a/src/main/java/eu/siacs/conversations/entities/Presences.java
+++ b/src/main/java/eu/siacs/conversations/entities/Presences.java
@@ -1,8 +1,11 @@
package eu.siacs.conversations.entities;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import eu.siacs.conversations.xml.Element;
@@ -52,9 +55,44 @@ public class Presences {
}
}
+ public List<PresenceTemplate> asTemplates() {
+ synchronized (this.presences) {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
+ for(Presence p : presences.values()) {
+ templates.add(new PresenceTemplate(p.getStatus(),p.getMessage()));
+ }
+ return templates;
+ }
+ }
+
public boolean has(String presence) {
synchronized (this.presences) {
return presences.containsKey(presence);
}
}
+
+ public List<String> getStatusMessages() {
+ ArrayList<String> messages = new ArrayList<>();
+ synchronized (this.presences) {
+ for(Presence presence : this.presences.values()) {
+ String message = presence.getMessage() == null ? null : presence.getMessage().trim();
+ if (message != null && !message.isEmpty() && !messages.contains(message)) {
+ messages.add(message);
+ }
+ }
+ }
+ return messages;
+ }
+
+ public boolean allOrNonSupport(String namespace) {
+ synchronized (this.presences) {
+ for(Presence presence : this.presences.values()) {
+ ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
+ if (disco == null || !disco.getFeatures().contains(namespace)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
index a8f60e39..40499ede 100644
--- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
+++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
@@ -53,6 +53,7 @@ public class ServiceDiscoveryResult {
}
public Identity(final JSONObject o) {
+
this(
o.optString("category", null),
o.optString("type", null),
@@ -157,6 +158,54 @@ public class ServiceDiscoveryResult {
this.features.add(features.getString(i));
}
}
+ JSONArray forms = o.optJSONArray("forms");
+ if (forms != null) {
+ for(int i = 0; i < forms.length(); i++) {
+ this.forms.add(createFormFromJSONObject(forms.getJSONObject(i)));
+ }
+ }
+ }
+
+ private static Data createFormFromJSONObject(JSONObject o) {
+ Data data = new Data();
+ JSONArray names = o.names();
+ for(int i = 0; i < names.length(); ++i) {
+ try {
+ String name = names.getString(i);
+ JSONArray jsonValues = o.getJSONArray(name);
+ ArrayList<String> values = new ArrayList<>(jsonValues.length());
+ for(int j = 0; j < jsonValues.length(); ++j) {
+ values.add(jsonValues.getString(j));
+ }
+ data.put(name, values);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return data;
+ }
+
+ private static JSONObject createJSONFromForm(Data data) {
+ JSONObject object = new JSONObject();
+ for(Field field : data.getFields()) {
+ try {
+ JSONArray jsonValues = new JSONArray();
+ for(String value : field.getValues()) {
+ jsonValues.put(value);
+ }
+ object.put(field.getFieldName(), jsonValues);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ JSONArray jsonValues = new JSONArray();
+ jsonValues.put(data.getFormType());
+ object.put(Data.FORM_TYPE, jsonValues);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ return object;
}
public String getVer() {
@@ -273,10 +322,16 @@ public class ServiceDiscoveryResult {
for(Identity id : this.getIdentities()) {
ids.put(id.toJSON());
}
- o.put("identites", ids);
+ o.put("identities", ids);
o.put("features", new JSONArray(this.getFeatures()));
+ JSONArray forms = new JSONArray();
+ for(Data data : this.forms) {
+ forms.put(createJSONFromForm(data));
+ }
+ o.put("forms", forms);
+
return o;
} catch(JSONException e) {
return null;
diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
index 649f767d..0c750765 100644
--- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -31,7 +31,7 @@ public abstract class AbstractGenerator {
"urn:xmpp:ping",
"jabber:iq:version",
"http://jabber.org/protocol/chatstates",
- AxolotlService.PEP_DEVICE_LIST+"+notify"};
+ AxolotlService.PEP_DEVICE_LIST_NOTIFY};
private final String[] MESSAGE_CONFIRMATION_FEATURES = {
"urn:xmpp:chat-markers:0",
"urn:xmpp:receipts"
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index eff9d9c0..4e3025f0 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -261,8 +261,10 @@ public class IqGenerator extends AbstractGenerator {
register.setTo(account.getServer());
register.setId(id);
- register.query("jabber:iq:register").addChild(data);
-
+ Element query = register.query("jabber:iq:register");
+ if (data != null) {
+ query.addChild(data);
+ }
return register;
}
diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
index 9ac7d318..d9ad691c 100644
--- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
@@ -39,7 +39,7 @@ public class PresenceGenerator extends AbstractGenerator {
}
packet.setFrom(account.getJid());
String sig = account.getPgpSignature();
- if (sig != null) {
+ if (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/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index ad368f11..bdd13932 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -7,8 +7,11 @@ import java.util.Locale;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
@@ -71,16 +74,12 @@ public abstract class AbstractParser {
return dateFormat.parse(timestamp);
}
- protected void updateLastseen(final AbstractStanza packet, final Account account, final boolean presenceOverwrite) {
- updateLastseen(getTimestamp(packet), account, packet.getFrom(), presenceOverwrite);
- }
-
- protected void updateLastseen(long timestamp, final Account account, final Jid from, final boolean presenceOverwrite) {
+ protected void updateLastseen(long timestamp, final Account account, final Jid from) {
final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
final Contact contact = account.getRoster().getContact(from);
if (timestamp >= contact.lastseen.time) {
contact.lastseen.time = timestamp;
- if (!presence.isEmpty() && presenceOverwrite) {
+ if (!presence.isEmpty()) {
contact.lastseen.presence = presence;
}
}
@@ -93,4 +92,24 @@ public abstract class AbstractParser {
}
return item.findChildContent("data", "urn:xmpp:avatar:data");
}
+
+ public static MucOptions.User parseItem(Conversation conference, Element item) {
+ final String local = conference.getJid().getLocalpart();
+ final String domain = conference.getJid().getDomainpart();
+ String affiliation = item.getAttribute("affiliation");
+ String role = item.getAttribute("role");
+ String nick = item.getAttribute("nick");
+ Jid fullJid;
+ try {
+ fullJid = nick != null ? Jid.fromParts(local, domain, nick) : null;
+ } catch (InvalidJidException e) {
+ fullJid = null;
+ }
+ Jid realJid = item.getAttributeAsJid("jid");
+ MucOptions.User user = new MucOptions.User(conference.getMucOptions(), nick == null ? null : fullJid);
+ user.setRealJid(realJid);
+ user.setAffiliation(affiliation);
+ user.setRole(role);
+ return user;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index c03ed42f..365c9e5e 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -56,6 +56,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
final String name = item.getAttribute("name");
final String subscription = item.getAttribute("subscription");
final Contact contact = account.getRoster().getContact(jid);
+ boolean bothPre = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
contact.setServerName(name);
contact.parseGroupsFromElement(item);
@@ -71,6 +72,14 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
contact.parseSubscriptionFromElement(item);
}
}
+ boolean both = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+ if ((both != bothPre) && both) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": gained mutual presence subscription with "+contact.getJid());
+ AxolotlService axolotlService = account.getAxolotlService();
+ if (axolotlService != null) {
+ axolotlService.clearErrorsInFetchStatusMap(contact.getJid());
+ }
+ }
AvatarService.getInstance().clear(contact);
}
}
@@ -270,6 +279,11 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (Config.BACKGROUND_STANZA_LOGGING && (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET)) {
+ Element first = packet.getChildren().size() > 0 ? packet.getChildren().get(0) : null;
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": IQ request from "+packet.getFrom()+(first == null ? "" : " "+first));
+ }
+
if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) {
return;
} else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) {
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index c79110c9..57a737ef 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.parser;
+import android.text.Html;
import android.util.Log;
import android.util.Pair;
@@ -12,6 +13,9 @@ import net.java.otr4j.session.SessionStatus;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
import java.util.Set;
import de.thedevstack.android.logcat.Logging;
@@ -29,6 +33,8 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
+import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.MessageArchiveService;
@@ -41,8 +47,10 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
-public class MessageParser extends AbstractParser implements
- OnMessagePacketReceived {
+public class MessageParser extends AbstractParser implements OnMessagePacketReceived {
+
+ private static final List<String> CLIENTS_SENDING_HTML_IN_OTR = Arrays.asList(new String[]{"Pidgin","Adium"});
+
public MessageParser(XmppConnectionService service) {
super(service);
}
@@ -106,6 +114,11 @@ public class MessageParser extends AbstractParser implements
conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
return null;
}
+ if (clientMightSendHtml(conversation.getAccount(), from)) {
+ Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": received OTR message from bad behaving client. escaping HTML…");
+ body = Html.fromHtml(body).toString();
+ }
+
final OtrService otrService = conversation.getAccount().getOtrService();
Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED);
finishedMessage.setFingerprint(otrService.getFingerprint(otrSession.getRemotePublicKey()));
@@ -118,6 +131,30 @@ public class MessageParser extends AbstractParser implements
}
}
+ private static boolean clientMightSendHtml(Account account, Jid from) {
+ String resource = from.getResourcepart();
+ if (resource == null) {
+ return false;
+ }
+ Presence presence = account.getRoster().getContact(from).getPresences().getPresences().get(resource);
+ ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
+ if (disco == null) {
+ return false;
+ }
+ return hasIdentityKnowForSendingHtml(disco.getIdentities());
+ }
+
+ private static boolean hasIdentityKnowForSendingHtml(List<ServiceDiscoveryResult.Identity> identities) {
+ for(ServiceDiscoveryResult.Identity identity : identities) {
+ if (identity.getName() != null) {
+ if (CLIENTS_SENDING_HTML_IN_OTR.contains(identity.getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
Message finishedMessage = null;
AxolotlService service = conversation.getAccount().getAxolotlService();
@@ -320,7 +357,7 @@ public class MessageParser extends AbstractParser implements
}
boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
- boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() <= 1);
+ boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() == 0);
boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
if (packet.fromAccount(account)) {
status = Message.STATUS_SEND;
@@ -374,7 +411,7 @@ public class MessageParser extends AbstractParser implements
} else if (axolotlEncrypted != null && Config.supportOmemo()) {
Jid origin;
if (conversation.getMode() == Conversation.MODE_MULTI) {
- origin = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart());
+ origin = conversation.getMucOptions().getTrueCounterpart(counterpart);
if (origin == null) {
Log.d(Config.LOGTAG,"axolotl message in non anonymous conference received");
return;
@@ -406,16 +443,13 @@ public class MessageParser extends AbstractParser implements
message.setOob(isOob);
message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
if (conversation.getMode() == Conversation.MODE_MULTI) {
- Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart());
+ Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(counterpart);
message.setTrueCounterpart(trueCounterpart);
- if (trueCounterpart != null) {
- updateLastseen(timestamp, account, trueCounterpart, false);
- }
if (!isTypeGroupChat) {
message.setType(Message.TYPE_PRIVATE);
}
} else {
- updateLastseen(timestamp, account, packet.getFrom(), true);
+ updateLastseen(timestamp, account, from);
}
boolean checkForDuplicates = query != null
@@ -486,8 +520,11 @@ public class MessageParser extends AbstractParser implements
}
}
} else if (!packet.hasChild("body")){ //no body
+ if (Config.BACKGROUND_STANZA_LOGGING && !mXmppConnectionService.checkListeners()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + original);
+ }
+ Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
if (isTypeGroupChat) {
- Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
if (packet.hasChild("subject")) {
if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0);
@@ -503,18 +540,36 @@ public class MessageParser extends AbstractParser implements
return;
}
}
-
- if (conversation != null && isMucStatusMessage) {
+ }
+ 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();
+ }
+ }
+ }
}
}
}
+
+
Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
if (received == null) {
received = packet.findChild("received", "urn:xmpp:receipts");
@@ -531,7 +586,7 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.markRead(conversation);
}
} else {
- updateLastseen(timestamp, account, packet.getFrom(), true);
+ updateLastseen(timestamp, account, from);
final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED);
Message message = displayedMessage == null ? null : displayedMessage.prev();
while (message != null
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index 76da5a31..a06e0d49 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -73,11 +73,7 @@ public class PresenceParser extends AbstractParser implements
MucOptions.User user = new MucOptions.User(mucOptions, from);
user.setAffiliation(item.getAttribute("affiliation"));
user.setRole(item.getAttribute("role"));
- Jid real = item.getAttributeAsJid("jid");
- if (real != null) {
- user.setJid(real);
- mucOptions.putMember(real.toBareJid());
- }
+ user.setRealJid(item.getAttributeAsJid("jid"));
if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
mucOptions.setOnline();
mucOptions.setSelf(user);
@@ -153,7 +149,7 @@ public class PresenceParser extends AbstractParser implements
} else if (error != null && error.hasChild("forbidden")) {
mucOptions.setError(MucOptions.Error.BANNED);
} else if (error != null && error.hasChild("registration-required")) {
- mucOptions.setError(MucOptions.Error.BANNED);
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
}
}
}
@@ -177,7 +173,7 @@ public class PresenceParser extends AbstractParser implements
public void parseContactPresence(final PresencePacket packet, final Account account) {
final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
final Jid from = packet.getFrom();
- if (from == null) {
+ if (from == null || from.equals(account.getJid())) {
return;
}
final String type = packet.getAttribute("type");
@@ -202,7 +198,8 @@ public class PresenceParser extends AbstractParser implements
final String show = packet.findChildContent("show");
final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
- final Presence presence = Presence.parse(show, caps);
+ final String message = packet.findChildContent("status");
+ final Presence presence = Presence.parse(show, caps, message);
contact.updatePresence(resource, presence);
if (presence.hasCaps() && Config.REQUEST_DISCO) {
mXmppConnectionService.fetchCaps(account, from, presence);
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index bd20e694..ad1db5a5 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -44,6 +44,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.PresenceTemplate;
import eu.siacs.conversations.entities.Roster;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -54,7 +55,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 25;
+ private static final int DATABASE_VERSION = 27;
private static final int C_TO_CPLUS_VERSION_OFFSET = 1000;
private static final int CPLUS_DATABASE_VERSION = 1;
private static final int CPLUS_DATABASE_VERSION_MULTIPLIER = 100;
@@ -80,6 +81,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
+ ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
+ private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
+ + PresenceTemplate.TABELNAME + "("
+ + PresenceTemplate.UUID + " TEXT, "
+ + PresenceTemplate.LAST_USED + " NUMBER,"
+ + PresenceTemplate.MESSAGE + " TEXT,"
+ + PresenceTemplate.STATUS + " TEXT,"
+ + "UNIQUE("+PresenceTemplate.MESSAGE + "," +PresenceTemplate.STATUS+") ON CONFLICT REPLACE);";
+
private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
+ SQLiteAxolotlStore.PREKEY_TABLENAME + "("
+ SQLiteAxolotlStore.ACCOUNT + " TEXT, "
@@ -151,13 +160,19 @@ public class DatabaseBackend extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("PRAGMA foreign_keys=ON;");
- db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID
- + " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
- + Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
+ db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID+ " TEXT PRIMARY KEY,"
+ + Account.USERNAME + " TEXT,"
+ + Account.SERVER + " TEXT,"
+ + Account.PASSWORD + " TEXT,"
+ Account.DISPLAY_NAME + " TEXT, "
- + Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
- + " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS
- + " TEXT, " + Account.HOSTNAME + " TEXT, " + Account.PORT + " NUMBER DEFAULT 5222)");
+ + Account.STATUS + " TEXT,"
+ + Account.STATUS_MESSAGE + " TEXT,"
+ + Account.ROSTERVERSION + " TEXT,"
+ + Account.OPTIONS + " NUMBER, "
+ + Account.AVATAR + " TEXT, "
+ + Account.KEYS + " TEXT, "
+ + Account.HOSTNAME + " TEXT, "
+ + Account.PORT + " NUMBER DEFAULT 5222)");
db.execSQL("create table " + Conversation.TABLENAME + " ("
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
@@ -191,6 +206,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_PREKEYS_STATEMENT);
db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
db.execSQL(CREATE_IDENTITIES_STATEMENT);
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
// Create Conversations+ related tables
db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0);
@@ -359,6 +375,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
recreateAxolotlDb(db);
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ Message.FINGERPRINT + " TEXT");
+ } else if (oldVersion < 22 && newVersion >= 22) {
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
}
if (oldVersion < 16 && newVersion >= 16) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
@@ -371,6 +389,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
}
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
+ }
/* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization.
*/
@@ -405,10 +427,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
}
- if (oldVersion < 22 && oldVersion >= 15 && newVersion >= 22) {
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
- }
-
if (oldVersion < 23 && newVersion >= 23) {
db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
}
@@ -420,6 +438,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 25 && newVersion >= 25) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
}
+
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ }
+
+ if (oldVersion < 27 && newVersion >= 27) {
+ db.execSQL("DELETE FROM "+ServiceDiscoveryResult.TABLENAME);
+ }
}
public static synchronized DatabaseBackend getInstance(Context context) {
@@ -476,6 +502,30 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return result;
}
+ public void insertPresenceTemplate(PresenceTemplate template) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates() {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.query(PresenceTemplate.TABELNAME,null,null,null,null,null,PresenceTemplate.LAST_USED+" desc");
+ while (cursor.moveToNext()) {
+ templates.add(PresenceTemplate.fromCursor(cursor));
+ }
+ cursor.close();
+ return templates;
+ }
+
+ public void deletePresenceTemplate(PresenceTemplate template) {
+ Log.d(Config.LOGTAG,"deleting presence template with uuid "+template.getUuid());
+ SQLiteDatabase db = this.getWritableDatabase();
+ String where = PresenceTemplate.UUID+"=?";
+ String[] whereArgs = {template.getUuid()};
+ db.delete(PresenceTemplate.TABELNAME,where,whereArgs);
+ }
+
public CopyOnWriteArrayList<Conversation> getConversations(int status) {
CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index 16d9539a..492f786b 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -262,7 +262,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
return get(c, size, cachedOnly);
} else if (message.getConversation().getMode() == Conversation.MODE_MULTI){
- MucOptions.User user = conversation.getMucOptions().findUser(message.getCounterpart().getResourcepart());
+ MucOptions.User user = conversation.getMucOptions().findUserByFullJid(message.getCounterpart());
if (user != null) {
return getImpl(user,size,cachedOnly);
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 5f7a9a9e..72a5f8dd 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -430,6 +430,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
this.wakeLock.acquire();
+ boolean pingNow = false;
+ HashSet<Account> pingCandidates = new HashSet<>();
+
for (Account account : accounts) {
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
if (!hasInternetConnection()) {
@@ -458,12 +461,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
int secs = (int) (pingTimeoutIn / 1000);
this.scheduleWakeUpCall(secs, account.getUuid().hashCode());
}
- } else if (msToNextPing <= 0) {
- account.getXmppConnection().sendPing();
- Logging.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping");
- this.scheduleWakeUpCall(Config.PING_TIMEOUT, account.getUuid().hashCode());
} else {
- this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
+ pingCandidates.add(account);
+ if (msToNextPing <= 0) {
+ pingNow = true;
+ } else {
+ this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
+ }
}
} else if (account.getStatus() == Account.State.OFFLINE) {
reconnectAccount(account, true, interactive);
@@ -474,6 +478,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
long timeout = Config.CONNECT_TIMEOUT - secondsSinceLastConnect;
if (timeout < 0) {
Logging.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting");
+ account.getXmppConnection().resetAttemptCount();
reconnectAccount(account, true, interactive);
} else if (discoTimeout < 0) {
account.getXmppConnection().sendDiscoTimeout();
@@ -493,6 +498,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
}
+ if (pingNow) {
+ for (Account account : pingCandidates) {
+ account.getXmppConnection().sendPing();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping");
+ this.scheduleWakeUpCall(Config.PING_TIMEOUT, account.getUuid().hashCode());
+ }
+ }
if (wakeLock.isHeld()) {
try {
wakeLock.release();
@@ -1388,6 +1400,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
account.setPassword(newPassword);
+ account.setOption(Account.OPTION_MAGIC_CREATE, false);
databaseBackend.updateAccount(account);
callback.onPasswordChangeSucceeded();
} else {
@@ -1769,7 +1782,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
for(Element child : query.getChildren()) {
if ("item".equals(child.getName())) {
- conversation.getMucOptions().putMember(child.getAttributeAsJid("jid"));
+ MucOptions.User user = AbstractParser.parseItem(conversation,child);
+ if (!user.realJidMatchesAccount()) {
+ conversation.getMucOptions().addUser(user);
+ getAvatarService().clear(conversation);
+ updateMucRosterUi();
+ updateConversationUi();
+ }
}
}
} else {
@@ -2039,13 +2058,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- public void changeAffiliationInConference(final Conversation conference, Jid user, MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
+ public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
final Jid jid = user.toBareJid();
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
+ conference.getMucOptions().changeAffiliation(jid, affiliation);
+ getAvatarService().clear(conference);
callback.onAffiliationChangedSuccessful(jid);
} else {
callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
@@ -2057,8 +2078,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) {
List<Jid> jids = new ArrayList<>();
for (MucOptions.User user : conference.getMucOptions().getUsers()) {
- if (user.getAffiliation() == before && user.getJid() != null) {
- jids.add(user.getJid());
+ if (user.getAffiliation() == before && user.getRealJid() != null) {
+ jids.add(user.getRealJid());
}
}
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString());
@@ -2240,6 +2261,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (connection == null) {
connection = createConnection(account);
account.setXmppConnection(connection);
+ } else {
+ connection.interrupt();
}
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
if (!force) {
@@ -2343,17 +2366,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) {
- boolean rc = false;
if (mOnCaptchaRequested != null) {
DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity),
(int) (captcha.getHeight() * metrics.scaledDensity), false);
mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled);
- rc = true;
+ return true;
}
-
- return rc;
+ return false;
}
public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
@@ -2496,6 +2517,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
}
+ if(Config.DOMAIN_LOCK != null && !hosts.contains(Config.DOMAIN_LOCK)) {
+ hosts.add(Config.DOMAIN_LOCK);
+ }
+ if(Config.MAGIC_CREATE_DOMAIN != null && !hosts.contains(Config.MAGIC_CREATE_DOMAIN)) {
+ hosts.add(Config.MAGIC_CREATE_DOMAIN);
+ }
return hosts;
}
@@ -2535,7 +2562,17 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void sendPresence(final Account account) {
- XmppSendUtil.sendPresencePacket(account, mPresenceGenerator.selfPresence(account, getTargetPresence()));
+ PresencePacket packet;
+ if (manuallyChangePresence()) {
+ packet = mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
+ String message = account.getPresenceStatusMessage();
+ if (message != null && !message.isEmpty()) {
+ packet.addChild(new Element("status").setContent(message));
+ }
+ } else {
+ packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
+ }
+ XmppSendUtil.sendPresencePacket(account, packet);
}
public void refreshAllPresences() {
@@ -2674,7 +2711,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- private ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String,String> key) {
+ public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String, String> key) {
ServiceDiscoveryResult result = discoCache.get(key);
if (result != null) {
return result;
@@ -2748,6 +2785,53 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return mPushManagementService;
}
+ public Account getPendingAccount() {
+ Account pending = null;
+ for(Account account : getAccounts()) {
+ if (account.isOptionSet(Account.OPTION_REGISTER)) {
+ pending = account;
+ } else {
+ return null;
+ }
+ }
+ return pending;
+ }
+
+ public void changeStatus(Account account, Presence.Status status, String statusMessage, boolean send) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ changeStatusReal(account, status, statusMessage, send);
+ }
+
+ private void changeStatusReal(Account account, Presence.Status status, String statusMessage, boolean send) {
+ account.setPresenceStatus(status);
+ account.setPresenceStatusMessage(statusMessage);
+ databaseBackend.updateAccount(account);
+ if (!account.isOptionSet(Account.OPTION_DISABLED) && send) {
+ sendPresence(account);
+ }
+ }
+
+ public void changeStatus(Presence.Status status, String statusMessage) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ for(Account account : getAccounts()) {
+ changeStatusReal(account, status, statusMessage, true);
+ }
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates(Account account) {
+ List<PresenceTemplate> templates = databaseBackend.getPresenceTemplates();
+ for(PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) {
+ if (!templates.contains(template)) {
+ templates.add(0, template);
+ }
+ }
+ return templates;
+ }
+
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed();
diff --git a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
index 5a85c17b..abec8ed7 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
@@ -49,7 +49,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
if (account != null) {
for (final Jid jid : account.getBlocklist()) {
final Contact contact = account.getRoster().getContact(jid);
- if (contact.match(needle) && contact.isBlocked()) {
+ if (contact.match(this, needle) && contact.isBlocked()) {
getListItems().add(contact);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
index 9f4a4bc3..76842c02 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java
@@ -1,9 +1,11 @@
package eu.siacs.conversations.ui;
+import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.TextView;
import android.widget.Toast;
import de.thedevstack.conversationsplus.ConversationsPlusColors;
@@ -23,7 +25,7 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
final String currentPassword = mCurrentPassword.getText().toString();
final String newPassword = mNewPassword.getText().toString();
final String newPasswordConfirm = mNewPasswordConfirm.getText().toString();
- if (!currentPassword.equals(mAccount.getPassword())) {
+ if (!mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(mAccount.getPassword())) {
mCurrentPassword.requestFocus();
mCurrentPassword.setError(getString(R.string.account_status_unauthorized));
} else if (!newPassword.equals(newPasswordConfirm)) {
@@ -45,6 +47,7 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
}
}
};
+ private TextView mCurrentPasswordLabel;
private EditText mCurrentPassword;
private EditText mNewPassword;
private EditText mNewPasswordConfirm;
@@ -53,7 +56,13 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
@Override
void onBackendConnected() {
this.mAccount = extractAccount(getIntent());
-
+ if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ this.mCurrentPasswordLabel.setVisibility(View.GONE);
+ this.mCurrentPassword.setVisibility(View.GONE);
+ } else {
+ this.mCurrentPasswordLabel.setVisibility(View.VISIBLE);
+ this.mCurrentPassword.setVisibility(View.VISIBLE);
+ }
}
@Override
@@ -69,12 +78,24 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
});
this.mChangePasswordButton = (Button) findViewById(R.id.right_button);
this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
+ this.mCurrentPasswordLabel = (TextView) findViewById(R.id.current_password_label);
this.mCurrentPassword = (EditText) findViewById(R.id.current_password);
this.mNewPassword = (EditText) findViewById(R.id.new_password);
this.mNewPasswordConfirm = (EditText) findViewById(R.id.new_password_confirm);
}
@Override
+ protected void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ String password = intent != null ? intent.getStringExtra("password") : null;
+ if (password != null) {
+ this.mNewPassword.getEditableText().clear();
+ this.mNewPassword.getEditableText().append(password);
+ }
+ }
+
+ @Override
public void onPasswordChangeSucceeded() {
runOnUiThread(new Runnable() {
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
index c5357a5e..1698343b 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
@@ -144,7 +144,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInRoster() &&
!filterContacts.contains(contact.getJid().toBareJid().toString())
- && contact.match(needle)) {
+ && contact.match(this, needle)) {
getListItems().add(contact);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index eac2c1b8..86003e22 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -1,6 +1,5 @@
package eu.siacs.conversations.ui;
-import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
@@ -8,7 +7,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
-import android.os.Build;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -368,13 +366,13 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
final Contact contact = user.getContact();
if (contact != null) {
name = contact.getDisplayName();
- } else if (user.getJid() != null){
- name = user.getJid().toBareJid().toString();
+ } else if (user.getRealJid() != null){
+ name = user.getRealJid().toBareJid().toString();
} else {
name = user.getName();
}
menu.setHeaderTitle(name);
- if (user.getJid() != null) {
+ if (user.getRealJid() != null) {
MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
MenuItem startConversation = menu.findItem(R.id.start_conversation);
MenuItem giveMembership = menu.findItem(R.id.give_membership);
@@ -427,23 +425,25 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
startConversation(mSelectedUser);
return true;
case R.id.give_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.ADMIN,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.ADMIN,this);
return true;
case R.id.give_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.MEMBER,this);
return true;
case R.id.remove_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.NONE,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.NONE,this);
return true;
case R.id.remove_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.MEMBER,this);
return true;
case R.id.remove_from_room:
removeFromRoom(mSelectedUser);
return true;
case R.id.ban_from_conference:
- xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.OUTCAST,this);
- xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getRealJid(), MucOptions.Affiliation.OUTCAST,this);
+ if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
+ }
return true;
case R.id.send_private_message:
privateMsgInMuc(mConversation,mSelectedUser.getName());
@@ -455,8 +455,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
private void removeFromRoom(final User user) {
if (mConversation.getMucOptions().membersOnly()) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.NONE,this);
- xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.NONE,this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.ban_from_conference);
@@ -465,8 +467,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
- xmppConnectionService.changeRoleInConference(mConversation,mSelectedUser.getName(), MucOptions.Role.NONE,ConferenceDetailsActivity.this);
+ xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
}
});
builder.create().show();
@@ -474,8 +478,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
protected void startConversation(User user) {
- if (user.getJid() != null) {
- Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getJid().toBareJid(),false);
+ if (user.getRealJid() != null) {
+ Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getRealJid().toBareJid(),false);
switchToConversation(conversation);
}
}
@@ -531,11 +535,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
mAccountJid.setText(getString(R.string.using_account, account));
mYourPhoto.setImageBitmap(AvatarService.getInstance().get(mConversation.getAccount(), getPixel(48)));
setTitle(mConversation.getName());
- if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && mConversation.getJid().getDomainpart().equals(Config.CONFERENCE_DOMAIN_LOCK)) {
- mFullJid.setText(mConversation.getJid().getLocalpart());
- } else {
- mFullJid.setText(mConversation.getJid().toBareJid().toString());
- }
+ mFullJid.setText(mConversation.getJid().toBareJid().toString());
mYourNick.setText(mucOptions.getActualNick());
mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
if (mucOptions.online()) {
@@ -582,12 +582,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
membersView.removeAllViews();
final ArrayList<User> users = mucOptions.getUsers();
- Collections.sort(users,new Comparator<User>() {
- @Override
- public int compare(User lhs, User rhs) {
- return lhs.getName().compareToIgnoreCase(rhs.getName());
- }
- });
+ Collections.sort(users);
for (final User user : users) {
View view = inflater.inflate(R.layout.contact, membersView,false);
this.setListItemBackgroundOnView(view);
@@ -614,11 +609,12 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
}
Contact contact = user.getContact();
+ String name = user.getName();
if (contact != null) {
tvDisplayName.setText(contact.getDisplayName());
- tvStatus.setText(user.getName() + " \u2022 " + getStatus(user));
+ tvStatus.setText((name != null ? name+ " \u2022 " : "") + getStatus(user));
} else {
- tvDisplayName.setText(user.getName());
+ tvDisplayName.setText(name == null ? "" : name);
tvStatus.setText(getStatus(user));
}
@@ -646,17 +642,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
}
- @SuppressWarnings("deprecation")
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private void setListItemBackgroundOnView(View view) {
- int sdk = android.os.Build.VERSION.SDK_INT;
- if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
- view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
- } else {
- view.setBackground(getResources().getDrawable(R.drawable.greybackground));
- }
- }
-
private void viewPgpKey(User user) {
PgpEngine pgp = xmppConnectionService.getPgpEngine();
if (pgp != null) {
@@ -675,7 +660,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override
public void onAffiliationChangedSuccessful(Jid jid) {
-
+ refreshUi();
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index b11564a9..ff504964 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -42,6 +42,7 @@ import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
@@ -107,6 +108,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
private Jid contactJid;
private TextView contactJidTv;
private TextView accountJidTv;
+ private TextView statusMessage;
private TextView lastseen;
private CheckBox send;
private CheckBox receive;
@@ -203,6 +205,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
contactJidTv = (TextView) findViewById(R.id.details_contactjid);
accountJidTv = (TextView) findViewById(R.id.details_account);
+ statusMessage = (TextView) findViewById(R.id.status_message);
lastseen = (TextView) findViewById(R.id.details_lastseen);
send = (CheckBox) findViewById(R.id.details_send_presence);
receive = (CheckBox) findViewById(R.id.details_receive_presence);
@@ -310,6 +313,25 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
send.setOnCheckedChangeListener(null);
receive.setOnCheckedChangeListener(null);
+ List<String> statusMessages = contact.getPresences().getStatusMessages();
+ if (statusMessages.size() == 0) {
+ statusMessage.setVisibility(View.GONE);
+ } else {
+ StringBuilder builder = new StringBuilder();
+ statusMessage.setVisibility(View.VISIBLE);
+ int s = statusMessages.size();
+ for(int i = 0; i < s; ++i) {
+ if (s > 1) {
+ builder.append("• ");
+ }
+ builder.append(statusMessages.get(i));
+ if (i < s - 1) {
+ builder.append("\n");
+ }
+ }
+ statusMessage.setText(builder);
+ }
+
if (contact.getOption(Contact.Options.FROM)) {
send.setText(R.string.send_presence_updates);
send.setChecked(true);
@@ -440,7 +462,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
keys.setVisibility(View.GONE);
}
- List<ListItem.Tag> tagList = contact.getTags();
+ List<ListItem.Tag> tagList = contact.getTags(this);
if (tagList.size() == 0 || !this.showDynamicTags) {
tags.setVisibility(View.GONE);
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index f7e53112..23687607 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -5,6 +5,7 @@ import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
@@ -835,7 +836,7 @@ public class ConversationActivity extends XmppActivity
conversation.setNextEncryption(Message.ENCRYPTION_PGP);
item.setChecked(true);
} else {
- announcePgp(conversation.getAccount(), conversation);
+ announcePgp(conversation.getAccount(), conversation, onOpenPGPKeyPublished);
}
} else {
showInstallPgpDialog();
@@ -963,6 +964,9 @@ public class ConversationActivity extends XmppActivity
if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
toggleConversationsOverview();
return true;
+ } else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
+ startActivity(new Intent(this, StartConversationActivity.class));
+ return true;
} else if (modifier && key == downKey) {
if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
showConversationsOverview();
@@ -1136,6 +1140,8 @@ public class ConversationActivity extends XmppActivity
if (mRedirected.compareAndSet(false, true)) {
if (Config.X509_VERIFICATION) {
startActivity(new Intent(this, ManageAccountActivity.class));
+ } else if (Config.MAGIC_CREATE_DOMAIN != null) {
+ startActivity(new Intent(this, WelcomeActivity.class));
} else {
startActivity(new Intent(this, EditAccountActivity.class));
}
@@ -1143,9 +1149,14 @@ public class ConversationActivity extends XmppActivity
}
} else if (conversationList.size() <= 0) {
if (mRedirected.compareAndSet(false, true)) {
- Intent intent = new Intent(this, StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(intent);
+ Account pendingAccount = xmppConnectionService.getPendingAccount();
+ if (pendingAccount == null) {
+ Intent intent = new Intent(this, StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(intent);
+ } else {
+ switchToAccount(pendingAccount, true);
+ }
finish();
}
} else if (getIntent() != null && VIEW_CONVERSATION.equals(getIntent().getType())) {
@@ -1286,7 +1297,7 @@ public class ConversationActivity extends XmppActivity
// associate selected PGP keyId with the account
mSelectedConversation.getAccount().setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
// we need to announce the key as described in XEP-027
- announcePgp(mSelectedConversation.getAccount(), null);
+ announcePgp(mSelectedConversation.getAccount(), null, onOpenPGPKeyPublished);
} else {
choosePgpSignId(mSelectedConversation.getAccount());
}
@@ -1296,7 +1307,7 @@ public class ConversationActivity extends XmppActivity
}
} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
if (xmppConnectionServiceBound) {
- announcePgp(mSelectedConversation.getAccount(), mSelectedConversation);
+ announcePgp(mSelectedConversation.getAccount(), mSelectedConversation, onOpenPGPKeyPublished);
this.mPostponedActivityResult = null;
} else {
this.mPostponedActivityResult = new Pair<>(requestCode, data);
@@ -1392,7 +1403,11 @@ public class ConversationActivity extends XmppActivity
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
Uri uri = Uri.parse("package:" + getPackageName());
intent.setData(uri);
- startActivityForResult(intent, REQUEST_BATTERY_OP);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(ConversationActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 6d170787..fd77e528 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -11,6 +11,7 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
+import android.os.Handler;
import android.support.annotation.Nullable;
import android.text.InputType;
import android.view.ContextMenu;
@@ -464,21 +465,27 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
public void onContactPictureClicked(Message message) {
if (message.getStatus() <= Message.STATUS_RECEIVED) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- if (message.getCounterpart() != null) {
- String user = message.getCounterpart().isBareJid() ? message.getCounterpart().toString() : message.getCounterpart().getResourcepart();
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
if (!message.getConversation().getMucOptions().isUserInRoom(user)) {
- Toast.makeText(activity,activity.getString(R.string.user_has_left_conference,user),Toast.LENGTH_SHORT).show();
+ Toast.makeText(activity,activity.getString(R.string.user_has_left_conference,user.getResourcepart()),Toast.LENGTH_SHORT).show();
}
- highlightInConference(user);
+ highlightInConference(user.getResourcepart());
}
} else {
activity.switchToContactDetails(message.getContact(), message.getFingerprint());
}
} else {
Account account = message.getConversation().getAccount();
- Intent intent = new Intent(activity, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- intent.putExtra("fingerprint", message.getFingerprint());
+ Intent intent;
+ if (activity.manuallyChangePresence()) {
+ intent = new Intent(activity, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
+ } else {
+ intent = new Intent(activity, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("fingerprint", message.getFingerprint());
+ }
startActivity(intent);
}
}
@@ -490,14 +497,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
public void onContactPictureLongClicked(Message message) {
if (message.getStatus() <= Message.STATUS_RECEIVED) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- if (message.getCounterpart() != null) {
- String user = message.getCounterpart().getResourcepart();
- if (user != null) {
- if (message.getConversation().getMucOptions().isUserInRoom(user)) {
- privateMessageWith(message.getCounterpart());
- } else {
- Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user), Toast.LENGTH_SHORT).show();
- }
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
+ if (message.getConversation().getMucOptions().isUserInRoom(user)) {
+ privateMessageWith(user);
+ } else {
+ Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
}
}
}
@@ -935,10 +940,15 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private void messageSent() {
- int size = this.messageList.size();
- messagesView.setSelection(size - 1);
mEditMessage.setText("");
updateChatMsgHint();
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ int size = messageList.size();
+ messagesView.setSelection(size - 1);
+ }
+ });
}
public void setFocusOnInputField() {
@@ -1147,7 +1157,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
return;
}
if (conversation.getAccount().getPgpSignature() == null) {
- activity.announcePgp(conversation.getAccount(), conversation);
+ activity.announcePgp(conversation.getAccount(), conversation, activity.onOpenPGPKeyPublished);
return;
}
if (conversation.getMode() == Conversation.MODE_SINGLE) {
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index d63516bc..42dc01cd 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -3,6 +3,7 @@ package eu.siacs.conversations.ui;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -33,6 +34,8 @@ import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
+import android.util.Log;
+
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -105,6 +108,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private Jid jidToEdit;
private boolean mInitMode = false;
+ private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
private boolean mShowOptions = false;
private Account mAccount;
private String messageFingerprint;
@@ -115,6 +119,13 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override
public void onClick(final View v) {
+ final String password = mPassword.getText().toString();
+ final String passwordConfirm = mPasswordConfirm.getText().toString();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ gotoChangePassword(password);
+ return;
+ }
if (mInitMode && mAccount != null) {
mAccount.setOption(Account.OPTION_DISABLED, false);
}
@@ -125,20 +136,20 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
return;
}
final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
- if (Config.DOMAIN_LOCK != null && mAccountJid.getText().toString().contains("@")) {
+ if (mUsernameMode && mAccountJid.getText().toString().contains("@")) {
mAccountJid.setError(getString(R.string.invalid_username));
mAccountJid.requestFocus();
return;
}
final Jid jid;
try {
- if (Config.DOMAIN_LOCK != null) {
- jid = Jid.fromParts(mAccountJid.getText().toString(), Config.DOMAIN_LOCK, null);
+ if (mUsernameMode) {
+ jid = Jid.fromParts(mAccountJid.getText().toString(), getUserModeDomain(), null);
} else {
jid = Jid.fromString(mAccountJid.getText().toString());
}
} catch (final InvalidJidException e) {
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
mAccountJid.setError(getString(R.string.invalid_username));
} else {
mAccountJid.setError(getString(R.string.invalid_jid));
@@ -172,7 +183,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
if (jid.isDomainJid()) {
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
mAccountJid.setError(getString(R.string.invalid_username));
} else {
mAccountJid.setError(getString(R.string.invalid_jid));
@@ -180,8 +191,6 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
mAccountJid.requestFocus();
return;
}
- final String password = mPassword.getText().toString();
- final String passwordConfirm = mPasswordConfirm.getText().toString();
if (registerNewAccount) {
if (!password.equals(passwordConfirm)) {
mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
@@ -190,6 +199,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
}
if (mAccount != null) {
+ if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password));
+ }
mAccount.setJid(jid);
mAccount.setPort(numericPort);
mAccount.setHostname(hostname);
@@ -312,9 +324,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
public void run() {
final Intent intent;
final XmppConnection connection = mAccount.getXmppConnection();
+ final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1;
if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
intent = new Intent(getApplicationContext(), StartConversationActivity.class);
- if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
+ if (wasFirstAccount) {
intent.putExtra("init", true);
}
} else {
@@ -322,6 +335,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
intent.putExtra("setup", true);
}
+ if (wasFirstAccount) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
startActivity(intent);
finish();
}
@@ -337,9 +353,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
protected void updateSaveButton() {
- if (accountInfoEdited() && !mInitMode) {
+ boolean accountInfoEdited = accountInfoEdited();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.change_password);
+ } else if (accountInfoEdited && !mInitMode) {
TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.save);
- } else if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) {
+ } else if (mAccount != null
+ && (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL|| mFetchingAvatar)) {
TextViewUtil.disable(mSaveButton, ConversationsPlusColors.secondaryText(), R.string.account_status_connecting);
} else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.enable);
@@ -348,7 +369,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (!mInitMode) {
if (mAccount != null && mAccount.isOnlineAndConnected()) {
this.mSaveButton.setText(R.string.save);
- if (!accountInfoEdited()) {
+ if (!accountInfoEdited) {
TextViewUtil.disable(mSaveButton, ConversationsPlusColors.secondaryText());
}
} else {
@@ -364,16 +385,28 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (this.mAccount == null) {
return false;
}
+ return jidEdited() ||
+ !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
+ !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
+ !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
+ }
+
+ protected boolean jidEdited() {
final String unmodified;
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
unmodified = this.mAccount.getJid().getLocalpart();
} else {
unmodified = this.mAccount.getJid().toBareJid().toString();
}
- return !unmodified.equals(this.mAccountJid.getText().toString()) ||
- !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
- !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
- !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
+ return !unmodified.equals(this.mAccountJid.getText().toString());
+ }
+
+ protected boolean passwordChangedInMagicCreateMode() {
+ return mAccount != null
+ && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !this.mAccount.getPassword().equals(this.mPassword.getText().toString())
+ && !this.jidEdited()
+ && mAccount.isOnlineAndConnected();
}
@Override
@@ -392,10 +425,6 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
this.mAccountJid.addTextChangedListener(this.mTextWatcher);
this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
- if (Config.DOMAIN_LOCK != null) {
- this.mAccountJidLabel.setText(R.string.username);
- this.mAccountJid.setHint(R.string.username_hint);
- }
this.mPassword = (EditText) findViewById(R.id.account_password);
this.mPassword.addTextChangedListener(this.mTextWatcher);
this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
@@ -411,7 +440,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
Uri uri = Uri.parse("package:"+getPackageName());
intent.setData(uri);
- startActivityForResult(intent,REQUEST_BATTERY_OP);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
}
});
this.mSessionEst = (TextView) findViewById(R.id.session_est);
@@ -471,10 +504,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
+ final MenuItem showPassword = menu.findItem(R.id.action_show_password);
final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
-
+ final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
if (mAccount != null && mAccount.isOnlineAndConnected()) {
@@ -489,6 +523,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (otherDevices == null || otherDevices.isEmpty()) {
clearDevices.setVisible(false);
}
+ changePresence.setVisible(manuallyChangePresence());
} else {
showQrCode.setVisible(false);
showBlocklist.setVisible(false);
@@ -496,6 +531,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
changePassword.setVisible(false);
clearDevices.setVisible(false);
mamPrefs.setVisible(false);
+ changePresence.setVisible(false);
+ }
+
+ if (mAccount != null) {
+ showPassword.setVisible(mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !mAccount.isOptionSet(Account.OPTION_REGISTER));
+ } else {
+ showPassword.setVisible(false);
}
return super.onCreateOptionsMenu(menu);
}
@@ -531,8 +574,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
protected void onBackendConnected() {
if (this.jidToEdit != null) {
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
- this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount != null) {
+ this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
+ this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount.getPrivateKeyAlias() != null) {
this.mPassword.setHint(R.string.authenticate_with_certificate);
if (this.mInitMode) {
@@ -541,7 +585,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
updateAccountInformation(true);
}
- } else if (this.xmppConnectionService.getAccounts().size() == 0) {
+ }
+ if ((Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0)
+ || (this.mAccount != null && this.mAccount == xmppConnectionService.getPendingAccount())) {
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(false);
getActionBar().setDisplayShowHomeEnabled(false);
@@ -549,9 +595,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
TextViewUtil.disable(mCancelButton, ConversationsPlusColors.secondaryText());
}
- if (Config.DOMAIN_LOCK == null) {
+ if (mUsernameMode) {
+ this.mAccountJidLabel.setText(R.string.username);
+ this.mAccountJid.setHint(R.string.username_hint);
+ } else {
final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
- android.R.layout.simple_list_item_1,
+ R.layout.simple_list_item,
xmppConnectionService.getKnownHosts());
this.mAccountJid.setAdapter(mKnownHostsAdapter);
}
@@ -559,6 +608,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
invalidateOptionsMenu();
}
+ private String getUserModeDomain() {
+ if (mAccount != null) {
+ return mAccount.getJid().getDomainpart();
+ } else {
+ return Config.DOMAIN_LOCK;
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
@@ -572,9 +629,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
item.setChecked(!item.isChecked());
break;
case R.id.action_change_password_on_server:
- final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
- changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
- startActivity(changePasswordIntent);
+ gotoChangePassword(null);
break;
case R.id.action_mam_prefs:
editMamPrefs();
@@ -585,14 +640,35 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
case R.id.action_renew_certificate:
renewCertificate();
break;
+ case R.id.action_change_presence:
+ changePresence();
+ break;
+ case R.id.action_show_password:
+ showPassword();
+ break;
}
return super.onOptionsItemSelected(item);
}
+ private void gotoChangePassword(String newPassword) {
+ final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
+ changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
+ if (newPassword != null) {
+ changePasswordIntent.putExtra("password", newPassword);
+ }
+ startActivity(changePasswordIntent);
+ }
+
private void renewCertificate() {
KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
}
+ private void changePresence() {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,mAccount.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
@Override
public void alias(String alias) {
if (alias != null) {
@@ -603,7 +679,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private void updateAccountInformation(boolean init) {
if (init) {
this.mAccountJid.getEditableText().clear();
- if (Config.DOMAIN_LOCK != null) {
+ if (mUsernameMode) {
this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
} else {
this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
@@ -616,12 +692,6 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
}
- mPassword.setEnabled(!Config.LOCK_SETTINGS);
- mAccountJid.setEnabled(!Config.LOCK_SETTINGS);
- mHostname.setEnabled(!Config.LOCK_SETTINGS);
- mPort.setEnabled(!Config.LOCK_SETTINGS);
- mPasswordConfirm.setEnabled(!Config.LOCK_SETTINGS);
- mRegisterNew.setEnabled(!Config.LOCK_SETTINGS);
if (!mInitMode) {
this.mAvatar.setVisibility(View.VISIBLE);
@@ -843,71 +913,72 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
xmppConnectionService.fetchMamPreferences(mAccount, this);
}
+ private void showPassword() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ View view = getLayoutInflater().inflate(R.layout.dialog_show_password, null);
+ TextView password = (TextView) view.findViewById(R.id.password);
+ password.setText(mAccount.getPassword());
+ builder.setTitle(R.string.password);
+ builder.setView(view);
+ builder.setPositiveButton(R.string.cancel, null);
+ builder.create().show();
+ }
+
@Override
public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
refreshUi();
}
@Override
- public void onCaptchaRequested(final Account account, final String id, final Data data,
- final Bitmap captcha) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- final ImageView view = new ImageView(this);
- final LinearLayout layout = new LinearLayout(this);
- final EditText input = new EditText(this);
-
- view.setImageBitmap(captcha);
- view.setScaleType(ImageView.ScaleType.FIT_CENTER);
-
- input.setHint(getString(R.string.captcha_hint));
-
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.addView(view);
- layout.addView(input);
+ public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
+ mCaptchaDialog.dismiss();
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(EditAccountActivity.this);
+ final View view = getLayoutInflater().inflate(R.layout.captcha, null);
+ final ImageView imageView = (ImageView) view.findViewById(R.id.captcha);
+ final EditText input = (EditText) view.findViewById(R.id.input);
+ imageView.setImageBitmap(captcha);
- builder.setTitle(getString(R.string.captcha_required));
- builder.setView(layout);
+ builder.setTitle(getString(R.string.captcha_required));
+ builder.setView(view);
- builder.setPositiveButton(getString(R.string.ok),
- new DialogInterface.OnClickListener() {
+ builder.setPositiveButton(getString(R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String rc = input.getText().toString();
+ data.put("username", account.getUsername());
+ data.put("password", account.getPassword());
+ data.put("ocr", rc);
+ data.submit();
+
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(
+ account, id, data);
+ }
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- String rc = input.getText().toString();
- data.put("username", account.getUsername());
- data.put("password", account.getPassword());
- data.put("ocr", rc);
- data.submit();
-
- if (xmppConnectionServiceBound) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(
- account, id, data);
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
}
}
});
- builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
-
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
- mCaptchaDialog.dismiss();
- }
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
+ }
+ }
+ });
mCaptchaDialog = builder.create();
mCaptchaDialog.show();
}
diff --git a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
index a6b3c73c..f77d9c0a 100644
--- a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
@@ -43,17 +43,14 @@ public class EnterJidDialog {
final String title, final String positiveButton,
final String prefilledJid, final String account, boolean allowEditJid
) {
- final boolean lock = Config.LOCK_DOMAINS_IN_CONVERSATIONS && Config.DOMAIN_LOCK != null;
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
View dialogView = LayoutInflater.from(context).inflate(R.layout.enter_jid_dialog, null);
final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
- jabberIdDesc.setText(lock ? R.string.username : R.string.account_settings_jabber_id);
+ jabberIdDesc.setText(R.string.account_settings_jabber_id);
final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
- if (!lock) {
- jid.setAdapter(new KnownHostsAdapter(context, android.R.layout.simple_list_item_1, knownHosts));
- }
+ jid.setAdapter(new KnownHostsAdapter(context, R.layout.simple_list_item, knownHosts));
if (prefilledJid != null) {
jid.append(prefilledJid);
if (!allowEditJid) {
@@ -64,16 +61,16 @@ public class EnterJidDialog {
}
}
- jid.setHint(Config.LOCK_DOMAINS_IN_CONVERSATIONS && Config.DOMAIN_LOCK != null ? R.string.username_hint : R.string.account_settings_example_jabber_id);
+ jid.setHint(R.string.account_settings_example_jabber_id);
if (account == null) {
StartConversationActivity.populateAccountSpinner(context, activatedAccounts, spinner);
} else {
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item,
+ R.layout.simple_list_item,
new String[] { account });
spinner.setEnabled(false);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
spinner.setAdapter(adapter);
}
@@ -100,13 +97,9 @@ public class EnterJidDialog {
}
final Jid contactJid;
try {
- if (lock) {
- contactJid = Jid.fromParts(jid.getText().toString(), Config.DOMAIN_LOCK, null);
- } else {
- contactJid = Jid.fromString(jid.getText().toString());
- }
+ contactJid = Jid.fromString(jid.getText().toString());
} catch (final InvalidJidException e) {
- jid.setError(context.getString(lock ? R.string.invalid_username : R.string.invalid_jid));
+ jid.setError(context.getString(R.string.invalid_jid));
return;
}
diff --git a/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java b/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java
new file mode 100644
index 00000000..1b16e565
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java
@@ -0,0 +1,116 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.security.SecureRandom;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class MagicCreateActivity extends XmppActivity implements TextWatcher {
+
+ private TextView mFullJidDisplay;
+ private EditText mUsername;
+ private SecureRandom mRandom;
+
+ private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?";
+ private static final int PW_LENGTH = 10;
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.magic_create);
+ mFullJidDisplay = (TextView) findViewById(R.id.full_jid);
+ mUsername = (EditText) findViewById(R.id.username);
+ mRandom = new SecureRandom();
+ Button next = (Button) findViewById(R.id.create_account);
+ next.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String username = mUsername.getText().toString();
+ if (username.contains("@") || username.length() < 3) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ } else {
+ mUsername.setError(null);
+ try {
+ Jid jid = Jid.fromParts(username.toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account == null) {
+ account = new Account(jid, createPassword());
+ account.setOption(Account.OPTION_REGISTER, true);
+ account.setOption(Account.OPTION_DISABLED, true);
+ account.setOption(Account.OPTION_MAGIC_CREATE, true);
+ xmppConnectionService.createAccount(account);
+ }
+ Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("init", true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
+ startActivity(intent);
+ } catch (InvalidJidException e) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ }
+ }
+ }
+ });
+ mUsername.addTextChangedListener(this);
+ }
+
+ private String createPassword() {
+ StringBuilder builder = new StringBuilder(PW_LENGTH);
+ for(int i = 0; i < PW_LENGTH; ++i) {
+ builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1)));
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.toString().trim().length() > 0) {
+ try {
+ mFullJidDisplay.setVisibility(View.VISIBLE);
+ Jid jid = Jid.fromParts(s.toString().toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ mFullJidDisplay.setText(getString(R.string.your_full_jid_will_be, jid.toString()));
+ } catch (InvalidJidException e) {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+
+ } else {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
index c83a0275..203ffa48 100644
--- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -121,9 +121,11 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
menu.findItem(R.id.mgmt_account_disable).setVisible(false);
menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(false);
} else {
menu.findItem(R.id.mgmt_account_enable).setVisible(false);
menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(manuallyChangePresence());
}
menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString());
}
@@ -154,10 +156,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
if (Config.X509_VERIFICATION) {
addAccount.setVisible(false);
addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- } else {
- addAccount.setVisible(!Config.LOCK_SETTINGS);
}
- addAccountWithCertificate.setVisible(!Config.LOCK_SETTINGS);
if (!accountsLeftToEnable()) {
enableAll.setVisible(false);
@@ -187,6 +186,9 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
case R.id.mgmt_account_announce_pgp:
publishOpenPGPPublicKey(selectedAccount);
return true;
+ case R.id.mgmt_account_change_presence:
+ changePresence(selectedAccount);
+ return true;
default:
return super.onContextItemSelected(item);
}
@@ -235,6 +237,12 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
}
}
+ private void changePresence(Account account) {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,account.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
public void onClickTglAccountState(Account account, boolean enable) {
if (enable) {
enableAccount(account);
@@ -320,7 +328,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
private void publishOpenPGPPublicKey(Account account) {
if (ManageAccountActivity.this.hasPgp()) {
- choosePgpSignId(selectedAccount);
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
} else {
this.showInstallPgpDialog();
}
@@ -352,12 +360,12 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
if (requestCode == REQUEST_CHOOSE_PGP_ID) {
if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
- announcePgp(selectedAccount, null);
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
} else {
choosePgpSignId(selectedAccount);
}
} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(selectedAccount, null);
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
}
this.mPostponedActivityResult = null;
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java b/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java
new file mode 100644
index 00000000..0962d209
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java
@@ -0,0 +1,239 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import android.util.Log;
+
+import org.openintents.openpgp.util.OpenPgpApi;
+
+import java.util.List;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.PresenceTemplate;
+import eu.siacs.conversations.utils.UIHelper;
+
+public class SetPresenceActivity extends XmppActivity implements View.OnClickListener {
+
+ //data
+ protected Account mAccount;
+ private List<PresenceTemplate> mTemplates;
+
+ //UI Elements
+ protected ScrollView mScrollView;
+ protected EditText mStatusMessage;
+ protected Spinner mShowSpinner;
+ protected CheckBox mAllAccounts;
+ protected LinearLayout mTemplatesView;
+ private Pair<Integer, Intent> mPostponedActivityResult;
+
+ private Runnable onPresenceChanged = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ };
+
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_set_presence);
+ mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+ mShowSpinner = (Spinner) findViewById(R.id.presence_show);
+ ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
+ R.array.presence_show_options,
+ R.layout.simple_list_item);
+ mShowSpinner.setAdapter(adapter);
+ mShowSpinner.setSelection(1);
+ mStatusMessage = (EditText) findViewById(R.id.presence_status_message);
+ mAllAccounts = (CheckBox) findViewById(R.id.all_accounts);
+ mTemplatesView = (LinearLayout) findViewById(R.id.templates);
+ final Button changePresence = (Button) findViewById(R.id.change_presence);
+ changePresence.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ executeChangePresence();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.change_presence, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.action_account_details) {
+ if (mAccount != null) {
+ switchToAccount(mAccount);
+ }
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (xmppConnectionServiceBound && mAccount != null) {
+ if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ }
+ }
+
+ private void executeChangePresence() {
+ Presence.Status status = getStatusFromSpinner();
+ boolean allAccounts = mAllAccounts.isChecked();
+ String statusMessage = mStatusMessage.getText().toString().trim();
+ if (allAccounts && noAccountUsesPgp()) {
+ xmppConnectionService.changeStatus(status, statusMessage);
+ finish();
+ } else if (mAccount != null) {
+ if (mAccount.getPgpId() == 0 || !hasPgp()) {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, true);
+ finish();
+ } else {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, false);
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ }
+ }
+
+ private Presence.Status getStatusFromSpinner() {
+ switch (mShowSpinner.getSelectedItemPosition()) {
+ case 0:
+ return Presence.Status.CHAT;
+ case 2:
+ return Presence.Status.AWAY;
+ case 3:
+ return Presence.Status.XA;
+ case 4:
+ return Presence.Status.DND;
+ default:
+ return Presence.Status.ONLINE;
+ }
+ }
+
+ private void setStatusInSpinner(Presence.Status status) {
+ switch(status) {
+ case AWAY:
+ mShowSpinner.setSelection(2);
+ break;
+ case XA:
+ mShowSpinner.setSelection(3);
+ break;
+ case CHAT:
+ mShowSpinner.setSelection(0);
+ break;
+ case DND:
+ mShowSpinner.setSelection(4);
+ break;
+ default:
+ mShowSpinner.setSelection(1);
+ break;
+ }
+ }
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+ mAccount = extractAccount(getIntent());
+ if (mAccount != null) {
+ setStatusInSpinner(mAccount.getPresenceStatus());
+ String message = mAccount.getPresenceStatusMessage();
+ if (mStatusMessage.getText().length() == 0 && message != null) {
+ mStatusMessage.append(message);
+ }
+ mTemplates = xmppConnectionService.getPresenceTemplates(mAccount);
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
+ boolean e = noAccountUsesPgp();
+ mAllAccounts.setEnabled(e);
+ mAllAccounts.setTextColor(e ? getPrimaryTextColor() : getSecondaryTextColor());
+ }
+ redrawTemplates();
+ }
+
+ private void redrawTemplates() {
+ if (mTemplates == null || mTemplates.size() == 0) {
+ mTemplatesView.setVisibility(View.GONE);
+ } else {
+ mTemplatesView.removeAllViews();
+ mTemplatesView.setVisibility(View.VISIBLE);
+ LayoutInflater inflater = getLayoutInflater();
+ for (PresenceTemplate template : mTemplates) {
+ View templateLayout = inflater.inflate(R.layout.presence_template, mTemplatesView, false);
+ templateLayout.setTag(template);
+ setListItemBackgroundOnView(templateLayout);
+ templateLayout.setOnClickListener(this);
+ TextView message = (TextView) templateLayout.findViewById(R.id.presence_status_message);
+ TextView status = (TextView) templateLayout.findViewById(R.id.status);
+ ImageButton button = (ImageButton) templateLayout.findViewById(R.id.delete_button);
+ button.setTag(template);
+ button.setOnClickListener(this);
+ ListItem.Tag tag = UIHelper.getTagForStatus(this, template.getStatus());
+ status.setText(tag.getName());
+ status.setBackgroundColor(tag.getColor());
+ message.setText(template.getStatusMessage());
+ mTemplatesView.addView(templateLayout);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ PresenceTemplate template = (PresenceTemplate) v.getTag();
+ if (template == null) {
+ return;
+ }
+ if (v.getId() == R.id.presence_template) {
+ setStatusInSpinner(template.getStatus());
+ mStatusMessage.getEditableText().clear();
+ mStatusMessage.getEditableText().append(template.getStatusMessage());
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mScrollView.smoothScrollTo(0,0);
+ }
+ });
+ } else if (v.getId() == R.id.delete_button) {
+ xmppConnectionService.databaseBackend.deletePresenceTemplate(template);
+ mTemplates.remove(template);
+ redrawTemplates();
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index 30f71229..a6bd842d 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -130,6 +130,16 @@ public class SettingsActivity extends XmppActivity implements
return true;
}
});
+
+ final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs");
+ exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ hasStoragePermission(REQUEST_WRITE_LOGS);
+ return true;
+ }
+ });
+
// Avoid appearence of setting to enable or disable omemo in screen
Preference omemoEnabledPreference = this.mSettingsFragment.findPreference("omemo_enabled");
PreferenceCategory otherExpertSettingsGroup = (PreferenceCategory) this.mSettingsFragment.findPreference("other_expert_settings");
@@ -151,7 +161,8 @@ public class SettingsActivity extends XmppActivity implements
"confirm_messages_list",
"xa_on_silent_mode",
"away_when_screen_off",
- "treat_vibrate_as_silent");
+ "treat_vibrate_as_silent",
+ "manually_change_presence");
// need to synchronize the settings class first
Settings.synchronizeSettingsClassWithPreferences(preferences, name);
if (name.equals("resource")) {
@@ -174,9 +185,13 @@ public class SettingsActivity extends XmppActivity implements
xmppConnectionService.toggleForegroundService();
} else if (resendPresence.contains(name)) {
if (xmppConnectionServiceBound) {
- if (name.equals("away_when_screen_off")) {
+ if (name.equals("away_when_screen_off")
+ || name.equals("manually_change_presence")) {
xmppConnectionService.toggleScreenEventReceiver();
}
+ if (name.equals("manually_change_presence") && !noAccountUsesPgp()) {
+ Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show();
+ }
xmppConnectionService.refreshAllPresences();
}
} else if (name.equals("dont_trust_system_cas")) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
index 7fcd6b33..96aef56e 100644
--- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
@@ -282,9 +282,8 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
return;
}
if (share.uris.size() != 0) {
- final long max = account.getXmppConnection()
- .getFeatures()
- .getMaxHttpUploadSize();
+ final XmppConnection connection = account.getXmppConnection();
+ final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize();
OnPresenceSelected callback;
if (this.share.image) {
// TODO: attachementCounter should be set and decremented correctly
diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
index 188cd3fe..ed4f7432 100644
--- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -92,7 +92,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private List<String> mKnownHosts;
private List<String> mKnownConferenceHosts;
private Invite mPendingInvite = null;
- private Menu mOptionsMenu;
private EditText mSearchEditText;
private AtomicBoolean mRequestedContactsPermission = new AtomicBoolean(false);
private final int REQUEST_SYNC_CONTACTS = 0x3b28cf;
@@ -117,9 +116,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
+ hideKeyboard();
mSearchEditText.setText("");
filter(null);
return true;
@@ -170,6 +167,28 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
int count) {
}
};
+
+ private TextView.OnEditorActionListener mSearchDone = new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (getActionBar().getSelectedNavigationIndex() == 0) {
+ if (contacts.size() == 1) {
+ openConversationForContact((Contact) contacts.get(0));
+ } else {
+ hideKeyboard();
+ mContactsListFragment.getListView().requestFocus();
+ }
+ } else {
+ if (conferences.size() == 1) {
+ openConversationsForBookmark((Bookmark) conferences.get(0));
+ } else {
+ hideKeyboard();
+ mConferenceListFragment.getListView().requestFocus();
+ }
+ }
+ return true;
+ }
+ };
private MenuItem mMenuSearchView;
private ListItemAdapter.OnTagClickedListener mOnTagClickedListener = new ListItemAdapter.OnTagClickedListener() {
@Override
@@ -261,6 +280,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
protected void openConversationForContact(int position) {
Contact contact = (Contact) contacts.get(position);
+ openConversationForContact(contact);
+ }
+
+ protected void openConversationForContact(Contact contact) {
Conversation conversation = xmppConnectionService
.findOrCreateConversation(contact.getAccount(),
contact.getJid(), false);
@@ -278,6 +301,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
protected void openConversationForBookmark(int position) {
Bookmark bookmark = (Bookmark) conferences.get(position);
+ openConversationsForBookmark(bookmark);
+ }
+
+ protected void openConversationsForBookmark(Bookmark bookmark) {
Jid jid = bookmark.getJid();
if (jid == null) {
Toast.makeText(this,R.string.invalid_jid,Toast.LENGTH_SHORT).show();
@@ -391,13 +418,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
final View dialogView = getLayoutInflater().inflate(R.layout.join_conference_dialog, null);
final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
- final boolean lock = Config.LOCK_DOMAINS_IN_CONVERSATIONS && Config.CONFERENCE_DOMAIN_LOCK != null;
final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
- jabberIdDesc.setText(lock ? R.string.conference_name : R.string.conference_address);
- jid.setHint(lock ? R.string.conference_name : R.string.conference_address_example);
- if (!lock) {
- jid.setAdapter(new KnownHostsAdapter(this, android.R.layout.simple_list_item_1, mKnownConferenceHosts));
- }
+ jabberIdDesc.setText(R.string.conference_address);
+ jid.setHint(R.string.conference_address_example);
+ jid.setAdapter(new KnownHostsAdapter(this, R.layout.simple_list_item, mKnownConferenceHosts));
if (prefilledJid != null) {
jid.append(prefilledJid);
}
@@ -423,13 +447,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
}
final Jid conferenceJid;
try {
- if (lock) {
- conferenceJid = Jid.fromParts(jid.getText().toString(),Config.CONFERENCE_DOMAIN_LOCK, null);
- } else {
- conferenceJid = Jid.fromString(jid.getText().toString());
- }
+ conferenceJid = Jid.fromString(jid.getText().toString());
} catch (final InvalidJidException e) {
- jid.setError(getString(lock ? R.string.invalid_conference_name : R.string.invalid_jid));
+ jid.setError(getString(R.string.invalid_jid));
return;
}
@@ -495,16 +515,15 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
public static void populateAccountSpinner(Context context, List<String> accounts, Spinner spinner) {
if (accounts.size() > 0) {
- ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item, accounts);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(context, R.layout.simple_list_item, accounts);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
spinner.setAdapter(adapter);
spinner.setEnabled(true);
} else {
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item,
+ R.layout.simple_list_item,
Arrays.asList(new String[]{context.getString(R.string.no_accounts)}));
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
spinner.setAdapter(adapter);
spinner.setEnabled(false);
}
@@ -512,7 +531,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- this.mOptionsMenu = menu;
getMenuInflater().inflate(R.menu.start_conversation, menu);
MenuItem menuCreateContact = menu.findItem(R.id.action_create_contact);
MenuItem menuCreateConference = menu.findItem(R.id.action_join_conference);
@@ -524,6 +542,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
mSearchEditText = (EditText) mSearchView
.findViewById(R.id.search_field);
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
+ mSearchEditText.setOnEditorActionListener(mSearchDone);
if (getActionBar().getSelectedNavigationIndex() == 0) {
menuCreateConference.setVisible(false);
} else {
@@ -564,12 +583,26 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH && !event.isLongPress()) {
- mOptionsMenu.findItem(R.id.action_search).expandActionView();
+ openSearch();
return true;
}
+ int c = event.getUnicodeChar();
+ if (c > 32) {
+ if (mSearchEditText != null && !mSearchEditText.isFocused()) {
+ openSearch();
+ mSearchEditText.append(Character.toString((char) c));
+ return true;
+ }
+ }
return super.onKeyUp(keyCode, event);
}
+ private void openSearch() {
+ if (mMenuSearchView != null) {
+ mMenuSearchView.expandActionView();
+ }
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
@@ -740,7 +773,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
for (Contact contact : account.getRoster().getContacts()) {
Presence p = contact.getPresences().getMostAvailablePresence();
Presence.Status s = p == null ? Presence.Status.OFFLINE : p.getStatus();
- if (contact.showInRoster() && contact.match(needle)
+ if (contact.showInRoster() && contact.match(this, needle)
&& (!this.mHideOfflineContacts
|| (needle != null && !needle.isEmpty())
|| s.compareTo(Presence.Status.OFFLINE) < 0)) {
@@ -758,7 +791,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
for (Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
for (Bookmark bookmark : account.getBookmarks()) {
- if (bookmark.match(needle)) {
+ if (bookmark.match(this, needle)) {
this.conferences.add(bookmark);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java b/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java
new file mode 100644
index 00000000..78f0f609
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/WelcomeActivity.java
@@ -0,0 +1,36 @@
+package eu.siacs.conversations.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import eu.siacs.conversations.R;
+
+public class WelcomeActivity extends Activity {
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.welcome);
+ final Button createAccount = (Button) findViewById(R.id.create_account);
+ createAccount.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(WelcomeActivity.this, MagicCreateActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(intent);
+ }
+ });
+ final Button useOwnProvider = (Button) findViewById(R.id.use_own_provider);
+ useOwnProvider.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent(WelcomeActivity.this, EditAccountActivity.class));
+ }
+ });
+
+ }
+
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 26b7909b..28405537 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -108,6 +108,13 @@ public abstract class XmppActivity extends Activity {
private DisplayMetrics metrics;
protected int mTheme;
+ protected Runnable onOpenPGPKeyPublished = new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(XmppActivity.this,R.string.openpgp_has_been_published, Toast.LENGTH_SHORT).show();
+ }
+ };
+
private long mLastUiRefresh = 0;
private Handler mRefreshUiHandler = new Handler();
private Runnable mRefreshUiRunnable = new Runnable() {
@@ -357,19 +364,6 @@ public abstract class XmppActivity extends Activity {
}
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- final MenuItem menuSettings = menu.findItem(R.id.action_settings);
- final MenuItem menuManageAccounts = menu.findItem(R.id.action_accounts);
- if (menuSettings != null) {
- menuSettings.setVisible(!Config.LOCK_SETTINGS);
- }
- if (menuManageAccounts != null) {
- menuManageAccounts.setVisible(!Config.LOCK_SETTINGS);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
protected boolean isOptimizingBattery() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
@@ -451,8 +445,8 @@ public abstract class XmppActivity extends Activity {
ChooseContactActivity.class);
List<String> contacts = new ArrayList<>();
if (conversation.getMode() == Conversation.MODE_MULTI) {
- for (MucOptions.User user : conversation.getMucOptions().getUsers()) {
- Jid jid = user.getJid();
+ for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
+ Jid jid = user.getRealJid();
if (jid != null) {
contacts.add(jid.toBareJid().toString());
}
@@ -468,18 +462,23 @@ public abstract class XmppActivity extends Activity {
startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
}
- protected void announcePgp(Account account, final Conversation conversation) {
- if (account.getPgpId() == -1) {
+ protected void announcePgp(Account account, final Conversation conversation, final Runnable onSuccess) {
+ if (account.getPgpId() == 0) {
choosePgpSignId(account);
} else {
- xmppConnectionService.getPgpEngine().generateSignature(account, "", new UiCallback<Account>() {
+ String status = null;
+ if (manuallyChangePresence()) {
+ status = account.getPresenceStatusMessage();
+ }
+ if (status == null) {
+ status = "";
+ }
+ xmppConnectionService.getPgpEngine().generateSignature(account, status, new UiCallback<Account>() {
@Override
- public void userInputRequried(PendingIntent pi,
- Account account) {
+ public void userInputRequried(PendingIntent pi, Account account) {
try {
- startIntentSenderForResult(pi.getIntentSender(),
- REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
+ startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
} catch (final SendIntentException ignored) {
}
}
@@ -492,6 +491,9 @@ public abstract class XmppActivity extends Activity {
conversation.setNextEncryption(Message.ENCRYPTION_PGP);
xmppConnectionService.databaseBackend.updateConversation(conversation);
}
+ if (onSuccess != null) {
+ runOnUiThread(onSuccess);
+ }
}
@Override
@@ -502,6 +504,29 @@ public abstract class XmppActivity extends Activity {
}
}
+ protected boolean noAccountUsesPgp() {
+ if (!hasPgp()) {
+ return true;
+ }
+ for(Account account : xmppConnectionService.getAccounts()) {
+ if (account.getPgpId() != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ protected void setListItemBackgroundOnView(View view) {
+ int sdk = android.os.Build.VERSION.SDK_INT;
+ if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
+ } else {
+ view.setBackground(getResources().getDrawable(R.drawable.greybackground));
+ }
+ }
+
protected void choosePgpSignId(Account account) {
xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
@Override
@@ -524,7 +549,7 @@ public abstract class XmppActivity extends Activity {
});
}
- public void displayErrorDialog(final int errorCode) {
+ protected void displayErrorDialog(final int errorCode) {
runOnUiThread(new Runnable() {
@Override
@@ -1069,13 +1094,16 @@ public abstract class XmppActivity extends Activity {
private final boolean setSize;
private Message message = null;
- public BitmapWorkerTask(ImageView imageView, boolean setSize) {
+ public BitmapWorkerTask(ImageView imageView) {
imageViewReference = new WeakReference<>(imageView);
this.setSize = setSize;
}
@Override
protected Bitmap doInBackground(Message... params) {
+ if (isCancelled()) {
+ return null;
+ }
message = params[0];
try {
return ImageUtil.getThumbnail(message, (int) (metrics.density * 288), false);
@@ -1086,7 +1114,7 @@ public abstract class XmppActivity extends Activity {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -1109,6 +1137,7 @@ public abstract class XmppActivity extends Activity {
}
if (bm != null) {
+ cancelPotentialWork(message, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
if (setSize) {
@@ -1126,13 +1155,13 @@ public abstract class XmppActivity extends Activity {
try {
task.execute(message);
} catch (final RejectedExecutionException ignored) {
+ ignored.printStackTrace();
}
}
}
}
- public static boolean cancelPotentialWork(Message message,
- ImageView imageView) {
+ public static boolean cancelPotentialWork(Message message, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
index 0567de06..35d8797b 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -96,7 +96,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
|| message.getTransferable().getStatus() != Transferable.STATUS_DELETED)) {
mLastMessage.setVisibility(View.GONE);
imagePreview.setVisibility(View.VISIBLE);
- activity.loadBitmap(message, imagePreview, false);
+ activity.loadBitmap(message, imagePreview);
} else {
Pair<String,Boolean> preview = UIHelper.getMessagePreview(activity,message);
mLastMessage.setVisibility(View.VISIBLE);
@@ -147,7 +147,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
notificationStatus.setImageResource(R.drawable.ic_notifications_none_grey600_24dp);
}
- mTimestamp.setText(UIHelper.readableTimeDifference(activity, message.getTimeSent()));
+ mTimestamp.setText(UIHelper.readableTimeDifference(activity,conversation.getLatestMessage().getTimeSent()));
ImageView profilePicture = (ImageView) view.findViewById(R.id.conversation_image);
if (conversation.getMode() == Conversation.MODE_SINGLE) {
profilePicture.setOnLongClickListener(new ShowResourcesListDialogListener(activity, conversation.getContact()));
@@ -172,7 +172,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -186,6 +186,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
if (cancelPotentialWork(conversation, imageView)) {
final Bitmap bm = AvatarService.getInstance().get(conversation, activity.getPixel(56), true);
if (bm != null) {
+ cancelPotentialWork(conversation, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
index 29d706c7..3f9ecf72 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
@@ -68,7 +68,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
ImageView picture = (ImageView) view.findViewById(R.id.contact_photo);
LinearLayout tagLayout = (LinearLayout) view.findViewById(R.id.tags);
- List<ListItem.Tag> tags = item.getTags();
+ List<ListItem.Tag> tags = item.getTags(activity);
if (tags.size() == 0 || !this.showDynamicTags) {
tagLayout.setVisibility(View.GONE);
} else {
@@ -117,7 +117,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -131,6 +131,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
if (cancelPotentialWork(item, imageView)) {
final Bitmap bm = AvatarService.getInstance().get(item,activity.getPixel(48),true);
if (bm != null) {
+ cancelPotentialWork(item, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
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 9d2917d5..b1b0bfae 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -741,7 +741,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null) {
+ if (bitmap != null && !isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
@@ -755,6 +755,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (cancelPotentialWork(message, imageView)) {
final Bitmap bm = AvatarService.getInstance().get(message, activity.getPixel(48), true);
if (bm != null) {
+ cancelPotentialWork(message, imageView);
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000);
} else {
diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
index 1568eb8c..26a7e195 100644
--- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
@@ -9,6 +9,7 @@ import android.net.RouteInfo;
import android.os.Build;
import java.io.IOException;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -41,8 +42,10 @@ public class DNSHelper {
protected static Client client = new Client();
+ protected static Context context;
+
static {
- client.setTimeout(Config.PING_TIMEOUT * 1000);
+ client.setTimeout(Config.SOCKET_TIMEOUT * 1000);
}
/**
@@ -81,9 +84,9 @@ public class DNSHelper {
LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
if (linkProperties != null) {
if (hasDefaultRoute(linkProperties)) {
- servers.addAll(0, linkProperties.getDnsServers());
+ servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
} else {
- servers.addAll(linkProperties.getDnsServers());
+ servers.addAll(getIPv4First(linkProperties.getDnsServers()));
}
}
}
@@ -93,6 +96,18 @@ public class DNSHelper {
return servers.size() > 0 ? servers : getDnsServersPreLollipop();
}
+ private static List<InetAddress> getIPv4First(List<InetAddress> in) {
+ List<InetAddress> out = new ArrayList<>();
+ for(InetAddress addr : in) {
+ if (addr instanceof Inet4Address) {
+ out.add(0, addr);
+ } else {
+ out.add(addr);
+ }
+ }
+ return out;
+ }
+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean hasDefaultRoute(LinkProperties linkProperties) {
for(RouteInfo route: linkProperties.getRoutes()) {
diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java
index 4e3ec236..efc83bcb 100644
--- a/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java
+++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java
@@ -27,19 +27,7 @@ public class ExceptionHandler implements UncaughtExceptionHandler {
ex.printStackTrace(printWriter);
String stacktrace = result.toString();
printWriter.close();
- try {
- OutputStream os = context.openFileOutput("stacktrace.txt",
- Context.MODE_PRIVATE);
- os.write(stacktrace.getBytes());
- os.flush();
- os.close();
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+ ExceptionHelper.writeToStacktraceFile(context, stacktrace);
this.defaultHandler.uncaughtException(thread, ex);
}
diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
index 9c8db210..9ae297ba 100644
--- a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
@@ -11,8 +11,10 @@ import android.text.format.DateUtils;
import java.io.BufferedReader;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.util.List;
import de.thedevstack.android.logcat.Logging;
@@ -114,4 +116,14 @@ public class ExceptionHelper {
return false;
}
}
+
+ public static void writeToStacktraceFile(Context context, String msg) {
+ try {
+ OutputStream os = context.openFileOutput("stacktrace.txt", Context.MODE_PRIVATE);
+ os.write(msg.getBytes());
+ os.flush();
+ os.close();
+ } catch (IOException ignored) {
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
index 74f91a98..9a93df38 100644
--- a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
@@ -54,13 +55,16 @@ public class GeoHelper {
Intent locationPluginIntent = new Intent("eu.siacs.conversations.location.show");
locationPluginIntent.putExtra("latitude",latitude);
locationPluginIntent.putExtra("longitude",longitude);
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- locationPluginIntent.putExtra("name",conversation.getName());
- locationPluginIntent.putExtra("jid",message.getCounterpart().toString());
- }
- else {
- locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString());
+ if (message.getStatus() != Message.STATUS_RECEIVED) {
+ locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString());
+ locationPluginIntent.putExtra("name",conversation.getAccount().getJid().getLocalpart());
+ } else {
+ Contact contact = message.getContact();
+ if (contact != null) {
+ locationPluginIntent.putExtra("name", contact.getDisplayName());
+ locationPluginIntent.putExtra("jid", contact.getJid().toString());
+ } else {
+ locationPluginIntent.putExtra("name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
}
}
intents.add(locationPluginIntent);
diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
index a97b16a4..e3ee0027 100644
--- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
@@ -281,6 +281,21 @@ public class UIHelper {
return LOCATION_QUESTIONS.contains(body);
}
+ public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
+ switch (status) {
+ case CHAT:
+ return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
+ case AWAY:
+ return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
+ case XA:
+ return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
+ case DND:
+ return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
+ default:
+ return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
+ }
+ }
+
public static String getHumanReadableFileSize(long filesize) {
if (0 > filesize) {
return "?";
diff --git a/src/main/java/eu/siacs/conversations/xml/XmlReader.java b/src/main/java/eu/siacs/conversations/xml/XmlReader.java
index 74e65fcd..f46e7718 100644
--- a/src/main/java/eu/siacs/conversations/xml/XmlReader.java
+++ b/src/main/java/eu/siacs/conversations/xml/XmlReader.java
@@ -96,17 +96,8 @@ public class XmlReader {
} catch (RuntimeException re) {
}
}
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IOException(
- "xml parser mishandled ArrayIndexOufOfBounds", e);
- } catch (StringIndexOutOfBoundsException e) {
- throw new IOException(
- "xml parser mishandled StringIndexOufOfBounds", e);
- } catch (NullPointerException e) {
- throw new IOException("xml parser mishandled NullPointerException",
- e);
- } catch (IndexOutOfBoundsException e) {
- throw new IOException("xml parser mishandled IndexOutOfBound", e);
+ } catch (Exception e) {
+ throw new IOException("xml parser mishandled "+e.getClass().getName(), e);
}
return null;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index a45cce01..b9094a61 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -185,21 +185,21 @@ public class XmppConnection implements Runnable {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.setOption(Account.OPTION_REGISTER,
- false);
+ account.setOption(Account.OPTION_REGISTER, false);
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
} else if (packet.hasChild("error")
- && (packet.findChild("error")
- .hasChild("conflict"))) {
+ && (packet.findChild("error").hasChild("conflict"))) {
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_CONFLICT);
} else {
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_FAILED);
Log.d(Config.LOGTAG, packet.toString());
}
- disconnect(true);
}
};
- }
+ }
public XmppConnection(final Account account, final XmppConnectionService service) {
this.account = account;
@@ -333,7 +333,7 @@ public class XmppConnection implements Runnable {
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
this.changeStatus(Account.State.OFFLINE);
- this.attempt--; //don't count attempt when reconnecting instantly anyway
+ this.attempt = Math.max(0, this.attempt - 1);
} finally {
forceCloseSocket();
if (wakeLock.isHeld()) {
@@ -459,7 +459,7 @@ public class XmppConnection implements Runnable {
if ("true".equals(enabled.getAttribute("resume"))) {
this.streamId = enabled.getAttribute("id");
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": stream managment(" + smVersion
+ + ": stream management(" + smVersion
+ ") enabled (resumable)");
} else {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
@@ -515,12 +515,18 @@ public class XmppConnection implements Runnable {
try {
final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
acknowledgeStanzaUpTo(serverSequence);
- } catch (NumberFormatException e) {
+ } catch (NumberFormatException | NullPointerException e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server send ack without sequence number");
}
} else if (nextTag.isStart("failed")) {
- tagReader.readElement(nextTag);
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": resumption failed");
+ Element failed = tagReader.readElement(nextTag);
+ try {
+ final int serverCount = Integer.parseInt(failed.getAttribute("h"));
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed but server acknowledged stanza #"+serverCount);
+ acknowledgeStanzaUpTo(serverCount);
+ } catch (NumberFormatException | NullPointerException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed");
+ }
resetStreamId();
if (account.getStatus() != Account.State.ONLINE) {
sendBindRequest();
@@ -714,8 +720,8 @@ public class XmppConnection implements Runnable {
}
} else if (!this.streamFeatures.hasChild("register")
&& account.isOptionSet(Account.OPTION_REGISTER)) {
+ forceCloseSocket();
changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
- disconnect(true);
} else if (this.streamFeatures.hasChild("mechanisms")
&& shouldAuthenticate
&& (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
@@ -783,15 +789,6 @@ public class XmppConnection implements Runnable {
return mechanisms;
}
- public void sendCaptchaRegistryRequest(String id, Data data) {
- if (data == null) {
- setAccountCreationFailed("");
- } else {
- IqPacket request = getIqGenerator().generateCreateAccountWithCaptcha(account, id, data);
- sendIqPacket(request, createPacketReceiveHandler());
- }
- }
-
private void sendRegistryRequest() {
final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
register.query("jabber:iq:register");
@@ -861,6 +858,7 @@ public class XmppConnection implements Runnable {
}
public void resetEverything() {
+ resetAttemptCount();
resetStreamId();
clearIqCallbacks();
mStanzaQueue.clear();
@@ -896,19 +894,22 @@ public class XmppConnection implements Runnable {
} catch (final InvalidJidException e) {
// TODO: Handle the case where an external JID is technically invalid?
}
- if (streamFeatures.hasChild("session")) {
- sendStartSession();
- } else {
- sendPostBindInitialization();
- }
+ if (streamFeatures.hasChild("session")) {
+ sendStartSession();
+ } else {
+ sendPostBindInitialization();
+ }
} else {
Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)");
- disconnect(true);
}
} else {
Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure ("+packet.toString());
- disconnect(true);
+ }
+ } else {
+ Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
}
+ forceCloseSocket();
+ changeStatus(Account.State.BIND_FAILURE);
}
});
}
@@ -1002,7 +1003,7 @@ public class XmppConnection implements Runnable {
final String ver = caps == null ? null : caps.getAttribute("ver");
ServiceDiscoveryResult discoveryResult = null;
if (hash != null && ver != null) {
- discoveryResult = mXmppConnectionService.databaseBackend.findDiscoveryResult(hash, ver);
+ discoveryResult = mXmppConnectionService.getCachedServiceDiscoveryResult(new Pair<>(hash, ver));
}
if (discoveryResult == null) {
sendServiceDiscoveryInfo(account.getServer());
@@ -1153,15 +1154,20 @@ public class XmppConnection implements Runnable {
private void processStreamError(final Tag currentTag)
throws XmlPullParserException, IOException {
final Element streamError = tagReader.readElement(currentTag);
- if (streamError != null && streamError.hasChild("conflict")) {
+ if (streamError == null) {
+ return;
+ }
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
+ if (streamError.hasChild("conflict")) {
final String resource = account.getResource().split("\\.")[0];
account.setResource(resource + "." + nextRandomId());
Logging.d(Config.LOGTAG,
account.getJid().toBareJid() + ": switching resource due to conflict ("
+ account.getResource() + ")");
- } else if (streamError != null) {
- Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
+ } else if (streamError.hasChild("host-unknown")) {
+ changeStatus(Account.State.HOST_UNKNOWN);
}
+ forceCloseSocket();
}
private void sendStartStream() throws IOException {
@@ -1285,10 +1291,8 @@ public class XmppConnection implements Runnable {
}
socket.close();
Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream");
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ } catch (IOException | InterruptedException e) {
+ return;
}
}
}).start();
@@ -1308,8 +1312,13 @@ public class XmppConnection implements Runnable {
}
}
+ public void interrupt() {
+ Thread.currentThread().interrupt();
+ }
+
public void disconnect(final boolean force) {
- Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
+ interrupt();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
if (force) {
forceCloseSocket();
return;
@@ -1389,7 +1398,7 @@ public class XmppConnection implements Runnable {
}
public int getTimeToNextAttempt() {
- final int interval = (int) (25 * Math.pow(1.5, attempt));
+ final int interval = Math.min((int) (25 * Math.pow(1.3, attempt)), 300);
final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
return interval - secondsSinceLast;
}
@@ -1547,7 +1556,12 @@ public class XmppConnection implements Runnable {
if (items.size() > 0) {
try {
long maxsize = Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
- return filesize <= maxsize;
+ if(filesize <= maxsize) {
+ return true;
+ } else {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": http upload is not available for files with size "+filesize+" (max is "+maxsize+")");
+ return false;
+ }
} catch (Exception e) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
index 8dabcb5b..c81c5a1f 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
@@ -9,7 +9,7 @@ import eu.siacs.conversations.xml.Element;
public class Data extends Element {
- private static final String FORM_TYPE = "FORM_TYPE";
+ public static final String FORM_TYPE = "FORM_TYPE";
public Data() {
super("x");