aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java2
-rw-r--r--src/main/java/de/pixart/messenger/entities/Bookmark.java23
-rw-r--r--src/main/java/de/pixart/messenger/entities/Conversation.java4
-rw-r--r--src/main/java/de/pixart/messenger/entities/MucOptions.java133
-rw-r--r--src/main/java/de/pixart/messenger/entities/ReadByMarker.java6
-rw-r--r--src/main/java/de/pixart/messenger/entities/Roster.java1
-rw-r--r--src/main/java/de/pixart/messenger/generator/IqGenerator.java11
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java18
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpUploadConnection.java2
-rw-r--r--src/main/java/de/pixart/messenger/parser/MessageParser.java24
-rw-r--r--src/main/java/de/pixart/messenger/parser/PresenceParser.java36
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java13
-rw-r--r--src/main/java/de/pixart/messenger/services/AvatarService.java10
-rw-r--r--src/main/java/de/pixart/messenger/services/MessageArchiveService.java83
-rw-r--r--src/main/java/de/pixart/messenger/services/NotificationService.java2
-rw-r--r--src/main/java/de/pixart/messenger/services/UpdateService.java49
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java219
-rw-r--r--src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java223
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java17
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java60
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationsActivity.java17
-rw-r--r--src/main/java/de/pixart/messenger/ui/CreateConferenceDialog.java8
-rw-r--r--src/main/java/de/pixart/messenger/ui/EditAccountActivity.java2
-rw-r--r--src/main/java/de/pixart/messenger/ui/StartConversationActivity.java35
-rw-r--r--src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java12
-rw-r--r--src/main/java/de/pixart/messenger/ui/UpdaterActivity.java178
-rw-r--r--src/main/java/de/pixart/messenger/ui/WelcomeActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/XmppActivity.java29
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java6
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java6
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/EditMessageActionModeCallback.java9
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/EditMessage.java5
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/EmojiWrapperEditText.java17
-rw-r--r--src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java5
-rw-r--r--src/main/java/de/pixart/messenger/utils/CryptoHelper.java14
-rw-r--r--src/main/java/de/pixart/messenger/utils/Namespace.java2
-rw-r--r--src/main/java/de/pixart/messenger/utils/StringUtils.java50
-rw-r--r--src/main/java/de/pixart/messenger/utils/XmppUri.java5
-rw-r--r--src/main/java/de/pixart/messenger/xml/Element.java14
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/XmppConnection.java42
-rw-r--r--src/main/res/drawable-hdpi/ic_forward_white_24dp.pngbin0 -> 193 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_forward_white_24dp.pngbin0 -> 125 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_forward_white_24dp.pngbin0 -> 184 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_forward_white_24dp.pngbin0 -> 303 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_forward_white_24dp.pngbin0 -> 306 bytes
-rw-r--r--src/main/res/drawable/list_item_background_dark.xml32
-rw-r--r--src/main/res/drawable/list_item_background_light.xml32
-rw-r--r--src/main/res/drawable/message_border.xml15
-rw-r--r--src/main/res/drawable/message_border_dark.xml15
-rw-r--r--src/main/res/drawable/message_bubble_received_light.xml2
-rw-r--r--src/main/res/drawable/message_bubble_received_light_dark.xml2
-rw-r--r--src/main/res/drawable/message_bubble_received_warning.xml2
-rw-r--r--src/main/res/drawable/message_bubble_received_warning_dark.xml2
-rw-r--r--src/main/res/drawable/message_bubble_sent_blue.xml2
-rw-r--r--src/main/res/drawable/message_bubble_sent_blue_dark.xml2
-rw-r--r--src/main/res/layout/activity_contact_details.xml267
-rw-r--r--src/main/res/layout/activity_muc_details.xml13
-rw-r--r--src/main/res/layout/contact.xml2
-rw-r--r--src/main/res/layout/create_conference_dialog.xml14
-rw-r--r--src/main/res/layout/dialog_quickedit.xml4
-rw-r--r--src/main/res/menu/message_context.xml4
-rw-r--r--src/main/res/menu/select_multiple.xml10
-rw-r--r--src/main/res/values-v21/themes.xml7
-rw-r--r--src/main/res/values/attrs.xml5
-rw-r--r--src/main/res/values/colors.xml1
-rw-r--r--src/main/res/values/defaults.xml2
-rw-r--r--src/main/res/values/strings.xml11
-rw-r--r--src/main/res/values/themes.xml14
69 files changed, 1146 insertions, 712 deletions
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
index 51da8ccb0..02062eba1 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
@@ -1256,7 +1256,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return verified;
}
- public boolean hasPendingKeyFetches(Account account, List<Jid> jids) {
+ public boolean hasPendingKeyFetches(List<Jid> jids) {
SignalProtocolAddress ownAddress = new SignalProtocolAddress(account.getJid().asBareJid().toString(), 0);
if (fetchStatusMap.getAll(ownAddress.getName()).containsValue(FetchStatus.PENDING)) {
return true;
diff --git a/src/main/java/de/pixart/messenger/entities/Bookmark.java b/src/main/java/de/pixart/messenger/entities/Bookmark.java
index 079b283c0..fc7725bb5 100644
--- a/src/main/java/de/pixart/messenger/entities/Bookmark.java
+++ b/src/main/java/de/pixart/messenger/entities/Bookmark.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import de.pixart.messenger.utils.StringUtils;
import de.pixart.messenger.utils.UIHelper;
import de.pixart.messenger.xml.Element;
import de.pixart.messenger.xmpp.InvalidJid;
@@ -40,6 +41,14 @@ public class Bookmark extends Element implements ListItem {
return bookmark;
}
+ public static boolean printableValue(@Nullable String value, boolean permitNone) {
+ return value != null && !value.trim().isEmpty() && (permitNone || !"None".equals(value));
+ }
+
+ public static boolean printableValue(@Nullable String value) {
+ return printableValue(value, true);
+ }
+
public void setAutojoin(boolean autojoin) {
if (autojoin) {
this.setAttribute("autojoin", "true");
@@ -68,14 +77,6 @@ public class Bookmark extends Element implements ListItem {
}
}
- public static boolean printableValue(@Nullable String value, boolean permitNone) {
- return value != null && !value.trim().isEmpty() && (permitNone || !"None".equals(value));
- }
-
- public static boolean printableValue(@Nullable String value) {
- return printableValue(value, true);
- }
-
@Override
public int getOffline() {
return 0;
@@ -168,11 +169,11 @@ public class Bookmark extends Element implements ListItem {
public boolean setBookmarkName(String name) {
String before = getBookmarkName();
- if (name != null && !name.equals(before)) {
+ if (name != null) {
this.setAttribute("name", name);
- return true;
} else {
- return false;
+ this.removeAttribute("name");
}
+ return StringUtils.changed(before, name);
}
}
diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java
index 18a20532a..fe89009d3 100644
--- a/src/main/java/de/pixart/messenger/entities/Conversation.java
+++ b/src/main/java/de/pixart/messenger/entities/Conversation.java
@@ -134,6 +134,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
this.messagesLeftOnServer = value;
}
+ public void deleteMessage(Message message) {
+ this.messages.remove(message);
+ }
+
public Message getFirstUnreadMessage() {
Message first = null;
synchronized (this.messages) {
diff --git a/src/main/java/de/pixart/messenger/entities/MucOptions.java b/src/main/java/de/pixart/messenger/entities/MucOptions.java
index 2ef23882f..774620707 100644
--- a/src/main/java/de/pixart/messenger/entities/MucOptions.java
+++ b/src/main/java/de/pixart/messenger/entities/MucOptions.java
@@ -2,18 +2,19 @@ package de.pixart.messenger.entities;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
-import android.util.Log;
+import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Set;
import de.pixart.messenger.Config;
import de.pixart.messenger.R;
+import de.pixart.messenger.services.MessageArchiveService;
import de.pixart.messenger.utils.JidHelper;
-import de.pixart.messenger.utils.Namespace;
import de.pixart.messenger.utils.UIHelper;
import de.pixart.messenger.xmpp.chatstate.ChatState;
import de.pixart.messenger.xmpp.forms.Data;
@@ -30,8 +31,11 @@ public class MucOptions {
return this.conversation.getAccount();
}
- public void setSelf(User user) {
+ public boolean setSelf(User user) {
this.self = user;
+ final boolean roleChanged = this.conversation.setAttribute("role", user.role.toString());
+ final boolean affiliationChanged = this.conversation.setAttribute("affiliation", user.affiliation.toString());
+ return roleChanged || affiliationChanged;
}
public void changeAffiliation(Jid jid, Affiliation affiliation) {
@@ -67,20 +71,22 @@ public class MucOptions {
}
}
+ public boolean mamSupport() {
+ return MessageArchiveService.Version.has(getFeatures());
+ }
+
public enum Affiliation {
- OWNER("owner", 4, R.string.owner),
- ADMIN("admin", 3, R.string.admin),
- MEMBER("member", 2, R.string.member),
- OUTCAST("outcast", 0, R.string.outcast),
- NONE("none", 1, R.string.no_affiliation);
-
- Affiliation(String string, int rank, int resId) {
- this.string = string;
+ OWNER(4, R.string.owner),
+ ADMIN(3, R.string.admin),
+ MEMBER(2, R.string.member),
+ OUTCAST(0, R.string.outcast),
+ NONE(1, R.string.no_affiliation);
+
+ Affiliation(int rank, int resId) {
this.resId = resId;
this.rank = rank;
}
- private String string;
private int resId;
private int rank;
@@ -90,7 +96,7 @@ public class MucOptions {
@Override
public String toString() {
- return this.string;
+ return name().toLowerCase(Locale.US);
}
public boolean outranks(Affiliation affiliation) {
@@ -100,21 +106,30 @@ public class MucOptions {
public boolean ranks(Affiliation affiliation) {
return rank >= affiliation.rank;
}
+
+ public static Affiliation of(@Nullable String value) {
+ if (value == null) {
+ return NONE;
+ }
+ try {
+ return Affiliation.valueOf(value.toUpperCase(Locale.US));
+ } catch (IllegalArgumentException e) {
+ return NONE;
+ }
+ }
}
public enum Role {
- MODERATOR("moderator", R.string.moderator, 3),
- VISITOR("visitor", R.string.visitor, 1),
- PARTICIPANT("participant", R.string.participant, 2),
- NONE("none", R.string.no_role, 0);
+ MODERATOR(R.string.moderator, 3),
+ VISITOR(R.string.visitor, 1),
+ PARTICIPANT(R.string.participant, 2),
+ NONE(R.string.no_role, 0);
- Role(String string, int resId, int rank) {
- this.string = string;
+ Role(int resId, int rank) {
this.resId = resId;
this.rank = rank;
}
- private String string;
private int resId;
private int rank;
@@ -124,12 +139,23 @@ public class MucOptions {
@Override
public String toString() {
- return this.string;
+ return name().toLowerCase(Locale.US);
}
public boolean ranks(Role role) {
return rank >= role.rank;
}
+
+ public static Role of(@Nullable String value) {
+ if (value == null) {
+ return NONE;
+ }
+ try {
+ return Role.valueOf(value.toUpperCase(Locale.US));
+ } catch (IllegalArgumentException e) {
+ return NONE;
+ }
+ }
}
public enum Error {
@@ -140,8 +166,10 @@ public class MucOptions {
PASSWORD_REQUIRED,
BANNED,
MEMBERS_ONLY,
+ RESOURCE_CONSTRAINT,
KICKED,
SHUTDOWN,
+ DESTROYED,
INVALID_NICK,
UNKNOWN
}
@@ -193,25 +221,7 @@ public class MucOptions {
}
public void setRole(String role) {
- if (role == null) {
- this.role = Role.NONE;
- return;
- }
- role = role.toLowerCase();
- switch (role) {
- case "moderator":
- this.role = Role.MODERATOR;
- break;
- case "participant":
- this.role = Role.PARTICIPANT;
- break;
- case "visitor":
- this.role = Role.VISITOR;
- break;
- default:
- this.role = Role.NONE;
- break;
- }
+ this.role = Role.of(role);
}
public Affiliation getAffiliation() {
@@ -219,27 +229,7 @@ public class MucOptions {
}
public void setAffiliation(String affiliation) {
- if (affiliation == null) {
- this.affiliation = Affiliation.NONE;
- return;
- }
- affiliation = affiliation.toLowerCase();
- switch (affiliation) {
- case "admin":
- this.affiliation = Affiliation.ADMIN;
- break;
- case "owner":
- this.affiliation = Affiliation.OWNER;
- break;
- case "member":
- this.affiliation = Affiliation.MEMBER;
- break;
- case "outcast":
- this.affiliation = Affiliation.OUTCAST;
- break;
- default:
- this.affiliation = Affiliation.NONE;
- }
+ this.affiliation = Affiliation.of(affiliation);
}
public void setPgpKeyId(long id) {
@@ -377,6 +367,8 @@ public class MucOptions {
this.account = conversation.getAccount();
this.conversation = conversation;
this.self = new User(this, createJoinJid(getProposedNick()));
+ this.self.affiliation = Affiliation.of(conversation.getAttribute("affiliation"));
+ this.self.role = Role.of(conversation.getAttribute("role"));
}
public boolean updateConfiguration(ServiceDiscoveryResult serviceDiscoveryResult) {
@@ -384,12 +376,12 @@ public class MucOptions {
String name;
Field roomConfigName = getRoomInfoForm().getFieldByName("muc#roomconfig_roomname");
if (roomConfigName != null) {
- Log.d(Config.LOGTAG, "value of room config name " + roomConfigName.getValue());
name = roomConfigName.getValue();
} else {
List<ServiceDiscoveryResult.Identity> identities = serviceDiscoveryResult.getIdentities();
String identityName = identities.size() > 0 ? identities.get(0).getName() : null;
- if (!conversation.getJid().getEscapedLocal().equals(identityName)) {
+ final Jid jid = conversation.getJid();
+ if (identityName != null && !identityName.equals(jid == null ? null : jid.getEscapedLocal())) {
name = identityName;
} else {
name = null;
@@ -446,21 +438,15 @@ public class MucOptions {
}
public boolean participating() {
- return !online()
- || self.getRole().ranks(Role.PARTICIPANT)
- || hasFeature("muc_unmoderated");
+ return self.getRole().ranks(Role.PARTICIPANT) || !moderated();
}
public boolean membersOnly() {
return conversation.getBooleanAttribute(Conversation.ATTRIBUTE_MEMBERS_ONLY, false);
}
- public boolean mamSupport() {
- return hasFeature(Namespace.MAM) || hasFeature(Namespace.MAM_LEGACY);
- }
-
- public boolean mamLegacy() {
- return hasFeature(Namespace.MAM_LEGACY) && !hasFeature(Namespace.MAM);
+ public List<String> getFeatures() {
+ return this.serviceDiscoveryResult != null ? this.serviceDiscoveryResult.features : Collections.emptyList();
}
public boolean nonanonymous() {
@@ -714,8 +700,7 @@ public class MucOptions {
}
public String getName() {
- String mucName = this.conversation.getAttribute("muc_name");
- return conversation.getJid().getEscapedLocal().equals(mucName) ? null : mucName;
+ return this.conversation.getAttribute("muc_name");
}
private List<User> getFallbackUsersFromCryptoTargets() {
diff --git a/src/main/java/de/pixart/messenger/entities/ReadByMarker.java b/src/main/java/de/pixart/messenger/entities/ReadByMarker.java
index ec2d1aa73..86057c680 100644
--- a/src/main/java/de/pixart/messenger/entities/ReadByMarker.java
+++ b/src/main/java/de/pixart/messenger/entities/ReadByMarker.java
@@ -162,4 +162,10 @@ public class ReadByMarker {
return true;
}
+ public static boolean allUsersRepresented(Collection<MucOptions.User> users, Set<ReadByMarker> markers, ReadByMarker marker) {
+ HashSet<ReadByMarker> markersCopy = new HashSet<>(markers);
+ markersCopy.add(marker);
+ return allUsersRepresented(users, markersCopy);
+ }
+
}
diff --git a/src/main/java/de/pixart/messenger/entities/Roster.java b/src/main/java/de/pixart/messenger/entities/Roster.java
index 97ad41114..2db0c796e 100644
--- a/src/main/java/de/pixart/messenger/entities/Roster.java
+++ b/src/main/java/de/pixart/messenger/entities/Roster.java
@@ -76,7 +76,6 @@ public class Roster {
return;
}
contact.setAccount(account);
- contact.setOption(Contact.Options.IN_ROSTER);
synchronized (this.contacts) {
contacts.put(contact.getJid().asBareJid(), contact);
}
diff --git a/src/main/java/de/pixart/messenger/generator/IqGenerator.java b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
index eab087975..918c95e6f 100644
--- a/src/main/java/de/pixart/messenger/generator/IqGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
@@ -248,10 +248,10 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- final Element query = packet.query(mam.isLegacy() ? Namespace.MAM_LEGACY : Namespace.MAM);
+ final Element query = packet.query(mam.version.namespace);
query.setAttribute("queryid", mam.getQueryId());
final Data data = new Data();
- data.setFormType(mam.isLegacy() ? Namespace.MAM_LEGACY : Namespace.MAM);
+ data.setFormType(mam.version.namespace);
if (mam.muc()) {
packet.setTo(mam.getWith());
} else if (mam.getWith() != null) {
@@ -325,7 +325,7 @@ public class IqGenerator extends AbstractGenerator {
Element query = packet.query("http://jabber.org/protocol/muc#admin");
for (Jid jid : jids) {
Element item = query.addChild("item");
- item.setAttribute("jid", jid.toString());
+ item.setAttribute("jid", jid.toEscapedString());
item.setAttribute("affiliation", affiliation);
}
return packet;
@@ -455,8 +455,9 @@ public class IqGenerator extends AbstractGenerator {
options.putString("muc#roomconfig_membersonly", "1");
options.putString("muc#roomconfig_publicroom", "0");
options.putString("muc#roomconfig_whois", "anyone");
- options.putString("muc#roomconfig_enablearchiving", "1");
- options.putString("mam", "1");
+ options.putString("muc#roomconfig_enablearchiving", "1"); //prosody
+ options.putString("mam", "1"); //ejabberd community
+ options.putString("muc#roomconfig_mam", "1"); //ejabberd saas
return options;
}
diff --git a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
index b35e0aa83..efdfdc61e 100644
--- a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
@@ -52,7 +52,7 @@ public class HttpDownloadConnection implements Transferable {
private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- public HttpDownloadConnection(HttpConnectionManager manager) {
+ HttpDownloadConnection(HttpConnectionManager manager) {
this.mHttpConnectionManager = manager;
this.mXmppConnectionService = manager.getXmppConnectionService();
this.mUseTor = mXmppConnectionService.useTorToConnect();
@@ -64,7 +64,7 @@ public class HttpDownloadConnection implements Transferable {
if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) {
checkFileSize(true);
} else {
- new Thread(new FileDownloader(true)).start();
+ download(true);
}
return true;
} else {
@@ -113,12 +113,22 @@ public class HttpDownloadConnection implements Transferable {
this.message.setEncryption(Message.ENCRYPTION_NONE);
}
method = mUrl.getProtocol().equalsIgnoreCase(P1S3UrlStreamHandler.PROTOCOL_NAME) ? Method.P1_S3 : Method.HTTP_UPLOAD;
- checkFileSize(interactive);
+ long knownFileSize = message.getFileParams().size;
+ if (knownFileSize > 0 && interactive && method != Method.P1_S3) {
+ this.file.setExpectedSize(knownFileSize);
+ download(true);
+ } else {
+ checkFileSize(interactive);
+ }
} catch (MalformedURLException e) {
this.cancel();
}
}
+ private void download(boolean interactive) {
+ new Thread(new FileDownloader(interactive)).start();
+ }
+
private void checkFileSize(boolean interactive) {
new Thread(new FileSizeChecker(interactive)).start();
}
@@ -257,7 +267,7 @@ public class HttpDownloadConnection implements Transferable {
&& size <= mHttpConnectionManager.getAutoAcceptFileSize()
&& mXmppConnectionService.isDataSaverDisabled()) {
HttpDownloadConnection.this.acceptedAutomatically = true;
- new Thread(new FileDownloader(interactive)).start();
+ download(interactive);
} else {
changeStatus(STATUS_OFFER);
HttpDownloadConnection.this.acceptedAutomatically = false;
diff --git a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
index 090e7ad07..eee8b3c01 100644
--- a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
@@ -230,7 +230,7 @@ public class HttpUploadConnection implements Transferable {
Log.d(Config.LOGTAG, "http upload failed because response code was " + code);
fail("http upload failed because response code was " + code);
}
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
Log.d(Config.LOGTAG, "http upload failed " + e.getMessage());
fail(e.getMessage());
diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java
index 7fc2e2d50..e86758fd7 100644
--- a/src/main/java/de/pixart/messenger/parser/MessageParser.java
+++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java
@@ -26,7 +26,6 @@ import de.pixart.messenger.crypto.axolotl.AxolotlService;
import de.pixart.messenger.crypto.axolotl.NotEncryptedForThisDeviceException;
import de.pixart.messenger.crypto.axolotl.XmppAxolotlMessage;
import de.pixart.messenger.entities.Account;
-import de.pixart.messenger.entities.Bookmark;
import de.pixart.messenger.entities.Contact;
import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.entities.Message;
@@ -276,10 +275,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
mXmppConnectionService.updateAccountUi();
} else {
Contact contact = account.getRoster().getContact(from);
- contact.setAvatar(avatar);
- mXmppConnectionService.getAvatarService().clear(contact);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
+ if (contact.setAvatar(avatar)) {
+ mXmppConnectionService.syncRoster(account);
+ mXmppConnectionService.getAvatarService().clear(contact);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateRosterUi();
+ }
}
} else if (mXmppConnectionService.isDataSaverDisabled()) {
mXmppConnectionService.fetchAvatar(account, avatar);
@@ -337,16 +338,15 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
final boolean isForwarded;
boolean isCarbon = false;
String serverMsgId = null;
- final Element fin = original.findChild("fin", Namespace.MAM_LEGACY);
+ final Element fin = original.findChild("fin", MessageArchiveService.Version.MAM_0.namespace);
if (fin != null) {
mXmppConnectionService.getMessageArchiveService().processFinLegacy(fin, original.getFrom());
return;
}
- final boolean mamLegacy = original.hasChild("result", Namespace.MAM_LEGACY);
- final Element result = original.findChild("result", mamLegacy ? Namespace.MAM_LEGACY : Namespace.MAM);
+ final Element result = MessageArchiveService.Version.findResult(original);
final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
if (query != null && query.validFrom(original.getFrom())) {
- Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", mamLegacy ? Namespace.MAM_LEGACY : Namespace.MAM);
+ Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", query.version.namespace);
if (f == null) {
return;
}
@@ -759,12 +759,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (conversation.getMucOptions().setSubject(subject)) {
mXmppConnectionService.updateConversation(conversation);
}
- final Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null && bookmark.getBookmarkName() == null) {
- if (bookmark.setBookmarkName(subject)) {
- mXmppConnectionService.pushBookmarks(account);
- }
- }
mXmppConnectionService.updateConversationUi();
return;
}
diff --git a/src/main/java/de/pixart/messenger/parser/PresenceParser.java b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
index 1da8553dc..6602e8aef 100644
--- a/src/main/java/de/pixart/messenger/parser/PresenceParser.java
+++ b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
@@ -18,6 +18,7 @@ import de.pixart.messenger.generator.IqGenerator;
import de.pixart.messenger.generator.PresenceGenerator;
import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.utils.Namespace;
+import de.pixart.messenger.utils.XmppUri;
import de.pixart.messenger.xml.Element;
import de.pixart.messenger.xmpp.InvalidJid;
import de.pixart.messenger.xmpp.OnPresencePacketReceived;
@@ -71,7 +72,10 @@ public class PresenceParser extends AbstractParser implements
if (mucOptions.setOnline()) {
mXmppConnectionService.getAvatarService().clear(mucOptions);
}
- mucOptions.setSelf(user);
+ if (mucOptions.setSelf(user)) {
+ Log.d(Config.LOGTAG, "role or affiliation changed");
+ mXmppConnectionService.databaseBackend.updateConversation(conversation);
+ }
mXmppConnectionService.persistSelfNick(user);
invokeRenameListener(mucOptions, true);
}
@@ -118,7 +122,15 @@ public class PresenceParser extends AbstractParser implements
}
}
} else if (type.equals("unavailable")) {
- if (codes.contains(MucOptions.STATUS_CODE_SHUTDOWN) && from.equals(mucOptions.getSelf().getFullJid())) {
+ final boolean fullJidMatches = from.equals(mucOptions.getSelf().getFullJid());
+ if (x.hasChild("destroy") && fullJidMatches) {
+ Element destroy = x.findChild("destroy");
+ final Jid alternate = destroy == null ? null : InvalidJid.getNullForInvalid(destroy.getAttributeAsJid("jid"));
+ mucOptions.setError(MucOptions.Error.DESTROYED);
+ if (alternate != null) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": muc destroyed. alternate location " + alternate);
+ }
+ } else if (codes.contains(MucOptions.STATUS_CODE_SHUTDOWN) && fullJidMatches) {
mucOptions.setError(MucOptions.Error.SHUTDOWN);
} else if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE)) {
if (codes.contains(MucOptions.STATUS_CODE_KICKED)) {
@@ -162,6 +174,25 @@ public class PresenceParser extends AbstractParser implements
mucOptions.setError(MucOptions.Error.BANNED);
} else if (error.hasChild("registration-required")) {
mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
+ } else if (error.hasChild("resource-constraint")) {
+ mucOptions.setError(MucOptions.Error.RESOURCE_CONSTRAINT);
+ } else if (error.hasChild("gone")) {
+ final String gone = error.findChildContent("gone");
+ final Jid alternate;
+ if (gone != null) {
+ final XmppUri xmppUri = new XmppUri(gone);
+ if (xmppUri.isJidValid()) {
+ alternate = xmppUri.getJid();
+ } else {
+ alternate = null;
+ }
+ } else {
+ alternate = null;
+ }
+ mucOptions.setError(MucOptions.Error.DESTROYED);
+ if (alternate != null) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": muc destroyed. alternate location " + alternate);
+ }
} else {
final String text = error.findChildContent("text");
if (text != null && text.contains("attribute 'to'")) {
@@ -229,6 +260,7 @@ public class PresenceParser extends AbstractParser implements
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateAccountUi();
} else if (contact.setAvatar(avatar)) {
+ mXmppConnectionService.syncRoster(account);
mXmppConnectionService.getAvatarService().clear(contact);
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateRosterUi();
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index b2f4fe9d5..94710b86e 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -901,7 +901,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
final SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
for (Contact contact : roster.getContacts()) {
- if (contact.getOption(Contact.Options.IN_ROSTER)) {
+ if (contact.getOption(Contact.Options.IN_ROSTER) || contact.getAvatar() != null) {
db.insert(Contact.TABLENAME, null, contact.getContentValues());
} else {
String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
@@ -917,6 +917,17 @@ public class DatabaseBackend extends SQLiteOpenHelper {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persisted roster in " + duration + "ms");
}
+ public void deleteMessageInConversation(Message message) {
+ long start = SystemClock.elapsedRealtime();
+ final SQLiteDatabase db = this.getWritableDatabase();
+ db.beginTransaction();
+ String[] args = {message.getUuid()};
+ db.delete("messages_index", "uuid =?", args);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ Log.d(Config.LOGTAG, "deleted single message in " + (SystemClock.elapsedRealtime() - start) + "ms");
+ }
+
public void deleteMessagesInConversation(Conversation conversation) {
long start = SystemClock.elapsedRealtime();
final SQLiteDatabase db = this.getWritableDatabase();
diff --git a/src/main/java/de/pixart/messenger/services/AvatarService.java b/src/main/java/de/pixart/messenger/services/AvatarService.java
index 06b0e1f5f..bf415515b 100644
--- a/src/main/java/de/pixart/messenger/services/AvatarService.java
+++ b/src/main/java/de/pixart/messenger/services/AvatarService.java
@@ -54,7 +54,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
protected XmppConnectionService mXmppConnectionService = null;
- public AvatarService(XmppConnectionService service) {
+ AvatarService(XmppConnectionService service) {
this.mXmppConnectionService = service;
}
@@ -231,7 +231,13 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
if (bookmark.getConversation() != null) {
return get(bookmark.getConversation(), size, cachedOnly);
} else {
- String seed = bookmark.getJid() != null ? bookmark.getJid().asBareJid().toString() : null;
+ Jid jid = bookmark.getJid();
+ Account account = bookmark.getAccount();
+ Contact contact = jid == null ? null : account.getRoster().getContact(jid);
+ if (contact != null && contact.getAvatar() != null) {
+ return get(contact, size, cachedOnly);
+ }
+ String seed = jid != null ? jid.asBareJid().toString() : null;
return get(bookmark.getDisplayName(), seed, size, cachedOnly);
}
} else {
diff --git a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
index df376ab73..d9394d4dc 100644
--- a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
+++ b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
@@ -15,11 +15,11 @@ import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.entities.Conversational;
import de.pixart.messenger.entities.ReceiptRequest;
import de.pixart.messenger.generator.AbstractGenerator;
-import de.pixart.messenger.utils.Namespace;
import de.pixart.messenger.xml.Element;
import de.pixart.messenger.xmpp.OnAdvancedStreamFeaturesLoaded;
import de.pixart.messenger.xmpp.mam.MamReference;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
+import de.pixart.messenger.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
@@ -29,6 +29,68 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
private final HashSet<Query> queries = new HashSet<>();
private final ArrayList<Query> pendingQueries = new ArrayList<>();
+ public enum Version {
+ MAM_0("urn:xmpp:mam:0", true),
+ MAM_1("urn:xmpp:mam:1", false),
+ MAM_2("urn:xmpp:mam:2", false);
+
+ public final boolean legacy;
+ public final String namespace;
+
+ Version(String namespace, boolean legacy) {
+ this.namespace = namespace;
+ this.legacy = legacy;
+ }
+
+ public static Version get(Account account) {
+ return get(account, null);
+ }
+
+ public static Version get(Account account, Conversation conversation) {
+ if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) {
+ return get(account.getXmppConnection().getFeatures().getAccountFeatures());
+ } else {
+ return get(conversation.getMucOptions().getFeatures());
+ }
+ }
+
+ private static Version get(List<String> features) {
+ final Version[] values = values();
+ for (int i = values.length - 1; i >= 0; --i) {
+ for (String feature : features) {
+ if (values[i].namespace.equals(feature)) {
+ return values[i];
+ }
+ }
+ }
+ return MAM_0;
+ }
+
+ public static boolean has(List<String> features) {
+ for (String feature : features) {
+ for (Version version : values()) {
+ if (version.namespace.equals(feature)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static Element findResult(MessagePacket packet) {
+ for (Version version : values()) {
+ Element result = packet.findChild("result", version.namespace);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ }
+
+ ;
+
MessageArchiveService(final XmppConnectionService service) {
this.mXmppConnectionService = service;
}
@@ -168,7 +230,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": running mam query " + query.toString());
IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
this.mXmppConnectionService.sendIqPacket(account, packet, (a, p) -> {
- Element fin = p.findChild("fin", Namespace.MAM);
+ Element fin = p.findChild("fin", query.version.namespace);
if (p.getType() == IqPacket.TYPE.TIMEOUT) {
synchronized (MessageArchiveService.this.queries) {
MessageArchiveService.this.queries.remove(query);
@@ -389,16 +451,21 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
private PagingOrder pagingOrder = PagingOrder.NORMAL;
private XmppConnectionService.OnMoreMessagesLoaded callback = null;
private boolean catchup = true;
+ public final Version version;
Query(Conversation conversation, MamReference start, long end, boolean catchup) {
- this(conversation.getAccount(), catchup ? start : start.timeOnly(), end);
+ this(conversation.getAccount(), Version.get(conversation.getAccount(), conversation), catchup ? start : start.timeOnly(), end);
this.conversation = conversation;
this.pagingOrder = catchup ? PagingOrder.NORMAL : PagingOrder.REVERSE;
this.catchup = catchup;
}
Query(Account account, MamReference start, long end) {
+ this(account, Version.get(account), start, end);
+ }
+
+ Query(Account account, Version version, MamReference start, long end) {
this.account = account;
if (start.getReference() != null) {
this.reference = start.getReference();
@@ -407,10 +474,11 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
this.end = end;
this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ this.version = version;
}
private Query page(String reference) {
- Query query = new Query(this.account, new MamReference(this.start, reference), this.end);
+ Query query = new Query(this.account, this.version, new MamReference(this.start, reference), this.end);
query.conversation = conversation;
query.totalCount = totalCount;
query.actualCount = actualCount;
@@ -432,11 +500,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
public boolean isLegacy() {
- if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) {
- return account.getXmppConnection().getFeatures().mamLegacy();
- } else {
- return conversation.getMucOptions().mamLegacy();
- }
+ return version.legacy;
}
public boolean safeToExtractTrueCounterpart() {
@@ -569,6 +633,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
builder.append(this.reference);
}
builder.append(", catchup=").append(Boolean.toString(catchup));
+ builder.append(", ns=").append(version.namespace);
return builder.toString();
}
diff --git a/src/main/java/de/pixart/messenger/services/NotificationService.java b/src/main/java/de/pixart/messenger/services/NotificationService.java
index 9f8438de3..7fa091478 100644
--- a/src/main/java/de/pixart/messenger/services/NotificationService.java
+++ b/src/main/java/de/pixart/messenger/services/NotificationService.java
@@ -181,7 +181,7 @@ public class NotificationService {
public void push(final Message message) {
synchronized (CATCHUP_LOCK) {
final XmppConnection connection = message.getConversation().getAccount().getXmppConnection();
- if (connection.isWaitingForSmCatchup()) {
+ if (connection != null && connection.isWaitingForSmCatchup()) {
connection.incrementSmCatchupMessageCounter();
pushFromBacklog(message);
} else {
diff --git a/src/main/java/de/pixart/messenger/services/UpdateService.java b/src/main/java/de/pixart/messenger/services/UpdateService.java
index c08dab683..7e636b8e6 100644
--- a/src/main/java/de/pixart/messenger/services/UpdateService.java
+++ b/src/main/java/de/pixart/messenger/services/UpdateService.java
@@ -26,25 +26,20 @@ import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.ui.UpdaterActivity;
+import static de.pixart.messenger.http.HttpConnectionManager.getProxy;
import static de.pixart.messenger.services.NotificationService.UPDATE_NOTIFICATION_ID;
public class UpdateService extends AsyncTask<String, Object, UpdateService.Wrapper> {
- public UpdateService (){
- }
-
+ private boolean mUseTor;
private Context context;
private boolean playstore;
+ public UpdateService() {
+ }
- public UpdateService(Context context, boolean PlayStore) {
+ public UpdateService(Context context, boolean PlayStore, XmppConnectionService mXmppConnectionService) {
this.context = context;
this.playstore = PlayStore;
- }
-
- public class Wrapper
- {
- public boolean UpdateAvailable = false;
- public boolean NoUpdate = false;
- public boolean isError = false;
+ this.mUseTor = mXmppConnectionService.useTorToConnect();
}
@Override
@@ -60,9 +55,13 @@ public class UpdateService extends AsyncTask<String, Object, UpdateService.Wrapp
HttpsURLConnection connection = null;
- try {
+ try {
URL url = new URL(Config.UPDATE_URL);
- connection = (HttpsURLConnection)url.openConnection();
+ if (mUseTor) {
+ connection = (HttpsURLConnection) url.openConnection(getProxy());
+ } else {
+ connection = (HttpsURLConnection) url.openConnection();
+ }
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setRequestProperty("User-Agent", context.getString(R.string.app_name));
@@ -128,18 +127,14 @@ public class UpdateService extends AsyncTask<String, Object, UpdateService.Wrapp
return;
}
Handler handler = new Handler(Looper.getMainLooper());
- handler.post(new Runnable() {
-
- @Override
- public void run() {
- String ToastMessage = "";
- if (error) {
- ToastMessage = context.getString(R.string.failed);
- } else {
- ToastMessage = context.getString(R.string.no_update_available);
- }
- Toast.makeText(context, ToastMessage, Toast.LENGTH_LONG).show();
+ handler.post(() -> {
+ String ToastMessage = "";
+ if (error) {
+ ToastMessage = context.getString(R.string.failed);
+ } else {
+ ToastMessage = context.getString(R.string.no_update_available);
}
+ Toast.makeText(context, ToastMessage, Toast.LENGTH_LONG).show();
});
}
@@ -211,4 +206,10 @@ public class UpdateService extends AsyncTask<String, Object, UpdateService.Wrapp
}
return 0;
}
+
+ public class Wrapper {
+ public boolean UpdateAvailable = false;
+ public boolean NoUpdate = false;
+ public boolean isError = false;
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index 51f4793d1..7f7d319aa 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -31,6 +31,8 @@ import android.security.KeyChain;
import android.support.annotation.BoolRes;
import android.support.annotation.IntegerRes;
import android.support.v4.app.RemoteInput;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LruCache;
@@ -46,7 +48,6 @@ import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
-import java.math.BigInteger;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
@@ -66,6 +67,7 @@ import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -125,6 +127,7 @@ import de.pixart.messenger.utils.ReplacingSerialSingleThreadExecutor;
import de.pixart.messenger.utils.ReplacingTaskManager;
import de.pixart.messenger.utils.Resolver;
import de.pixart.messenger.utils.SerialSingleThreadExecutor;
+import de.pixart.messenger.utils.StringUtils;
import de.pixart.messenger.utils.WakeLockHelper;
import de.pixart.messenger.utils.XmppUri;
import de.pixart.messenger.xml.Element;
@@ -259,29 +262,35 @@ public class XmppConnectionService extends Service {
private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
@Override
- public void onMessageAcknowledged(Account account, String uuid) {
+ public boolean onMessageAcknowledged(Account account, String uuid) {
for (final Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) {
Message message = conversation.findUnsentMessageWithUuid(uuid);
if (message != null) {
- markMessage(message, Message.STATUS_SEND);
+ message.setStatus(Message.STATUS_SEND);
+ message.setErrorMessage(null);
+ databaseBackend.updateMessage(message, false);
+ return true;
}
}
}
+ return false;
}
};
private int unreadCount = -1;
//Ui callback listeners
- private final List<OnConversationUpdate> mOnConversationUpdates = new ArrayList<>();
- private final List<OnShowErrorToast> mOnShowErrorToasts = new ArrayList<>();
- private final List<OnAccountUpdate> mOnAccountUpdates = new ArrayList<>();
- private final List<OnCaptchaRequested> mOnCaptchaRequested = new ArrayList<>();
- private final List<OnRosterUpdate> mOnRosterUpdates = new ArrayList<>();
- private final List<OnUpdateBlocklist> mOnUpdateBlocklist = new ArrayList<>();
- private final List<OnMucRosterUpdate> mOnMucRosterUpdate = new ArrayList<>();
- private final List<OnKeyStatusUpdated> mOnKeyStatusUpdated = new ArrayList<>();
+ private final Set<OnConversationUpdate> mOnConversationUpdates = Collections.newSetFromMap(new WeakHashMap<OnConversationUpdate, Boolean>());
+ private final Set<OnShowErrorToast> mOnShowErrorToasts = Collections.newSetFromMap(new WeakHashMap<OnShowErrorToast, Boolean>());
+ private final Set<OnAccountUpdate> mOnAccountUpdates = Collections.newSetFromMap(new WeakHashMap<OnAccountUpdate, Boolean>());
+ private final Set<OnCaptchaRequested> mOnCaptchaRequested = Collections.newSetFromMap(new WeakHashMap<OnCaptchaRequested, Boolean>());
+ private final Set<OnRosterUpdate> mOnRosterUpdates = Collections.newSetFromMap(new WeakHashMap<OnRosterUpdate, Boolean>());
+ private final Set<OnUpdateBlocklist> mOnUpdateBlocklist = Collections.newSetFromMap(new WeakHashMap<OnUpdateBlocklist, Boolean>());
+ private final Set<OnMucRosterUpdate> mOnMucRosterUpdate = Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>());
+ private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated = Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>());
+
+ private final Object LISTENER_LOCK = new Object();
private final OnBindListener mOnBindListener = new OnBindListener() {
@@ -1101,7 +1110,10 @@ public class XmppConnectionService extends Service {
restoreFromDatabase();
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
- new Thread(fileObserver::startWatching).start();
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+ Log.d(Config.LOGTAG, "starting file observer");
+ new Thread(fileObserver::startWatching).start();
+ }
if (Config.supportOpenPgp()) {
this.pgpServiceConnection = new OpenPgpServiceConnection(this, "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() {
@Override
@@ -1160,6 +1172,11 @@ public class XmppConnectionService extends Service {
CancelAutomaticExport(true);
}
+ public void restartFileObserver() {
+ Log.d(Config.LOGTAG, "restarting file observer");
+ new Thread(fileObserver::restartWatching).start();
+ }
+
public void toggleScreenEventReceiver() {
if (awayWhenScreenOff() && !manuallyChangePresence()) {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
@@ -1214,10 +1231,13 @@ public class XmppConnectionService extends Service {
public void scheduleWakeUpCall(int seconds, int requestCode) {
final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000;
final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, EventReceiver.class);
+ if (alarmManager == null) {
+ return;
+ }
+ final Intent intent = new Intent(this, EventReceiver.class);
intent.setAction("ping");
- PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode, intent, 0);
try {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode, intent, 0);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, pendingIntent);
} catch (RuntimeException e) {
Log.e(Config.LOGTAG, "unable to schedule alarm for ping", e);
@@ -1230,10 +1250,13 @@ public class XmppConnectionService extends Service {
private void scheduleNextIdlePing() {
final long timeToWake = SystemClock.elapsedRealtime() + (Config.IDLE_PING_INTERVAL * 1000);
final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, EventReceiver.class);
+ if (alarmManager == null) {
+ return;
+ }
+ final Intent intent = new Intent(this, EventReceiver.class);
intent.setAction(ACTION_IDLE_PING);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
try {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, pendingIntent);
} catch (RuntimeException e) {
Log.d(Config.LOGTAG, "unable to schedule alarm for idle ping", e);
@@ -1709,7 +1732,7 @@ public class XmppConnectionService extends Service {
} else {
for (Conversation conversation : getConversations()) {
if (conversation.getMode() == Conversation.MODE_SINGLE
- || conversation.getAccount().httpUploadAvailable()) {
+ || (conversation.getAccount().httpUploadAvailable() && conversation.getMucOptions().participating())) {
list.add(conversation);
}
}
@@ -2123,18 +2146,19 @@ public class XmppConnectionService extends Service {
}
public void setOnConversationListChangedListener(OnConversationUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnConversationUpdates.add(listener);
- Log.d(Config.LOGTAG, "XmppConnectionService setOnConversationListChangedListener(): setIsInForeground = " + (this.mOnConversationUpdates.size() > 0));
+ if (!this.mOnConversationUpdates.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as ConversationListChangedListener");
+ }
this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0);
}
}
public void removeOnConversationListChangedListener(OnConversationUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnConversationUpdates.remove(listener);
Log.d(Config.LOGTAG, "XmppConnectionService removeOnConversationListChangedListener(): setIsInForeground = " + (this.mOnConversationUpdates.size() > 0));
this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0);
@@ -2144,17 +2168,19 @@ public class XmppConnectionService extends Service {
}
}
- public void setOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) {
- synchronized (this) {
+ public void setOnShowErrorToastListener(OnShowErrorToast listener) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnShowErrorToasts.add(onShowErrorToast);
+ if (!this.mOnShowErrorToasts.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnShowErrorToastListener");
+ }
}
}
public void removeOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnShowErrorToasts.remove(onShowErrorToast);
if (checkListeners()) {
switchToBackground();
@@ -2163,16 +2189,18 @@ public class XmppConnectionService extends Service {
}
public void setOnAccountListChangedListener(OnAccountUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnAccountUpdates.add(listener);
+ if (!this.mOnAccountUpdates.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnAccountListChangedtListener");
+ }
}
}
public void removeOnAccountListChangedListener(OnAccountUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnAccountUpdates.remove(listener);
if (checkListeners()) {
switchToBackground();
@@ -2181,16 +2209,18 @@ public class XmppConnectionService extends Service {
}
public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnCaptchaRequested.add(listener);
+ if (!this.mOnCaptchaRequested.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnCaptchaRequestListener");
+ }
}
}
public void removeOnCaptchaRequestedListener(OnCaptchaRequested listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnCaptchaRequested.remove(listener);
if (checkListeners()) {
switchToBackground();
@@ -2199,16 +2229,18 @@ public class XmppConnectionService extends Service {
}
public void setOnRosterUpdateListener(final OnRosterUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnRosterUpdates.add(listener);
+ if (!this.mOnRosterUpdates.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnRosterUpdateListener");
+ }
}
}
public void removeOnRosterUpdateListener(final OnRosterUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnRosterUpdates.remove(listener);
if (checkListeners()) {
switchToBackground();
@@ -2217,16 +2249,18 @@ public class XmppConnectionService extends Service {
}
public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnUpdateBlocklist.add(listener);
+ if (!this.mOnUpdateBlocklist.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnUpdateBlocklistListener");
+ }
}
}
public void removeOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnUpdateBlocklist.remove(listener);
if (checkListeners()) {
switchToBackground();
@@ -2235,16 +2269,18 @@ public class XmppConnectionService extends Service {
}
public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnKeyStatusUpdated.add(listener);
+ if (!this.mOnKeyStatusUpdated.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnKeyStatusUpdateListener");
+ }
}
}
public void removeOnNewKeysAvailableListener(final OnKeyStatusUpdated listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnKeyStatusUpdated.remove(listener);
if (checkListeners()) {
switchToBackground();
@@ -2253,16 +2289,18 @@ public class XmppConnectionService extends Service {
}
public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
if (checkListeners()) {
switchToForeground();
}
- this.mOnMucRosterUpdate.add(listener);
+ if (!this.mOnMucRosterUpdate.add(listener)) {
+ Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnMucRosterListener");
+ }
}
}
public void removeOnMucRosterUpdateListener(final OnMucRosterUpdate listener) {
- synchronized (this) {
+ synchronized (LISTENER_LOCK) {
this.mOnMucRosterUpdate.remove(listener);
if (checkListeners()) {
switchToBackground();
@@ -2275,6 +2313,7 @@ public class XmppConnectionService extends Service {
&& this.mOnConversationUpdates.size() == 0
&& this.mOnRosterUpdates.size() == 0
&& this.mOnCaptchaRequested.size() == 0
+ && this.mOnMucRosterUpdate.size() == 0
&& this.mOnUpdateBlocklist.size() == 0
&& this.mOnShowErrorToasts.size() == 0
&& this.mOnKeyStatusUpdated.size() == 0);
@@ -2625,7 +2664,7 @@ public class XmppConnectionService extends Service {
}
public boolean createAdhocConference(final Account account,
- final String subject,
+ final String name,
final Iterable<Jid> jids,
final UiCallback<Conversation> callback) {
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": creating adhoc conference with " + jids.toString());
@@ -2638,24 +2677,25 @@ public class XmppConnectionService extends Service {
}
return false;
}
- final Jid jid = Jid.of(new BigInteger(64, getRNG()).toString(Character.MAX_RADIX), server, null);
+ final Jid jid = Jid.of(CryptoHelper.pronounceable(getRNG()), server, null);
final Conversation conversation = findOrCreateConversation(account, jid, true, false, true);
joinMuc(conversation, new OnConferenceJoined() {
@Override
public void onConferenceJoined(final Conversation conversation) {
- pushConferenceConfiguration(conversation, IqGenerator.defaultRoomConfiguration(), new OnConfigurationPushed() {
+ final Bundle configuration = IqGenerator.defaultRoomConfiguration();
+ if (!TextUtils.isEmpty(name)) {
+ configuration.putString("muc#roomconfig_roomname", name);
+ }
+ pushConferenceConfiguration(conversation, configuration, new OnConfigurationPushed() {
@Override
public void onPushSucceeded() {
- if (subject != null && !subject.trim().isEmpty()) {
- pushSubjectToConference(conversation, subject.trim());
- }
for (Jid invite : jids) {
invite(conversation, invite);
}
if (account.countPresences() > 1) {
directInvite(conversation, account.getJid().asBareJid());
}
- saveConversationAsBookmark(conversation, subject);
+ saveConversationAsBookmark(conversation, name);
if (callback != null) {
callback.success(conversation);
}
@@ -2698,13 +2738,25 @@ public class XmppConnectionService extends Service {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
- if (conversation.getMucOptions().updateConfiguration(new ServiceDiscoveryResult(packet))) {
+ final MucOptions mucOptions = conversation.getMucOptions();
+ final Bookmark bookmark = conversation.getBookmark();
+ final boolean sameBefore = StringUtils.equals(bookmark == null ? null : bookmark.getBookmarkName(), mucOptions.getName());
+
+ if (mucOptions.updateConfiguration(new ServiceDiscoveryResult(packet))) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": muc configuration changed for " + conversation.getJid().asBareJid());
updateConversation(conversation);
}
+
+ if (bookmark != null && (sameBefore || bookmark.getBookmarkName() == null)) {
+ if (bookmark.setBookmarkName(StringUtils.nullOnEmpty(mucOptions.getName()))) {
+ pushBookmarks(account);
+ }
+ }
+
if (callback != null) {
callback.onConferenceConfigurationFetched(conversation);
}
+
updateConversationUi();
} else if (packet.getType() == IqPacket.TYPE.ERROR) {
if (callback != null) {
@@ -2786,7 +2838,7 @@ public class XmppConnectionService extends Service {
}
public void pushSubjectToConference(final Conversation conference, final String subject) {
- MessagePacket packet = this.getMessageGenerator().conferenceSubject(conference, subject);
+ MessagePacket packet = this.getMessageGenerator().conferenceSubject(conference, StringUtils.nullOnEmpty(subject));
this.sendMessagePacket(conference.getAccount(), packet);
}
@@ -3200,10 +3252,12 @@ public class XmppConnectionService extends Service {
updateAccountUi();
} else {
Contact contact = a.getRoster().getContact(avatar.owner);
- contact.setAvatar(avatar);
- getAvatarService().clear(contact);
- updateConversationUi();
- updateRosterUi();
+ if (contact.setAvatar(avatar)) {
+ syncRoster(account);
+ getAvatarService().clear(contact);
+ updateConversationUi();
+ updateRosterUi();
+ }
}
if (callback != null) {
callback.success(avatar);
@@ -3255,9 +3309,11 @@ public class XmppConnectionService extends Service {
updateAccountUi();
} else {
Contact contact = account.getRoster().getContact(avatar.owner);
- contact.setAvatar(avatar);
- getAvatarService().clear(contact);
- updateRosterUi();
+ if (contact.setAvatar(avatar)) {
+ syncRoster(account);
+ getAvatarService().clear(contact);
+ updateRosterUi();
+ }
}
updateConversationUi();
} else {
@@ -3525,26 +3581,32 @@ public class XmppConnectionService extends Service {
vibrator.vibrate(100);
}
+ private <T> List<T> threadSafeList(Set<T> set) {
+ synchronized (LISTENER_LOCK) {
+ return set.size() == 0 ? Collections.emptyList() : new ArrayList<>(set);
+ }
+ }
+
public void showErrorToastInUi(int resId) {
- for(OnShowErrorToast listener : this.mOnShowErrorToasts) {
+ for (OnShowErrorToast listener : threadSafeList(this.mOnShowErrorToasts)) {
listener.onShowErrorToast(resId);
}
}
public void updateConversationUi() {
- for(OnConversationUpdate listener : this.mOnConversationUpdates) {
+ for (OnConversationUpdate listener : threadSafeList(this.mOnConversationUpdates)) {
listener.onConversationUpdate();
}
}
public void updateAccountUi() {
- for(OnAccountUpdate listener : this.mOnAccountUpdates) {
+ for (OnAccountUpdate listener : threadSafeList(this.mOnAccountUpdates)) {
listener.onAccountUpdate();
}
}
public void updateRosterUi() {
- for(OnRosterUpdate listener : this.mOnRosterUpdates) {
+ for (OnRosterUpdate listener : threadSafeList(this.mOnRosterUpdates)) {
listener.onRosterUpdate();
}
}
@@ -3554,8 +3616,7 @@ public class XmppConnectionService extends Service {
DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity),
(int) (captcha.getHeight() * metrics.scaledDensity), false);
-
- for(OnCaptchaRequested listener : this.mOnCaptchaRequested) {
+ for (OnCaptchaRequested listener : threadSafeList(this.mOnCaptchaRequested)) {
listener.onCaptchaRequested(account, id, data, scaled);
}
return true;
@@ -3564,19 +3625,19 @@ public class XmppConnectionService extends Service {
}
public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
- for(OnUpdateBlocklist listener : this.mOnUpdateBlocklist) {
+ for (OnUpdateBlocklist listener : threadSafeList(this.mOnUpdateBlocklist)) {
listener.OnUpdateBlocklist(status);
}
}
public void updateMucRosterUi() {
- for(OnMucRosterUpdate listener : this.mOnMucRosterUpdate) {
+ for (OnMucRosterUpdate listener : threadSafeList(this.mOnMucRosterUpdate)) {
listener.onMucRosterUpdate();
}
}
public void keyStatusUpdated(AxolotlService.FetchStatus report) {
- for(OnKeyStatusUpdated listener : this.mOnKeyStatusUpdated) {
+ for (OnKeyStatusUpdated listener : threadSafeList(this.mOnKeyStatusUpdated)) {
listener.onKeyStatusUpdated(report);
}
}
@@ -3777,6 +3838,8 @@ public class XmppConnectionService extends Service {
final XmppConnection connection = account.getXmppConnection();
if (connection != null) {
connection.sendIqPacket(packet, callback);
+ } else if (callback != null) {
+ callback.onIqPacketReceived(account, new IqPacket(IqPacket.TYPE.TIMEOUT));
}
}
@@ -3908,6 +3971,16 @@ public class XmppConnectionService extends Service {
updateConversationUi();
}
+ public void deleteMessage(final Conversation conversation, Message message) {
+ conversation.deleteMessage(message);
+ Runnable runnable = () -> {
+ databaseBackend.deleteMessageInConversation(message);
+ databaseBackend.updateConversation(conversation);
+
+ };
+ mDatabaseWriterExecutor.execute(runnable);
+ }
+
public void clearConversationHistory(final Conversation conversation) {
final long clearDate;
final String reference;
@@ -4060,11 +4133,11 @@ public class XmppConnectionService extends Service {
}
public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) {
- final boolean legacy = account.getXmppConnection().getFeatures().mamLegacy();
+ final MessageArchiveService.Version version = MessageArchiveService.Version.get(account);
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.addChild("prefs",legacy ? Namespace.MAM_LEGACY : Namespace.MAM);
+ request.addChild("prefs", version.namespace);
sendIqPacket(account, request, (account1, packet) -> {
- Element prefs = packet.findChild("prefs",legacy ? Namespace.MAM_LEGACY : Namespace.MAM);
+ Element prefs = packet.findChild("prefs", version.namespace);
if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) {
callback.onPreferencesFetched(prefs);
} else {
@@ -4116,8 +4189,8 @@ public class XmppConnectionService extends Service {
if (!conversation.getJid().isBareJid()) {
bookmark.setNick(conversation.getJid().getResource());
}
- if (name != null && !name.trim().isEmpty()) {
- bookmark.setBookmarkName(name.trim());
+ if (!TextUtils.isEmpty(name)) {
+ bookmark.setBookmarkName(name);
}
bookmark.setAutojoin(getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin)));
account.getBookmarks().add(bookmark);
diff --git a/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java b/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
index e79be5c4a..a760a563b 100644
--- a/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
@@ -11,7 +11,6 @@ import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.view.ActionMode;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
@@ -38,12 +37,19 @@ import de.pixart.messenger.ui.util.PendingItem;
import de.pixart.messenger.utils.XmppUri;
import rocks.xmpp.addr.Jid;
-public class ChooseContactActivity extends AbstractSearchableListItemActivity {
+public class ChooseContactActivity extends AbstractSearchableListItemActivity implements MultiChoiceModeListener {
public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
+ public static final String EXTRA_GROUP_CHAT_NAME = "extra_group_chat_name";
+ public static final String EXTRA_SELECT_MULTIPLE = "extra_select_multiple";
+ public static final String EXTRA_SHOW_ENTER_JID = "extra_show_enter_jid";
+ public static final String EXTRA_CONVERSATION = "extra_conversation";
+ private static final String EXTRA_FILTERED_CONTACTS = "extra_filtered_contacts";
private List<String> mActivatedAccounts = new ArrayList<>();
private Set<String> selected = new HashSet<>();
private Set<String> filterContacts;
+ private boolean showEnterJid = false;
+
private PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
public static Intent create(Activity activity, Conversation conversation) {
@@ -59,14 +65,31 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
} else {
contacts.add(conversation.getJid().asBareJid().toString());
}
- intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
- intent.putExtra("conversation", conversation.getUuid());
- intent.putExtra("multiple", true);
- intent.putExtra("show_enter_jid", true);
+ intent.putExtra(EXTRA_FILTERED_CONTACTS, contacts.toArray(new String[contacts.size()]));
+ intent.putExtra(EXTRA_CONVERSATION, conversation.getUuid());
+ intent.putExtra(EXTRA_SELECT_MULTIPLE, true);
+ intent.putExtra(EXTRA_SHOW_ENTER_JID, true);
intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
return intent;
}
+ public static List<Jid> extractJabberIds(Intent result) {
+ List<Jid> jabberIds = new ArrayList<>();
+ try {
+ if (result.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false)) {
+ String[] toAdd = result.getStringArrayExtra("contacts");
+ for (String item : toAdd) {
+ jabberIds.add(Jid.of(item));
+ }
+ } else {
+ jabberIds.add(Jid.of(result.getStringExtra("contact")));
+ }
+ return jabberIds;
+ } catch (IllegalArgumentException e) {
+ return jabberIds;
+ }
+ }
+
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -78,81 +101,26 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
selected.addAll(Arrays.asList(selectedContacts));
}
}
- String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
+
+ String[] contacts = getIntent().getStringArrayExtra(EXTRA_FILTERED_CONTACTS);
if (contacts != null) {
Collections.addAll(filterContacts, contacts);
}
- if (getIntent().getBooleanExtra("multiple", false)) {
- getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
- getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- binding.fab.setVisibility(View.GONE);
- final View view = getSearchEditText();
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- if (view != null && imm != null) {
- imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
- }
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.select_multiple, menu);
- MenuItem selectButton = menu.findItem(R.id.selection_submit);
- String buttonText = getResources().getQuantityString(R.plurals.select_contact, selected.size(), selected.size());
- selectButton.setTitle(buttonText);
- return true;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- binding.fab.setVisibility(View.VISIBLE);
- selected.clear();
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- switch (item.getItemId()) {
- case R.id.selection_submit:
- final Intent request = getIntent();
- final Intent data = new Intent();
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- String[] selection = getSelectedContactJids();
- data.putExtra("contacts", selection);
- data.putExtra("multiple", true);
- data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
- data.putExtra("subject", request.getStringExtra("subject"));
- setResult(RESULT_OK, data);
- finish();
- return true;
- }
- return false;
- }
+ Intent intent = getIntent();
- @Override
- public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
- Contact item = (Contact) getListItems().get(position);
- if (checked) {
- selected.add(item.getJid().toString());
- } else {
- selected.remove(item.getJid().toString());
- }
- int numSelected = selected.size();
- MenuItem selectButton = mode.getMenu().findItem(R.id.selection_submit);
- String buttonText = getResources().getQuantityString(R.plurals.select_contact,
- numSelected, numSelected);
- selectButton.setTitle(buttonText);
- }
- });
+ final boolean multiple = intent.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false);
+ if (multiple) {
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ getListView().setMultiChoiceModeListener(this);
}
-
getListView().setOnItemClickListener((parent, view, position, id) -> {
+ if (multiple) {
+ startActionMode(this);
+ getListView().setItemChecked(position, true);
+ return;
+ }
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
final Intent request = getIntent();
@@ -164,42 +132,118 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
account = ((Contact) mListItem).getAccount().getJid().asBareJid().toString();
}
data.putExtra(EXTRA_ACCOUNT, account);
- data.putExtra("conversation", request.getStringExtra("conversation"));
- data.putExtra("multiple", false);
- data.putExtra("subject", request.getStringExtra("subject"));
+ data.putExtra(EXTRA_SELECT_MULTIPLE, false);
+ copy(request, data);
setResult(RESULT_OK, data);
finish();
});
final Intent i = getIntent();
- boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
- if (showEnterJid) {
- this.binding.fab.setOnClickListener((v) -> showEnterJidDialog(null));
+ this.showEnterJid = i != null && i.getBooleanExtra(EXTRA_SHOW_ENTER_JID, false);
+ this.binding.fab.setOnClickListener(this::onFabClicked);
+ if (this.showEnterJid) {
+ this.binding.fab.setVisibility(View.VISIBLE);
+ } else {
+ this.binding.fab.setVisibility(View.GONE);
+ }
+ }
+
+ private void onFabClicked(View v) {
+ if (selected.size() == 0) {
+ showEnterJidDialog(null);
+ } else {
+ submitSelection();
+ }
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mode.setTitle(getTitleFromIntent());
+ binding.fab.setImageResource(R.drawable.ic_forward_white_24dp);
+ binding.fab.setVisibility(View.VISIBLE);
+ final View view = getSearchEditText();
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (view != null && imm != null) {
+ imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
+ }
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ this.binding.fab.setImageResource(R.drawable.ic_person_add_white_24dp);
+ if (this.showEnterJid) {
+ this.binding.fab.setVisibility(View.VISIBLE);
} else {
this.binding.fab.setVisibility(View.GONE);
}
+ selected.clear();
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ private void submitSelection() {
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ data.putExtra("contacts", getSelectedContactJids());
+ data.putExtra(EXTRA_SELECT_MULTIPLE, true);
+ data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
+ copy(request, data);
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ private static void copy(Intent from, Intent to) {
+ to.putExtra(EXTRA_CONVERSATION, from.getStringExtra(EXTRA_CONVERSATION));
+ to.putExtra(EXTRA_GROUP_CHAT_NAME, from.getStringExtra(EXTRA_GROUP_CHAT_NAME));
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
+ if (selected.size() != 0) {
+ getListView().playSoundEffect(0);
+ }
+ Contact item = (Contact) getListItems().get(position);
+ if (checked) {
+ selected.add(item.getJid().toString());
+ } else {
+ selected.remove(item.getJid().toString());
+ }
}
@Override
public void onStart() {
super.onStart();
- Intent intent = getIntent();
- @StringRes
- int res = intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, R.string.title_activity_choose_contact) : R.string.title_activity_choose_contact;
ActionBar bar = getSupportActionBar();
if (bar != null) {
try {
- bar.setTitle(res);
+ bar.setTitle(getTitleFromIntent());
} catch (Exception e) {
bar.setTitle(R.string.title_activity_choose_contact);
}
}
}
+ public @StringRes
+ int getTitleFromIntent() {
+ final Intent intent = getIntent();
+ boolean multiple = intent != null && intent.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false);
+ @StringRes int fallback = multiple ? R.string.title_activity_choose_contacts : R.string.title_activity_choose_contact;
+ return intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, fallback) : fallback;
+ }
+
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
super.onCreateOptionsMenu(menu);
final Intent i = getIntent();
- boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
+ boolean showEnterJid = i != null && i.getBooleanExtra(EXTRA_SHOW_ENTER_JID, false);
menu.findItem(R.id.action_scan_qr_code).setVisible(isCameraFeatureAvailable() && showEnterJid);
return true;
}
@@ -264,7 +308,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
jid == null ? null : jid.asBareJid().toString(),
getIntent().getStringExtra(EXTRA_ACCOUNT),
true,
- xmppConnectionService.multipleAccounts()
+ true
);
dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
@@ -272,10 +316,8 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
final Intent data = new Intent();
data.putExtra("contact", contactJid.toString());
data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- data.putExtra("multiple", false);
- data.putExtra("subject", request.getStringExtra("subject"));
+ data.putExtra(EXTRA_SELECT_MULTIPLE, false);
+ copy(request, data);
setResult(RESULT_OK, data);
finish();
@@ -309,7 +351,6 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
@Override
void onBackendConnected() {
filterContacts();
-
this.mActivatedAccounts.clear();
for (Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
@@ -334,4 +375,4 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
index 8e8178609..db70c8147 100644
--- a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
@@ -53,6 +53,7 @@ import de.pixart.messenger.ui.util.MyLinkify;
import de.pixart.messenger.ui.util.SoftKeyboardUtils;
import de.pixart.messenger.utils.EmojiWrapper;
import de.pixart.messenger.utils.MenuDoubleTabUtil;
+import de.pixart.messenger.utils.StringUtils;
import de.pixart.messenger.utils.StylingHelper;
import de.pixart.messenger.utils.TimeframeUtils;
import de.pixart.messenger.utils.UIHelper;
@@ -60,6 +61,7 @@ import de.pixart.messenger.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import static de.pixart.messenger.entities.Bookmark.printableValue;
+import static de.pixart.messenger.utils.StringUtils.changed;
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher {
public static final String ACTION_VIEW_MUC = "view_muc";
@@ -405,19 +407,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
if (mucOptions.getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER) && changed(mucOptions.getName(), name)) {
Bundle options = new Bundle();
options.putString("muc#roomconfig_persistentroom", "1");
- options.putString("muc#roomconfig_roomname", name);
+ options.putString("muc#roomconfig_roomname", StringUtils.nullOnEmpty(name));
xmppConnectionService.pushConferenceConfiguration(mConversation, options, this);
}
}
- private static String blankOnNull(String input) {
- return input == null ? "" : input;
- }
-
- private static boolean changed(String one, String two) {
- return !blankOnNull(one).equals(blankOnNull(two));
- }
-
@Override
protected String getShareableUri(boolean http) {
if (mConversation != null) {
@@ -595,8 +589,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
protected void saveAsBookmark() {
- xmppConnectionService.saveConversationAsBookmark(mConversation,
- mConversation.getMucOptions().getSubject());
+ xmppConnectionService.saveConversationAsBookmark(mConversation, mConversation.getMucOptions().getName());
updateView();
}
@@ -666,7 +659,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
String subject = mucOptions.getSubject();
final boolean hasTitle;
if (printableValue(roomName)) {
- this.binding.mucTitle.setText(roomName);
+ this.binding.mucTitle.setText(EmojiWrapper.transform(roomName));
this.binding.mucTitle.setVisibility(View.VISIBLE);
hasTitle = true;
} else if (!printableValue(subject)) {
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index fa2ba5327..8ee0448ef 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -82,6 +82,7 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.entities.Blockable;
import de.pixart.messenger.entities.Contact;
import de.pixart.messenger.entities.Conversation;
+import de.pixart.messenger.entities.Conversational;
import de.pixart.messenger.entities.DownloadableFile;
import de.pixart.messenger.entities.Message;
import de.pixart.messenger.entities.MucOptions;
@@ -566,6 +567,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return true;
}
+ private static boolean writeGranted(int[] grantResults, String[] permission) {
+ for (int i = 0; i < grantResults.length; ++i) {
+ if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission[i])) {
+ return grantResults[i] == PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ return false;
+ }
+
private static String getFirstDenied(int[] grantResults, String[] permissions) {
for (int i = 0; i < grantResults.length; ++i) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
@@ -951,7 +961,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), targets).isEmpty();
boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(conversation).isEmpty();
boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets);
- if (hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) {
+ boolean downloadInProgress = axolotlService.hasPendingKeyFetches(targets);
+ if (hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted || downloadInProgress) {
axolotlService.createSessionsIfNeeded(conversation);
Intent intent = new Intent(getActivity(), TrustKeysActivity.class);
String[] contacts = new String[targets.size()];
@@ -1125,7 +1136,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final MenuItem menuNeedHelp = menu.findItem(R.id.action_create_issue);
final MenuItem menuSearchUpdates = menu.findItem(R.id.action_check_updates);
final MenuItem menuArchiveChat = menu.findItem(R.id.action_archive_chat);
- final MenuItem menuEditProfiles = menu.findItem(R.id.action_accounts);
if (conversation != null) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
@@ -1135,7 +1145,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
menuInviteContact.setVisible(false);
menuArchiveChat.setTitle(R.string.action_end_conversation);
}
- menuEditProfiles.setVisible(false);
menuNeedHelp.setVisible(true);
menuSearchUpdates.setVisible(false);
ConversationMenuConfigurator.configureAttachmentMenu(conversation, menu);
@@ -1288,6 +1297,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
MenuItem quoteMessage = menu.findItem(R.id.quote_message);
MenuItem retryDecryption = menu.findItem(R.id.retry_decryption);
MenuItem correctMessage = menu.findItem(R.id.correct_message);
+ MenuItem deleteMessage = menu.findItem(R.id.delete_message);
MenuItem shareWith = menu.findItem(R.id.share_with);
MenuItem sendAgain = menu.findItem(R.id.send_again);
MenuItem copyUrl = menu.findItem(R.id.copy_url);
@@ -1298,6 +1308,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (!m.isFileOrImage() && !encrypted && !m.isGeoUri() && !m.treatAsDownloadable()) {
copyMessage.setVisible(true);
quoteMessage.setVisible(MessageUtils.prepareQuote(m).length() > 0);
+ deleteMessage.setVisible(true);
String body = m.getMergedBody().toString();
if (ShareUtil.containsXmppUri(body)) {
copyLink.setTitle(R.string.copy_jabber_id);
@@ -1386,6 +1397,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case R.id.retry_decryption:
retryDecryption(selectedMessage);
return true;
+ case R.id.delete_message:
+ deleteMessage(selectedMessage);
+ return true;
case R.id.delete_file:
deleteFile(selectedMessage);
return true;
@@ -1618,6 +1632,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
Toast.makeText(getActivity(), res, Toast.LENGTH_SHORT).show();
}
}
+ if (writeGranted(grantResults, permissions)) {
+ if (activity != null && activity.xmppConnectionService != null) {
+ activity.xmppConnectionService.restartFileObserver();
+ }
+ }
}
public void startDownloadable(Message message) {
@@ -1831,6 +1850,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
builder.create().show();
}
+ private void deleteMessage(Message message) {
+ final Conversation conversation = (Conversation) message.getConversation();
+ activity.xmppConnectionService.deleteMessage(conversation, message);
+ activity.onConversationsListItemUpdated();
+ refresh();
+ }
+
private void deleteFile(Message message) {
if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
@@ -2035,7 +2061,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
if (this.conversation != null) {
final String msg = this.binding.textinput.getText().toString();
- if (this.conversation.getStatus() != Conversation.STATUS_ARCHIVED && this.conversation.setNextMessage(msg)) {
+ final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
+ if (this.conversation.getStatus() != Conversation.STATUS_ARCHIVED && participating && this.conversation.setNextMessage(msg)) {
this.activity.xmppConnectionService.updateConversation(this.conversation);
}
updateChatState(this.conversation, msg);
@@ -2059,7 +2086,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
Log.d(Config.LOGTAG, "ConversationFragment.saveMessageDraftStopAudioPlayer()");
final String msg = this.binding.textinput.getText().toString();
- if (previousConversation.setNextMessage(msg)) {
+ final boolean participating = previousConversation.getMode() == Conversational.MODE_SINGLE || previousConversation.getMucOptions().participating();
+ if (participating && previousConversation.setNextMessage(msg)) {
activity.xmppConnectionService.updateConversation(previousConversation);
}
updateChatState(this.conversation, msg);
@@ -2115,7 +2143,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.textSendButton.setContentDescription(activity.getString(R.string.send_message_to_x, conversation.getName()));
this.binding.textinput.setKeyboardListener(null);
this.binding.textinput.setText("");
- this.binding.textinput.append(this.conversation.getNextMessage());
+ final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
+ if (participating) {
+ this.binding.textinput.append(this.conversation.getNextMessage());
+ }
this.binding.textinput.setKeyboardListener(this);
messageListAdapter.updatePreferences();
refresh(false);
@@ -2315,17 +2346,23 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case MEMBERS_ONLY:
showSnackbar(R.string.conference_members_only, R.string.leave, leaveMuc);
break;
+ case RESOURCE_CONSTRAINT:
+ showSnackbar(R.string.conference_resource_constraint, R.string.try_again, joinMuc);
+ break;
case KICKED:
showSnackbar(R.string.conference_kicked, R.string.join, joinMuc);
break;
case UNKNOWN:
- showSnackbar(R.string.conference_unknown_error, R.string.leave, leaveMuc);
+ showSnackbar(R.string.conference_unknown_error, R.string.leave, joinMuc);
break;
case INVALID_NICK:
showSnackbar(R.string.invalid_muc_nick, R.string.edit, clickToMuc);
case SHUTDOWN:
showSnackbar(R.string.conference_shutdown, R.string.try_again, joinMuc);
break;
+ case DESTROYED:
+ showSnackbar(R.string.conference_destroyed, R.string.leave, leaveMuc);
+ break;
default:
hideSnackbar();
break;
@@ -2411,6 +2448,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
updateSendButton();
updateEditablity();
+ activity.invalidateOptionsMenu();
}
}
}
@@ -2422,8 +2460,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.textinput.append(conversation.getDraftMessage());
conversation.setDraftMessage(null);
}
- if (conversation.setNextMessage(this.binding.textinput.getText().toString())) {
- activity.xmppConnectionService.updateConversation(conversation);
+ final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
+ if (participating && conversation.setNextMessage(this.binding.textinput.getText().toString())) {
+ activity.xmppConnectionService.databaseBackend.updateConversation(conversation);
}
updateChatMsgHint();
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(activity);
@@ -2451,6 +2490,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.textinput.setFocusableInTouchMode(canWrite);
this.binding.textSendButton.setEnabled(canWrite);
this.binding.textinput.setCursorVisible(canWrite);
+ this.binding.textinput.setEnabled(canWrite);
}
public void updateSendButton() {
@@ -2508,6 +2548,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final String body;
if (size <= 4) {
body = getString(R.string.contacts_have_read_up_to_this_point, UIHelper.concatNames(shownMarkers));
+ } else if (ReadByMarker.allUsersRepresented(allUsers, markersForMessage, markerForSender)) {
+ body = getString(R.string.everyone_has_read_up_to_this_point);
} else {
body = getString(R.string.contacts_and_n_more_have_read_up_to_this_point, UIHelper.concatNames(shownMarkers, 3), size - 3);
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java
index 3882fd732..67618dc65 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java
@@ -85,6 +85,7 @@ import de.pixart.messenger.ui.interfaces.OnConversationsListItemUpdated;
import de.pixart.messenger.ui.util.ActivityResult;
import de.pixart.messenger.ui.util.ConversationMenuConfigurator;
import de.pixart.messenger.ui.util.PendingItem;
+import de.pixart.messenger.utils.EmojiWrapper;
import de.pixart.messenger.utils.ExceptionHelper;
import de.pixart.messenger.utils.MenuDoubleTabUtil;
import de.pixart.messenger.utils.UIHelper;
@@ -545,7 +546,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
case android.R.id.home:
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
- fm.popBackStack();
+ try {
+ fm.popBackStack();
+ } catch (IllegalArgumentException e) {
+ Log.w(Config.LOGTAG, "Unable to pop back stack after pressing home button");
+ }
return true;
}
break;
@@ -557,7 +562,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (!installFromUnknownSourceAllowed() && !xmppConnectionService.installedFromPlayStore()) {
openInstallFromUnknownSourcesDialogIfNeeded();
} else {
- UpdateService task = new UpdateService(this, xmppConnectionService.installedFromPlayStore());
+ UpdateService task = new UpdateService(this, xmppConnectionService.installedFromPlayStore(), xmppConnectionService);
task.executeOnExecutor(UpdateService.THREAD_POOL_EXECUTOR, "true");
Log.d(Config.LOGTAG, "AppUpdater started");
}
@@ -688,7 +693,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
actionBar.setDisplayShowCustomEnabled(true);
TextView abtitle = findViewById(android.R.id.text1);
TextView absubtitle = findViewById(android.R.id.text2);
- abtitle.setText(conversation.getName());
+ abtitle.setText(EmojiWrapper.transform(conversation.getName()));
abtitle.setOnClickListener(view1 -> {
if (conversation.getMode() == Conversation.MODE_SINGLE) {
switchToContactDetails(conversation.getContact());
@@ -766,7 +771,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (userWithChatStates.size() > 0) {
if (userWithChatStates.size() == 1) {
MucOptions.User user = userWithChatStates.get(0);
- absubtitle.setText(getString(R.string.contact_is_typing, UIHelper.getDisplayName(user)));
+ absubtitle.setText(EmojiWrapper.transform(getString(R.string.contact_is_typing, UIHelper.getDisplayName(user))));
} else {
StringBuilder builder = new StringBuilder();
for (MucOptions.User user : userWithChatStates) {
@@ -775,7 +780,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
builder.append(UIHelper.getDisplayName(user));
}
- absubtitle.setText(getString(R.string.contacts_are_typing, builder.toString()));
+ absubtitle.setText(EmojiWrapper.transform(getString(R.string.contacts_are_typing, builder.toString())));
}
}
} else {
@@ -952,7 +957,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (!installFromUnknownSourceAllowed() && !PlayStore) {
openInstallFromUnknownSourcesDialogIfNeeded();
} else {
- UpdateService task = new UpdateService(this, PlayStore);
+ UpdateService task = new UpdateService(this, PlayStore, xmppConnectionService);
task.executeOnExecutor(UpdateService.THREAD_POOL_EXECUTOR, "false");
Log.d(Config.LOGTAG, "AppUpdater started");
}
diff --git a/src/main/java/de/pixart/messenger/ui/CreateConferenceDialog.java b/src/main/java/de/pixart/messenger/ui/CreateConferenceDialog.java
index da0fe37de..900f4a42d 100644
--- a/src/main/java/de/pixart/messenger/ui/CreateConferenceDialog.java
+++ b/src/main/java/de/pixart/messenger/ui/CreateConferenceDialog.java
@@ -16,6 +16,7 @@ import java.util.List;
import de.pixart.messenger.R;
import de.pixart.messenger.databinding.CreateConferenceDialogBinding;
import de.pixart.messenger.services.XmppConnectionService;
+import de.pixart.messenger.ui.util.DelayedHintHelper;
public class CreateConferenceDialog extends DialogFragment {
@@ -45,10 +46,6 @@ public class CreateConferenceDialog extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.create_conference);
- //final View dialogView = getActivity().getLayoutInflater().inflate(R.layout.create_conference_dialog, null);
- //final TextView yourAccount = dialogView.findViewById(R.id.your_account);
- //final Spinner spinner = dialogView.findViewById(R.id.account);
- //final EditText subject = dialogView.findViewById(R.id.subject);
CreateConferenceDialogBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.create_conference_dialog, null, false);
if (getArguments().getBoolean(MULTIPLE_ACCOUNTS)) {
binding.yourAccount.setVisibility(View.VISIBLE);
@@ -60,8 +57,9 @@ public class CreateConferenceDialog extends DialogFragment {
ArrayList<String> mActivatedAccounts = getArguments().getStringArrayList(ACCOUNTS_LIST_KEY);
StartConversationActivity.populateAccountSpinner(getActivity(), mActivatedAccounts, binding.account);
builder.setView(binding.getRoot());
- builder.setPositiveButton(R.string.choose_participants, (dialog, which) -> mListener.onCreateDialogPositiveClick(binding.account, binding.subject.getText().toString()));
+ builder.setPositiveButton(R.string.choose_participants, (dialog, which) -> mListener.onCreateDialogPositiveClick(binding.account, binding.groupChatName.getText().toString().trim()));
builder.setNegativeButton(R.string.cancel, null);
+ DelayedHintHelper.setHint(R.string.providing_a_name_is_optional, binding.groupChatName);
return builder.create();
}
diff --git a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
index 9333b13b4..e33de0eb4 100644
--- a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
@@ -431,7 +431,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
protected void updateInfoButtons() {
- if (this.binding.accountRegisterNew.isChecked() && this.binding.accountJid.getText().length() > 0) {
+ if (this.binding.accountRegisterNew.isChecked() && this.binding.accountJid.getText().length() > 0 && !this.binding.accountJid.getText().toString().contains("@")) {
if (!mUsernameMode && Jid.of(this.binding.accountJid.getText()).getDomain().toLowerCase().equals("pix-art.de")) {
this.binding.showPrivacyPolicy.setVisibility(View.VISIBLE);
this.binding.showTermsOfUse.setVisibility(View.VISIBLE);
diff --git a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
index ddb5a74f8..a58c2e0c0 100644
--- a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
@@ -660,26 +660,10 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
this.mPostponedActivityResult = null;
if (requestCode == REQUEST_CREATE_CONFERENCE) {
Account account = extractAccount(intent);
- final String subject = intent.getStringExtra("subject");
- List<Jid> jids = new ArrayList<>();
- if (intent.getBooleanExtra("multiple", false)) {
- String[] toAdd = intent.getStringArrayExtra("contacts");
- for (String item : toAdd) {
- try {
- jids.add(Jid.of(item));
- } catch (IllegalArgumentException e) {
- //ignored
- }
- }
- } else {
- try {
- jids.add(Jid.of(intent.getStringExtra("contact")));
- } catch (Exception e) {
- //ignored
- }
- }
+ final String name = intent.getStringExtra(ChooseContactActivity.EXTRA_GROUP_CHAT_NAME);
+ final List<Jid> jids = ChooseContactActivity.extractJabberIds(intent);
if (account != null && jids.size() > 0) {
- if (xmppConnectionService.createAdhocConference(account, subject, jids, mAdhocConferenceCallback)) {
+ if (xmppConnectionService.createAdhocConference(account, name, jids, mAdhocConferenceCallback)) {
mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
mToast.show();
}
@@ -959,7 +943,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
}
@Override
- public void onCreateDialogPositiveClick(Spinner spinner, String subject) {
+ public void onCreateDialogPositiveClick(Spinner spinner, String name) {
if (!xmppConnectionServiceBound) {
return;
}
@@ -968,10 +952,10 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
return;
}
Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
- intent.putExtra("multiple", true);
- intent.putExtra("show_enter_jid", true);
- intent.putExtra("subject", subject);
- intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
+ intent.putExtra(ChooseContactActivity.EXTRA_SHOW_ENTER_JID, false);
+ intent.putExtra(ChooseContactActivity.EXTRA_SELECT_MULTIPLE, true);
+ intent.putExtra(ChooseContactActivity.EXTRA_GROUP_CHAT_NAME, name.trim());
+ intent.putExtra(ChooseContactActivity.EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
intent.putExtra(ChooseContactActivity.EXTRA_TITLE_RES_ID, R.string.choose_participants);
startActivityForResult(intent, REQUEST_CREATE_CONFERENCE);
overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
@@ -1054,8 +1038,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
}
@Override
- public void onCreateContextMenu(final ContextMenu menu, final View v,
- final ContextMenuInfo menuInfo) {
+ public void onCreateContextMenu(final ContextMenu menu, final View v, final ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
final StartConversationActivity activity = (StartConversationActivity) getActivity();
if (activity == null) {
diff --git a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
index 06fab28c4..e8e17e287 100644
--- a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
@@ -331,9 +331,13 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
processFingerprintVerification(this.mPendingFingerprintVerificationUri);
this.mPendingFingerprintVerificationUri = null;
} else {
- reloadFingerprints();
- populateView();
- invalidateOptionsMenu();
+ final boolean keysToTrust = reloadFingerprints();
+ if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
+ populateView();
+ invalidateOptionsMenu();
+ } else {
+ finishOk(false);
+ }
}
}
}
@@ -347,7 +351,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
}
private boolean hasPendingKeyFetches() {
- return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount, contactJids);
+ return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(contactJids);
}
diff --git a/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java b/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java
index f98872b7c..4832dc466 100644
--- a/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java
@@ -33,24 +33,29 @@ import java.util.List;
import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.persistance.FileBackend;
+import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.utils.WakeLockHelper;
+import static de.pixart.messenger.http.HttpConnectionManager.getProxy;
+
public class UpdaterActivity extends XmppActivity {
static final private String FileName = "update.apk";
-
String appURI = "";
String changelog = "";
Integer filesize = 0;
boolean playstore = false;
ProgressDialog mProgressDialog;
DownloadTask downloadTask;
+ XmppConnectionService mXmppConnectionService;
+ private boolean mUseTor;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//set activity
setContentView(R.layout.activity_updater);
-
+ this.mUseTor = mXmppConnectionService.useTorToConnect();
this.mTheme = findTheme();
setTheme(this.mTheme);
@@ -65,7 +70,7 @@ public class UpdaterActivity extends XmppActivity {
}
};
mProgressDialog.setMessage(getString(R.string.download_started));
- mProgressDialog.setProgressNumberFormat (null);
+ mProgressDialog.setProgressNumberFormat(null);
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
@@ -140,7 +145,8 @@ public class UpdaterActivity extends XmppActivity {
}
} else {
Toast.makeText(getApplicationContext(), getText(R.string.download_started), Toast.LENGTH_LONG).show();
- downloadTask = new DownloadTask(UpdaterActivity.this) {};
+ downloadTask = new DownloadTask(UpdaterActivity.this) {
+ };
downloadTask.execute(appURI);
}
} else {
@@ -188,14 +194,90 @@ public class UpdaterActivity extends XmppActivity {
super.onRestoreInstanceState(savedInstanceState);
}
+ //check for internet connection
+ private boolean isNetworkAvailable(Context context) {
+ ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (connectivity != null) {
+ NetworkInfo[] info = connectivity.getAllNetworkInfo();
+ if (info != null) {
+ for (int i = 0; i < info.length; i++) {
+ if (info[i].getState() == NetworkInfo.State.CONNECTED) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isStoragePermissionGranted() {
+ if (Build.VERSION.SDK_INT >= 23) {
+ if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ } else {
+
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
+ return false;
+ }
+ } else { //permission is automatically granted on sdk<23 upon installation
+ return true;
+ }
+ }
+
+ //show warning on back pressed
+ @Override
+ public void onBackPressed() {
+ showCancelDialog();
+ }
+
+ private void showCancelDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage(R.string.cancel_update)
+ .setCancelable(false)
+ .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ if (downloadTask != null && !downloadTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
+ downloadTask.cancel(true);
+ }
+ if (mProgressDialog.isShowing()) {
+ mProgressDialog.dismiss();
+ }
+ UpdaterActivity.this.finish();
+ }
+ })
+ .setNegativeButton(R.string.no, (dialog, id) -> dialog.cancel());
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (downloadTask != null && !downloadTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
+ downloadTask.cancel(true);
+ }
+ UpdaterActivity.this.finish();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (downloadTask != null && !downloadTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
+ downloadTask.cancel(true);
+ }
+ UpdaterActivity.this.finish();
+ }
+
private class DownloadTask extends AsyncTask<String, Integer, String> {
+ File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + FileBackend.getDirectoryName("Update", false));
+ File file = new File(dir, FileName);
private Context context;
-
private PowerManager.WakeLock mWakeLock;
private long startTime = 0;
- File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + FileBackend.getDirectoryName("Update", false));
- File file = new File(dir, FileName);
+
public DownloadTask(Context context) {
this.context = context;
}
@@ -233,7 +315,12 @@ public class UpdaterActivity extends XmppActivity {
Log.d(Config.LOGTAG, "AppUpdater: download update from url: " + sUrl[0] + " to file name: " + file.toString());
URL url = new URL(sUrl[0]);
- connection = (HttpURLConnection) url.openConnection();
+
+ if (mUseTor) {
+ connection = (HttpURLConnection) url.openConnection(getProxy());
+ } else {
+ connection = (HttpURLConnection) url.openConnection();
+ }
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
@@ -309,79 +396,4 @@ public class UpdaterActivity extends XmppActivity {
}
}
- //check for internet connection
- private boolean isNetworkAvailable(Context context) {
- ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (connectivity != null) {
- NetworkInfo[] info = connectivity.getAllNetworkInfo();
- if (info != null) {
- for (int i = 0; i < info.length; i++) {
- if (info[i].getState() == NetworkInfo.State.CONNECTED) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- public boolean isStoragePermissionGranted() {
- if (Build.VERSION.SDK_INT >= 23) {
- if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- } else {
-
- ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
- return false;
- }
- } else { //permission is automatically granted on sdk<23 upon installation
- return true;
- }
- }
-
- //show warning on back pressed
- @Override
- public void onBackPressed() {
- showCancelDialog();
- }
-
- private void showCancelDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setMessage(R.string.cancel_update)
- .setCancelable(false)
- .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- if (!downloadTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
- downloadTask.cancel(true);
- }
- if (mProgressDialog.isShowing()) {
- mProgressDialog.dismiss();
- }
- UpdaterActivity.this.finish();
- }
- })
- .setNegativeButton(R.string.no, (dialog, id) -> dialog.cancel());
- AlertDialog alert = builder.create();
- alert.show();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- if (downloadTask != null && !downloadTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
- downloadTask.cancel(true);
- }
- UpdaterActivity.this.finish();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (downloadTask != null && !downloadTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
- downloadTask.cancel(true);
- }
- UpdaterActivity.this.finish();
- }
}
diff --git a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
index 7906c7f40..3c03542ee 100644
--- a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
@@ -245,7 +245,9 @@ public class WelcomeActivity extends XmppActivity {
if (checkDB != null) {
checkDB.close();
}
- Log.d(Config.LOGTAG, "checkDB = " + checkDB.toString() + ", Backup DB = " + Backup_DB_Version + ", DB = " + DB_Version);
+ if (checkDB != null) {
+ Log.d(Config.LOGTAG, "checkDB = " + checkDB.toString() + ", Backup DB = " + Backup_DB_Version + ", DB = " + DB_Version);
+ }
if (checkDB != null && Backup_DB_Version != 0 && Backup_DB_Version <= DB_Version) {
try {
ImportDatabase();
diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
index cd78879ac..f52b2d72c 100644
--- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
@@ -61,7 +61,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import de.pixart.messenger.Config;
@@ -99,7 +98,6 @@ public abstract class XmppActivity extends ActionBarActivity {
public XmppConnectionService xmppConnectionService;
public boolean xmppConnectionServiceBound = false;
- protected final AtomicBoolean registeredListeners = new AtomicBoolean(false);
protected int mColorRed;
protected int mColorWarningButton;
@@ -122,9 +120,7 @@ public abstract class XmppActivity extends ActionBarActivity {
XmppConnectionBinder binder = (XmppConnectionBinder) service;
xmppConnectionService = binder.getService();
xmppConnectionServiceBound = true;
- if (registeredListeners.compareAndSet(false, true)) {
- registerListeners();
- }
+ registerListeners();
invalidateOptionsMenu();
onBackendConnected();
}
@@ -228,9 +224,7 @@ public abstract class XmppActivity extends ActionBarActivity {
connectToBackend();
}
} else {
- if (registeredListeners.compareAndSet(false, true)) {
- this.registerListeners();
- }
+ this.registerListeners();
this.onBackendConnected();
}
}
@@ -246,9 +240,7 @@ public abstract class XmppActivity extends ActionBarActivity {
protected void onStop() {
super.onStop();
if (xmppConnectionServiceBound) {
- if (registeredListeners.compareAndSet(true, false)) {
- this.unregisterListeners();
- }
+ this.unregisterListeners();
unbindService(mConnection);
xmppConnectionServiceBound = false;
}
@@ -1131,22 +1123,11 @@ public abstract class XmppActivity extends ActionBarActivity {
public static ConferenceInvite parse(Intent data) {
ConferenceInvite invite = new ConferenceInvite();
- invite.uuid = data.getStringExtra("conversation");
+ invite.uuid = data.getStringExtra(ChooseContactActivity.EXTRA_CONVERSATION);
if (invite.uuid == null) {
return null;
}
- try {
- if (data.getBooleanExtra("multiple", false)) {
- String[] toAdd = data.getStringArrayExtra("contacts");
- for (String item : toAdd) {
- invite.jids.add(Jid.of(item));
- }
- } else {
- invite.jids.add(Jid.of(data.getStringExtra("contact")));
- }
- } catch (final IllegalArgumentException ignored) {
- return null;
- }
+ invite.jids.addAll(ChooseContactActivity.extractJabberIds(data));
return invite;
}
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
index 11c129522..5294ba7cd 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
@@ -96,7 +96,11 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte
viewHolder.name.setText(EmojiWrapper.transform(name));
}
- viewHolder.frame.setBackgroundColor(Color.get(activity, conversation == ConversationFragment.getConversation(activity) ? R.attr.color_background_secondary : R.attr.color_background_primary));
+ if (conversation == ConversationFragment.getConversation(activity)) {
+ viewHolder.frame.setBackgroundColor(Color.get(activity, R.attr.color_background_tertiary));
+ } else {
+ viewHolder.frame.setBackgroundColor(Color.get(activity,R.attr.color_background_primary));
+ }
Message message = conversation.getLatestMessage();
final int failedCount = conversation.failedCount();
diff --git a/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
index eb7217b95..03f7638e3 100644
--- a/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
+++ b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
@@ -40,6 +40,7 @@ import de.pixart.messenger.R;
import de.pixart.messenger.crypto.OmemoSetting;
import de.pixart.messenger.crypto.axolotl.AxolotlService;
import de.pixart.messenger.entities.Conversation;
+import de.pixart.messenger.entities.Conversational;
import de.pixart.messenger.entities.Message;
public class ConversationMenuConfigurator {
@@ -71,6 +72,11 @@ public class ConversationMenuConfigurator {
public static void configureEncryptionMenu(@NonNull Conversation conversation, Menu menu) {
final MenuItem menuSecure = menu.findItem(R.id.action_security);
+ final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
+ if (!participating) {
+ menuSecure.setVisible(false);
+ return;
+ }
final MenuItem none = menu.findItem(R.id.encryption_choice_none);
final MenuItem otr = menu.findItem(R.id.encryption_choice_otr);
final MenuItem pgp = menu.findItem(R.id.encryption_choice_pgp);
diff --git a/src/main/java/de/pixart/messenger/ui/util/EditMessageActionModeCallback.java b/src/main/java/de/pixart/messenger/ui/util/EditMessageActionModeCallback.java
index d7e2ca347..cb50e4164 100644
--- a/src/main/java/de/pixart/messenger/ui/util/EditMessageActionModeCallback.java
+++ b/src/main/java/de/pixart/messenger/ui/util/EditMessageActionModeCallback.java
@@ -55,7 +55,10 @@ public class EditMessageActionModeCallback implements ActionMode.Callback {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.edit_message_actions, menu);
MenuItem pasteAsQuote = menu.findItem(R.id.paste_as_quote);
- pasteAsQuote.setVisible(clipboardManager.hasPrimaryClip() && clipboardManager.getPrimaryClipDescription().hasMimeType("text/plain"));
+ ClipData primaryClip = clipboardManager.getPrimaryClip();
+ if (primaryClip != null && primaryClip.getItemCount() >= 0) {
+ pasteAsQuote.setVisible(primaryClip.getDescription().getMimeType(0).startsWith("text/") && primaryClip.getItemAt(0).getText() != null);
+ }
return true;
}
@@ -67,8 +70,8 @@ public class EditMessageActionModeCallback implements ActionMode.Callback {
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.paste_as_quote) {
- ClipData primaryClip = clipboardManager.getPrimaryClip();
- if (primaryClip.getItemCount() >= 1) {
+ final ClipData primaryClip = clipboardManager.getPrimaryClip();
+ if (primaryClip != null && primaryClip.getItemCount() >= 1) {
editMessage.insertAsQuote(primaryClip.getItemAt(0).getText().toString());
return true;
}
diff --git a/src/main/java/de/pixart/messenger/ui/widget/EditMessage.java b/src/main/java/de/pixart/messenger/ui/widget/EditMessage.java
index 8ab582189..d95a4705f 100644
--- a/src/main/java/de/pixart/messenger/ui/widget/EditMessage.java
+++ b/src/main/java/de/pixart/messenger/ui/widget/EditMessage.java
@@ -6,7 +6,6 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
-import android.support.text.emoji.widget.EmojiAppCompatEditText;
import android.support.v13.view.inputmethod.EditorInfoCompat;
import android.support.v13.view.inputmethod.InputConnectionCompat;
import android.support.v13.view.inputmethod.InputContentInfoCompat;
@@ -22,7 +21,7 @@ import android.view.inputmethod.InputConnection;
import de.pixart.messenger.Config;
import de.pixart.messenger.R;
-public class EditMessage extends EmojiAppCompatEditText {
+public class EditMessage extends EmojiWrapperEditText {
private static final InputFilter SPAN_FILTER = (source, start, end, dest, dstart, dend) -> source instanceof Spanned ? source.toString() : source;
protected Handler mTypingHandler = new Handler();
@@ -148,7 +147,7 @@ public class EditMessage extends EmojiAppCompatEditText {
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
final InputConnection ic = super.onCreateInputConnection(editorInfo);
- if (mimeTypes != null && mCommitContentListener != null) {
+ if (mimeTypes != null && mCommitContentListener != null && ic != null) {
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
return InputConnectionCompat.createWrapper(ic, editorInfo, (inputContentInfo, flags, opts) -> EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes));
} else {
diff --git a/src/main/java/de/pixart/messenger/ui/widget/EmojiWrapperEditText.java b/src/main/java/de/pixart/messenger/ui/widget/EmojiWrapperEditText.java
new file mode 100644
index 000000000..38ce53299
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/widget/EmojiWrapperEditText.java
@@ -0,0 +1,17 @@
+package de.pixart.messenger.ui.widget;
+
+import android.content.Context;
+import android.support.text.emoji.widget.EmojiAppCompatEditText;
+import android.util.AttributeSet;
+
+public class EmojiWrapperEditText extends EmojiAppCompatEditText {
+
+ public EmojiWrapperEditText(Context context) {
+ super(context);
+ }
+
+ public EmojiWrapperEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
index 72a01a13c..4e275df3f 100644
--- a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
+++ b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
@@ -80,6 +80,11 @@ public abstract class ConversationsFileObserver {
abstract public void onEvent(int event, String path);
+ public void restartWatching() {
+ stopWatching();
+ startWatching();
+ }
+
private class SingleFileObserver extends FileObserver {
private final String path;
diff --git a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
index 8e70e7bb7..d34823d86 100644
--- a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
@@ -34,6 +34,10 @@ import de.pixart.messenger.http.AesGcmURLStreamHandler;
import rocks.xmpp.addr.Jid;
public final class CryptoHelper {
+
+ private static final char[] VOWELS = "aeiou".toCharArray();
+ private static final char[] CONSONANTS = "bcfghjklmnpqrstvwxyz".toCharArray();
+
public static final String FILETRANSFER = "?FILETRANSFERv1:";
private final static char[] hexArray = "0123456789abcdef".toCharArray();
@@ -50,6 +54,16 @@ public final class CryptoHelper {
return new String(hexChars);
}
+ public static String pronounceable(SecureRandom random) {
+ char[] output = new char[random.nextInt(4) * 2 + 5];
+ boolean vowel = random.nextBoolean();
+ for (int i = 0; i < output.length; ++i) {
+ output[i] = vowel ? VOWELS[random.nextInt(VOWELS.length)] : CONSONANTS[random.nextInt(CONSONANTS.length)];
+ vowel = !vowel;
+ }
+ return String.valueOf(output);
+ }
+
public static byte[] hexToBytes(String hexString) {
int len = hexString.length();
byte[] array = new byte[len / 2];
diff --git a/src/main/java/de/pixart/messenger/utils/Namespace.java b/src/main/java/de/pixart/messenger/utils/Namespace.java
index c3ff29697..d76e4fede 100644
--- a/src/main/java/de/pixart/messenger/utils/Namespace.java
+++ b/src/main/java/de/pixart/messenger/utils/Namespace.java
@@ -8,8 +8,6 @@ public final class Namespace {
public static final String HTTP_UPLOAD = "urn:xmpp:http:upload:0";
public static final String HTTP_UPLOAD_LEGACY = "urn:xmpp:http:upload";
public static final String STANZA_IDS = "urn:xmpp:sid:0";
- public static final String MAM = "urn:xmpp:mam:2";
- public static final String MAM_LEGACY = "urn:xmpp:mam:0";
public static final String IDLE = "urn:xmpp:idle:1";
public static final String DATA = "jabber:x:data";
public static final String OOB = "jabber:x:oob";
diff --git a/src/main/java/de/pixart/messenger/utils/StringUtils.java b/src/main/java/de/pixart/messenger/utils/StringUtils.java
new file mode 100644
index 000000000..8d0988a0d
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/utils/StringUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package de.pixart.messenger.utils;
+
+public class StringUtils {
+
+ private static String blankOnNull(String input) {
+ return input == null ? "" : input;
+ }
+
+ public static boolean equals(String one, String two) {
+ return blankOnNull(one).equals(blankOnNull(two));
+ }
+
+ public static boolean changed(String one, String two) {
+ return !equals(one, two);
+ }
+
+ public static String nullOnEmpty(String input) {
+ return input == null || input.trim().isEmpty() ? null : input;
+ }
+
+}
diff --git a/src/main/java/de/pixart/messenger/utils/XmppUri.java b/src/main/java/de/pixart/messenger/utils/XmppUri.java
index 23ec4efa4..db0002a29 100644
--- a/src/main/java/de/pixart/messenger/utils/XmppUri.java
+++ b/src/main/java/de/pixart/messenger/utils/XmppUri.java
@@ -57,7 +57,10 @@ public class XmppUri {
return safeSource;
}
- protected void parse(Uri uri) {
+ protected void parse(final Uri uri) {
+ if (uri == null) {
+ return;
+ }
this.uri = uri;
String scheme = uri.getScheme();
String host = uri.getHost();
diff --git a/src/main/java/de/pixart/messenger/xml/Element.java b/src/main/java/de/pixart/messenger/xml/Element.java
index f90efa555..d7cace655 100644
--- a/src/main/java/de/pixart/messenger/xml/Element.java
+++ b/src/main/java/de/pixart/messenger/xml/Element.java
@@ -73,7 +73,7 @@ public class Element {
return findInternationalizedChildContent(name, Locale.getDefault().getLanguage());
}
- public String findInternationalizedChildContent(String name, @NonNull String language) {
+ private String findInternationalizedChildContent(String name, @NonNull String language) {
HashMap<String, String> contents = new HashMap<>();
for (Element child : this.children) {
if (name.equals(child.getName())) {
@@ -88,8 +88,11 @@ public class Element {
}
}
}
-
- return contents.get(null);
+ String value = contents.get(null);
+ if (value != null) {
+ return value;
+ }
+ return contents.size() > 0 ? contents.values().iterator().next() : null;
}
public Element findChild(String name, String xmlns) {
@@ -134,6 +137,11 @@ public class Element {
return this;
}
+ public Element removeAttribute(String name) {
+ this.attributes.remove(name);
+ return this;
+ }
+
public Element setAttributes(Hashtable<String, String> attributes) {
this.attributes = attributes;
return this;
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java b/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
index 7ff5658a8..9c123af7c 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnMessageAcknowledged {
- public void onMessageAcknowledged(Account account, String id);
+ boolean onMessageAcknowledged(Account account, String id);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
index a279308e0..a6fe3756a 100644
--- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
@@ -31,6 +31,7 @@ import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
@@ -71,6 +72,7 @@ import de.pixart.messenger.entities.ServiceDiscoveryResult;
import de.pixart.messenger.generator.IqGenerator;
import de.pixart.messenger.persistance.FileBackend;
import de.pixart.messenger.services.MemorizingTrustManager;
+import de.pixart.messenger.services.MessageArchiveService;
import de.pixart.messenger.services.NotificationService;
import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.ui.EditAccountActivity;
@@ -211,8 +213,7 @@ public class XmppConnection implements Runnable {
protected void changeStatus(final Account.State nextStatus) {
synchronized (this) {
- this.mThread = Thread.currentThread();
- if (this.mThread.isInterrupted()) {
+ if (Thread.currentThread().isInterrupted()) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": not changing status to " + nextStatus + " because thread was interrupted");
return;
}
@@ -503,7 +504,8 @@ public class XmppConnection implements Runnable {
@Override
public void run() {
synchronized (this) {
- if (Thread.currentThread().isInterrupted()) {
+ this.mThread = Thread.currentThread();
+ if (this.mThread.isInterrupted()) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": aborting connect because thread was interrupted");
return;
}
@@ -601,6 +603,7 @@ public class XmppConnection implements Runnable {
final String h = resumed.getAttribute("h");
try {
ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>();
+ final boolean acknowledgedMessages;
synchronized (this.mStanzaQueue) {
final int serverCount = Integer.parseInt(h);
if (serverCount < stanzasSent) {
@@ -610,12 +613,15 @@ public class XmppConnection implements Runnable {
} else {
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": session resumed");
}
- acknowledgeStanzaUpTo(serverCount);
+ acknowledgedMessages = acknowledgeStanzaUpTo(serverCount);
for (int i = 0; i < this.mStanzaQueue.size(); ++i) {
failedStanzas.add(mStanzaQueue.valueAt(i));
}
mStanzaQueue.clear();
}
+ if (acknowledgedMessages) {
+ mXmppConnectionService.updateConversationUi();
+ }
Log.d(Config.LOGTAG, "resending " + failedStanzas.size() + " stanzas");
for (AbstractAcknowledgeableStanza packet : failedStanzas) {
if (packet instanceof MessagePacket) {
@@ -656,9 +662,13 @@ public class XmppConnection implements Runnable {
final Element ack = tagReader.readElement(nextTag);
lastPacketReceived = SystemClock.elapsedRealtime();
try {
+ final boolean acknowledgedMessages;
synchronized (this.mStanzaQueue) {
final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
- acknowledgeStanzaUpTo(serverSequence);
+ acknowledgedMessages = acknowledgeStanzaUpTo(serverSequence);
+ }
+ if (acknowledgedMessages) {
+ mXmppConnectionService.updateConversationUi();
}
} catch (NumberFormatException | NullPointerException e) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server send ack without sequence number");
@@ -668,8 +678,12 @@ public class XmppConnection implements Runnable {
try {
final int serverCount = Integer.parseInt(failed.getAttribute("h"));
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resumption failed but server acknowledged stanza #" + serverCount);
+ final boolean acknowledgedMessages;
synchronized (this.mStanzaQueue) {
- acknowledgeStanzaUpTo(serverCount);
+ acknowledgedMessages = acknowledgeStanzaUpTo(serverCount);
+ }
+ if (acknowledgedMessages) {
+ mXmppConnectionService.updateConversationUi();
}
} catch (NumberFormatException | NullPointerException e) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resumption failed");
@@ -690,10 +704,11 @@ public class XmppConnection implements Runnable {
}
}
- private void acknowledgeStanzaUpTo(int serverCount) {
+ private boolean acknowledgeStanzaUpTo(int serverCount) {
if (serverCount > stanzasSent) {
Log.e(Config.LOGTAG, "server acknowledged more stanzas than we sent. serverCount=" + serverCount + ", ourCount=" + stanzasSent);
}
+ boolean acknowledgedMessages = false;
for (int i = 0; i < mStanzaQueue.size(); ++i) {
if (serverCount >= mStanzaQueue.keyAt(i)) {
if (Config.EXTENDED_SM_LOGGING) {
@@ -702,12 +717,13 @@ public class XmppConnection implements Runnable {
AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
if (stanza instanceof MessagePacket && acknowledgedListener != null) {
MessagePacket packet = (MessagePacket) stanza;
- acknowledgedListener.onMessageAcknowledged(account, packet.getId());
+ acknowledgedMessages |= acknowledgedListener.onMessageAcknowledged(account, packet.getId());
}
mStanzaQueue.removeAt(i);
i--;
}
}
+ return acknowledgedMessages;
}
private @NonNull
@@ -1574,6 +1590,7 @@ public class XmppConnection implements Runnable {
for (final Entry<Jid, ServiceDiscoveryResult> cursor : disco.entrySet()) {
final ServiceDiscoveryResult value = cursor.getValue();
if (value.getFeatures().contains("http://jabber.org/protocol/muc")
+ && value.hasIdentity("conference", "text")
&& !value.getFeatures().contains("jabber:iq:gateway")
&& !value.hasIdentity("conference", "irc")) {
servers.add(cursor.getKey().toString());
@@ -1820,13 +1837,12 @@ public class XmppConnection implements Runnable {
}
public boolean mam() {
- return hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM)
- || hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM_LEGACY);
+ return MessageArchiveService.Version.has(getAccountFeatures());
}
- public boolean mamLegacy() {
- return !hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM)
- && hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM_LEGACY);
+ public List<String> getAccountFeatures() {
+ ServiceDiscoveryResult result = connection.disco.get(account.getJid().asBareJid());
+ return result == null ? Collections.emptyList() : result.getFeatures();
}
public boolean push() {
diff --git a/src/main/res/drawable-hdpi/ic_forward_white_24dp.png b/src/main/res/drawable-hdpi/ic_forward_white_24dp.png
new file mode 100644
index 000000000..b40c6b780
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_forward_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_forward_white_24dp.png b/src/main/res/drawable-mdpi/ic_forward_white_24dp.png
new file mode 100644
index 000000000..ec881edda
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_forward_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_forward_white_24dp.png b/src/main/res/drawable-xhdpi/ic_forward_white_24dp.png
new file mode 100644
index 000000000..551baf53b
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_forward_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_forward_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_forward_white_24dp.png
new file mode 100644
index 000000000..6eadc2bbf
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_forward_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_forward_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_forward_white_24dp.png
new file mode 100644
index 000000000..fae07bf4c
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_forward_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable/list_item_background_dark.xml b/src/main/res/drawable/list_item_background_dark.xml
new file mode 100644
index 000000000..eca985306
--- /dev/null
+++ b/src/main/res/drawable/list_item_background_dark.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ ~
+ ~ Redistribution and use in source and binary forms, with or without modification,
+ ~ are permitted provided that the following conditions are met:
+ ~
+ ~ 1. Redistributions of source code must retain the above copyright notice, this
+ ~ list of conditions and the following disclaimer.
+ ~
+ ~ 2. Redistributions in binary form must reproduce the above copyright notice,
+ ~ this list of conditions and the following disclaimer in the documentation and/or
+ ~ other materials provided with the distribution.
+ ~
+ ~ 3. Neither the name of the copyright holder nor the names of its contributors
+ ~ may be used to endorse or promote products derived from this software without
+ ~ specific prior written permission.
+ ~
+ ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ~ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ ~ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ ~ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ ~ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ ~ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ~ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ ~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/grey700" android:state_activated="true" />
+</selector> \ No newline at end of file
diff --git a/src/main/res/drawable/list_item_background_light.xml b/src/main/res/drawable/list_item_background_light.xml
new file mode 100644
index 000000000..f064403da
--- /dev/null
+++ b/src/main/res/drawable/list_item_background_light.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ ~
+ ~ Redistribution and use in source and binary forms, with or without modification,
+ ~ are permitted provided that the following conditions are met:
+ ~
+ ~ 1. Redistributions of source code must retain the above copyright notice, this
+ ~ list of conditions and the following disclaimer.
+ ~
+ ~ 2. Redistributions in binary form must reproduce the above copyright notice,
+ ~ this list of conditions and the following disclaimer in the documentation and/or
+ ~ other materials provided with the distribution.
+ ~
+ ~ 3. Neither the name of the copyright holder nor the names of its contributors
+ ~ may be used to endorse or promote products derived from this software without
+ ~ specific prior written permission.
+ ~
+ ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ~ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ ~ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ ~ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ ~ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ ~ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ~ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ ~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/grey300" android:state_activated="true" />
+</selector> \ No newline at end of file
diff --git a/src/main/res/drawable/message_border.xml b/src/main/res/drawable/message_border.xml
deleted file mode 100644
index cee834e98..000000000
--- a/src/main/res/drawable/message_border.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners android:radius="5dp" />
-
- <padding
- android:bottom="1.5dp"
- android:left="1.5dp"
- android:right="1.5dp"
- android:top="1.5dp" />
-
- <solid android:color="@color/grey800" />
-
-</shape> \ No newline at end of file
diff --git a/src/main/res/drawable/message_border_dark.xml b/src/main/res/drawable/message_border_dark.xml
deleted file mode 100644
index 83eef8eb8..000000000
--- a/src/main/res/drawable/message_border_dark.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners android:radius="5dp" />
-
- <padding
- android:bottom="1.5dp"
- android:left="1.5dp"
- android:right="1.5dp"
- android:top="1.5dp" />
-
- <solid android:color="@color/grey500" />
-
-</shape> \ No newline at end of file
diff --git a/src/main/res/drawable/message_bubble_received_light.xml b/src/main/res/drawable/message_bubble_received_light.xml
index 6e8e95e1d..32e146460 100644
--- a/src/main/res/drawable/message_bubble_received_light.xml
+++ b/src/main/res/drawable/message_bubble_received_light.xml
@@ -13,7 +13,7 @@
android:top="2dp" />
<stroke
android:width="1dp"
- android:color="@color/grey800">
+ android:color="@color/grey500">
</stroke>
<solid
android:color="@color/lightwhite">
diff --git a/src/main/res/drawable/message_bubble_received_light_dark.xml b/src/main/res/drawable/message_bubble_received_light_dark.xml
index edd1e62ce..8f9132566 100644
--- a/src/main/res/drawable/message_bubble_received_light_dark.xml
+++ b/src/main/res/drawable/message_bubble_received_light_dark.xml
@@ -13,7 +13,7 @@
android:top="2dp" />
<stroke
android:width="1dp"
- android:color="@color/grey500">
+ android:color="@color/grey700">
</stroke>
<solid
android:color="@color/darkwhite">
diff --git a/src/main/res/drawable/message_bubble_received_warning.xml b/src/main/res/drawable/message_bubble_received_warning.xml
index c3d553a7d..b3bf6d451 100644
--- a/src/main/res/drawable/message_bubble_received_warning.xml
+++ b/src/main/res/drawable/message_bubble_received_warning.xml
@@ -13,7 +13,7 @@
android:top="4dp" />
<stroke
android:width="1dp"
- android:color="@color/grey800">
+ android:color="@color/grey500">
</stroke>
<solid
android:color="@color/lightred">
diff --git a/src/main/res/drawable/message_bubble_received_warning_dark.xml b/src/main/res/drawable/message_bubble_received_warning_dark.xml
index 5d8a270ab..5867f67ce 100644
--- a/src/main/res/drawable/message_bubble_received_warning_dark.xml
+++ b/src/main/res/drawable/message_bubble_received_warning_dark.xml
@@ -13,7 +13,7 @@
android:top="4dp" />
<stroke
android:width="1dp"
- android:color="@color/grey500">
+ android:color="@color/grey700">
</stroke>
<solid
android:color="@color/darkred">
diff --git a/src/main/res/drawable/message_bubble_sent_blue.xml b/src/main/res/drawable/message_bubble_sent_blue.xml
index 0f1424d21..1ee3ad0c8 100644
--- a/src/main/res/drawable/message_bubble_sent_blue.xml
+++ b/src/main/res/drawable/message_bubble_sent_blue.xml
@@ -13,7 +13,7 @@
android:top="4dp" />
<stroke
android:width="1dp"
- android:color="@color/grey800">
+ android:color="@color/grey500">
</stroke>
<solid
android:color="@color/lightblue">
diff --git a/src/main/res/drawable/message_bubble_sent_blue_dark.xml b/src/main/res/drawable/message_bubble_sent_blue_dark.xml
index 3d1b72042..d9853bf74 100644
--- a/src/main/res/drawable/message_bubble_sent_blue_dark.xml
+++ b/src/main/res/drawable/message_bubble_sent_blue_dark.xml
@@ -13,7 +13,7 @@
android:top="4dp" />
<stroke
android:width="1dp"
- android:color="@color/grey500">
+ android:color="@color/grey700">
</stroke>
<solid
android:color="@color/darkblue">
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index f6a2b1069..dd3ff2c31 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -33,143 +33,162 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:padding="@dimen/card_padding_regular">
-
- <android.support.text.emoji.widget.EmojiTextView
- android:id="@+id/contact_display_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:paddingBottom="5dp"
- android:text="@string/contact"
- android:textAlignment="center"
- android:textAppearance="@style/TextAppearance.Conversations.Title"
- android:textIsSelectable="false"
- android:textStyle="bold" />
-
- <QuickContactBadge
- android:id="@+id/details_contact_badge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/contact_display_name"
- android:layout_centerHorizontal="true"
- android:adjustViewBounds="true"
- android:background="?attr/message_border"
- android:maxHeight="384dp"
- android:maxWidth="384dp"
- android:padding="1dp"
- android:scaleType="centerCrop" />
+ android:orientation="vertical">
- <LinearLayout
- android:id="@+id/details_jidbox"
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentStart="true"
- android:layout_below="@+id/details_contact_badge"
- android:layout_marginTop="16dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/details_contactjid"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:text="@string/account_settings_example_jabber_id"
- android:textAppearance="@style/TextAppearance.Conversations.Title"
- android:textIsSelectable="true"
- android:visibility="gone" />
-
- <com.wefika.flowlayout.FlowLayout
- android:id="@+id/tags"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="4dp"
- android:layout_marginLeft="-2dp"
- android:layout_marginTop="4dp"
- android:orientation="horizontal"></com.wefika.flowlayout.FlowLayout>
-
- <TextView
- android:id="@+id/details_lastseen"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:padding="@dimen/card_padding_regular">
<android.support.text.emoji.widget.EmojiTextView
- android:id="@+id/status_message"
+ android:id="@+id/contact_display_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:gravity="center_horizontal"
- android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+ android:textAppearance="@style/TextAppearance.Conversations.Title" />
+ </RelativeLayout>
- <android.support.text.emoji.widget.EmojiTextView
- android:id="@+id/resource"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:gravity="center_horizontal"
- android:textSize="?attr/TextSizeBody"
- android:textStyle="italic" />
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
- <Button
- android:id="@+id/add_contact_button"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:text="@string/add_contact" />
-
- <CheckBox
- android:id="@+id/details_send_presence"
- style="@style/Widget.Conversations.CheckBox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_below="@+id/add_contact_button"
- android:layout_marginTop="4dp"
- android:text="@string/send_presence_updates"
- android:textAppearance="@style/TextAppearance.Conversations.Body1" />
-
- <CheckBox
- android:id="@+id/details_receive_presence"
- style="@style/Widget.Conversations.CheckBox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_below="@+id/details_send_presence"
- android:text="@string/receive_presence_updates"
- android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:padding="@dimen/card_padding_regular">
- </LinearLayout>
+ <QuickContactBadge
+ android:id="@+id/details_contact_badge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/contact_display_name"
+ android:layout_centerHorizontal="true"
+ android:adjustViewBounds="true"
+ android:background="?attr/color_border"
+ android:maxHeight="384dp"
+ android:maxWidth="384dp"
+ android:padding="1dp"
+ android:scaleType="centerCrop" />
- <TextView
- android:id="@+id/details_account"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/details_jidbox"
- android:layout_marginTop="32dp"
- android:text="@string/using_account"
- android:textAppearance="@style/TextAppearance.Conversations.Caption"
- android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/details_jidbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/details_contact_badge"
+ android:layout_marginTop="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/details_contactjid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/account_settings_example_jabber_id"
+ android:textAppearance="@style/TextAppearance.Conversations.Title"
+ android:textIsSelectable="true"
+ android:visibility="gone" />
+
+ <com.wefika.flowlayout.FlowLayout
+ android:id="@+id/tags"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="-2dp"
+ android:layout_marginTop="4dp"
+ android:orientation="horizontal">
+ </com.wefika.flowlayout.FlowLayout>
+
+ <TextView
+ android:id="@+id/details_lastseen"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
+
+ <android.support.text.emoji.widget.EmojiTextView
+ android:id="@+id/status_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:gravity="center_horizontal"
+ android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+
+ <android.support.text.emoji.widget.EmojiTextView
+ android:id="@+id/resource"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:gravity="center_horizontal"
+ android:textSize="?attr/TextSizeBody"
+ android:textStyle="italic" />
+
+ <Button
+ android:id="@+id/add_contact_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:text="@string/add_contact" />
+
+ <CheckBox
+ android:id="@+id/details_send_presence"
+ style="@style/Widget.Conversations.CheckBox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/add_contact_button"
+ android:layout_marginTop="4dp"
+ android:text="@string/send_presence_updates"
+ android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+
+ <CheckBox
+ android:id="@+id/details_receive_presence"
+ style="@style/Widget.Conversations.CheckBox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/details_send_presence"
+ android:text="@string/receive_presence_updates"
+ android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="@dimen/card_padding_list">
+
+ <TextView
+ android:id="@+id/details_account"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:layout_marginTop="24dp"
+ android:text="@string/using_account"
+ android:textAppearance="@style/TextAppearance.Conversations.Caption"
+ android:visibility="visible" />
+ </LinearLayout>
+ </LinearLayout>
+ </RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml
index 2c1a0e682..2a450f964 100644
--- a/src/main/res/layout/activity_muc_details.xml
+++ b/src/main/res/layout/activity_muc_details.xml
@@ -51,7 +51,9 @@
android:id="@+id/muc_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_toLeftOf="@+id/edit_muc_name_button"
android:layout_toStartOf="@+id/edit_muc_name_button"
android:orientation="vertical">
@@ -85,9 +87,10 @@
android:id="@+id/muc_editor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_toLeftOf="@+id/edit_muc_name_button"
android:layout_toStartOf="@+id/edit_muc_name_button"
-
android:orientation="vertical"
android:visibility="gone">
@@ -97,7 +100,7 @@
app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error"
app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint">
- <android.support.design.widget.TextInputEditText
+ <de.pixart.messenger.ui.widget.EmojiWrapperEditText
android:id="@+id/muc_edit_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -111,7 +114,7 @@
app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error"
app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint">
- <android.support.design.widget.TextInputEditText
+ <de.pixart.messenger.ui.widget.EmojiWrapperEditText
android:id="@+id/muc_edit_subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -126,6 +129,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:alpha="?attr/icon_alpha"
android:background="?attr/selectableItemBackgroundBorderless"
@@ -145,6 +149,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/avatar_item_distance"
+ android:layout_marginRight="@dimen/avatar_item_distance"
android:adjustViewBounds="true"
android:maxHeight="384dp"
android:maxWidth="384dp"
@@ -267,7 +272,7 @@
android:layout_marginTop="24dp"
android:text="@string/using_account"
android:textAppearance="@style/TextAppearance.Conversations.Caption"
- android:visibility="visible" />
+ android:visibility="gone" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml
index c2e374356..0218e299b 100644
--- a/src/main/res/layout/contact.xml
+++ b/src/main/res/layout/contact.xml
@@ -5,7 +5,7 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/activatedBackgroundIndicator"
+ android:background="?attr/list_item_background"
android:padding="@dimen/list_padding">
<com.makeramen.roundedimageview.RoundedImageView
diff --git a/src/main/res/layout/create_conference_dialog.xml b/src/main/res/layout/create_conference_dialog.xml
index ca26e618b..384baa7ca 100644
--- a/src/main/res/layout/create_conference_dialog.xml
+++ b/src/main/res/layout/create_conference_dialog.xml
@@ -25,26 +25,18 @@
android:layout_height="wrap_content"
android:visibility="gone" />
- <View
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:focusable="true"
- android:focusableInTouchMode="true" />
-
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error"
app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint">
- <android.support.design.widget.TextInputEditText
- android:id="@+id/subject"
+ <de.pixart.messenger.ui.widget.EmojiWrapperEditText
+ android:id="@+id/group_chat_name"
style="@style/Widget.Conversations.EditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hint="@string/topic"
- android:nextFocusDown="@+id/subject"
- android:nextFocusUp="@+id/subject" />
+ android:hint="@string/create_dialog_group_chat_name"/>
</android.support.design.widget.TextInputLayout>
</LinearLayout>
</layout> \ No newline at end of file
diff --git a/src/main/res/layout/dialog_quickedit.xml b/src/main/res/layout/dialog_quickedit.xml
index 273bcf1e9..9ab308af8 100644
--- a/src/main/res/layout/dialog_quickedit.xml
+++ b/src/main/res/layout/dialog_quickedit.xml
@@ -18,7 +18,7 @@
app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error"
app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint">
- <android.support.design.widget.TextInputEditText
+ <de.pixart.messenger.ui.widget.EmojiWrapperEditText
android:id="@+id/input_edit_text"
style="@style/Widget.Conversations.EditText"
android:layout_width="match_parent"
@@ -27,7 +27,7 @@
android:inputType="textPersonName">
<requestFocus />
- </android.support.design.widget.TextInputEditText>
+ </de.pixart.messenger.ui.widget.EmojiWrapperEditText>
</android.support.design.widget.TextInputLayout>
</LinearLayout>
diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml
index 84ed0fb9f..4112dfb8c 100644
--- a/src/main/res/menu/message_context.xml
+++ b/src/main/res/menu/message_context.xml
@@ -26,6 +26,10 @@
android:title="@string/correct_message"
android:visible="false" />
<item
+ android:id="@+id/delete_message"
+ android:title="@string/delete_message"
+ android:visible="false" />
+ <item
android:id="@+id/copy_url"
android:title="@string/copy_original_url"
android:visible="false" />
diff --git a/src/main/res/menu/select_multiple.xml b/src/main/res/menu/select_multiple.xml
deleted file mode 100644
index 51f05e21a..000000000
--- a/src/main/res/menu/select_multiple.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/selection_submit"
- android:title="@string/invite_contact"
- app:showAsAction="always" />
-
-</menu>
diff --git a/src/main/res/values-v21/themes.xml b/src/main/res/values-v21/themes.xml
new file mode 100644
index 000000000..47f419248
--- /dev/null
+++ b/src/main/res/values-v21/themes.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="ConversationsTheme.Dark" parent="ConversationsTheme.Dark.Base">
+ <item name="android:navigationBarColor">@color/realblack</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index 10690b682..b261ea28a 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -17,6 +17,7 @@
<attr name="text_Color_Main" format="reference|color" />
+ <attr name="color_background_tertiary" format="reference|color" />
<attr name="color_background_secondary" format="reference|color" />
<attr name="color_background_primary" format="reference|color" />
<attr name="color_warning" format="reference|color" />
@@ -25,6 +26,8 @@
<attr name="activity_background_search" format="reference" />
<attr name="activity_background_no_results" format="reference" />
+ <attr name="list_item_background" format="reference" />
+
<attr name="TextColorOnline" format="reference|color" />
<attr name="TextColorError" format="reference|color" />
@@ -96,8 +99,6 @@
<attr name="color_border" format="reference|color" />
- <attr name="message_border" format="reference" />
-
<!-- settings -->
<attr name="ic_settings_ui" format="reference" />
<attr name="ic_settings_notifications" format="reference" />
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index 56444212f..6ea10548f 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -22,6 +22,7 @@
<color name="grey200">#ffeeeeee</color>
<color name="grey300">#ffe0e0e0</color>
<color name="grey500">#ff9e9e9e</color>
+ <color name="grey700">#ff616161</color>
<color name="grey800">#ff424242</color>
<color name="grey900">#ff282828</color>
<color name="red800">#ffc62828</color>
diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml
index 9e269d271..49c1a849a 100644
--- a/src/main/res/values/defaults.xml
+++ b/src/main/res/values/defaults.xml
@@ -225,6 +225,8 @@
<item>chat.hipchat.com</item>
<item>googlemail.com</item>
<item>nsa.li</item>
+ <item>blabber.im</item>
+ <item>buzon.uy</item>
</string-array>
</resources>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 57edf9ba6..89cecf15f 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -21,6 +21,7 @@
<string name="title_activity_sharewith">Share with Conversation</string>
<string name="title_activity_start_conversation">Start Conversation</string>
<string name="title_activity_choose_contact">Choose contact</string>
+ <string name="title_activity_choose_contacts">Choose Contacts</string>
<string name="title_activity_block_list">Block list</string>
<string name="just_now">just now</string>
<string name="minute_ago">1 min ago</string>
@@ -426,10 +427,6 @@
<item quantity="one">%d certificate deleted</item>
<item quantity="other">%d certificates deleted</item>
</plurals>
- <plurals name="select_contact">
- <item quantity="one">Select %d contact</item>
- <item quantity="other">Select %d contacts</item>
- </plurals>
<string name="pref_quick_action_summary">Replace send button with quick action</string>
<string name="pref_quick_action">Quick Action</string>
<string name="none">None</string>
@@ -802,4 +799,10 @@
<string name="contact_name">Contact name</string>
<string name="nickname">Nickname for this group chat</string>
<string name="group_chat_name">Name</string>
+ <string name="providing_a_name_is_optional">Providing a name is optional</string>
+ <string name="create_dialog_group_chat_name">Group chat name</string>
+ <string name="everyone_has_read_up_to_this_point">Everyone has read up to this point</string>
+ <string name="conference_resource_constraint">Resource constraint</string>
+ <string name="delete_message">Delete message</string>
+ <string name="conference_destroyed">This group chat has been destroyed</string>
</resources>
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index 6eef29072..f1b66319f 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -12,12 +12,14 @@
<item name="color_background_primary">@color/grey50</item>
<item name="color_background_secondary">@color/grey200</item>
+ <item name="color_background_tertiary">@color/grey300</item>
<item name="color_warning">@color/red_a700</item>
<item name="TextColorOnline">@color/green500</item>
<item name="TextColorError">@color/red800</item>
<item name="activity_background_search">@drawable/search_background_light</item>
<item name="activity_background_no_results">@drawable/no_results_background_light</item>
+ <item name="list_item_background">@drawable/list_item_background_light</item>
<item name="EmojiColor">@color/realblack</item>
@@ -26,8 +28,7 @@
<item name="color_bubble_date">@color/lightgreen</item>
<item name="color_bubble_warning">@color/lightred</item>
- <item name="color_border">@color/grey800</item>
- <item name="message_border">@drawable/message_border</item>
+ <item name="color_border">@color/grey500</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:actionModeBackground">@color/accent</item>
@@ -135,7 +136,9 @@
<item name="ic_settings_about" type="reference">@drawable/ic_help_black_24dp</item>
</style>
- <style name="ConversationsTheme.Dark" parent="Theme.AppCompat.NoActionBar">
+ <style name="ConversationsTheme.Dark" parent="ConversationsTheme.Dark.Base" />
+
+ <style name="ConversationsTheme.Dark.Base" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
@@ -146,12 +149,14 @@
<item name="color_background_primary">@color/grey800</item>
<item name="color_background_secondary">@color/grey900</item>
+ <item name="color_background_tertiary">@color/grey700</item>
<item name="color_warning">@color/red_a700</item>
<item name="TextColorOnline">@color/green500</item>
<item name="TextColorError">@color/red500</item>
<item name="activity_background_search">@drawable/search_background_dark</item>
<item name="activity_background_no_results">@drawable/no_results_background_dark</item>
+ <item name="list_item_background">@drawable/list_item_background_dark</item>
<item name="EmojiColor">@color/realwhite</item>
@@ -160,8 +165,7 @@
<item name="color_bubble_date">@color/darkgreen</item>
<item name="color_bubble_warning">@color/darkred</item>
- <item name="color_border">@color/grey500</item>
- <item name="message_border">@drawable/message_border_dark</item>
+ <item name="color_border">@color/grey700</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:actionModeBackground">@color/accent</item>