aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java2
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java19
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java16
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java23
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java9
-rw-r--r--src/main/java/eu/siacs/conversations/entities/MucOptions.java259
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java58
-rw-r--r--src/main/java/eu/siacs/conversations/parser/AbstractParser.java23
-rw-r--r--src/main/java/eu/siacs/conversations/parser/IqParser.java11
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java28
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java13
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java6
-rw-r--r--src/main/java/eu/siacs/conversations/services/AvatarService.java2
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java21
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java52
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java22
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java22
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java4
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java4
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/forms/Data.java2
-rw-r--r--src/main/res/layout/dialog_show_password.xml19
-rw-r--r--src/main/res/layout/message_received.xml1
-rw-r--r--src/main/res/layout/message_sent.xml1
-rw-r--r--src/main/res/menu/editaccount.xml5
-rw-r--r--src/main/res/values/strings.xml11
25 files changed, 471 insertions, 162 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 1035a6a6c..5b3b2f9df 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -87,7 +87,7 @@ public final class Config {
public static final boolean DISABLE_HTTP_UPLOAD = false;
public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance
public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts
- public static final boolean EXTENDED_IQ_LOGGING = true; // log iq requests
+ public static final boolean BACKGROUND_STANZA_LOGGING = true;
public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption
public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false;
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
index ae1b3a03f..aac814433 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
@@ -216,6 +216,20 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> {
+ public void clearErrorFor(Jid jid) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, FetchStatus> devices = this.map.get(jid.toBareJid().toString());
+ if (devices == null) {
+ return;
+ }
+ for(Map.Entry<Integer, FetchStatus> entry : devices.entrySet()) {
+ if (entry.getValue() == FetchStatus.ERROR) {
+ Log.d(Config.LOGTAG,"resetting error for "+jid.toBareJid()+"("+entry.getKey()+")");
+ entry.setValue(FetchStatus.TIMEOUT);
+ }
+ }
+ }
+ }
}
public static String getLogprefix(Account account) {
@@ -320,6 +334,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
numPublishTriesOnEmptyPep = 0;
}
+ public void clearErrorsInFetchStatusMap(Jid jid) {
+ fetchStatusMap.clearErrorFor(jid);
+ }
+
public void regenerateKeys(boolean wipeOther) {
axolotlStore.regenerate();
sessions.clear();
@@ -643,7 +661,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
jids = Arrays.asList(conversation.getJid().toBareJid());
} else {
jids = conversation.getMucOptions().getMembers();
- jids.remove(account.getJid().toBareJid());
}
return jids;
}
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 979a2845d..4eb73313d 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) {
+ if (ownKey != null) {
+ return ownKey;
+ } else {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
+ ownKey = generateIdentityKeyPair();
+ mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
+ }
return ownKey;
- } else {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
- ownKey = generateIdentityKeyPair();
- mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
}
- return ownKey;
}
private int loadRegistrationId() {
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index a86002862..e29ebb52c 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -655,23 +655,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) {
@@ -700,12 +683,8 @@ public class Conversation extends AbstractEntity implements Blockable {
&& getAccount().getSelfContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY)
&& getContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY)) {
return Message.ENCRYPTION_AXOLOTL;
- }
- int outgoing = this.getMostRecentlyUsedOutgoingEncryption();
- if (outgoing == Message.ENCRYPTION_NONE) {
- next = this.getMostRecentlyUsedIncomingEncryption();
} else {
- next = outgoing;
+ next = this.getMostRecentlyUsedIncomingEncryption();
}
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 225d05315..818ac1d65 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -492,6 +492,9 @@ public class Message extends AbstractEntity {
Message current = this;
while(current.mergeable(current.next())) {
current = current.next();
+ if (current == null) {
+ break;
+ }
body.append(MERGE_SEPARATOR);
body.append(current.getBody().trim());
}
@@ -507,6 +510,9 @@ public class Message extends AbstractEntity {
Message current = this;
while(current.mergeable(current.next())) {
current = current.next();
+ if (current == null) {
+ break;
+ }
status = current.status;
}
return status;
@@ -517,6 +523,9 @@ public class Message extends AbstractEntity {
Message current = this;
while(current.mergeable(current.next())) {
current = current.next();
+ if (current == null) {
+ break;
+ }
time = current.timeSent;
}
return time;
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 1309956eb..d4a15ea7a 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;
}
@@ -410,7 +549,7 @@ public class MucOptions {
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/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
index 9accec95d..9ee1d180e 100644
--- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
+++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java
@@ -146,7 +146,7 @@ public class ServiceDiscoveryResult {
this.hash = hash;
this.ver = ver;
- JSONArray identities = o.optJSONArray("identites");
+ JSONArray identities = o.optJSONArray("identities");
if (identities != null) {
for (int i = 0; i < identities.length(); i++) {
this.identities.add(new Identity(identities.getJSONObject(i)));
@@ -158,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() {
@@ -274,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/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index 7370465c8..c14a7eb0d 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -8,8 +8,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;
@@ -70,4 +73,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 afbc04124..189df4a7b 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -54,6 +54,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);
@@ -69,6 +70,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());
+ }
+ }
mXmppConnectionService.getAvatarService().clear(contact);
}
}
@@ -268,7 +277,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (Config.EXTENDED_IQ_LOGGING && (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET)) {
+ if (Config.BACKGROUND_STANZA_LOGGING && (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET)) {
Element first = packet.getChildren().size() > 0 ? packet.getChildren().get(0) : null;
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": IQ request from "+packet.getFrom()+(first == null ? "" : " "+first));
}
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 3d07c0fad..cd1c55986 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -402,7 +402,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} 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;
@@ -430,7 +430,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
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);
@@ -536,8 +536,11 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
}
}
} else if (!packet.hasChild("body")){ //no body
+ if (Config.BACKGROUND_STANZA_LOGGING && !mXmppConnectionService.checkListeners()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + 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);
@@ -553,18 +556,33 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
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);
+ }
+ }
+ }
}
}
}
+
+
Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
if (received == null) {
received = packet.findChild("received", "urn:xmpp:receipts");
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index 7664dee61..ae5d576a6 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -14,15 +14,12 @@ 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.generator.PresenceGenerator;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
-import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
@@ -67,11 +64,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);
@@ -127,7 +120,7 @@ public class PresenceParser extends AbstractParser implements
Log.d(Config.LOGTAG, "unknown error in conference: " + packet);
}
} else if (!from.isBareJid()){
- MucOptions.User user = mucOptions.deleteUser(from.getResourcepart());
+ MucOptions.User user = mucOptions.deleteUser(from);
if (user != null) {
mXmppConnectionService.getAvatarService().clear(user);
}
@@ -147,7 +140,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);
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index dde385bd9..f1155b07d 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -52,7 +52,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 26;
+ private static final int DATABASE_VERSION = 27;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -402,6 +402,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
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) {
diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index 4cf9ade14..0ab0db2de 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -246,7 +246,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 18c72d0ca..5d1f87fb6 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -83,6 +83,7 @@ import eu.siacs.conversations.generator.IqGenerator;
import eu.siacs.conversations.generator.MessageGenerator;
import eu.siacs.conversations.generator.PresenceGenerator;
import eu.siacs.conversations.http.HttpConnectionManager;
+import eu.siacs.conversations.parser.AbstractParser;
import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.parser.MessageParser;
import eu.siacs.conversations.parser.PresenceParser;
@@ -1762,7 +1763,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- private boolean checkListeners() {
+ public boolean checkListeners() {
return (this.mOnAccountUpdate == null
&& this.mOnConversationUpdate == null
&& this.mOnRosterUpdate == null
@@ -1894,11 +1895,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
+
Element query = packet.query("http://jabber.org/protocol/muc#admin");
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);
+ }
}
}
} else {
@@ -2170,13 +2175,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);
@@ -2188,8 +2195,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());
@@ -2570,7 +2577,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else {
Conversation conversation = find(account, avatar.owner.toBareJid());
if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
- MucOptions.User user = conversation.getMucOptions().findUser(avatar.owner.getResourcepart());
+ MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner);
if (user != null) {
if (user.setAvatar(avatar)) {
getAvatarService().clear(user);
@@ -3169,7 +3176,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;
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index 0ddc8b3d8..ee6d9417c 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;
@@ -366,13 +364,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);
@@ -425,23 +423,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());
@@ -453,8 +453,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);
@@ -463,8 +465,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();
@@ -472,8 +476,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);
}
}
@@ -576,12 +580,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);
@@ -608,11 +607,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));
}
@@ -658,7 +658,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/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 6d3571492..04c3d82df 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -472,12 +472,12 @@ 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 {
if (!message.getContact().isSelf()) {
@@ -506,14 +506,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();
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index 57d3f9550..15701c950 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -507,6 +507,7 @@ 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);
@@ -535,6 +536,13 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
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);
}
@@ -651,6 +659,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
case R.id.action_change_presence:
changePresence();
break;
+ case R.id.action_show_password:
+ showPassword();
+ break;
}
return super.onOptionsItemSelected(item);
}
@@ -909,6 +920,17 @@ 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();
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 5d249d863..f3385b950 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -485,8 +485,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());
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index c4d5bfdf1..b42be17b9 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -1040,7 +1040,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());
@@ -1435,7 +1435,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;
}
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 8dabcb5be..c81c5a1f3 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");
diff --git a/src/main/res/layout/dialog_show_password.xml b/src/main/res/layout/dialog_show_password.xml
new file mode 100644
index 000000000..dba0690cb
--- /dev/null
+++ b/src/main/res/layout/dialog_show_password.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="monospace"
+ android:textSize="?attr/TextSizeHeadline"
+ android:fontFamily="monospace"
+ android:layout_gravity="center_horizontal"
+ android:textColor="@color/black87" >
+ </TextView>
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml
index 14aea096f..60a566187 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -59,6 +59,7 @@
android:autoLink="web"
android:textColorLink="@color/dark"
android:textColor="@color/dark"
+ android:maxLines="256"
android:textColorHighlight="@color/grey800"
android:textSize="?attr/TextSizeBody" />
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 2617c1212..26c24509e 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -60,6 +60,7 @@
android:autoLink="web"
android:textColorLink="@color/black54"
android:textColor="@color/black54"
+ android:maxLines="256"
android:textColorHighlight="@color/grey500"
android:textSize="?attr/TextSizeBody" />
diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml
index b28edf1ed..4ff3752f7 100644
--- a/src/main/res/menu/editaccount.xml
+++ b/src/main/res/menu/editaccount.xml
@@ -37,6 +37,11 @@
android:title="@string/mam_prefs"/>
<item
+ android:id="@+id/action_show_password"
+ android:showAsAction="never"
+ android:title="@string/show_password"/>
+
+ <item
android:id="@+id/action_change_password_on_server"
android:showAsAction="always"
android:icon="@drawable/ic_vpn_key_white_24dp"
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index ce626b760..5185f37aa 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -419,7 +419,7 @@
<string name="password_should_not_be_empty">Password should not be empty</string>
<string name="perform_action_with">Perform action with</string>
<string name="no_affiliation">No affiliation</string>
- <string name="no_role">No role</string>
+ <string name="no_role">Offline</string>
<string name="outcast">Outcast</string>
<string name="member">Member</string>
<string name="advanced_mode">Advanced mode</string>
@@ -520,7 +520,7 @@
<string name="download_started">Download started</string>
<string name="account_status_tor_unavailable">Tor network unavailable</string>
<string name="account_status_bind_failure">Bind failure</string>
- <string name="account_status_host_unknown">Domain not recognized</string>
+ <string name="account_status_host_unknown">Host unknown</string>
<string name="server_info_broken">Broken</string>
<string name="pref_presence_settings">Presence</string>
<string name="pref_away_when_screen_off">Away when screen is off</string>
@@ -624,13 +624,14 @@
<string name="presence_dnd">Busy</string>
<string name="secure_password_generated">A secure password has been generated</string>
<string name="device_does_not_support_battery_op">Your device does not support opting out of battery optimization</string>
- <string name="share">Share</string>
+ <string name="share">Share</string>
<string name="share_location">Share location</string>
- <string name="location_sharing_disabled">Location sharing is disabled in settings</string>
- <string name="locating">Locating…</string>
+ <string name="location_sharing_disabled">Location sharing is disabled in settings</string>
+ <string name="locating">Locating…</string>
<string name="uninstall_plugins">We have integrated the VoiceRecorder and ShareLocation plugin directly into the messenger. You should uninstall the plugins.</string>
<string name="uninstall">Uninstall</string>
<string name="action_end_conversation_muc">Leave conference</string>
<string name="leave_conference_warning">Do you really want to leave this conference? You will no longer be notified of new messages until joining the conference again.</string>
+ <string name="show_password">Show password</string>
</resources>