aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/AndroidManifest.xml4
-rw-r--r--src/main/java/de/pixart/messenger/Config.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java123
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java102
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java4
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java26
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java1
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java120
-rw-r--r--src/main/java/de/pixart/messenger/entities/Account.java387
-rw-r--r--src/main/java/de/pixart/messenger/entities/Message.java4
-rw-r--r--src/main/java/de/pixart/messenger/entities/MucOptions.java1
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpUploadConnection.java41
-rw-r--r--src/main/java/de/pixart/messenger/parser/MessageParser.java1
-rw-r--r--src/main/java/de/pixart/messenger/parser/PresenceParser.java2
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java2
-rw-r--r--src/main/java/de/pixart/messenger/persistance/FileBackend.java15
-rw-r--r--src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java41
-rw-r--r--src/main/java/de/pixart/messenger/services/NotificationService.java63
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java24
-rw-r--r--src/main/java/de/pixart/messenger/ui/BlockContactDialog.java19
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java29
-rw-r--r--src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java14
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java148
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationsActivity.java1
-rw-r--r--src/main/java/de/pixart/messenger/ui/EditAccountActivity.java14
-rw-r--r--src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java16
-rw-r--r--src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java (renamed from src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java)151
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java36
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java34
-rw-r--r--src/main/java/de/pixart/messenger/ui/StartConversationActivity.java40
-rw-r--r--src/main/java/de/pixart/messenger/ui/XmppActivity.java33
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java5
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java6
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java23
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java3
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/JidDialog.java22
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java15
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/ViewUtil.java6
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/FailedCountCustomView.java76
-rw-r--r--src/main/java/de/pixart/messenger/utils/Compatibility.java4
-rw-r--r--src/main/java/de/pixart/messenger/utils/CryptoHelper.java18
-rw-r--r--src/main/java/de/pixart/messenger/utils/PRNGFixes.java124
-rw-r--r--src/main/java/de/pixart/messenger/utils/Resolver.java42
-rw-r--r--src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java91
-rw-r--r--src/main/java/de/pixart/messenger/utils/TLSSocketFactory.java20
-rw-r--r--src/main/java/de/pixart/messenger/utils/XmppUri.java2
-rw-r--r--src/main/java/de/pixart/messenger/xml/XmlReader.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/XmppConnection.java108
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java76
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java25
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java4
-rw-r--r--src/main/res/drawable-hdpi/ic_android_grey600_48dp.pngbin0 -> 866 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_delete_black_24dp.pngbin0 -> 492 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_menu_white_24dp.pngbin0 -> 417 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_qrcode_grey600_24dp.pngbin0 -> 630 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_qrcode_scan_white_24dp.pngbin0 -> 284 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_qrcode_white_24dp.pngbin0 -> 568 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_send_attachment_online.pngbin946 -> 806 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_share_black_24dp.pngbin0 -> 753 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_video_black_24dp.pngbin0 -> 237 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_video_white_24dp.pngbin0 -> 239 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_android_grey600_48dp.pngbin0 -> 511 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_delete_black_24dp.pngbin0 -> 410 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_menu_white_24dp.pngbin0 -> 366 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_qrcode_grey600_24dp.pngbin0 -> 439 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_qrcode_scan_white_24dp.pngbin0 -> 188 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_qrcode_white_24dp.pngbin0 -> 435 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_send_attachment_online.pngbin497 -> 372 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_share_black_24dp.pngbin0 -> 573 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_video_black_24dp.pngbin0 -> 146 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_video_white_24dp.pngbin0 -> 150 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_android_grey600_48dp.pngbin0 -> 966 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_delete_black_24dp.pngbin0 -> 479 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_menu_white_24dp.pngbin0 -> 386 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_qrcode_grey600_24dp.pngbin0 -> 476 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_qrcode_scan_white_24dp.pngbin0 -> 273 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_qrcode_white_24dp.pngbin0 -> 473 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_send_attachment_online.pngbin708 -> 465 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_share_black_24dp.pngbin0 -> 852 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_video_black_24dp.pngbin0 -> 212 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_video_white_24dp.pngbin0 -> 206 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_android_grey600_48dp.pngbin0 -> 1865 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_delete_black_24dp.pngbin0 -> 575 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_menu_white_24dp.pngbin0 -> 420 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_qrcode_grey600_24dp.pngbin0 -> 536 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_qrcode_scan_white_24dp.pngbin0 -> 409 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_qrcode_white_24dp.pngbin0 -> 531 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_send_attachment_online.pngbin1466 -> 957 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_share_black_24dp.pngbin0 -> 1190 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_video_black_24dp.pngbin0 -> 325 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_video_white_24dp.pngbin0 -> 332 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_android_grey600_48dp.pngbin0 -> 2224 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_delete_black_24dp.pngbin0 -> 668 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_menu_white_24dp.pngbin0 -> 450 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_qrcode_grey600_24dp.pngbin0 -> 585 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_qrcode_scan_white_24dp.pngbin0 -> 492 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_qrcode_white_24dp.pngbin0 -> 582 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_send_attachment_online.pngbin1064 -> 2159 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_share_black_24dp.pngbin0 -> 1556 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_video_black_24dp.pngbin0 -> 425 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_video_white_24dp.pngbin0 -> 419 bytes
-rw-r--r--src/main/res/drawable/date_bubble.xml6
-rw-r--r--src/main/res/drawable/date_bubble_dark.xml6
-rw-r--r--src/main/res/drawable/message_bubble_received_light.xml48
-rw-r--r--src/main/res/drawable/message_bubble_received_light_dark.xml48
-rw-r--r--src/main/res/drawable/message_bubble_received_warning.xml48
-rw-r--r--src/main/res/drawable/message_bubble_received_warning_dark.xml48
-rw-r--r--src/main/res/drawable/message_bubble_sent_blue.xml49
-rw-r--r--src/main/res/drawable/message_bubble_sent_blue_dark.xml48
-rw-r--r--src/main/res/layout/ab_title.xml4
-rw-r--r--src/main/res/layout/account_row.xml2
-rw-r--r--src/main/res/layout/activity_contact_details.xml8
-rw-r--r--src/main/res/layout/activity_edit_account.xml65
-rw-r--r--src/main/res/layout/activity_media_viewer.xml (renamed from src/main/res/layout/activity_fullscreen_message.xml)4
-rw-r--r--src/main/res/layout/activity_muc_details.xml53
-rw-r--r--src/main/res/layout/activity_publish_profile_picture.xml131
-rw-r--r--src/main/res/layout/contact.xml4
-rw-r--r--src/main/res/layout/conversation_list_row.xml4
-rw-r--r--src/main/res/layout/fragment_conversation.xml5
-rw-r--r--src/main/res/layout/message_content.xml2
-rw-r--r--src/main/res/layout/message_received.xml4
-rw-r--r--src/main/res/layout/message_sent.xml4
-rw-r--r--src/main/res/layout/message_status.xml2
-rw-r--r--src/main/res/menu/contact_details.xml2
-rw-r--r--src/main/res/menu/editaccount.xml48
-rw-r--r--src/main/res/menu/media_viewer.xml17
-rw-r--r--src/main/res/menu/muc_details.xml2
-rw-r--r--src/main/res/values-fil/strings.xml2
-rw-r--r--src/main/res/values-tr/strings.xml2
-rw-r--r--src/main/res/values-w384dp/dimens.xml1
-rw-r--r--src/main/res/values/attrs.xml2
-rw-r--r--src/main/res/values/colors.xml2
-rw-r--r--src/main/res/values/defaults.xml3
-rw-r--r--src/main/res/values/dimens.xml2
-rw-r--r--src/main/res/values/strings.xml26
-rw-r--r--src/main/res/values/themes.xml14
-rw-r--r--src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java1
-rw-r--r--src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java1
-rw-r--r--src/standardPush/java/de/pixart/messenger/services/PushManagementService.java10
-rw-r--r--src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java3
141 files changed, 1577 insertions, 1326 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index bfdb2ec3d..ce3bfc406 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -104,8 +104,8 @@
android:name=".ui.ConversationsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
- android:minHeight="300dp"
android:minWidth="300dp"
+ android:minHeight="300dp"
android:windowSoftInputMode="stateHidden"></activity>
<activity
android:name=".ui.ScanActivity"
@@ -238,7 +238,7 @@
android:name=".ui.SearchActivity"
android:label="@string/search_messages" />
<activity
- android:name=".ui.ShowFullscreenMessageActivity"
+ android:name=".ui.MediaViewerActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/ConversationsTheme.FullScreen"></activity>
<activity
diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java
index 147bb6873..5181b24b1 100644
--- a/src/main/java/de/pixart/messenger/Config.java
+++ b/src/main/java/de/pixart/messenger/Config.java
@@ -78,7 +78,7 @@ public final class Config {
public static final int CONNECT_DISCO_TIMEOUT = 30;
public static final int MINI_GRACE_PERIOD = 750;
- public static final boolean XEP_0392 = false; //enables a variant of XEP-0392 that is based on HSLUV
+ public static final boolean XEP_0392 = true; //enables XEP-0392 v0.6.0
public static final int FILE_SIZE = 1048576; // 1 MiB
diff --git a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
index 3f3c358a7..80a6086aa 100644
--- a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
+++ b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
@@ -32,6 +32,70 @@ public class XmppDomainVerifier implements DomainHostnameVerifier {
private static final String SRV_NAME = "1.3.6.1.5.5.7.8.7";
private static final String XMPP_ADDR = "1.3.6.1.5.5.7.8.5";
+ private static List<String> getCommonNames(X509Certificate certificate) {
+ List<String> domains = new ArrayList<>();
+ try {
+ X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
+ RDN[] rdns = x500name.getRDNs(BCStyle.CN);
+ for (int i = 0; i < rdns.length; ++i) {
+ domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
+ }
+ return domains;
+ } catch (CertificateEncodingException e) {
+ return domains;
+ }
+ }
+
+ private static Pair<String, String> parseOtherName(byte[] otherName) {
+ try {
+ ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
+ if (asn1Primitive instanceof DERTaggedObject) {
+ ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
+ if (inner instanceof DLSequence) {
+ DLSequence sequence = (DLSequence) inner;
+ if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
+ String oid = sequence.getObjectAt(0).toString();
+ ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
+ if (value instanceof DERUTF8String) {
+ return new Pair<>(oid, ((DERUTF8String) value).getString());
+ } else if (value instanceof DERIA5String) {
+ return new Pair<>(oid, ((DERIA5String) value).getString());
+ }
+ }
+ }
+ }
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static boolean matchDomain(String needle, List<String> haystack) {
+ for (String entry : haystack) {
+ if (entry.startsWith("*.")) {
+ int offset = 0;
+ while (offset < needle.length()) {
+ int i = needle.indexOf('.', offset);
+ if (i < 0) {
+ break;
+ }
+ Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
+ if (needle.substring(i).equals(entry.substring(1))) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ offset = i + 1;
+ }
+ } else {
+ if (entry.equals(needle)) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public boolean verify(String domain, String hostname, SSLSession sslSession) {
try {
@@ -92,63 +156,6 @@ public class XmppDomainVerifier implements DomainHostnameVerifier {
}
}
- private static List<String> getCommonNames(X509Certificate certificate) {
- List<String> domains = new ArrayList<>();
- try {
- X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
- RDN[] rdns = x500name.getRDNs(BCStyle.CN);
- for (int i = 0; i < rdns.length; ++i) {
- domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
- }
- return domains;
- } catch (CertificateEncodingException e) {
- return domains;
- }
- }
-
- private static Pair<String, String> parseOtherName(byte[] otherName) {
- try {
- ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
- if (asn1Primitive instanceof DERTaggedObject) {
- ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
- if (inner instanceof DLSequence) {
- DLSequence sequence = (DLSequence) inner;
- if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
- String oid = sequence.getObjectAt(0).toString();
- ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
- if (value instanceof DERUTF8String) {
- return new Pair<>(oid, ((DERUTF8String) value).getString());
- } else if (value instanceof DERIA5String) {
- return new Pair<>(oid, ((DERIA5String) value).getString());
- }
- }
- }
- }
- return null;
- } catch (IOException e) {
- return null;
- }
- }
-
- private static boolean matchDomain(String needle, List<String> haystack) {
- for (String entry : haystack) {
- if (entry.startsWith("*.")) {
- int i = needle.indexOf('.');
- Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
- if (i != -1 && needle.substring(i).equals(entry.substring(1))) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- } else {
- if (entry.equals(needle)) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- }
- }
- return false;
- }
-
private boolean isSelfSigned(X509Certificate certificate) {
try {
certificate.verify(certificate.getPublicKey());
@@ -162,4 +169,4 @@ public class XmppDomainVerifier implements DomainHostnameVerifier {
public boolean verify(String domain, SSLSession sslSession) {
return verify(domain, null, sslSession);
}
-}
+} \ No newline at end of file
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 02062eba1..cc9d85008 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
@@ -483,9 +483,18 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
publishOwnDeviceId(deviceIds);
}
}
+ final Set<Integer> oldSet = this.deviceIds.get(jid);
+ final boolean changed = oldSet == null || oldSet.hashCode() != hash;
this.deviceIds.put(jid, deviceIds);
- mXmppConnectionService.updateConversationUi(); //update the lock icon
- mXmppConnectionService.keyStatusUpdated(null);
+ if (changed) {
+ mXmppConnectionService.updateConversationUi(); //update the lock icon
+ mXmppConnectionService.keyStatusUpdated(null);
+ if (me) {
+ mXmppConnectionService.updateAccountUi();
+ }
+ } else {
+ Log.d(Config.LOGTAG, "skipped device list update because it hasn't changed");
+ }
}
public void wipeOtherPepDevices() {
@@ -516,6 +525,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
Log.d(Config.LOGTAG, getLogprefix(account) + "Timeout received while retrieving own Device Ids.");
} else {
+ //TODO consider calling registerDevices only after item-not-found to account for broken PEPs
Element item = mXmppConnectionService.getIqParser().getItem(packet);
Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retrieved own device list: " + deviceIds);
@@ -540,7 +550,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} else {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": own device " + session.getFingerprint() + " was active " + hours + " hours ago");
}
- }
+ } //TODO print last activation diff
}
}
return devices;
@@ -1012,28 +1022,33 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
if (packet != null) {
mXmppConnectionService.sendIqPacket(account, packet, (account, response) -> {
- synchronized (fetchDeviceIdsMap) {
- List<OnDeviceIdsFetched> callbacks = fetchDeviceIdsMap.remove(jid);
- if (response.getType() == IqPacket.TYPE.RESULT) {
- fetchDeviceListStatus.put(jid, true);
- Element item = mXmppConnectionService.getIqParser().getItem(response);
- Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
- registerDevices(jid, deviceIds);
- if (callbacks != null) {
- for (OnDeviceIdsFetched c : callbacks) {
- c.fetched(jid, deviceIds);
- }
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ fetchDeviceListStatus.put(jid, true);
+ Element item = mXmppConnectionService.getIqParser().getItem(response);
+ Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ registerDevices(jid, deviceIds);
+ final List<OnDeviceIdsFetched> callbacks;
+ synchronized (fetchDeviceIdsMap) {
+ callbacks = fetchDeviceIdsMap.remove(jid);
+ }
+ if (callbacks != null) {
+ for (OnDeviceIdsFetched c : callbacks) {
+ c.fetched(jid, deviceIds);
}
+ }
+ } else {
+ if (response.getType() == IqPacket.TYPE.TIMEOUT) {
+ fetchDeviceListStatus.remove(jid);
} else {
- if (response.getType() == IqPacket.TYPE.TIMEOUT) {
- fetchDeviceListStatus.remove(jid);
- } else {
- fetchDeviceListStatus.put(jid, false);
- }
- if (callbacks != null) {
- for (OnDeviceIdsFetched c : callbacks) {
- c.fetched(jid, null);
- }
+ fetchDeviceListStatus.put(jid, false);
+ }
+ final List<OnDeviceIdsFetched> callbacks;
+ synchronized (fetchDeviceIdsMap) {
+ callbacks = fetchDeviceIdsMap.remove(jid);
+ }
+ if (callbacks != null) {
+ for (OnDeviceIdsFetched c : callbacks) {
+ c.fetched(jid, null);
}
}
}
@@ -1148,8 +1163,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
Set<SignalProtocolAddress> addresses = new HashSet<>();
for (Jid jid : getCryptoTargets(conversation)) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + jid);
- if (deviceIds.get(jid) != null) {
- for (Integer foreignId : this.deviceIds.get(jid)) {
+ final Set<Integer> ids = deviceIds.get(jid);
+ if (ids != null && !ids.isEmpty()) {
+ for (Integer foreignId : ids) {
SignalProtocolAddress address = new SignalProtocolAddress(jid.toString(), foreignId);
if (sessions.get(address) == null) {
IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
@@ -1172,22 +1188,21 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!");
}
}
- if (deviceIds.get(account.getJid().asBareJid()) != null) {
- for (Integer ownId : this.deviceIds.get(account.getJid().asBareJid())) {
- SignalProtocolAddress address = new SignalProtocolAddress(account.getJid().asBareJid().toString(), ownId);
- if (sessions.get(address) == null) {
- IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
- if (identityKey != null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
- XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
- sessions.put(address, session);
+ Set<Integer> ownIds = this.deviceIds.get(account.getJid().asBareJid());
+ for (Integer ownId : (ownIds != null ? ownIds : new HashSet<Integer>())) {
+ SignalProtocolAddress address = new SignalProtocolAddress(account.getJid().asBareJid().toString(), ownId);
+ if (sessions.get(address) == null) {
+ IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
+ if (identityKey != null) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
+ XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
+ sessions.put(address, session);
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().asBareJid() + ":" + ownId);
+ if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
+ addresses.add(address);
} else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().asBareJid() + ":" + ownId);
- if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
- addresses.add(address);
- } else {
- Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
- }
+ Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
}
}
}
@@ -1206,12 +1221,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": createSessionsIfNeeded() - jids with empty device list: " + jidsWithEmptyDeviceList);
if (jidsWithEmptyDeviceList.size() > 0) {
- fetchDeviceIds(jidsWithEmptyDeviceList, new OnMultipleDeviceIdFetched() {
- @Override
- public void fetched() {
- createSessionsIfNeededActual(conversation);
- }
- });
+ fetchDeviceIds(jidsWithEmptyDeviceList, () -> createSessionsIfNeededActual(conversation));
return true;
} else {
return createSessionsIfNeededActual(conversation);
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
index d47df0a0b..cfe8bea4d 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
@@ -78,6 +78,10 @@ public class FingerprintStatus implements Comparable<FingerprintStatus> {
return status;
}
+ public static FingerprintStatus createActive(Boolean trusted) {
+ return createActive(trusted != null && trusted);
+ }
+
public static FingerprintStatus createActive(boolean trusted) {
final FingerprintStatus status = new FingerprintStatus();
status.trust = trusted ? Trust.TRUSTED : Trust.UNTRUSTED;
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
index f9b2539c3..71fbd40db 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
@@ -27,12 +27,12 @@ import rocks.xmpp.addr.Jid;
public class XmppAxolotlMessage {
public static final String CONTAINERTAG = "encrypted";
- public static final String HEADER = "header";
- public static final String SOURCEID = "sid";
- public static final String KEYTAG = "key";
- public static final String REMOTEID = "rid";
- public static final String IVTAG = "iv";
- public static final String PAYLOAD = "payload";
+ private static final String HEADER = "header";
+ private static final String SOURCEID = "sid";
+ private static final String KEYTAG = "key";
+ private static final String REMOTEID = "rid";
+ private static final String IVTAG = "iv";
+ private static final String PAYLOAD = "payload";
private static final String KEYTYPE = "AES";
private static final String CIPHERMODE = "AES/GCM/NoPadding";
@@ -50,7 +50,7 @@ public class XmppAxolotlMessage {
private final String plaintext;
private final String fingerprint;
- public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
+ XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
this.plaintext = plaintext;
this.fingerprint = fingerprint;
}
@@ -70,7 +70,7 @@ public class XmppAxolotlMessage {
private final byte[] key;
private final byte[] iv;
- public XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
+ XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
this.fingerprint = fingerprint;
this.key = key;
this.iv = iv;
@@ -140,7 +140,7 @@ public class XmppAxolotlMessage {
}
}
- public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
+ XmppAxolotlMessage(Jid from, int sourceDeviceId) {
this.from = from;
this.sourceDeviceId = sourceDeviceId;
this.keys = new SparseArray<>();
@@ -174,11 +174,11 @@ public class XmppAxolotlMessage {
return ciphertext != null;
}
- public void encrypt(String plaintext) throws CryptoFailedException {
+ void encrypt(String plaintext) throws CryptoFailedException {
try {
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
- Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
this.ciphertext = cipher.doFinal(Config.OMEMO_PADDING ? getPaddedBytes(plaintext) : plaintext.getBytes());
if (Config.PUT_AUTH_TAG_INTO_KEY && this.ciphertext != null) {
@@ -273,7 +273,7 @@ public class XmppAxolotlMessage {
return session.processReceiving(encryptedKey);
}
- public XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
+ XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
return new XmppAxolotlKeyTransportMessage(session.getFingerprint(), unpackKey(session, sourceDeviceId), getIV());
}
@@ -294,7 +294,7 @@ public class XmppAxolotlMessage {
key = newKey;
}
- Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
index 09045f032..caa7f9c23 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
@@ -110,6 +110,7 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> {
}
if (!status.isActive()) {
setTrust(status.toActive());
+ //TODO: also (re)add to device list?
}
} else {
throw new CryptoFailedException("not encrypting omemo message from fingerprint "+getFingerprint()+" because it was marked as compromised");
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java b/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java
index 046b68fda..e1a153ef6 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java
@@ -22,31 +22,19 @@ import de.pixart.messenger.xml.TagWriter;
abstract class ScramMechanism extends SaslMechanism {
// TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage.
private final static String GS2_HEADER = "n,,";
- private String clientFirstMessageBare;
- private final String clientNonce;
- private byte[] serverSignature = null;
- static HMac HMAC;
- static Digest DIGEST;
private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes();
private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
-
- private static class KeyPair {
- final byte[] clientKey;
- final byte[] serverKey;
-
- KeyPair(final byte[] clientKey, final byte[] serverKey) {
- this.clientKey = clientKey;
- this.serverKey = serverKey;
- }
- }
+ private static final LruCache<String, KeyPair> CACHE;
+ static HMac HMAC;
+ static Digest DIGEST;
static {
CACHE = new LruCache<String, KeyPair>(10) {
protected KeyPair create(final String k) {
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations,SASL-Mechanism".
// Changing any of these values forces a cache miss. `CryptoHelper.bytesToHex()'
// is applied to prevent commas in the strings breaking things.
- final String[] kparts = k.split(",", 4);
+ final String[] kparts = k.split(",", 5);
try {
final byte[] saltedPassword, serverKey, clientKey;
saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(),
@@ -62,9 +50,10 @@ abstract class ScramMechanism extends SaslMechanism {
};
}
- private static final LruCache<String, KeyPair> CACHE;
-
+ private final String clientNonce;
protected State state = State.INITIAL;
+ private String clientFirstMessageBare;
+ private byte[] serverSignature = null;
ScramMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
super(tagWriter, account, rng);
@@ -74,6 +63,41 @@ abstract class ScramMechanism extends SaslMechanism {
clientFirstMessageBare = "";
}
+ private static synchronized byte[] hmac(final byte[] key, final byte[] input)
+ throws InvalidKeyException {
+ HMAC.init(new KeyParameter(key));
+ HMAC.update(input, 0, input.length);
+ final byte[] out = new byte[HMAC.getMacSize()];
+ HMAC.doFinal(out, 0);
+ return out;
+ }
+
+ public static synchronized byte[] digest(byte[] bytes) {
+ DIGEST.reset();
+ DIGEST.update(bytes, 0, bytes.length);
+ final byte[] out = new byte[DIGEST.getDigestSize()];
+ DIGEST.doFinal(out, 0);
+ return out;
+ }
+
+ /*
+ * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the
+ * pseudorandom function (PRF) and with dkLen == output length of
+ * HMAC() == output length of H().
+ */
+ private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations)
+ throws InvalidKeyException {
+ byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE));
+ byte[] out = u.clone();
+ for (int i = 1; i < iterations; i++) {
+ u = hmac(key, u);
+ for (int j = 0; j < u.length; j++) {
+ out[j] ^= u[j];
+ }
+ }
+ return out;
+ }
+
@Override
public String getClientFirstMessage() {
if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) {
@@ -120,13 +144,13 @@ abstract class ScramMechanism extends SaslMechanism {
nonce = token.substring(2);
break;
case 'm':
- /*
- * RFC 5802:
- * m: This attribute is reserved for future extensibility. In this
- * version of SCRAM, its presence in a client or a server message
- * MUST cause authentication failure when the attribute is parsed by
- * the other end.
- */
+ /*
+ * RFC 5802:
+ * m: This attribute is reserved for future extensibility. In this
+ * version of SCRAM, its presence in a client or a server message
+ * MUST cause authentication failure when the attribute is parsed by
+ * the other end.
+ */
throw new AuthenticationException("Server sent reserved token: `m'");
}
}
@@ -147,12 +171,13 @@ abstract class ScramMechanism extends SaslMechanism {
final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ','
+ clientFinalMessageWithoutProof).getBytes();
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations,SASL-Mechanism".
final KeyPair keys = CACHE.get(
CryptoHelper.bytesToHex(account.getJid().asBareJid().toString().getBytes()) + ","
+ CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
+ CryptoHelper.bytesToHex(salt.getBytes()) + ","
- + String.valueOf(iterationCount)
+ + String.valueOf(iterationCount) + ","
+ + getMechanism()
);
if (keys == null) {
throw new AuthenticationException("Invalid keys generated");
@@ -188,7 +213,7 @@ abstract class ScramMechanism extends SaslMechanism {
}
state = State.VALID_SERVER_RESPONSE;
return "";
- } catch(Exception e) {
+ } catch (Exception e) {
throw new AuthenticationException("Server final message does not match calculated final message");
}
default:
@@ -196,38 +221,13 @@ abstract class ScramMechanism extends SaslMechanism {
}
}
- private static synchronized byte[] hmac(final byte[] key, final byte[] input)
- throws InvalidKeyException {
- HMAC.init(new KeyParameter(key));
- HMAC.update(input, 0, input.length);
- final byte[] out = new byte[HMAC.getMacSize()];
- HMAC.doFinal(out, 0);
- return out;
- }
-
- public static synchronized byte[] digest(byte[] bytes) {
- DIGEST.reset();
- DIGEST.update(bytes, 0, bytes.length);
- final byte[] out = new byte[DIGEST.getDigestSize()];
- DIGEST.doFinal(out, 0);
- return out;
- }
+ private static class KeyPair {
+ final byte[] clientKey;
+ final byte[] serverKey;
- /*
- * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the
- * pseudorandom function (PRF) and with dkLen == output length of
- * HMAC() == output length of H().
- */
- private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations)
- throws InvalidKeyException {
- byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE));
- byte[] out = u.clone();
- for (int i = 1; i < iterations; i++) {
- u = hmac(key, u);
- for (int j = 0; j < u.length; j++) {
- out[j] ^= u[j];
- }
+ KeyPair(final byte[] clientKey, final byte[] serverKey) {
+ this.clientKey = clientKey;
+ this.serverKey = serverKey;
}
- return out;
}
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java
index 6fd4c1c05..9e3711535 100644
--- a/src/main/java/de/pixart/messenger/entities/Account.java
+++ b/src/main/java/de/pixart/messenger/entities/Account.java
@@ -59,197 +59,34 @@ public class Account extends AbstractEntity {
public static final int OPTION_USECOMPRESSION = 3;
public static final int OPTION_MAGIC_CREATE = 4;
public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5;
- public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
+ public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
public static final int OPTION_HTTP_UPLOAD_AVAILABLE = 7;
+ private static final String KEY_PGP_SIGNATURE = "pgp_signature";
+ private static final String KEY_PGP_ID = "pgp_id";
public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
-
- public boolean httpUploadAvailable(long filesize) {
- return xmppConnection != null && (xmppConnection.getFeatures().httpUpload(filesize) || xmppConnection.getFeatures().p1S3FileTransfer());
- }
-
- public boolean httpUploadAvailable() {
- return isOptionSet(OPTION_HTTP_UPLOAD_AVAILABLE) || httpUploadAvailable(0);
- }
-
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public XmppConnection.Identity getServerIdentity() {
- if (xmppConnection == null) {
- return XmppConnection.Identity.UNKNOWN;
- } else {
- return xmppConnection.getServerIdentity();
- }
- }
-
- public Contact getSelfContact() {
- return getRoster().getContact(jid);
- }
-
- public boolean hasPendingPgpIntent(Conversation conversation) {
- return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
- }
-
- public boolean isPgpDecryptionServiceConnected() {
- return pgpDecryptionService != null && pgpDecryptionService.isConnected();
- }
-
- public boolean setShowErrorNotification(boolean newValue) {
- boolean oldValue = showErrorNotification();
- setKey("show_error", Boolean.toString(newValue));
- return newValue != oldValue;
- }
-
- public boolean showErrorNotification() {
- String key = getKey("show_error");
- return key == null || Boolean.parseBoolean(key);
- }
-
- public boolean isEnabled() {
- return !isOptionSet(Account.OPTION_DISABLED);
- }
-
- public enum State {
- DISABLED(false, false),
- OFFLINE(false),
- CONNECTING(false),
- ONLINE(false),
- NO_INTERNET(false),
- UNAUTHORIZED,
- SERVER_NOT_FOUND,
- REGISTRATION_SUCCESSFUL(false),
- REGISTRATION_FAILED(true, false),
- REGISTRATION_WEB(true, false),
- REGISTRATION_CONFLICT(true, false),
- REGISTRATION_NOT_SUPPORTED(true, false),
- REGISTRATION_PLEASE_WAIT(true, false),
- REGISTRATION_PASSWORD_TOO_WEAK(true, false),
- TLS_ERROR,
- INCOMPATIBLE_SERVER,
- TOR_NOT_AVAILABLE,
- DOWNGRADE_ATTACK,
- SESSION_FAILURE,
- BIND_FAILURE,
- HOST_UNKNOWN,
- STREAM_ERROR,
- POLICY_VIOLATION,
- PAYMENT_REQUIRED,
- MISSING_INTERNET_PERMISSION(false);
-
- private final boolean isError;
- private final boolean attemptReconnect;
-
- public boolean isError() {
- return this.isError;
- }
-
- public boolean isAttemptReconnect() {
- return this.attemptReconnect;
- }
-
- State(final boolean isError) {
- this(isError, true);
- }
-
- State(final boolean isError, final boolean reconnect) {
- this.isError = isError;
- this.attemptReconnect = reconnect;
- }
-
- State() {
- this(true, true);
- }
-
- public int getReadableId() {
- switch (this) {
- case DISABLED:
- return R.string.account_status_disabled;
- case ONLINE:
- return R.string.account_status_online;
- case CONNECTING:
- return R.string.account_status_connecting;
- case OFFLINE:
- return R.string.account_status_offline;
- case UNAUTHORIZED:
- return R.string.account_status_unauthorized;
- case SERVER_NOT_FOUND:
- return R.string.account_status_not_found;
- case NO_INTERNET:
- return R.string.account_status_no_internet;
- case REGISTRATION_FAILED:
- return R.string.account_status_regis_fail;
- case REGISTRATION_WEB:
- return R.string.account_status_regis_web;
- case REGISTRATION_CONFLICT:
- return R.string.account_status_regis_conflict;
- case REGISTRATION_SUCCESSFUL:
- return R.string.account_status_regis_success;
- case REGISTRATION_NOT_SUPPORTED:
- return R.string.account_status_regis_not_sup;
- case TLS_ERROR:
- return R.string.account_status_tls_error;
- case INCOMPATIBLE_SERVER:
- return R.string.account_status_incompatible_server;
- case TOR_NOT_AVAILABLE:
- return R.string.account_status_tor_unavailable;
- case BIND_FAILURE:
- return R.string.account_status_bind_failure;
- case SESSION_FAILURE:
- return R.string.session_failure;
- case DOWNGRADE_ATTACK:
- return R.string.sasl_downgrade;
- case HOST_UNKNOWN:
- return R.string.account_status_host_unknown;
- case POLICY_VIOLATION:
- return R.string.account_status_policy_violation;
- case REGISTRATION_PLEASE_WAIT:
- return R.string.registration_please_wait;
- case REGISTRATION_PASSWORD_TOO_WEAK:
- return R.string.registration_password_too_weak;
- case STREAM_ERROR:
- return R.string.account_status_stream_error;
- case PAYMENT_REQUIRED:
- return R.string.payment_required;
- case MISSING_INTERNET_PERMISSION:
- return R.string.missing_internet_permission;
- default:
- return R.string.account_status_unknown;
- }
- }
- }
-
+ protected final JSONObject keys;
+ private final Roster roster = new Roster(this);
+ private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>();
public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>();
-
- private static final String KEY_PGP_SIGNATURE = "pgp_signature";
- private static final String KEY_PGP_ID = "pgp_id";
-
protected Jid jid;
protected String password;
protected int options = 0;
- private String rosterVersion;
protected State status = State.OFFLINE;
- protected final JSONObject keys;
protected String resource;
protected String avatar;
- protected String displayName = null;
protected String hostname = null;
protected int port = 5222;
protected boolean online = false;
private OtrService mOtrService = null;
+ private String rosterVersion;
+ private String displayName = null;
private AxolotlService axolotlService = null;
private PgpDecryptionService pgpDecryptionService = null;
private XmppConnection xmppConnection = null;
private long mEndGracePeriod = 0L;
private String otrFingerprint;
- private final Roster roster = new Roster(this);
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
- private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
private Presence.Status presenceStatus = Presence.Status.ONLINE;
private String presenceStatusMessage = null;
@@ -308,6 +145,57 @@ public class Account extends AbstractEntity {
cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
}
+ public boolean httpUploadAvailable(long filesize) {
+ return xmppConnection != null && (xmppConnection.getFeatures().httpUpload(filesize) || xmppConnection.getFeatures().p1S3FileTransfer());
+ }
+
+ public boolean httpUploadAvailable() {
+ return isOptionSet(OPTION_HTTP_UPLOAD_AVAILABLE) || httpUploadAvailable(0);
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public XmppConnection.Identity getServerIdentity() {
+ if (xmppConnection == null) {
+ return XmppConnection.Identity.UNKNOWN;
+ } else {
+ return xmppConnection.getServerIdentity();
+ }
+ }
+
+ public Contact getSelfContact() {
+ return getRoster().getContact(jid);
+ }
+
+ public boolean hasPendingPgpIntent(Conversation conversation) {
+ return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
+ }
+
+ public boolean isPgpDecryptionServiceConnected() {
+ return pgpDecryptionService != null && pgpDecryptionService.isConnected();
+ }
+
+ public boolean setShowErrorNotification(boolean newValue) {
+ boolean oldValue = showErrorNotification();
+ setKey("show_error", Boolean.toString(newValue));
+ return newValue != oldValue;
+ }
+
+ public boolean showErrorNotification() {
+ String key = getKey("show_error");
+ return key == null || Boolean.parseBoolean(key);
+ }
+
+ public boolean isEnabled() {
+ return !isOptionSet(Account.OPTION_DISABLED);
+ }
+
public boolean isOptionSet(final int option) {
return ((options & (1 << option)) != 0);
}
@@ -354,27 +242,27 @@ public class Account extends AbstractEntity {
this.password = password;
}
- public void setHostname(String hostname) {
- this.hostname = hostname;
- }
-
public String getHostname() {
return this.hostname == null ? "" : this.hostname;
}
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
public boolean isOnion() {
final String server = getServer();
return server != null && server.endsWith(".onion");
}
- public void setPort(int port) {
- this.port = port;
- }
-
public int getPort() {
return this.port;
}
+ public void setPort(int port) {
+ this.port = port;
+ }
+
public State getStatus() {
if (isOptionSet(OPTION_DISABLED)) {
return State.DISABLED;
@@ -383,14 +271,14 @@ public class Account extends AbstractEntity {
}
}
- public State getTrueStatus() {
- return this.status;
- }
-
public void setStatus(final State status) {
this.status = status;
}
+ public State getTrueStatus() {
+ return this.status;
+ }
+
public boolean errorStatus() {
return getStatus().isError();
}
@@ -401,22 +289,22 @@ public class Account extends AbstractEntity {
&& getXmppConnection().getAttempt() >= 3;
}
- public void setPresenceStatus(Presence.Status status) {
- this.presenceStatus = status;
- }
-
public Presence.Status getPresenceStatus() {
return this.presenceStatus;
}
- public void setPresenceStatusMessage(String message) {
- this.presenceStatusMessage = message;
+ public void setPresenceStatus(Presence.Status status) {
+ this.presenceStatus = status;
}
public String getPresenceStatusMessage() {
return this.presenceStatusMessage;
}
+ public void setPresenceStatusMessage(String message) {
+ this.presenceStatusMessage = message;
+ }
+
public String getResource() {
return jid.getResource();
}
@@ -613,7 +501,7 @@ public class Account extends AbstractEntity {
return getBookmark(conferenceJid) != null;
}
- public Bookmark getBookmark(final Jid jid) {
+ Bookmark getBookmark(final Jid jid) {
for (final Bookmark bookmark : this.bookmarks) {
if (bookmark.getJid() != null && jid.asBareJid().equals(bookmark.getJid().asBareJid())) {
return bookmark;
@@ -653,16 +541,17 @@ public class Account extends AbstractEntity {
List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
String uri = "xmpp:" + this.getJid().asBareJid().toEscapedString();
if (fingerprints.size() > 0) {
- return XmppUri.getFingerprintUri(uri,fingerprints,';');
+ return XmppUri.getFingerprintUri(uri, fingerprints, ';');
} else {
return uri;
}
}
+
public String getShareableLink() {
List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
String uri = Config.inviteUserURL + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
if (fingerprints.size() > 0) {
- return XmppUri.getFingerprintUri(uri,fingerprints,'&');
+ return XmppUri.getFingerprintUri(uri, fingerprints, '&');
} else {
return uri;
}
@@ -706,4 +595,116 @@ public class Account extends AbstractEntity {
public boolean isOnlineAndConnected() {
return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
}
-}
+
+ public enum State {
+ DISABLED(false, false),
+ OFFLINE(false),
+ CONNECTING(false),
+ ONLINE(false),
+ NO_INTERNET(false),
+ UNAUTHORIZED,
+ SERVER_NOT_FOUND,
+ REGISTRATION_SUCCESSFUL(false),
+ REGISTRATION_FAILED(true, false),
+ REGISTRATION_WEB(true, false),
+ REGISTRATION_CONFLICT(true, false),
+ REGISTRATION_NOT_SUPPORTED(true, false),
+ REGISTRATION_PLEASE_WAIT(true, false),
+ REGISTRATION_PASSWORD_TOO_WEAK(true, false),
+ TLS_ERROR,
+ INCOMPATIBLE_SERVER,
+ TOR_NOT_AVAILABLE,
+ DOWNGRADE_ATTACK,
+ SESSION_FAILURE,
+ BIND_FAILURE,
+ HOST_UNKNOWN,
+ STREAM_ERROR,
+ STREAM_OPENING_ERROR,
+ POLICY_VIOLATION,
+ PAYMENT_REQUIRED,
+ MISSING_INTERNET_PERMISSION(false);
+
+ private final boolean isError;
+ private final boolean attemptReconnect;
+
+ State(final boolean isError) {
+ this(isError, true);
+ }
+
+ State(final boolean isError, final boolean reconnect) {
+ this.isError = isError;
+ this.attemptReconnect = reconnect;
+ }
+
+ State() {
+ this(true, true);
+ }
+
+ public boolean isError() {
+ return this.isError;
+ }
+
+ public boolean isAttemptReconnect() {
+ return this.attemptReconnect;
+ }
+
+ public int getReadableId() {
+ switch (this) {
+ case DISABLED:
+ return R.string.account_status_disabled;
+ case ONLINE:
+ return R.string.account_status_online;
+ case CONNECTING:
+ return R.string.account_status_connecting;
+ case OFFLINE:
+ return R.string.account_status_offline;
+ case UNAUTHORIZED:
+ return R.string.account_status_unauthorized;
+ case SERVER_NOT_FOUND:
+ return R.string.account_status_not_found;
+ case NO_INTERNET:
+ return R.string.account_status_no_internet;
+ case REGISTRATION_FAILED:
+ return R.string.account_status_regis_fail;
+ case REGISTRATION_WEB:
+ return R.string.account_status_regis_web;
+ case REGISTRATION_CONFLICT:
+ return R.string.account_status_regis_conflict;
+ case REGISTRATION_SUCCESSFUL:
+ return R.string.account_status_regis_success;
+ case REGISTRATION_NOT_SUPPORTED:
+ return R.string.account_status_regis_not_sup;
+ case TLS_ERROR:
+ return R.string.account_status_tls_error;
+ case INCOMPATIBLE_SERVER:
+ return R.string.account_status_incompatible_server;
+ case TOR_NOT_AVAILABLE:
+ return R.string.account_status_tor_unavailable;
+ case BIND_FAILURE:
+ return R.string.account_status_bind_failure;
+ case SESSION_FAILURE:
+ return R.string.session_failure;
+ case DOWNGRADE_ATTACK:
+ return R.string.sasl_downgrade;
+ case HOST_UNKNOWN:
+ return R.string.account_status_host_unknown;
+ case POLICY_VIOLATION:
+ return R.string.account_status_policy_violation;
+ case REGISTRATION_PLEASE_WAIT:
+ return R.string.registration_please_wait;
+ case REGISTRATION_PASSWORD_TOO_WEAK:
+ return R.string.registration_password_too_weak;
+ case STREAM_ERROR:
+ return R.string.account_status_stream_error;
+ case STREAM_OPENING_ERROR:
+ return R.string.account_status_stream_opening_error;
+ case PAYMENT_REQUIRED:
+ return R.string.payment_required;
+ case MISSING_INTERNET_PERMISSION:
+ return R.string.missing_internet_permission;
+ default:
+ return R.string.account_status_unknown;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/entities/Message.java b/src/main/java/de/pixart/messenger/entities/Message.java
index 8193cbb4d..9a529877b 100644
--- a/src/main/java/de/pixart/messenger/entities/Message.java
+++ b/src/main/java/de/pixart/messenger/entities/Message.java
@@ -72,7 +72,7 @@ public class Message extends AbstractEntity {
public static final String READ_BY_MARKERS = "readByMarkers";
public static final String MARKABLE = "markable";
public static final String ME_COMMAND = "/me";
-
+ public static final String ERROR_MESSAGE_CANCELLED = "eu.siacs.conversations.cancelled";
public boolean markable = false;
protected String conversationUuid;
@@ -708,6 +708,8 @@ public class Message extends AbstractEntity {
extension = MimeUtils.extractRelevantExtension(url);
} catch (MalformedURLException e) {
return null;
+ } catch (Exception e) {
+ return null;
}
}
return MimeUtils.guessMimeTypeFromExtension(extension);
diff --git a/src/main/java/de/pixart/messenger/entities/MucOptions.java b/src/main/java/de/pixart/messenger/entities/MucOptions.java
index cf09e73a7..ec7770d77 100644
--- a/src/main/java/de/pixart/messenger/entities/MucOptions.java
+++ b/src/main/java/de/pixart/messenger/entities/MucOptions.java
@@ -655,6 +655,7 @@ public class MucOptions {
public enum Error {
NO_RESPONSE,
SERVER_NOT_FOUND,
+ REMOTE_SERVER_TIMEOUT,
NONE,
NICK_IN_USE,
PASSWORD_REQUIRED,
diff --git a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
index eee8b3c01..589e46d6c 100644
--- a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
@@ -2,10 +2,8 @@ package de.pixart.messenger.http;
import android.os.PowerManager;
import android.util.Log;
-import android.util.Pair;
-import java.io.FileNotFoundException;
-import java.io.IOException;
+import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
@@ -31,7 +29,7 @@ import de.pixart.messenger.utils.WakeLockHelper;
public class HttpUploadConnection implements Transferable {
- public static final List<String> WHITE_LISTED_HEADERS = Arrays.asList(
+ static final List<String> WHITE_LISTED_HEADERS = Arrays.asList(
"Authorization",
"Cookie",
"Expires"
@@ -42,7 +40,7 @@ public class HttpUploadConnection implements Transferable {
private final SlotRequester mSlotRequester;
private final Method method;
private final boolean mUseTor;
- private boolean canceled = false;
+ private boolean cancelled = false;
private boolean delayed = false;
private DownloadableFile file;
private Message message;
@@ -52,8 +50,6 @@ public class HttpUploadConnection implements Transferable {
private long transmitted = 0;
- private InputStream mFileInputStream;
-
public HttpUploadConnection(Method method, HttpConnectionManager httpConnectionManager) {
this.method = method;
this.mHttpConnectionManager = httpConnectionManager;
@@ -87,14 +83,13 @@ public class HttpUploadConnection implements Transferable {
@Override
public void cancel() {
- this.canceled = true;
+ this.cancelled = true;
}
private void fail(String errorMessage) {
mHttpConnectionManager.finishUploadConnection(this);
message.setTransferable(null);
- mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, errorMessage);
- FileBackend.close(mFileInputStream);
+ mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage);
}
public void init(Message message, boolean delay) {
@@ -110,7 +105,7 @@ public class HttpUploadConnection implements Transferable {
if (Config.ENCRYPT_ON_HTTP_UPLOADED
|| message.getEncryption() == Message.ENCRYPTION_AXOLOTL
|| message.getEncryption() == Message.ENCRYPTION_OTR) {
- this.key = new byte[48]; // todo: change this to 44 for 12-byte IV instead of 16-byte at some point in future
+ this.key = new byte[48];
mXmppConnectionService.getRNG().nextBytes(this.key);
this.file.setKeyAndIv(this.key);
}
@@ -119,7 +114,7 @@ public class HttpUploadConnection implements Transferable {
if (method == Method.P1_S3) {
try {
- md5 = Checksum.md5(AbstractConnectionManager.createInputStream(file, true).first);
+ md5 = Checksum.md5(AbstractConnectionManager.upgrade(file, new FileInputStream(file), true));
} catch (Exception e) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to calculate md5()", e);
fail(e.getMessage());
@@ -128,22 +123,12 @@ public class HttpUploadConnection implements Transferable {
} else {
md5 = null;
}
-
- Pair<InputStream, Integer> pair;
- try {
- pair = AbstractConnectionManager.createInputStream(file, true);
- } catch (FileNotFoundException e) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not find file to upload - " + e.getMessage());
- fail(e.getMessage());
- return;
- }
- this.file.setExpectedSize(pair.second);
+ this.file.setExpectedSize(file.getSize() + (file.getKey() != null ? 16 : 0));
message.resetFileParams();
- this.mFileInputStream = pair.first;
this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() {
@Override
public void success(SlotRequester.Slot slot) {
- if (!canceled) {
+ if (!cancelled) {
HttpUploadConnection.this.slot = slot;
new Thread(HttpUploadConnection.this::upload).start();
}
@@ -160,9 +145,11 @@ public class HttpUploadConnection implements Transferable {
private void upload() {
OutputStream os = null;
+ InputStream fileInputStream = null;
HttpURLConnection connection = null;
PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_" + message.getUuid());
try {
+ fileInputStream = new FileInputStream(file);
final int expectedFileSize = (int) file.getExpectedSize();
final int readTimeout = (expectedFileSize / 2048) + Config.SOCKET_TIMEOUT; //assuming a minimum transfer speed of 16kbit/s
wakeLock.acquire(readTimeout);
@@ -189,18 +176,18 @@ public class HttpUploadConnection implements Transferable {
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(readTimeout * 1000);
connection.connect();
+ final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream, true);
os = connection.getOutputStream();
transmitted = 0;
int count;
byte[] buffer = new byte[4096];
- while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
+ while (((count = innerInputStream.read(buffer)) != -1) && !cancelled) {
transmitted += count;
os.write(buffer, 0, count);
mHttpConnectionManager.updateConversationUi(false);
}
os.flush();
os.close();
- mFileInputStream.close();
int code = connection.getResponseCode();
InputStream is = connection.getErrorStream();
if (is != null) {
@@ -235,7 +222,7 @@ public class HttpUploadConnection implements Transferable {
Log.d(Config.LOGTAG, "http upload failed " + e.getMessage());
fail(e.getMessage());
} finally {
- FileBackend.close(mFileInputStream);
+ FileBackend.close(fileInputStream);
FileBackend.close(os);
if (connection != null) {
connection.disconnect();
diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java
index df85f2f25..dca999069 100644
--- a/src/main/java/de/pixart/messenger/parser/MessageParser.java
+++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java
@@ -303,7 +303,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received PEP device list " + deviceIds + " update from " + from + ", processing... ");
AxolotlService axolotlService = account.getAxolotlService();
axolotlService.registerDevices(from, deviceIds);
- mXmppConnectionService.updateAccountUi();
} else if (Namespace.BOOKMARKS.equals(node)) {
Log.d(Config.LOGTAG, "received bookmarks from " + from);
if (account.getJid().asBareJid().equals(from)) {
diff --git a/src/main/java/de/pixart/messenger/parser/PresenceParser.java b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
index 6a878d27f..710eca717 100644
--- a/src/main/java/de/pixart/messenger/parser/PresenceParser.java
+++ b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
@@ -184,6 +184,8 @@ public class PresenceParser extends AbstractParser implements
mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
} else if (error.hasChild("resource-constraint")) {
mucOptions.setError(MucOptions.Error.RESOURCE_CONSTRAINT);
+ } else if (error.hasChild("remote-server-timeout")) {
+ mucOptions.setError(MucOptions.Error.REMOTE_SERVER_TIMEOUT);
} else if (error.hasChild("gone")) {
final String gone = error.findChildContent("gone");
final Jid alternate;
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index 4cc5b7955..dac626718 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -663,7 +663,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
SQLiteDatabase db = this.getReadableDatabase();
String where = Resolver.Result.DOMAIN + "=?";
String[] whereArgs = {domain};
- Cursor cursor = db.query(RESOLVER_RESULTS_TABLENAME, null, where, whereArgs, null, null, null);
+ final Cursor cursor = db.query(RESOLVER_RESULTS_TABLENAME, null, where, whereArgs, null, null, null);
Resolver.Result result = null;
if (cursor != null) {
if (cursor.moveToFirst()) {
diff --git a/src/main/java/de/pixart/messenger/persistance/FileBackend.java b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
index 8397a7bb8..765766b5f 100644
--- a/src/main/java/de/pixart/messenger/persistance/FileBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
@@ -111,8 +111,7 @@ public class FileBackend {
}
}
- public boolean deleteFile(Message message) {
- File file = getFile(message);
+ public boolean deleteFile(File file) {
if (file.delete()) {
updateMediaScanner(file);
return true;
@@ -121,6 +120,11 @@ public class FileBackend {
}
}
+ public boolean deleteFile(Message message) {
+ File file = getFile(message);
+ return deleteFile(file);
+ }
+
public DownloadableFile getFile(Message message) {
return getFile(message, true);
}
@@ -219,8 +223,8 @@ public class FileBackend {
}
public static String getConversationsDirectory(final String type) {
- if (type.equalsIgnoreCase("null") || type == null) {
- return getAppMediaDirectory() + "Pix-Art Messenger" + "/";
+ if (type.equalsIgnoreCase("null")) {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "Pix-Art Messenger" + "/";
} else {
return getAppMediaDirectory() + "Pix-Art Messenger" + " " + type + "/";
}
@@ -858,6 +862,7 @@ public class FileBackend {
return cropCenterSquare(input, size);
}
} catch (FileNotFoundException | SecurityException e) {
+ Log.d(Config.LOGTAG, "unable to open file " + image.toString(), e);
return null;
} finally {
close(is);
@@ -1097,7 +1102,7 @@ public class FileBackend {
drawOverlay(bitmap, R.drawable.play_video, 0.75f);
} else {
bitmap = cropCenterSquare(attachment.getUri(), size);
- if ("image/gif".equals(attachment.getMime())) {
+ if (bitmap != null && "image/gif".equals(attachment.getMime())) {
Bitmap withGifOverlay = bitmap.copy(Bitmap.Config.ARGB_8888, true);
drawOverlay(withGifOverlay, R.drawable.play_gif, 1.0f);
bitmap.recycle();
diff --git a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
index 65711ef42..4b2239895 100644
--- a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
@@ -3,21 +3,26 @@ package de.pixart.messenger.services;
import android.content.Context;
import android.os.PowerManager;
import android.os.SystemClock;
-import android.util.Pair;
+import android.util.Log;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
+import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.entities.DownloadableFile;
import de.pixart.messenger.utils.Compatibility;
@@ -34,32 +39,26 @@ public class AbstractConnectionManager {
this.mXmppConnectionService = service;
}
- public static Pair<InputStream, Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
- FileInputStream is;
- int size;
- is = new FileInputStream(file);
- size = (int) file.getSize();
- if (file.getKey() == null) {
- return new Pair<>(is, size);
- }
- try {
+ public static InputStream upgrade(DownloadableFile file, InputStream is, boolean gcm) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, NoSuchProviderException {
+ if (file.getKey() != null && file.getIv() != null) {
if (gcm) {
- Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(file.getIv());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
- return new Pair<>(new CipherInputStream(is, cipher), cipher.getOutputSize(size));
+ return new CipherInputStream(is, cipher);
} else {
IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), KEYTYPE), ips);
- return new Pair<>(new CipherInputStream(is, cipher), (size / 16 + 1) * 16);
+ return new CipherInputStream(is, cipher);
}
- } catch (Exception e) {
- throw new AssertionError(e);
+ } else {
+ return is;
}
}
+
public static OutputStream createAppendedOutputStream(DownloadableFile file) {
return createOutputStream(file, false, true);
}
@@ -76,23 +75,25 @@ public class AbstractConnectionManager {
return os;
}
} catch (FileNotFoundException e) {
+ Log.d(Config.LOGTAG, "unable to create output stream", e);
return null;
}
try {
if (gcm) {
- Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(file.getIv());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return new CipherOutputStream(os, cipher);
} else {
IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), KEYTYPE), ips);
return new CipherOutputStream(os, cipher);
}
} catch (Exception e) {
- throw new AssertionError(e);
+ Log.d(Config.LOGTAG, "unable to create cipher output stream", e);
+ return null;
}
}
diff --git a/src/main/java/de/pixart/messenger/services/NotificationService.java b/src/main/java/de/pixart/messenger/services/NotificationService.java
index 4982e9fe2..a2d6f541a 100644
--- a/src/main/java/de/pixart/messenger/services/NotificationService.java
+++ b/src/main/java/de/pixart/messenger/services/NotificationService.java
@@ -64,18 +64,18 @@ import de.pixart.messenger.xmpp.XmppConnection;
public class NotificationService {
public static final Object CATCHUP_LOCK = new Object();
-
- private static final String CONVERSATIONS_GROUP = "de.pixart.messenger";
- private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024;
- public static final int NOTIFICATION_ID = 2 * NOTIFICATION_ID_MULTIPLIER;
- public static final int FOREGROUND_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 4;
- public static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6;
public static final String MESSAGES_CHANNEL_ID = "messages";
public static final String FOREGROUND_CHANNEL_ID = "foreground";
public static final String BACKUP_CHANNEL_ID = "backup";
public static final String UPDATE_CHANNEL_ID = "appupdate";
public static final String VIDEOCOMPRESSION_CHANNEL_ID = "compression";
public static final String ERROR_CHANNEL_ID = "error";
+ private static final String CONVERSATIONS_GROUP = "de.pixart.messenger";
+ private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024;
+ public static final int NOTIFICATION_ID = 2 * NOTIFICATION_ID_MULTIPLIER;
+ public static final int FOREGROUND_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 4;
+ public static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6;
+ private static final int LED_COLOR = 0xff0080FF;
private final XmppConnectionService mXmppConnectionService;
private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>();
@@ -107,7 +107,7 @@ public class NotificationService {
@RequiresApi(api = Build.VERSION_CODES.O)
public void initializeChannels() {
final Context c = mXmppConnectionService;
- NotificationManager notificationManager = c.getSystemService(NotificationManager.class);
+ final NotificationManager notificationManager = c.getSystemService(NotificationManager.class);
if (notificationManager == null) {
return;
}
@@ -159,7 +159,7 @@ public class NotificationService {
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.build());
- messagesChannel.setLightColor(0xff0080FF);
+ messagesChannel.setLightColor(LED_COLOR);
final int dat = 70;
final long[] pattern = {0, 3 * dat, dat, dat};
messagesChannel.setVibrationPattern(pattern);
@@ -173,10 +173,21 @@ public class NotificationService {
NotificationManager.IMPORTANCE_LOW);
silentMessagesChannel.setDescription(c.getString(R.string.silent_messages_channel_description));
silentMessagesChannel.setShowBadge(true);
- silentMessagesChannel.setLightColor(0xff0080FF);
+ silentMessagesChannel.setLightColor(LED_COLOR);
silentMessagesChannel.enableLights(true);
silentMessagesChannel.setGroup("chats");
notificationManager.createNotificationChannel(silentMessagesChannel);
+
+ final NotificationChannel quietHoursChannel = new NotificationChannel("quiet_hours",
+ c.getString(R.string.title_pref_quiet_hours),
+ NotificationManager.IMPORTANCE_LOW);
+ quietHoursChannel.setShowBadge(true);
+ quietHoursChannel.setLightColor(LED_COLOR);
+ quietHoursChannel.enableLights(true);
+ quietHoursChannel.setGroup("chats");
+ quietHoursChannel.enableVibration(false);
+ quietHoursChannel.setSound(null, null);
+ notificationManager.createNotificationChannel(quietHoursChannel);
}
public boolean notify(final Message message) {
@@ -184,8 +195,7 @@ public class NotificationService {
return message.getStatus() == Message.STATUS_RECEIVED
&& !conversation.isMuted()
&& (conversation.alwaysNotify() || wasHighlightedOrPrivate(message))
- && (!conversation.isWithStranger() || notificationsFromStrangers())
- ;
+ && (!conversation.isWithStranger() || notificationsFromStrangers());
}
private boolean notificationsFromStrangers() {
@@ -200,6 +210,7 @@ public class NotificationService {
final long startTime = preferences.getLong("quiet_hours_start", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
final long endTime = preferences.getLong("quiet_hours_end", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
final long nowTime = Calendar.getInstance().getTimeInMillis() % Config.MILLISECONDS_IN_DAY;
+
if (endTime < startTime) {
return nowTime > startTime || nowTime < endTime;
} else {
@@ -248,6 +259,7 @@ public class NotificationService {
}
}
}
+
private List<String> getBacklogConversations(Account account) {
final List<String> conversations = new ArrayList<>();
for (Iterator<Map.Entry<Conversation, AtomicInteger>> it = mBacklogMessageCounter.entrySet().iterator(); it.hasNext(); ) {
@@ -376,6 +388,7 @@ public class NotificationService {
private void updateNotification(final boolean notify, final List<String> conversations, final boolean summaryOnly) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService);
+ final boolean quiteHours = isQuietHours();
final boolean notifyOnlyOneChild = notify && conversations != null && conversations.size() == 1; //if this check is changed to > 0 catchup messages will create one notification per conversation
if (notifications.size() == 0) {
cancel(NOTIFICATION_ID);
@@ -385,24 +398,24 @@ public class NotificationService {
}
final Builder mBuilder;
if (notifications.size() == 1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
- mBuilder = buildSingleConversations(notifications.values().iterator().next(), notify);
- modifyForSoundVibrationAndLight(mBuilder, notify, preferences);
+ mBuilder = buildSingleConversations(notifications.values().iterator().next(), notify, quiteHours);
+ modifyForSoundVibrationAndLight(mBuilder, notify, quiteHours, preferences);
notify(NOTIFICATION_ID, mBuilder.build());
} else {
- mBuilder = buildMultipleConversation(notify);
+ mBuilder = buildMultipleConversation(notify, quiteHours);
if (notifyOnlyOneChild) {
mBuilder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN);
}
- modifyForSoundVibrationAndLight(mBuilder, notify, preferences);
+ modifyForSoundVibrationAndLight(mBuilder, notify, quiteHours, preferences);
if (!summaryOnly) {
for (Map.Entry<String, ArrayList<Message>> entry : notifications.entrySet()) {
String uuid = entry.getKey();
final boolean notifyThis = notifyOnlyOneChild ? conversations.contains(uuid) : notify;
- Builder singleBuilder = buildSingleConversations(entry.getValue(), notifyThis);
+ Builder singleBuilder = buildSingleConversations(entry.getValue(), notifyThis, quiteHours);
if (!notifyOnlyOneChild) {
singleBuilder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
}
- modifyForSoundVibrationAndLight(singleBuilder, notifyThis, preferences);
+ modifyForSoundVibrationAndLight(singleBuilder, notifyThis, quiteHours, preferences);
singleBuilder.setGroup(CONVERSATIONS_GROUP);
setNotificationColor(singleBuilder);
notify(entry.getKey(), NOTIFICATION_ID, singleBuilder.build());
@@ -413,13 +426,13 @@ public class NotificationService {
}
}
- private void modifyForSoundVibrationAndLight(Builder mBuilder, boolean notify, SharedPreferences preferences) {
+ private void modifyForSoundVibrationAndLight(Builder mBuilder, boolean notify, boolean quietHours, SharedPreferences preferences) {
final Resources resources = mXmppConnectionService.getResources();
final String ringtone = preferences.getString("notification_ringtone", resources.getString(R.string.notification_ringtone));
final boolean vibrate = preferences.getBoolean("vibrate_on_notification", resources.getBoolean(R.bool.vibrate_on_notification));
final boolean led = preferences.getBoolean("led", resources.getBoolean(R.bool.led));
final boolean headsup = preferences.getBoolean("notification_headsup", resources.getBoolean(R.bool.headsup_notifications));
- if (notify && !isQuietHours()) {
+ if (notify && !quietHours) {
if (vibrate) {
final int dat = 70;
final long[] pattern = {0, 3 * dat, dat, dat};
@@ -441,7 +454,7 @@ public class NotificationService {
setNotificationColor(mBuilder);
mBuilder.setDefaults(0);
if (led) {
- mBuilder.setLights(0xff0080FF, 2000, 3000);
+ mBuilder.setLights(LED_COLOR, 2000, 3000);
}
}
@@ -453,8 +466,8 @@ public class NotificationService {
}
}
- private Builder buildMultipleConversation(final boolean notify) {
- final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, notify ? "messages" : "silent_messages");
+ private Builder buildMultipleConversation(final boolean notify, final boolean quietHours) {
+ final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages"));
final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
style.setBigContentTitle(notifications.size()
+ " "
@@ -500,8 +513,8 @@ public class NotificationService {
return mBuilder;
}
- private Builder buildSingleConversations(final ArrayList<Message> messages, final boolean notify) {
- final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, notify ? "messages" : "silent_messages");
+ private Builder buildSingleConversations(final ArrayList<Message> messages, final boolean notify, final boolean quietHours) {
+ final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages"));
if (messages.size() >= 1) {
final Conversation conversation = (Conversation) messages.get(0).getConversation();
final UnreadConversation.Builder mUnreadBuilder = new UnreadConversation.Builder(conversation.getName().toString());
@@ -896,7 +909,7 @@ public class NotificationService {
}
mBuilder.setContentIntent(createOpenConversationsIntent());
mBuilder.setWhen(0);
- mBuilder.setPriority(Notification.PRIORITY_LOW);
+ mBuilder.setPriority(Notification.PRIORITY_MIN);
mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp);
if (Compatibility.runsTwentySix()) {
mBuilder.setChannelId(FOREGROUND_CHANNEL_ID);
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index 132ede7b6..f47893e48 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -47,12 +47,14 @@ import net.java.otr4j.session.SessionID;
import net.java.otr4j.session.SessionImpl;
import net.java.otr4j.session.SessionStatus;
+import org.conscrypt.Conscrypt;
import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.net.URL;
import java.security.SecureRandom;
+import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
@@ -125,7 +127,6 @@ import de.pixart.messenger.utils.ExceptionHelper;
import de.pixart.messenger.utils.MimeUtils;
import de.pixart.messenger.utils.Namespace;
import de.pixart.messenger.utils.OnPhoneContactsLoadedListener;
-import de.pixart.messenger.utils.PRNGFixes;
import de.pixart.messenger.utils.PhoneHelper;
import de.pixart.messenger.utils.QuickLoader;
import de.pixart.messenger.utils.ReplacingSerialSingleThreadExecutor;
@@ -490,11 +491,6 @@ public class XmppConnectionService extends Service {
}
public void attachFileToConversation(final Conversation conversation, final Uri uri, final String type, final UiCallback<Message> callback) {
- if (FileBackend.weOwnFile(this, uri)) {
- Log.d(Config.LOGTAG, "trying to attach file that belonged to us");
- callback.error(R.string.security_error_invalid_file_access, null);
- return;
- }
final Message message;
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
@@ -512,11 +508,6 @@ public class XmppConnectionService extends Service {
}
public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
- if (FileBackend.weOwnFile(this, uri)) {
- Log.d(Config.LOGTAG, "trying to attach file that belonged to us");
- callback.error(R.string.security_error_invalid_file_access, null);
- return;
- }
final String mimeType = MimeUtils.guessMimeTypeFromUri(this, uri);
final String compressPictures = getCompressPicturesPreference();
@@ -1100,7 +1091,11 @@ public class XmppConnectionService extends Service {
public void onCreate() {
OmemoSetting.load(this);
ExceptionHelper.init(getApplicationContext());
- PRNGFixes.apply();
+ try {
+ Security.insertProviderAt(Conscrypt.newProvider(), 1);
+ } catch (Throwable throwable) {
+ Log.e(Config.LOGTAG, "unable to initialize security provider", throwable);
+ }
Resolver.init(this);
this.mRandom = new SecureRandom();
updateMemorizingTrustmanager();
@@ -1160,7 +1155,7 @@ public class XmppConnectionService extends Service {
}
this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService");
+ this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Config.LOGTAG + ":Service");
toggleForegroundService();
updateUnreadCountBadge();
toggleScreenEventReceiver();
@@ -1875,8 +1870,7 @@ public class XmppConnectionService extends Service {
public List<Conversation> findAllConferencesWith(Contact contact) {
ArrayList<Conversation> results = new ArrayList<>();
for (final Conversation c : conversations) {
- if (c.getMode() == Conversation.MODE_MULTI
- && (c.getJid().asBareJid().equals(c.getJid().asBareJid()) || c.getMucOptions().isContactInRoom(contact))) {
+ if (c.getMode() == Conversation.MODE_MULTI && c.getMucOptions().isContactInRoom(contact)) {
results.add(c);
}
}
diff --git a/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java b/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
index f7711ce34..4f768b009 100644
--- a/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
+++ b/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
@@ -1,10 +1,8 @@
package de.pixart.messenger.ui;
import android.databinding.DataBindingUtil;
+import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.style.TypefaceSpan;
import android.view.View;
import android.widget.Toast;
@@ -12,6 +10,7 @@ import de.pixart.messenger.R;
import de.pixart.messenger.databinding.DialogBlockContactBinding;
import de.pixart.messenger.entities.Blockable;
import de.pixart.messenger.entities.Conversation;
+import de.pixart.messenger.ui.util.JidDialog;
import rocks.xmpp.addr.Jid;
public final class BlockContactDialog {
@@ -24,23 +23,19 @@ public final class BlockContactDialog {
binding.reportSpam.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE);
builder.setView(binding.getRoot());
- String value;
- SpannableString spannable;
+ final String value;
+ @StringRes int res;
if (blockable.getJid().getLocal() == null || blockable.getAccount().isBlocked(Jid.ofDomain(blockable.getJid().getDomain()))) {
builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
value = Jid.ofDomain(blockable.getJid().getDomain()).toString();
- spannable = new SpannableString(xmppActivity.getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text, value));
+ res = isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text;
} else {
int resBlockAction = blockable instanceof Conversation && ((Conversation) blockable).isWithStranger() ? R.string.block_stranger : R.string.action_block_contact;
builder.setTitle(isBlocked ? R.string.action_unblock_contact : resBlockAction);
value = blockable.getJid().asBareJid().toString();
- spannable = new SpannableString(xmppActivity.getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text, value));
+ res = isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text;
}
- int start = spannable.toString().indexOf(value);
- if (start >= 0) {
- spannable.setSpan(new TypefaceSpan("monospace"), start, start + value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- binding.text.setText(spannable);
+ binding.text.setText(JidDialog.style(xmppActivity, res, value));
builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, (dialog, which) -> {
if (isBlocked) {
xmppActivity.xmppConnectionService.sendUnblockRequest(blockable);
diff --git a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
index 070914bb8..901972c80 100644
--- a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
@@ -47,6 +47,7 @@ import de.pixart.messenger.entities.Contact;
import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.entities.MucOptions;
import de.pixart.messenger.entities.MucOptions.User;
+import de.pixart.messenger.services.EmojiService;
import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.services.XmppConnectionService.OnConversationUpdate;
import de.pixart.messenger.services.XmppConnectionService.OnMucRosterUpdate;
@@ -68,6 +69,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.ui.SettingsActivity.USE_BUNDLED_EMOJIS;
import static de.pixart.messenger.utils.StringUtils.changed;
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher, OnMediaLoaded {
@@ -284,6 +286,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ boolean useBundledEmoji = getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji));
+ new EmojiService(this).init(useBundledEmoji);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_details);
this.binding.changeConferenceButton.setOnClickListener(this.mChangeConferenceSettings);
this.binding.invite.setOnClickListener(inviteListener);
@@ -470,9 +474,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
name = user.getName();
}
menu.setHeaderTitle(name);
- MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
- MenuItem highlightInMuc = menu.findItem(R.id.highlight_in_muc);
- highlightInMuc.setVisible(true);
MucDetailsContextMenuHelper.configureMucDetailsContextMenu(this, menu, mConversation, user);
}
super.onCreateContextMenu(menu, v, menuInfo);
@@ -531,7 +532,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
updateView();
}
}
- this.binding.detailsMucAvatar.setImageBitmap(avatarService().get(mConversation, getPixel(Config.AVATAR_SIZE)));
}
@Override
@@ -566,6 +566,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
} else {
this.binding.detailsAccount.setVisibility(View.GONE);
}
+ this.binding.detailsMucAvatar.setImageBitmap(avatarService().get(mConversation, getPixel(Config.AVATAR_SIZE)));
this.binding.yourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
String roomName = mucOptions.getName();
String subject = mucOptions.getSubject();
@@ -593,7 +594,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
} else {
this.binding.mucSubject.setVisibility(View.GONE);
}
- this.binding.mucYourNick.setText(mucOptions.getActualNick());
+ this.binding.mucYourNick.setText(EmojiWrapper.transform(mucOptions.getActualNick()));
if (mucOptions.online()) {
this.binding.mucMoreDetails.setVisibility(View.VISIBLE);
this.binding.mucSettings.setVisibility(View.VISIBLE);
@@ -695,12 +696,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
Collections.sort(users);
for (final User user : users) {
ContactBinding binding = DataBindingUtil.inflate(inflater, R.layout.contact, this.binding.mucMembers, false);
- final Contact contact = user.getContact();
- final String name = user.getName();
this.setListItemBackgroundOnView(binding.getRoot());
- if (contact != null && contact.showInRoster()) {
- binding.getRoot().setOnClickListener((OnClickListener) view -> switchToContactDetails(contact));
- }
+ binding.getRoot().setOnClickListener(view1 -> highlightInMuc(mConversation, user.getName()));
registerForContextMenu(binding.getRoot());
binding.getRoot().setTag(user);
if (mAdvancedMode && user.getPgpKeyId() != 0) {
@@ -708,19 +705,21 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
binding.key.setOnClickListener(v -> viewPgpKey(user));
binding.key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
}
+ Contact contact = user.getContact();
+ String name = user.getName();
if (contact != null) {
- binding.contactDisplayName.setText(contact.getDisplayName());
- binding.contactJid.setText((name != null ? name + " \u2022 " : "") + getStatus(user));
+ binding.contactDisplayName.setText(EmojiWrapper.transform(contact.getDisplayName()));
+ binding.contactJid.setText((name != null ? EmojiWrapper.transform(name) + " \u2022 " : "") + getStatus(user));
} else {
- binding.contactDisplayName.setText(name == null ? "" : name);
+ binding.contactDisplayName.setText(name == null ? "" : EmojiWrapper.transform(name));
binding.contactJid.setText(getStatus(user));
}
loadAvatar(user, binding.contactPhoto);
if (user.getRole() == MucOptions.Role.NONE) {
- binding.contactDisplayName.setAlpha(INACTIVE_ALPHA);
- binding.key.setAlpha(INACTIVE_ALPHA);
binding.contactJid.setAlpha(INACTIVE_ALPHA);
+ binding.key.setAlpha(INACTIVE_ALPHA);
+ binding.contactDisplayName.setAlpha(INACTIVE_ALPHA);
binding.contactPhoto.setAlpha(INACTIVE_ALPHA);
}
this.binding.mucMembers.addView(binding.getRoot());
diff --git a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
index 1dfc80e3c..619c2b3f2 100644
--- a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
@@ -51,8 +51,10 @@ import de.pixart.messenger.ui.adapter.MediaAdapter;
import de.pixart.messenger.ui.interfaces.OnMediaLoaded;
import de.pixart.messenger.ui.util.Attachment;
import de.pixart.messenger.ui.util.GridManager;
+import de.pixart.messenger.ui.util.JidDialog;
import de.pixart.messenger.utils.Compatibility;
import de.pixart.messenger.utils.CryptoHelper;
+import de.pixart.messenger.utils.EmojiWrapper;
import de.pixart.messenger.utils.IrregularUnicodeDetector;
import de.pixart.messenger.utils.MenuDoubleTabUtil;
import de.pixart.messenger.utils.Namespace;
@@ -393,7 +395,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
ab.setDisplayShowCustomEnabled(true);
TextView abtitle = findViewById(android.R.id.text1);
TextView absubtitle = findViewById(android.R.id.text2);
- abtitle.setText(contact.getDisplayName());
+ abtitle.setText(EmojiWrapper.transform(contact.getDisplayName()));
abtitle.setSelected(true);
abtitle.setClickable(false);
absubtitle.setVisibility(View.GONE);
@@ -419,10 +421,10 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
binding.addContactButton.setOnClickListener(view -> {
final AlertDialog.Builder deleteFromRosterDialog = new AlertDialog.Builder(ContactDetailsActivity.this);
- deleteFromRosterDialog.setNegativeButton(getString(R.string.cancel), null);
- deleteFromRosterDialog.setTitle(getString(R.string.action_delete_contact));
- deleteFromRosterDialog.setMessage(getString(R.string.remove_contact_text, contact.getJid().toString()));
- deleteFromRosterDialog.setPositiveButton(getString(R.string.delete), removeFromRoster).create().show();
+ deleteFromRosterDialog.setNegativeButton(getString(R.string.cancel), null)
+ .setTitle(getString(R.string.action_delete_contact))
+ .setMessage(JidDialog.style(this, R.string.remove_contact_text, contact.getJid().toEscapedString()))
+ .setPositiveButton(getString(R.string.delete), removeFromRoster).create().show();
});
binding.detailsSendPresence.setOnCheckedChangeListener(null);
binding.detailsReceivePresence.setOnCheckedChangeListener(null);
@@ -443,7 +445,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
builder.append("\n");
}
}
- binding.statusMessage.setText(builder);
+ binding.statusMessage.setText(EmojiWrapper.transform(builder));
}
String resources = contact.getPresences().getMostAvailableResource();
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index a0f4bf894..f1df07e0b 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -32,6 +32,7 @@ import android.support.v7.view.menu.MenuBuilder;
import android.support.v7.view.menu.MenuPopupHelper;
import android.support.v7.widget.PopupMenu;
import android.text.Editable;
+import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
@@ -117,7 +118,6 @@ import de.pixart.messenger.utils.UIHelper;
import de.pixart.messenger.xmpp.XmppConnection;
import de.pixart.messenger.xmpp.chatstate.ChatState;
import de.pixart.messenger.xmpp.jingle.JingleConnection;
-import in.championswimmer.sfg.lib.SimpleFingerGestures;
import rocks.xmpp.addr.Jid;
import static de.pixart.messenger.ui.XmppActivity.EXTRA_ACCOUNT;
@@ -180,7 +180,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
};
private boolean reInitRequiredOnStart = true;
private MediaPreviewAdapter mediaPreviewAdapter;
- private SimpleFingerGestures gesturesDetector = new SimpleFingerGestures();
private OnClickListener clickToMuc = new OnClickListener() {
@Override
@@ -625,7 +624,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final boolean hideVoice = p.getBoolean("show_record_voice_btn", activity.getResources().getBoolean(R.bool.show_record_voice_btn));
PopupMenu popup = new PopupMenu(activity, v);
popup.inflate(R.menu.choose_attachment);
- Menu menu = popup.getMenu();
+ final Menu menu = popup.getMenu();
ConversationMenuConfigurator.configureQuickShareAttachmentMenu(conversation, menu, hideVoice);
popup.setOnMenuItemClickListener(attachmentItem -> {
switch (attachmentItem.getItemId()) {
@@ -658,7 +657,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
hideUnreadMessagesCount();
} else {
binding.scrollToBottomButton.setEnabled(true);
- binding.scrollToBottomButton.setVisibility(View.VISIBLE);
+ binding.scrollToBottomButton.show();
if (lastMessageUuid == null) {
lastMessageUuid = conversation.getLatestMessage().getUuid();
}
@@ -887,11 +886,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
commitAttachments();
return;
}
- final String body = binding.textinput.getText().toString();
+ final Editable text = this.binding.textinput.getText();
+ final String body = text == null ? "" : text.toString();
final Conversation conversation = this.conversation;
if (body.length() == 0 || conversation == null) {
return;
}
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL && trustKeysIfNeeded(REQUEST_TRUST_KEYS_TEXT)) {
+ return;
+ }
final Message message;
if (conversation.getCorrectingMessage() == null) {
message = new Message(conversation, body, conversation.getNextEncryption());
@@ -916,11 +919,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case Message.ENCRYPTION_PGP:
sendPgpMessage(message);
break;
- case Message.ENCRYPTION_AXOLOTL:
- if (!trustKeysIfNeeded(REQUEST_TRUST_KEYS_TEXT)) {
- sendMessage(message);
- }
- break;
default:
sendMessage(message);
}
@@ -989,9 +987,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
private void handlePositiveActivityResult(int requestCode, final Intent data) {
switch (requestCode) {
case REQUEST_TRUST_KEYS_TEXT:
- final String body = binding.textinput.getText().toString();
- Message message = new Message(conversation, body, conversation.getNextEncryption());
- sendMessage(message);
+ sendMessage();
break;
case REQUEST_TRUST_KEYS_ATTACHMENTS:
commitAttachments();
@@ -1139,13 +1135,21 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (conversation.getMode() == Conversation.MODE_MULTI) {
menuInviteContact.setVisible(true);
menuArchiveChat.setTitle(R.string.action_end_conversation_muc);
- menuGroupDetails.setVisible(true);
- menuContactDetails.setVisible(false);
} else {
menuInviteContact.setVisible(false);
menuArchiveChat.setTitle(R.string.action_end_conversation);
+ }
+ if (getFragmentManager().findFragmentById(R.id.secondary_fragment) != null) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ menuGroupDetails.setVisible(true);
+ menuContactDetails.setVisible(false);
+ } else {
+ menuGroupDetails.setVisible(false);
+ menuContactDetails.setVisible(true);
+ }
+ } else {
menuGroupDetails.setVisible(false);
- menuContactDetails.setVisible(true);
+ menuContactDetails.setVisible(false);
}
menuNeedHelp.setVisible(true);
menuSearchUpdates.setVisible(false);
@@ -1360,20 +1364,21 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|| m.isGeoUri()
|| m.isXmppUri()
|| m.treatAsDownloadable()
- || (t != null && t instanceof HttpDownloadConnection)) {
+ || t instanceof HttpDownloadConnection) {
copyUrl.setVisible(true);
}
if (m.isFileOrImage() && deleted && m.hasFileOnRemoteHost()) {
downloadFile.setVisible(true);
downloadFile.setTitle(activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, m)));
}
- boolean waitingOfferedSending = m.getStatus() == Message.STATUS_WAITING
+ final boolean waitingOfferedSending = m.getStatus() == Message.STATUS_WAITING
|| m.getStatus() == Message.STATUS_UNSEND
|| m.getStatus() == Message.STATUS_OFFERED;
- if ((t != null && !deleted) || waitingOfferedSending && m.needsUploading()) {
+ final boolean cancelable = (t != null && !deleted) || waitingOfferedSending && m.needsUploading();
+ if (cancelable) {
cancelTransmission.setVisible(true);
}
- if (m.isFileOrImage() && !deleted) {
+ if (m.isFileOrImage() && !deleted && !cancelable) {
String path = m.getRelativeFilePath();
Log.d(Config.LOGTAG, "Path = " + path);
if (path == null || !path.startsWith("/") || path.contains(FileBackend.getConversationsDirectory("null"))) {
@@ -1381,7 +1386,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
deleteFile.setTitle(activity.getString(R.string.delete_x_file, UIHelper.getFileDescriptionString(activity, m)));
}
}
- if (m.getStatus() == Message.STATUS_SEND_FAILED && m.getErrorMessage() != null) {
+ if (m.getStatus() == Message.STATUS_SEND_FAILED && m.getErrorMessage() != null && !Message.ERROR_MESSAGE_CANCELLED.equals(m.getErrorMessage())) {
showErrorMessage.setVisible(true);
}
}
@@ -1716,7 +1721,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
builder.setView(dialogView);
builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.delete_messages), (dialog, which) -> {
+ builder.setPositiveButton(getString(R.string.confirm), (dialog, which) -> {
this.activity.xmppConnectionService.clearConversationHistory(conversation);
if (endConversationCheckBox.isChecked()) {
this.activity.xmppConnectionService.archiveConversation(conversation);
@@ -1879,12 +1884,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
refresh();
}
- private void deleteFile(Message message) {
- if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- activity.onConversationsListItemUpdated();
- refresh();
- }
+ private void deleteFile(final Message message) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setTitle(R.string.delete_file_dialog);
+ builder.setMessage(R.string.delete_file_dialog_msg);
+ builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
+ if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ activity.onConversationsListItemUpdated();
+ refresh();
+ }
+ });
+ builder.create().show();
}
public void resendMessage(final Message message) {
@@ -1951,7 +1963,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (transferable != null) {
transferable.cancel();
} else if (message.getStatus() != Message.STATUS_RECEIVED) {
- activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, Message.ERROR_MESSAGE_CANCELLED);
}
}
@@ -2207,48 +2219,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.messagesView.post(this::fireReadEvent);
//TODO if we only do this when this fragment is running on main it won't *bing* in tablet layout which might be unnecessary since we can *see* it
activity.xmppConnectionService.getNotificationService().setOpenConversation(this.conversation);
-
- // todo temporarly disable swipe gestures
- /*
- gesturesDetector.setOnFingerGestureListener(new SimpleFingerGestures.OnFingerGestureListener() {
- @Override
- public boolean onSwipeUp(int fingers, long gestureDuration, double gestureDistance) {
- return false;
- }
-
- @Override
- public boolean onSwipeDown(int fingers, long gestureDuration, double gestureDistance) {
- return false;
- }
-
- @Override
- public boolean onSwipeLeft(int fingers, long gestureDuration, double gestureDistance) {
- return false;
- }
-
- @Override
- public boolean onSwipeRight(int fingers, long gestureDuration, double gestureDistance) {
- activity.onBackPressed();
- return false;
- }
-
- @Override
- public boolean onPinch(int fingers, long gestureDuration, double gestureDistance) {
- return false;
- }
-
- @Override
- public boolean onUnpinch(int fingers, long gestureDuration, double gestureDistance) {
- return false;
- }
-
- @Override
- public boolean onDoubleTap(int fingers) {
- return false;
- }
- });
- this.binding.messagesView.setOnTouchListener(gesturesDetector);
- */
return true;
}
@@ -2262,7 +2232,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return;
}
this.binding.scrollToBottomButton.setEnabled(false);
- this.binding.scrollToBottomButton.setVisibility(View.GONE);
+ this.binding.scrollToBottomButton.hide();
this.binding.unreadCountCustomView.setVisibility(View.GONE);
}
@@ -2282,9 +2252,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final String nick = extras.getString(ConversationsActivity.EXTRA_NICK);
final boolean asQuote = extras.getBoolean(ConversationsActivity.EXTRA_AS_QUOTE);
final boolean pm = extras.getBoolean(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, false);
+ final boolean doNotAppend = extras.getBoolean(ConversationsActivity.EXTRA_DO_NOT_APPEND, false);
final List<Uri> uris = extractUris(extras);
if (uris != null && uris.size() > 0) {
- mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uris));
+ final List<Uri> cleanedUris = cleanUris(new ArrayList<>(uris));
+ mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), cleanedUris));
toggleInputMethod();
return;
}
@@ -2307,7 +2279,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (text != null && asQuote) {
quoteText(text);
} else {
- appendText(text);
+ appendText(text, doNotAppend);
}
}
final Message message = downloadUuid == null ? null : conversation.findMessageWithFileAndUuid(downloadUuid);
@@ -2329,6 +2301,18 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
+ private List<Uri> cleanUris(List<Uri> uris) {
+ Iterator<Uri> iterator = uris.iterator();
+ while (iterator.hasNext()) {
+ final Uri uri = iterator.next();
+ if (FileBackend.weOwnFile(getActivity(), uri)) {
+ iterator.remove();
+ Toast.makeText(getActivity(), R.string.security_violation_not_attaching_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+ return uris;
+ }
+
private boolean showBlockSubmenu(View view) {
final Jid jid = conversation.getJid();
if (jid.getLocal() == null) {
@@ -2386,6 +2370,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
showSnackbar(R.string.remote_server_not_found, R.string.leave, leaveMuc);
}
break;
+ case REMOTE_SERVER_TIMEOUT:
+ if (conversation.receivedMessagesCount() > 0) {
+ showSnackbar(R.string.remote_server_timeout, R.string.try_again, joinMuc);
+ } else {
+ showSnackbar(R.string.remote_server_timeout, R.string.leave, leaveMuc);
+ }
+ break;
case PASSWORD_REQUIRED:
showSnackbar(R.string.conference_requires_password, R.string.enter_password, enterPassword);
break;
@@ -2832,11 +2823,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
});
}
- public void appendText(String text) {
+ public void appendText(String text, final boolean doNotAppend) {
if (text == null) {
return;
}
- String previous = this.binding.textinput.getText().toString();
+ final Editable editable = this.binding.textinput.getText();
+ String previous = editable == null ? "" : editable.toString();
+ if (doNotAppend && !TextUtils.isEmpty(previous)) {
+ Toast.makeText(getActivity(), R.string.already_drafting_message, Toast.LENGTH_LONG).show();
+ return;
+ }
if (UIHelper.isLastLineQuote(previous)) {
text = '\n' + text;
} else if (previous.length() != 0 && !Character.isWhitespace(previous.charAt(previous.length() - 1))) {
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java
index 2d07be344..1f2c68159 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java
@@ -108,6 +108,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
public static final String EXTRA_AS_QUOTE = "as_quote";
public static final String EXTRA_NICK = "nick";
public static final String EXTRA_IS_PRIVATE_MESSAGE = "pm";
+ public static final String EXTRA_DO_NOT_APPEND = "do_not_append";
public static final String ACTION_DESTROY_MUC = "de.pixart.messenger.DESTROY_MUC";
public static final int REQUEST_OPEN_MESSAGE = 0x9876;
public static final int REQUEST_PLAY_PAUSE = 0x5432;
diff --git a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
index be4222e78..9d5bd279b 100644
--- a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
@@ -73,6 +73,7 @@ import de.pixart.messenger.xmpp.XmppConnection.Features;
import de.pixart.messenger.xmpp.forms.Data;
import de.pixart.messenger.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
+
public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate, OnUpdateBlocklist,
OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
@@ -98,6 +99,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
public void onClick(final View v) {
final String password = binding.accountPassword.getText().toString();
final boolean wasDisabled = mAccount != null && mAccount.getStatus() == Account.State.DISABLED;
+ final boolean accountInfoEdited = accountInfoEdited();
if (!mInitMode && passwordChangedInMagicCreateMode()) {
gotoChangePassword(password);
@@ -106,7 +108,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
if (mInitMode && mAccount != null) {
mAccount.setOption(Account.OPTION_DISABLED, false);
}
- if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
+ if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited) {
mAccount.setOption(Account.OPTION_DISABLED, false);
if (!xmppConnectionService.updateAccount(mAccount)) {
Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
@@ -122,7 +124,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
- boolean openRegistrationUrl = registerNewAccount && mAccount != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB;
+ boolean openRegistrationUrl = registerNewAccount && !accountInfoEdited && mAccount != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB;
boolean openPaymentUrl = mAccount != null && mAccount.getStatus() == Account.State.PAYMENT_REQUIRED;
final boolean redirectionWorthyStatus = openPaymentUrl || openRegistrationUrl;
URL url = connection != null && redirectionWorthyStatus ? connection.getRedirectionUrl() : null;
@@ -263,7 +265,8 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
if (mAccount != null
&& mAccount.getStatus() != Account.State.ONLINE
&& mFetchingAvatar) {
- startActivity(new Intent(getApplicationContext(), ManageAccountActivity.class));
+ //TODO: maybe better redirect to StartConversationActivity
+ startActivity(new Intent(this, ManageAccountActivity.class));
overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
finish();
} else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
@@ -486,7 +489,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} else {
XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
URL url = connection != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB ? connection.getRedirectionUrl() : null;
- if (url != null && this.binding.accountRegisterNew.isChecked()) {
+ if (url != null && this.binding.accountRegisterNew.isChecked() && !accountInfoEdited) {
this.binding.saveButton.setText(R.string.open_website);
} else {
this.binding.saveButton.setText(R.string.next);
@@ -834,7 +837,6 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("image/png");
startActivity(Intent.createChooser(intent, getText(R.string.share_with)));
-
}
private void changeMoreTableVisibility(boolean visible) {
@@ -1125,7 +1127,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
hasKeys = true;
}
}
- if (hasKeys && Config.supportOmemo()) {
+ if (hasKeys && Config.supportOmemo()) { //TODO: either the button should be visible if we print an active device or the device list should be fed with reactived devices
this.binding.otherDeviceKeysCard.setVisibility(View.VISIBLE);
Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
if (otherDevices == null || otherDevices.isEmpty()) {
diff --git a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
index b8f11be12..10c68cc1d 100644
--- a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
@@ -22,6 +22,7 @@ import java.util.List;
import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.entities.Account;
+import de.pixart.messenger.utils.CryptoHelper;
import rocks.xmpp.addr.Jid;
public class MagicCreateActivity extends XmppActivity implements TextWatcher, AdapterView.OnItemSelectedListener {
@@ -29,12 +30,8 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher, Ad
private TextView mFullJidDisplay;
private EditText mUsername;
private Spinner mServer;
- private SecureRandom mRandom;
String domain = null;
- private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?";
- private static final int PW_LENGTH = 10;
-
@Override
protected void refreshUiReal() {
@@ -74,7 +71,6 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher, Ad
mServer.setSelection(defaultServer);
mServer.setOnItemSelectedListener(this);
adapter.setDropDownViewResource(android.R.layout.select_dialog_singlechoice);
- mRandom = new SecureRandom();
Button next = findViewById(R.id.create_account);
next.setOnClickListener(v -> {
try {
@@ -90,7 +86,7 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher, Ad
mUsername.setError(null);
Account account = xmppConnectionService.findAccountByJid(jid);
if (account == null) {
- account = new Account(jid, createPassword());
+ account = new Account(jid, CryptoHelper.createPassword(new SecureRandom()));
account.setOption(Account.OPTION_REGISTER, true);
account.setOption(Account.OPTION_DISABLED, true);
account.setOption(Account.OPTION_MAGIC_CREATE, true);
@@ -115,14 +111,6 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher, Ad
mUsername.addTextChangedListener(this);
}
- private String createPassword() {
- StringBuilder builder = new StringBuilder(PW_LENGTH);
- for (int i = 0; i < PW_LENGTH; ++i) {
- builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1)));
- }
- return builder.toString();
- }
-
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
diff --git a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java b/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java
index b34e49653..a8285d594 100644
--- a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java
@@ -4,6 +4,8 @@ import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -13,37 +15,40 @@ import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.ActionBar;
+import android.support.v7.view.menu.MenuBuilder;
+import android.support.v7.view.menu.MenuPopupHelper;
+import android.support.v7.widget.PopupMenu;
import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
import android.widget.ImageView;
import android.widget.Toast;
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.resource.drawable.GlideDrawable;
-import com.bumptech.glide.request.animation.GlideAnimation;
-import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
-import com.github.chrisbanes.photoview.PhotoView;
-import com.github.chrisbanes.photoview.PhotoViewAttacher;
+import com.davemorrissey.labs.subscaleview.ImageSource;
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
import com.github.rtoshiro.view.video.FullscreenVideoLayout;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.persistance.FileBackend;
import de.pixart.messenger.utils.ExifHelper;
+import de.pixart.messenger.utils.MimeUtils;
import static de.pixart.messenger.persistance.FileBackend.close;
-public class ShowFullscreenMessageActivity extends XmppActivity {
+public class MediaViewerActivity extends XmppActivity {
Integer oldOrientation;
- PhotoView mImage;
+ SubsamplingScaleImageView mImage;
FullscreenVideoLayout mVideo;
ImageView mFullscreenbutton;
Uri mFileUri;
@@ -52,6 +57,22 @@ public class ShowFullscreenMessageActivity extends XmppActivity {
int height = 0;
int width = 0;
int rotation = 0;
+ boolean isImage = false;
+ boolean isVideo = false;
+
+ public static String getMimeType(String path) {
+ try {
+ String type = null;
+ String extension = path.substring(path.lastIndexOf(".") + 1, path.length());
+ if (extension != null) {
+ type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ }
+ return type;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -73,18 +94,59 @@ public class ShowFullscreenMessageActivity extends XmppActivity {
getWindow().setAttributes(layout);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- setContentView(R.layout.activity_fullscreen_message);
+ setContentView(R.layout.activity_media_viewer);
mImage = findViewById(R.id.message_image_view);
mVideo = findViewById(R.id.message_video_view);
mFullscreenbutton = findViewById(R.id.vcv_img_fullscreen);
fab = findViewById(R.id.fab);
fab.setOnClickListener(v -> {
- mVideo.reset();
- shareWith(mFile);
+ PopupMenu popup = new PopupMenu(MediaViewerActivity.this, v);
+ popup.inflate(R.menu.media_viewer);
+ final Menu menu = popup.getMenu();
+ MenuItem delete = menu.findItem(R.id.action_delete);
+ MenuItem open = menu.findItem(R.id.action_open);
+ Log.d(Config.LOGTAG, "Path = " + mFile.toString());
+ if (mFile == null || !mFile.toString().startsWith("/") || mFile.toString().contains(FileBackend.getConversationsDirectory("null"))) {
+ delete.setVisible(true);
+ } else {
+ delete.setVisible(false);
+ }
+ if (isVideo) {
+ if (isDarkTheme()) {
+ open.setIcon(R.drawable.ic_video_white_24dp);
+ } else {
+ open.setIcon(R.drawable.ic_video_black_24dp);
+ }
+ } else if (isImage) {
+ if (isDarkTheme()) {
+ open.setIcon(R.drawable.ic_image_white_24dp);
+ } else {
+ open.setIcon(R.drawable.ic_image_black_24dp);
+ }
+ }
+ popup.setOnMenuItemClickListener(item -> {
+ switch (item.getItemId()) {
+ case R.id.action_share:
+ share();
+ break;
+ case R.id.action_open:
+ open();
+ break;
+ case R.id.action_delete:
+ deleteFile();
+ break;
+ default:
+ return false;
+ }
+ return true;
+ });
+ MenuPopupHelper menuHelper = new MenuPopupHelper(MediaViewerActivity.this, (MenuBuilder) menu, v);
+ menuHelper.setForceShowIcon(true);
+ menuHelper.show();
});
}
- private void shareWith(File mFile) {
+ private void share() {
Intent share = new Intent(Intent.ACTION_SEND);
share.setType(getMimeType(mFile.toString()));
share.putExtra(Intent.EXTRA_STREAM, FileBackend.getUriForFile(this, mFile));
@@ -96,18 +158,35 @@ public class ShowFullscreenMessageActivity extends XmppActivity {
}
}
- public static String getMimeType(String path) {
+ private void deleteFile() {
+ this.xmppConnectionService.getFileBackend().deleteFile(mFile);
+ finish();
+ }
+
+ private void open() {
+ Uri uri;
try {
- String type = null;
- String extension = path.substring(path.lastIndexOf(".") + 1, path.length());
- if (extension != null) {
- type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- }
- return type;
- } catch (Exception e) {
- e.printStackTrace();
+ uri = FileBackend.getUriForFile(this, mFile);
+ } catch (SecurityException e) {
+ Log.d(Config.LOGTAG, "No permission to access " + mFile.getAbsolutePath(), e);
+ Toast.makeText(this, this.getString(R.string.no_permission_to_access_x, mFile.getAbsolutePath()), Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String mime = MimeUtils.guessMimeTypeFromUri(this, uri);
+ Intent openIntent = new Intent(Intent.ACTION_VIEW);
+ openIntent.setDataAndType(uri, mime);
+ openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ PackageManager manager = this.getPackageManager();
+ List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
+ if (info.size() == 0) {
+ openIntent.setDataAndType(uri, "*/*");
+ }
+ try {
+ this.startActivity(openIntent);
+ finish();
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
}
- return null;
}
@Override
@@ -125,34 +204,38 @@ public class ShowFullscreenMessageActivity extends XmppActivity {
mFile = new File(mFileUri.getPath());
if (mFileUri != null && mFile.exists() && mFile.length() > 0) {
try {
- DisplayImage(mFile);
+ isImage = true;
+ DisplayImage(mFile, mFileUri);
} catch (Exception e) {
+ isImage = false;
Log.d(Config.LOGTAG, "Illegal exeption :" + e);
- Toast.makeText(ShowFullscreenMessageActivity.this, getString(R.string.error_file_corrupt), Toast.LENGTH_SHORT).show();
+ Toast.makeText(MediaViewerActivity.this, getString(R.string.error_file_corrupt), Toast.LENGTH_SHORT).show();
finish();
}
} else {
- Toast.makeText(ShowFullscreenMessageActivity.this, getString(R.string.file_deleted), Toast.LENGTH_SHORT).show();
+ Toast.makeText(MediaViewerActivity.this, getString(R.string.file_deleted), Toast.LENGTH_SHORT).show();
}
} else if (intent.hasExtra("video")) {
mFileUri = intent.getParcelableExtra("video");
mFile = new File(mFileUri.getPath());
if (mFileUri != null && mFile.exists() && mFile.length() > 0) {
try {
+ isVideo = true;
DisplayVideo(mFileUri);
} catch (Exception e) {
+ isVideo = false;
Log.d(Config.LOGTAG, "Illegal exeption :" + e);
- Toast.makeText(ShowFullscreenMessageActivity.this, getString(R.string.error_file_corrupt), Toast.LENGTH_SHORT).show();
+ Toast.makeText(MediaViewerActivity.this, getString(R.string.error_file_corrupt), Toast.LENGTH_SHORT).show();
finish();
}
} else {
- Toast.makeText(ShowFullscreenMessageActivity.this, getString(R.string.file_deleted), Toast.LENGTH_SHORT).show();
+ Toast.makeText(MediaViewerActivity.this, getString(R.string.file_deleted), Toast.LENGTH_SHORT).show();
}
}
}
}
- private void DisplayImage(final File file) {
+ private void DisplayImage(final File file, final Uri FileUri) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(new File(file.getPath()).getAbsolutePath(), options);
@@ -163,19 +246,9 @@ public class ShowFullscreenMessageActivity extends XmppActivity {
if (useAutoRotateScreen()) {
rotateScreen(width, height, rotation);
}
- final PhotoViewAttacher mAttacher = new PhotoViewAttacher(mImage);
mImage.setVisibility(View.VISIBLE);
try {
- Glide.with(this)
- .load(file)
- .dontAnimate()
- .into(new GlideDrawableImageViewTarget(mImage) {
- @Override
- public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
- super.onResourceReady(resource, animation);
- mAttacher.update();
- }
- });
+ mImage.setImage(ImageSource.uri(FileUri));
} catch (Exception e) {
Toast.makeText(this, getString(R.string.error_file_corrupt), Toast.LENGTH_LONG).show();
e.printStackTrace();
diff --git a/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java b/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java
index 7aa0d3f64..2d535a44c 100644
--- a/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java
@@ -2,9 +2,6 @@ package de.pixart.messenger.ui;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import java.util.ArrayList;
@@ -46,27 +43,22 @@ public class ShareViaAccountActivity extends XmppActivity {
setSupportActionBar(findViewById(R.id.toolbar));
configureActionBar(getSupportActionBar());
accountListView = findViewById(R.id.account_list);
- this.mAccountAdapter = new AccountAdapter(this, accountList);
+ this.mAccountAdapter = new AccountAdapter(this, accountList, false);
accountListView.setAdapter(this.mAccountAdapter);
- accountListView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View view,
- int position, long arg3) {
- final Account account = accountList.get(position);
- final String body = getIntent().getStringExtra(EXTRA_BODY);
-
- try {
- final Jid contact = Jid.of(getIntent().getStringExtra(EXTRA_CONTACT));
- final Conversation conversation = xmppConnectionService.findOrCreateConversation(
- account, contact, false, false);
- switchToConversation(conversation, body, false);
- } catch (IllegalArgumentException e) {
- // ignore error
- }
+ accountListView.setOnItemClickListener((arg0, view, position, arg3) -> {
+ final Account account = accountList.get(position);
+ final String body = getIntent().getStringExtra(EXTRA_BODY);
- finish();
+ try {
+ final Jid contact = Jid.of(getIntent().getStringExtra(EXTRA_CONTACT));
+ final Conversation conversation = xmppConnectionService.findOrCreateConversation(
+ account, contact, false, false);
+ switchToConversation(conversation, body);
+ } catch (IllegalArgumentException e) {
+ // ignore error
}
+
+ finish();
});
}
@@ -91,7 +83,7 @@ public class ShareViaAccountActivity extends XmppActivity {
final Jid contact = Jid.of(getIntent().getStringExtra(EXTRA_CONTACT));
final Conversation conversation = xmppConnectionService.findOrCreateConversation(
account, contact, false, false);
- switchToConversation(conversation, body, false);
+ switchToConversation(conversation, body);
} catch (IllegalArgumentException e) {
// ignore error
}
diff --git a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
index b3ff43d3d..a12d398f2 100644
--- a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
@@ -34,9 +34,9 @@ import de.pixart.messenger.utils.MenuDoubleTabUtil;
import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS;
public class ShowLocationActivity extends XmppActivity {
+ FloatingActionButton fab;
private Location location;
private String mLocationName;
- FloatingActionButton fab;
private static String getAddress(Context context, Location location) {
double longitude = location.getLongitude();
@@ -159,6 +159,22 @@ public class ShowLocationActivity extends XmppActivity {
}
}
+ private void navigate(Location location) {
+ if (location == null) {
+ Log.d(Config.LOGTAG, "No location given");
+ return;
+ }
+ double longitude = location.getLongitude();
+ double latitude = location.getLatitude();
+ try {
+ Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("google.navigation:q=" + String.valueOf(latitude) + "," + String.valueOf(longitude)));
+ startActivity(intent);
+ overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.no_application_found_to_display_location, Toast.LENGTH_SHORT).show();
+ }
+ }
+
private class getAddressAsync extends AsyncTask<Void, Void, Void> {
String address = null;
@@ -186,20 +202,4 @@ public class ShowLocationActivity extends XmppActivity {
showLocation(location, address);
}
}
-
- private void navigate (Location location) {
- if (location == null) {
- Log.d(Config.LOGTAG, "No location given");
- return;
- }
- double longitude = location.getLongitude();
- double latitude = location.getLatitude();
- try {
- Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude)));
- startActivity(intent);
- overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.no_application_found_to_display_location, Toast.LENGTH_SHORT).show();
- }
- }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
index 52bff613c..5b337abb6 100644
--- a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
@@ -26,10 +26,7 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
-import android.text.SpannableString;
-import android.text.Spanned;
import android.text.TextWatcher;
-import android.text.style.TypefaceSpan;
import android.util.Log;
import android.util.Pair;
import android.view.ContextMenu;
@@ -71,6 +68,7 @@ import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.services.XmppConnectionService.OnRosterUpdate;
import de.pixart.messenger.ui.adapter.ListItemAdapter;
import de.pixart.messenger.ui.interfaces.OnBackendConnected;
+import de.pixart.messenger.ui.util.JidDialog;
import de.pixart.messenger.ui.util.PendingItem;
import de.pixart.messenger.ui.util.SoftKeyboardUtils;
import de.pixart.messenger.utils.MenuDoubleTabUtil;
@@ -429,7 +427,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.action_delete_contact);
- builder.setMessage(getString(R.string.remove_contact_text, contact.getJid()));
+ builder.setMessage(JidDialog.style(this, R.string.remove_contact_text, contact.getJid().toEscapedString()));
builder.setPositiveButton(R.string.delete, (dialog, which) -> {
xmppConnectionService.deleteContactOnServer(contact);
filter(mSearchEditText.getText().toString());
@@ -444,7 +442,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_bookmark);
- builder.setMessage(getString(R.string.remove_bookmark_text, bookmark.getJid()));
+ builder.setMessage(JidDialog.style(this, R.string.remove_bookmark_text, bookmark.getJid().toEscapedString()));
builder.setPositiveButton(R.string.delete, (dialog, which) -> {
bookmark.setConversation(null);
Account account = bookmark.getAccount();
@@ -495,7 +493,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
contact.setServerName(invite.getName());
}
if (contact.isSelf()) {
- switchToConversation(contact, null);
+ switchToConversation(contact);
return true;
} else if (contact.showInRoster()) {
throw new EnterJidDialog.JidError(getString(R.string.contact_already_exists));
@@ -504,7 +502,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
if (invite != null && invite.hasFingerprints()) {
xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
}
- switchToConversation(contact, invite == null ? null : invite.getBody());
+ switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody());
return true;
}
});
@@ -552,9 +550,14 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
return xmppConnectionService.findAccountByJid(jid);
}
- protected void switchToConversation(Contact contact, String body) {
+ protected void switchToConversation(Contact contact) {
Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true);
- switchToConversation(conversation, body, false);
+ switchToConversation(conversation);
+ }
+
+ protected void switchToConversationDoNotAppend(Contact contact, String body) {
+ Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true);
+ switchToConversationDoNotAppend(conversation, body);
}
@Override
@@ -790,7 +793,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
if (invite.isAction(XmppUri.ACTION_JOIN)) {
Conversation muc = xmppConnectionService.findFirstMuc(invite.getJid());
if (muc != null) {
- switchToConversation(muc, invite.getBody(), false);
+ switchToConversationDoNotAppend(muc, invite.getBody());
return true;
} else {
showJoinConferenceDialog(invite.getJid().asBareJid().toString());
@@ -812,7 +815,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
if (invite.account != null) {
xmppConnectionService.getShortcutService().report(contact);
}
- switchToConversation(contact, invite.getBody());
+ switchToConversationDoNotAppend(contact, invite.getBody());
}
return true;
} else {
@@ -834,19 +837,13 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
View view = getLayoutInflater().inflate(R.layout.dialog_verify_fingerprints, null);
final CheckBox isTrustedSource = view.findViewById(R.id.trusted_source);
TextView warning = view.findViewById(R.id.warning);
- String jid = contact.getJid().asBareJid().toString();
- SpannableString spannable = new SpannableString(getString(R.string.verifying_omemo_keys_trusted_source, jid, contact.getDisplayName()));
- int start = spannable.toString().indexOf(jid);
- if (start >= 0) {
- spannable.setSpan(new TypefaceSpan("monospace"), start, start + jid.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- warning.setText(spannable);
+ warning.setText(JidDialog.style(this, R.string.verifying_omemo_keys_trusted_source, contact.getJid().asBareJid().toEscapedString(), contact.getDisplayName()));
builder.setView(view);
builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
if (isTrustedSource.isChecked() && invite.hasFingerprints()) {
xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
}
- switchToConversation(contact, invite.getBody());
+ switchToConversationDoNotAppend(contact, invite.getBody());
});
builder.setNegativeButton(R.string.cancel, (dialog, which) -> StartConversationActivity.this.finish());
AlertDialog dialog = builder.create();
@@ -1128,13 +1125,13 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
- assert (0 <= position && position < fragments.length);
FragmentTransaction trans = fragmentManager.beginTransaction();
trans.remove(fragments[position]);
trans.commit();
fragments[position] = null;
}
+ @NonNull
@Override
public Fragment instantiateItem(@NonNull ViewGroup container, int position) {
Fragment fragment = getItem(position);
@@ -1167,8 +1164,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
}
}
- public Fragment getItem(int position) {
- assert (0 <= position && position < fragments.length);
+ Fragment getItem(int position) {
if (fragments[position] == null) {
final MyListFragment listFragment = new MyListFragment();
if (position == 1) {
diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
index d01f29ac5..dcf3afed4 100644
--- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
@@ -447,15 +447,19 @@ public abstract class XmppActivity extends ActionBarActivity {
}
public void switchToConversation(Conversation conversation) {
- switchToConversation(conversation, null, false);
+ switchToConversation(conversation, null);
}
public void switchToConversationAndQuote(Conversation conversation, String text) {
switchToConversation(conversation, text, true, null, false, false);
}
- public void switchToConversation(Conversation conversation, String text, boolean newTask) {
- switchToConversation(conversation, text, false, null, false, newTask);
+ public void switchToConversation(Conversation conversation, String text) {
+ switchToConversation(conversation, text, false, null, false, false);
+ }
+
+ public void switchToConversationDoNotAppend(Conversation conversation, String text) {
+ switchToConversation(conversation, text, false, null, false, true);
}
public void highlightInMuc(Conversation conversation, String nick) {
@@ -466,7 +470,7 @@ public abstract class XmppActivity extends ActionBarActivity {
switchToConversation(conversation, null, false, nick, true, false);
}
- private void switchToConversation(Conversation conversation, String text, boolean asQuote, String nick, boolean pm, boolean newTask) {
+ private void switchToConversation(Conversation conversation, String text, boolean asQuote, String nick, boolean pm, boolean doNotAppend) {
Intent intent = new Intent(this, ConversationsActivity.class);
intent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION);
intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
@@ -480,13 +484,10 @@ public abstract class XmppActivity extends ActionBarActivity {
intent.putExtra(ConversationsActivity.EXTRA_NICK, nick);
intent.putExtra(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, pm);
}
- if (newTask) {
- intent.setFlags(intent.getFlags()
- | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- } else {
- intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (doNotAppend) {
+ intent.putExtra(ConversationsActivity.EXTRA_DO_NOT_APPEND, true);
}
+ intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
finish();
@@ -598,18 +599,6 @@ public abstract class XmppActivity extends ActionBarActivity {
}
}
- protected boolean noAccountUsesPgp() {
- if (!hasPgp()) {
- return true;
- }
- for (Account account : xmppConnectionService.getAccounts()) {
- if (account.getPgpId() != 0) {
- return false;
- }
- }
- return true;
- }
-
@SuppressWarnings("deprecation")
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
protected void setListItemBackgroundOnView(View view) {
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 55dd6d65f..d61510611 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
@@ -32,6 +32,7 @@ import de.pixart.messenger.entities.Transferable;
import de.pixart.messenger.ui.ConversationFragment;
import de.pixart.messenger.ui.XmppActivity;
import de.pixart.messenger.ui.util.StyledAttributes;
+import de.pixart.messenger.ui.widget.FailedCountCustomView;
import de.pixart.messenger.ui.widget.UnreadCountCustomView;
import de.pixart.messenger.utils.EmojiWrapper;
import de.pixart.messenger.utils.IrregularUnicodeDetector;
@@ -125,7 +126,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte
}
if (failedCount > 0) {
viewHolder.failedCount.setVisibility(View.VISIBLE);
- viewHolder.failedCount.setUnreadCount(failedCount);
+ viewHolder.failedCount.setFailedCount(failedCount);
} else {
viewHolder.failedCount.setVisibility(View.GONE);
}
@@ -356,7 +357,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte
private TextView sender;
private ImageView notificationIcon;
private UnreadCountCustomView unreadCount;
- private UnreadCountCustomView failedCount;
+ private FailedCountCustomView failedCount;
private ImageView receivedStatus;
private ImageView readStatus;
private ImageView avatar;
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
index 17479cf99..e99353f13 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
@@ -8,7 +8,6 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
-import android.support.text.emoji.widget.EmojiTextView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -28,6 +27,7 @@ import de.pixart.messenger.entities.ListItem;
import de.pixart.messenger.ui.SettingsActivity;
import de.pixart.messenger.ui.XmppActivity;
import de.pixart.messenger.ui.util.StyledAttributes;
+import de.pixart.messenger.utils.EmojiWrapper;
import de.pixart.messenger.utils.IrregularUnicodeDetector;
import de.pixart.messenger.utils.UIHelper;
import rocks.xmpp.addr.Jid;
@@ -97,7 +97,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
} else {
viewHolder.jid.setVisibility(View.GONE);
}
- viewHolder.name.setText(item.getDisplayName());
+ viewHolder.name.setText(EmojiWrapper.transform(item.getDisplayName()));
if (tags.size() != 0) {
for (ListItem.Tag tag : tags) {
offline = tag.getOffline() == 1;
@@ -220,7 +220,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
}
private static class ViewHolder {
- private EmojiTextView name;
+ private TextView name;
private TextView jid;
private ImageView avatar;
private FlowLayout tags;
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
index b13ccb910..bc65fd68a 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
@@ -275,7 +275,11 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
if (isResendable && file.exists()) {
info = getContext().getString(R.string.send_failed_resend);
} else {
- info = getContext().getString(R.string.send_failed);
+ if (Message.ERROR_MESSAGE_CANCELLED.equals(message.getErrorMessage())) {
+ info = getContext().getString(R.string.cancelled);
+ } else {
+ info = getContext().getString(R.string.send_failed);
+ }
}
error = true;
break;
@@ -408,10 +412,15 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
viewHolder.download_button.setText(add_contact);
viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_account_card_details_grey600_48dp, 0, 0, 0);
viewHolder.download_button.setOnClickListener(v -> {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(body));
- activity.startActivity(intent);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(body));
+ activity.startActivity(intent);
+ activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
+ } catch (Exception e) {
+ Toast.makeText(activity, R.string.no_application_found_to_view_contact, Toast.LENGTH_LONG).show();
+ }
+
});
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
@@ -635,7 +644,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
e.printStackTrace();
}
}
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_grey600_48dp, 0, 0, 0);
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_android_grey600_48dp, 0, 0, 0);
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message) + APKName));
}
@@ -839,7 +848,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
} else {
viewHolder.status_message.setVisibility(View.VISIBLE);
viewHolder.load_more_messages.setVisibility(View.GONE);
- viewHolder.status_message.setText(message.getBody());
+ viewHolder.status_message.setText(EmojiWrapper.transform(message.getBody()));
boolean showAvatar;
if (conversation.getMode() == Conversation.MODE_SINGLE) {
showAvatar = true;
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 edca88f78..dd69b2337 100644
--- a/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
+++ b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
@@ -66,6 +66,9 @@ public class ConversationMenuConfigurator {
}
public static void configureAttachmentMenu(@NonNull Conversation conversation, Menu menu, Boolean Quick_share_attachment_choice, boolean hasAttachments) {
+ if (menu == null) {
+ return;
+ }
final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
if (Quick_share_attachment_choice && !hasAttachments) {
menuAttach.setVisible(false);
diff --git a/src/main/java/de/pixart/messenger/ui/util/JidDialog.java b/src/main/java/de/pixart/messenger/ui/util/JidDialog.java
new file mode 100644
index 000000000..291bb873c
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/util/JidDialog.java
@@ -0,0 +1,22 @@
+package de.pixart.messenger.ui.util;
+
+import android.content.Context;
+import android.support.annotation.StringRes;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.TypefaceSpan;
+
+public class JidDialog {
+
+ public static SpannableString style(Context context, @StringRes int res, String... args) {
+ SpannableString spannable = new SpannableString(context.getString(res, (Object[]) args));
+ if (args.length >= 1) {
+ final String value = args[0];
+ int start = spannable.toString().indexOf(value);
+ if (start >= 0) {
+ spannable.setSpan(new TypefaceSpan("monospace"), start, start + value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ return spannable;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java b/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java
index dc338e02c..f4777e8f3 100644
--- a/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java
+++ b/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java
@@ -25,6 +25,8 @@ import rocks.xmpp.addr.Jid;
public final class MucDetailsContextMenuHelper {
public static void configureMucDetailsContextMenu(Activity activity, Menu menu, Conversation conversation, User user) {
final boolean advancedMode = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("advanced_muc_mode", false);
+ final MucOptions mucOptions = conversation.getMucOptions();
+ MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
if (user != null && user.getRealJid() != null) {
MenuItem showContactDetails = menu.findItem(R.id.action_contact_details);
MenuItem startConversation = menu.findItem(R.id.start_conversation);
@@ -35,6 +37,7 @@ public final class MucDetailsContextMenuHelper {
MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
MenuItem invite = menu.findItem(R.id.invite);
+ MenuItem highlightInMuc = menu.findItem(R.id.highlight_in_muc);
startConversation.setVisible(true);
final Contact contact = user.getContact();
final User self = conversation.getMucOptions().getSelf();
@@ -44,6 +47,11 @@ public final class MucDetailsContextMenuHelper {
if (activity instanceof ConferenceDetailsActivity && user.getRole() == MucOptions.Role.NONE) {
invite.setVisible(true);
}
+ if (activity instanceof ConversationsActivity) {
+ highlightInMuc.setVisible(false);
+ } else if (activity instanceof ConferenceDetailsActivity) {
+ highlightInMuc.setVisible(true);
+ }
if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
self.getAffiliation().outranks(user.getAffiliation())) {
if (advancedMode) {
@@ -66,9 +74,9 @@ public final class MucDetailsContextMenuHelper {
removeAdminPrivileges.setVisible(true);
}
}
+ sendPrivateMessage.setVisible(true);
+ sendPrivateMessage.setEnabled(mucOptions.allowPm());
} else {
- final MucOptions mucOptions = conversation.getMucOptions();
- MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
sendPrivateMessage.setVisible(true);
sendPrivateMessage.setEnabled(user != null && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR));
}
@@ -122,6 +130,9 @@ public final class MucDetailsContextMenuHelper {
case R.id.invite:
activity.xmppConnectionService.directInvite(conversation, jid);
return true;
+ case R.id.highlight_in_muc:
+ activity.highlightInMuc(conversation, user.getName());
+ return true;
default:
return false;
}
diff --git a/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java b/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java
index b1905479b..07e25e31d 100644
--- a/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java
+++ b/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java
@@ -15,7 +15,7 @@ import java.util.List;
import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.persistance.FileBackend;
-import de.pixart.messenger.ui.ShowFullscreenMessageActivity;
+import de.pixart.messenger.ui.MediaViewerActivity;
public class ViewUtil {
@@ -36,7 +36,7 @@ public class ViewUtil {
}
// use internal viewer for images and videos
if (mime.startsWith("image/")) {
- Intent intent = new Intent(context, ShowFullscreenMessageActivity.class);
+ Intent intent = new Intent(context, MediaViewerActivity.class);
intent.putExtra("image", Uri.fromFile(file));
try {
context.startActivity(intent);
@@ -45,7 +45,7 @@ public class ViewUtil {
//ignored
}
} else if (mime.startsWith("video/")) {
- Intent intent = new Intent(context, ShowFullscreenMessageActivity.class);
+ Intent intent = new Intent(context, MediaViewerActivity.class);
intent.putExtra("video", Uri.fromFile(file));
try {
context.startActivity(intent);
diff --git a/src/main/java/de/pixart/messenger/ui/widget/FailedCountCustomView.java b/src/main/java/de/pixart/messenger/ui/widget/FailedCountCustomView.java
new file mode 100644
index 000000000..261ace3a4
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/widget/FailedCountCustomView.java
@@ -0,0 +1,76 @@
+package de.pixart.messenger.ui.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.support.v4.content.ContextCompat;
+import android.util.AttributeSet;
+import android.view.View;
+
+import de.pixart.messenger.R;
+
+public class FailedCountCustomView extends View {
+
+ private int count;
+ private Paint paint, textPaint;
+ private int backgroundColor = 0xffd50000;
+
+ public FailedCountCustomView(Context context) {
+ super(context);
+ init();
+ }
+
+ public FailedCountCustomView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initXMLAttrs(context, attrs);
+ init();
+ }
+
+ public FailedCountCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initXMLAttrs(context, attrs);
+ init();
+ }
+
+ private void initXMLAttrs(Context context, AttributeSet attrs) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnreadCountCustomView);
+ //setBackgroundColor(a.getColor(a.getIndex(0), ContextCompat.getColor(context, R.color.accent)));
+ setBackgroundColor(ContextCompat.getColor(context, R.color.red700));
+ a.recycle();
+ }
+
+ void init() {
+ paint = new Paint();
+ paint.setColor(backgroundColor);
+ paint.setAntiAlias(true);
+ textPaint = new Paint();
+ textPaint.setColor(Color.WHITE);
+ textPaint.setTextAlign(Paint.Align.CENTER);
+ textPaint.setAntiAlias(true);
+ textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ float midx = canvas.getWidth() / 2.0f;
+ float midy = canvas.getHeight() / 2.0f;
+ float radius = Math.min(canvas.getWidth(), canvas.getHeight()) / 2.0f;
+ float textOffset = canvas.getWidth() / 6.0f;
+ textPaint.setTextSize(0.95f * radius);
+ canvas.drawCircle(midx, midy, radius * 0.94f, paint);
+ canvas.drawText(count > 999 ? "\u221E" : String.valueOf(count), midx, midy + textOffset, textPaint);
+ }
+
+ public void setFailedCount(int count) {
+ this.count = count;
+ invalidate();
+ }
+
+ public void setBackgroundColor(int backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+}
diff --git a/src/main/java/de/pixart/messenger/utils/Compatibility.java b/src/main/java/de/pixart/messenger/utils/Compatibility.java
index 6d5f35bc4..f178f17c7 100644
--- a/src/main/java/de/pixart/messenger/utils/Compatibility.java
+++ b/src/main/java/de/pixart/messenger/utils/Compatibility.java
@@ -37,8 +37,8 @@ public class Compatibility {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
- public static boolean twentyTwo() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
+ public static boolean twentyEight() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}
private static boolean getBooleanPreference(Context context, String name, @BoolRes int res) {
diff --git a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
index d34823d86..e16d0d012 100644
--- a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
@@ -35,15 +35,15 @@ import rocks.xmpp.addr.Jid;
public final class CryptoHelper {
+ public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
+ final public static byte[] ONE = new byte[]{0, 0, 0, 1};
+ private static final char[] CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789+-/#$!?".toCharArray();
+ private static final int PW_LENGTH = 10;
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();
- public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
- final public static byte[] ONE = new byte[]{0, 0, 0, 1};
-
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
@@ -54,6 +54,14 @@ public final class CryptoHelper {
return new String(hexChars);
}
+ public static String createPassword(SecureRandom random) {
+ StringBuilder builder = new StringBuilder(PW_LENGTH);
+ for (int i = 0; i < PW_LENGTH; ++i) {
+ builder.append(CHARS[random.nextInt(CHARS.length - 1)]);
+ }
+ return builder.toString();
+ }
+
public static String pronounceable(SecureRandom random) {
char[] output = new char[random.nextInt(4) * 2 + 5];
boolean vowel = random.nextBoolean();
@@ -293,4 +301,4 @@ public final class CryptoHelper {
final String u = url.toLowerCase();
return !u.contains(" ") && (u.startsWith("https://") || u.startsWith("http://") || u.startsWith("p1s3://")) && u.endsWith(".pgp");
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/PRNGFixes.java b/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
index 211ebca8e..c26fe9f13 100644
--- a/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
+++ b/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
@@ -133,6 +133,58 @@ public final class PRNGFixes {
}
/**
+ * Generates a device- and invocation-specific seed to be mixed into the
+ * Linux PRNG.
+ */
+ private static byte[] generateSeed() {
+ try {
+ ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
+ DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
+ seedBufferOut.writeLong(System.currentTimeMillis());
+ seedBufferOut.writeLong(System.nanoTime());
+ seedBufferOut.writeInt(Process.myPid());
+ seedBufferOut.writeInt(Process.myUid());
+ seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
+ seedBufferOut.close();
+ return seedBuffer.toByteArray();
+ } catch (IOException e) {
+ throw new SecurityException("Failed to generate seed", e);
+ }
+ }
+
+ /**
+ * Gets the hardware serial number of this device.
+ *
+ * @return serial number or {@code null} if not available.
+ */
+ private static String getDeviceSerialNumber() {
+ // We're using the Reflection API because Build.SERIAL is only available
+ // since API Level 9 (Gingerbread, Android 2.3).
+ try {
+ return (String) Build.class.getField("SERIAL").get(null);
+ } catch (Exception ignored) {
+ return null;
+ }
+ }
+
+ private static byte[] getBuildFingerprintAndDeviceSerial() {
+ StringBuilder result = new StringBuilder();
+ String fingerprint = Build.FINGERPRINT;
+ if (fingerprint != null) {
+ result.append(fingerprint);
+ }
+ String serial = getDeviceSerialNumber();
+ if (serial != null) {
+ result.append(serial);
+ }
+ try {
+ return result.toString().getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF-8 encoding not supported");
+ }
+ }
+
+ /**
* {@code Provider} of {@code SecureRandom} engines which pass through all
* requests to the Linux PRNG.
*/
@@ -157,17 +209,17 @@ public final class PRNGFixes {
*/
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
- /*
+ /*
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
- * are passed through to the Linux PRNG (/dev/urandom). Instances of
- * this class seed themselves by mixing in the current time, PID, UID,
- * build fingerprint, and hardware serial number (where available) into
- * Linux PRNG.
- *
- * Concurrency: Read requests to the underlying Linux PRNG are
- * serialized (on sLock) to ensure that multiple threads do not get
- * duplicated PRNG output.
- */
+ * are passed through to the Linux PRNG (/dev/urandom). Instances of
+ * this class seed themselves by mixing in the current time, PID, UID,
+ * build fingerprint, and hardware serial number (where available) into
+ * Linux PRNG.
+ *
+ * Concurrency: Read requests to the underlying Linux PRNG are
+ * serialized (on sLock) to ensure that multiple threads do not get
+ * duplicated PRNG output.
+ */
private static final File URANDOM_FILE = new File("/dev/urandom");
@@ -271,56 +323,4 @@ public final class PRNGFixes {
}
}
}
-
- /**
- * Generates a device- and invocation-specific seed to be mixed into the
- * Linux PRNG.
- */
- private static byte[] generateSeed() {
- try {
- ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
- DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
- seedBufferOut.writeLong(System.currentTimeMillis());
- seedBufferOut.writeLong(System.nanoTime());
- seedBufferOut.writeInt(Process.myPid());
- seedBufferOut.writeInt(Process.myUid());
- seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
- seedBufferOut.close();
- return seedBuffer.toByteArray();
- } catch (IOException e) {
- throw new SecurityException("Failed to generate seed", e);
- }
- }
-
- /**
- * Gets the hardware serial number of this device.
- *
- * @return serial number or {@code null} if not available.
- */
- private static String getDeviceSerialNumber() {
- // We're using the Reflection API because Build.SERIAL is only available
- // since API Level 9 (Gingerbread, Android 2.3).
- try {
- return (String) Build.class.getField("SERIAL").get(null);
- } catch (Exception ignored) {
- return null;
- }
- }
-
- private static byte[] getBuildFingerprintAndDeviceSerial() {
- StringBuilder result = new StringBuilder();
- String fingerprint = Build.FINGERPRINT;
- if (fingerprint != null) {
- result.append(fingerprint);
- }
- String serial = getDeviceSerialNumber();
- if (serial != null) {
- result.append(serial);
- }
- try {
- return result.toString().getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 encoding not supported");
- }
- }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/Resolver.java b/src/main/java/de/pixart/messenger/utils/Resolver.java
index e159c3dd0..7817e7914 100644
--- a/src/main/java/de/pixart/messenger/utils/Resolver.java
+++ b/src/main/java/de/pixart/messenger/utils/Resolver.java
@@ -63,14 +63,26 @@ public class Resolver {
final Field useHardcodedDnsServers = DNSClient.class.getDeclaredField("useHardcodedDnsServers");
useHardcodedDnsServers.setAccessible(true);
useHardcodedDnsServers.setBoolean(dnsClient, false);
- } catch (NoSuchFieldException e) {
- Log.e(Config.LOGTAG, "Unable to disable hardcoded DNS servers", e);
- } catch (IllegalAccessException e) {
+ } catch (NoSuchFieldException | IllegalAccessException e) {
Log.e(Config.LOGTAG, "Unable to disable hardcoded DNS servers", e);
}
}
+ public static List<Result> fromHardCoded(String hostname, int port) {
+ Result result = new Result();
+ result.hostname = DNSName.from(hostname);
+ result.port = port;
+ result.directTls = port == 443 || port == 5223;
+ result.authenticated = true;
+ return Collections.singletonList(result);
+ }
+
+
public static List<Result> resolve(String domain) {
+ final List<Result> ipResults = fromIpAddress(domain);
+ if (ipResults.size() > 0) {
+ return ipResults;
+ }
final List<Result> results = new ArrayList<>();
final List<Result> fallbackResults = new ArrayList<>();
Thread[] threads = new Thread[3];
@@ -125,9 +137,21 @@ public class Resolver {
for (Thread thread : threads) {
thread.interrupt();
}
- synchronized (results) {
- return new ArrayList<>(results);
- }
+ return Collections.emptyList();
+ }
+ }
+
+ private static List<Result> fromIpAddress(String domain) {
+ if (!IP.matches(domain)) {
+ return Collections.emptyList();
+ }
+ try {
+ Result result = new Result();
+ result.ip = InetAddress.getByName(domain);
+ result.port = 5222;
+ return Collections.singletonList(result);
+ } catch (UnknownHostException e) {
+ return Collections.emptyList();
}
}
@@ -277,7 +301,8 @@ public class Resolver {
} catch (UnknownHostException e) {
result.ip = null;
}
- result.hostname = DNSName.from(cursor.getString(cursor.getColumnIndex(HOSTNAME)));
+ final String hostname = cursor.getString(cursor.getColumnIndex(HOSTNAME));
+ result.hostname = hostname == null ? null : DNSName.from(hostname);
result.port = cursor.getInt(cursor.getColumnIndex(PORT));
result.priority = cursor.getInt(cursor.getColumnIndex(PRIORITY));
result.authenticated = cursor.getInt(cursor.getColumnIndex(AUTHENTICATED)) > 0;
@@ -369,7 +394,7 @@ public class Resolver {
public ContentValues toContentValues() {
final ContentValues contentValues = new ContentValues();
contentValues.put(IP, ip == null ? null : ip.getAddress());
- contentValues.put(HOSTNAME, hostname.toString());
+ contentValues.put(HOSTNAME, hostname == null ? null : hostname.toString());
contentValues.put(PORT, port);
contentValues.put(PRIORITY, priority);
contentValues.put(DIRECT_TLS, directTls ? 1 : 0);
@@ -377,5 +402,4 @@ public class Resolver {
return contentValues;
}
}
-
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java b/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
index ad3629354..ce8aa009f 100644
--- a/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
@@ -1,20 +1,30 @@
package de.pixart.messenger.utils;
import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+import org.conscrypt.Conscrypt;
import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedList;
+import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
+
+import de.pixart.messenger.Config;
+import de.pixart.messenger.entities.Account;
public class SSLSocketHelper {
- public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
+ public static void setSecurity(final SSLSocket sslSocket) {
final String[] supportProtocols;
final Collection<String> supportedProtocols = new LinkedList<>(
Arrays.asList(sslSocket.getSupportedProtocols()));
@@ -30,44 +40,65 @@ public class SSLSocketHelper {
}
}
- public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
- if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
- ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
+ public static void setHostname(final SSLSocket socket, final String hostname) {
+ if (Conscrypt.isConscrypt(socket)) {
+ Conscrypt.setHostname(socket, hostname);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ setHostnameNougat(socket, hostname);
} else {
- try {
- socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
- } catch (Throwable e) {
- // ignore any error, we just can't set the hostname...
- }
+ setHostnameReflection(socket, hostname);
}
}
- public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
+ private static void setHostnameReflection(final SSLSocket socket, final String hostname) {
+ try {
+ socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, "unable to set SNI name on socket (" + hostname + ")", e);
+ }
+ }
+
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private static void setHostnameNougat(final SSLSocket socket, final String hostname) {
+ final SSLParameters parameters = new SSLParameters();
+ parameters.setServerNames(Collections.singletonList(new SNIHostName(hostname)));
+ socket.setSSLParameters(parameters);
+ }
+
+ private static void setApplicationProtocolReflection(final SSLSocket socket, final String protocol) {
try {
- if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
- // can't call directly because of @hide?
- //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
- android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
- } else {
- final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
- // the concatenation of 8-bit, length prefixed protocol names, just one in our case...
- // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
- final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
- final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
- lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
- System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
- method.invoke(socket, new Object[]{lengthPrefixedProtocols});
- }
+ final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
+ // the concatenation of 8-bit, length prefixed protocol names, just one in our case...
+ // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
+ final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
+ final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
+ lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
+ System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
+ method.invoke(socket, new Object[]{lengthPrefixedProtocols});
} catch (Throwable e) {
- // ignore any error, we just can't set the alpn protocol...
+ Log.e(Config.LOGTAG, "unable to set ALPN on socket", e);
+ }
+ }
+
+ public static void setApplicationProtocol(final SSLSocket socket, final String protocol) {
+ if (Conscrypt.isConscrypt(socket)) {
+ Conscrypt.setApplicationProtocols(socket, new String[]{protocol});
+ } else {
+ setApplicationProtocolReflection(socket, protocol);
}
}
public static SSLContext getSSLContext() throws NoSuchAlgorithmException {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ try {
+ return SSLContext.getInstance("TLSv1.3");
+ } catch (NoSuchAlgorithmException e) {
return SSLContext.getInstance("TLSv1.2");
- } else {
- return SSLContext.getInstance("TLS");
}
}
-}
+
+ public static void log(Account account, SSLSocket socket) {
+ SSLSession session = socket.getSession();
+ Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": protocol=" + session.getProtocol() + " cipher=" + session.getCipherSuite());
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/TLSSocketFactory.java b/src/main/java/de/pixart/messenger/utils/TLSSocketFactory.java
index cfefbd93d..6daa9ac9b 100644
--- a/src/main/java/de/pixart/messenger/utils/TLSSocketFactory.java
+++ b/src/main/java/de/pixart/messenger/utils/TLSSocketFactory.java
@@ -17,11 +17,18 @@ public class TLSSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory(X509TrustManager[] trustManager, SecureRandom random) throws KeyManagementException, NoSuchAlgorithmException {
- SSLContext context = SSLContext.getInstance("TLS");
+ SSLContext context = SSLSocketHelper.getSSLContext();
context.init(null, trustManager, random);
this.internalSSLSocketFactory = context.getSocketFactory();
}
+ private static Socket enableTLSOnSocket(Socket socket) {
+ if (socket instanceof SSLSocket) {
+ SSLSocketHelper.setSecurity((SSLSocket) socket);
+ }
+ return socket;
+ }
+
@Override
public String[] getDefaultCipherSuites() {
return CryptoHelper.getOrderedCipherSuites(internalSSLSocketFactory.getDefaultCipherSuites());
@@ -56,15 +63,4 @@ public class TLSSocketFactory extends SSLSocketFactory {
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
-
- private static Socket enableTLSOnSocket(Socket socket) {
- if(socket != null && (socket instanceof SSLSocket)) {
- try {
- SSLSocketHelper.setSecurity((SSLSocket) socket);
- } catch (NoSuchAlgorithmException e) {
- //ignoring
- }
- }
- return socket;
- }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/XmppUri.java b/src/main/java/de/pixart/messenger/utils/XmppUri.java
index db0002a29..a44c9a174 100644
--- a/src/main/java/de/pixart/messenger/utils/XmppUri.java
+++ b/src/main/java/de/pixart/messenger/utils/XmppUri.java
@@ -22,7 +22,7 @@ public class XmppUri {
private String name;
private String action;
private boolean safeSource = true;
- private static final String OMEMO_URI_PARAM = "omemo-sid-";
+ public static final String OMEMO_URI_PARAM = "omemo-sid-";
private static final String OTR_URI_PARAM = "otr-fingerprint";
public static final String ACTION_JOIN = "join";
public static final String ACTION_MESSAGE = "message";
diff --git a/src/main/java/de/pixart/messenger/xml/XmlReader.java b/src/main/java/de/pixart/messenger/xml/XmlReader.java
index 703f1239c..a67442f49 100644
--- a/src/main/java/de/pixart/messenger/xml/XmlReader.java
+++ b/src/main/java/de/pixart/messenger/xml/XmlReader.java
@@ -48,7 +48,7 @@ public class XmlReader {
}
}
- public Tag readTag() throws XmlPullParserException, IOException {
+ public Tag readTag() throws IOException {
try {
while (this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.getEventType() == XmlPullParser.START_TAG) {
diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
index cbfdbc365..73cfbab86 100644
--- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
@@ -48,7 +48,6 @@ import java.util.regex.Matcher;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509KeyManager;
@@ -77,7 +76,6 @@ import de.pixart.messenger.services.NotificationService;
import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.ui.EditAccountActivity;
import de.pixart.messenger.utils.CryptoHelper;
-import de.pixart.messenger.utils.IP;
import de.pixart.messenger.utils.Namespace;
import de.pixart.messenger.utils.Patterns;
import de.pixart.messenger.utils.Resolver;
@@ -210,7 +208,7 @@ public class XmppConnection implements Runnable {
}
}
- protected void changeStatus(final Account.State nextStatus) {
+ private void changeStatus(final Account.State nextStatus) {
synchronized (this) {
if (Thread.currentThread().isInterrupted()) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": not changing status to " + nextStatus + " because thread was interrupted");
@@ -304,69 +302,32 @@ public class XmppConnection implements Runnable {
} catch (Exception e) {
throw new IOException(e.getMessage());
}
- } else if (extended && !account.getHostname().isEmpty()) {
-
- this.verifiedHostname = account.getHostname();
-
- try {
- InetSocketAddress address = new InetSocketAddress(this.verifiedHostname, account.getPort());
- features.encryptionEnabled = address.getPort() == 5223;
- if (features.encryptionEnabled) {
- try {
- final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
- localSocket = tlsFactoryVerifier.factory.createSocket();
- localSocket.connect(address, Config.SOCKET_TIMEOUT * 1000);
- final SSLSession session = ((SSLSocket) localSocket).getSession();
- final String domain = account.getJid().getDomain();
- if (!tlsFactoryVerifier.verifier.verify(domain, this.verifiedHostname, session)) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed");
- throw new StateChangingException(Account.State.TLS_ERROR);
- }
- } catch (KeyManagementException e) {
- throw new StateChangingException(Account.State.TLS_ERROR);
- }
- } else {
- localSocket = new Socket();
- localSocket.connect(address, Config.SOCKET_TIMEOUT * 1000);
- }
- } catch (IOException | IllegalArgumentException e) {
- throw new UnknownHostException();
+ } else {
+ final String domain = account.getJid().getDomain();
+ final List<Resolver.Result> results;
+ final boolean hardcoded = extended && !account.getHostname().isEmpty();
+ if (hardcoded) {
+ results = Resolver.fromHardCoded(account.getHostname(), account.getPort());
+ } else {
+ results = Resolver.resolve(domain);
}
- try {
- startXmpp(localSocket);
- } catch (InterruptedException e) {
- Log.d(Config.LOGTAG,account.getJid().asBareJid()+": thread was interrupted before beginning stream");
+ if (Thread.currentThread().isInterrupted()) {
+ Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": Thread was interrupted");
return;
- } catch (Exception e) {
- throw new IOException(e.getMessage());
}
- } else if (IP.matches(account.getServer())) {
- localSocket = new Socket();
- try {
- localSocket.connect(new InetSocketAddress(account.getServer(), 5222), Config.SOCKET_TIMEOUT * 1000);
- } catch (IOException e) {
- throw new UnknownHostException();
- }
- try {
- startXmpp(localSocket);
- } catch (InterruptedException e) {
- Log.d(Config.LOGTAG,account.getJid().asBareJid()+": thread was interrupted before beginning stream");
+ if (results.size() == 0) {
+ Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": Resolver results were empty");
return;
- } catch (Exception e) {
- throw new IOException(e.getMessage());
}
- } else {
- final String domain = account.getJid().getDomain();
- List<Resolver.Result> results = Resolver.resolve(account.getJid().getDomain());
- Resolver.Result storedBackupResult;
- if (!Thread.currentThread().isInterrupted()) {
+ final Resolver.Result storedBackupResult;
+ if (hardcoded) {
+ storedBackupResult = null;
+ } else {
storedBackupResult = mXmppConnectionService.databaseBackend.findResolverResult(domain);
if (storedBackupResult != null && !results.contains(storedBackupResult)) {
results.add(storedBackupResult);
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": loaded backup resolver result from db: " + storedBackupResult);
}
- } else {
- storedBackupResult = null;
}
for (Iterator<Resolver.Result> iterator = results.iterator(); iterator.hasNext(); ) {
final Resolver.Result result = iterator.next();
@@ -378,16 +339,17 @@ public class XmppConnection implements Runnable {
// if tls is true, encryption is implied and must not be started
features.encryptionEnabled = result.isDirectTls();
verifiedHostname = result.isAuthenticated() ? result.getHostname().toString() : null;
+ Log.d(Config.LOGTAG, "verified hostname " + verifiedHostname);
final InetSocketAddress addr;
if (result.getIp() != null) {
addr = new InetSocketAddress(result.getIp(), result.getPort());
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString()
- + ": using values from dns " + result.getHostname().toString()
- + "/" + result.getIp().getHostAddress() + ":" + result.getPort() + " tls: " + features.encryptionEnabled);
+ + ": using values from resolver " + (result.getHostname() == null ? "" : result.getHostname().toString()
+ + "/") + result.getIp().getHostAddress() + ":" + result.getPort() + " tls: " + features.encryptionEnabled);
} else {
addr = new InetSocketAddress(IDN.toASCII(result.getHostname().toString()), result.getPort());
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString()
- + ": using values from dns "
+ + ": using values from resolver "
+ result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled);
}
@@ -403,8 +365,8 @@ public class XmppConnection implements Runnable {
}
SSLSocketHelper.setSecurity((SSLSocket) localSocket);
- SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) localSocket, account.getServer());
- SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) localSocket, "xmpp-client");
+ SSLSocketHelper.setHostname((SSLSocket) localSocket, account.getServer());
+ SSLSocketHelper.setApplicationProtocol((SSLSocket) localSocket, "xmpp-client");
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
@@ -415,14 +377,18 @@ public class XmppConnection implements Runnable {
}
}
}
-
+ localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000);
if (startXmpp(localSocket)) {
- if (!result.equals(storedBackupResult)) {
+ localSocket.setSoTimeout(0); //reset to 0; once the connection is established we don’t want this
+ if (!hardcoded && !result.equals(storedBackupResult)) {
mXmppConnectionService.databaseBackend.saveResolverResult(domain, result);
}
break; // successfully connected to server that speaks xmpp
} else {
localSocket.close();
+ if (!iterator.hasNext()) {
+ throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
+ }
}
} catch (final StateChangingException e) {
throw e;
@@ -446,7 +412,7 @@ public class XmppConnection implements Runnable {
this.changeStatus(Account.State.SERVER_NOT_FOUND);
} catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
- } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
+ } catch (final IOException | XmlPullParserException e) {
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": " + e.getMessage());
this.changeStatus(Account.State.OFFLINE);
this.attempt = Math.max(0, this.attempt - 1);
@@ -482,6 +448,9 @@ public class XmppConnection implements Runnable {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
+ if (socket instanceof SSLSocket) {
+ SSLSocketHelper.log(account, (SSLSocket) socket);
+ }
return tag != null && tag.isStart("stream");
}
@@ -524,7 +493,7 @@ public class XmppConnection implements Runnable {
} else if (nextTag.isStart("features")) {
processStreamFeatures(nextTag);
} else if (nextTag.isStart("proceed")) {
- switchOverToTls(nextTag);
+ switchOverToTls();
} else if (nextTag.isStart("success")) {
final String challenge = tagReader.readElement(nextTag).getContent();
try {
@@ -542,7 +511,7 @@ public class XmppConnection implements Runnable {
if (tag != null && tag.isStart("stream")) {
processStream();
} else {
- throw new IOException("server didn't restart stream after successful auth");
+ throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
}
break;
} else if (nextTag.isStart("failure")) {
@@ -852,7 +821,7 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(startTLS);
}
- private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
+ private void switchOverToTls() throws XmlPullParserException, IOException {
tagReader.readTag();
try {
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
@@ -869,6 +838,8 @@ public class XmppConnection implements Runnable {
}
SSLSocketHelper.setSecurity(sslSocket);
+ SSLSocketHelper.setHostname(sslSocket, account.getServer());
+ SSLSocketHelper.setApplicationProtocol(sslSocket, "xmpp-client");
if (!tlsFactoryVerifier.verifier.verify(account.getServer(), this.verifiedHostname, sslSocket.getSession())) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed");
@@ -881,9 +852,10 @@ public class XmppConnection implements Runnable {
features.encryptionEnabled = true;
final Tag tag = tagReader.readTag();
if (tag != null && tag.isStart("stream")) {
+ SSLSocketHelper.log(account, sslSocket);
processStream();
} else {
- throw new IOException("server didn't restart stream after STARTTLS");
+ throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
}
sslSocket.close();
} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
index 6c726c8b9..2f3ec09ea 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
@@ -2,8 +2,8 @@ package de.pixart.messenger.xmpp.jingle;
import android.util.Base64;
import android.util.Log;
-import android.util.Pair;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -84,6 +84,7 @@ public class JingleConnection implements Transferable {
private boolean sentCandidate = false;
private boolean acceptedAutomatically = false;
+ private boolean cancelled = false;
private XmppAxolotlMessage mXmppAxolotlMessage;
@@ -92,13 +93,9 @@ public class JingleConnection implements Transferable {
private OutputStream mFileOutputStream;
private InputStream mFileInputStream;
- private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
- fail(IqParser.extractErrorMessage(packet));
- }
+ private OnIqPacketReceived responseListener = (account, packet) -> {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
+ fail(IqParser.extractErrorMessage(packet));
}
};
private byte[] expectedHash = new byte[0];
@@ -489,36 +486,32 @@ public class JingleConnection implements Transferable {
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
content.setTransportId(this.transportId);
this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
- Pair<InputStream, Integer> pair;
- try {
- if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- Conversational conversation = this.message.getConversation();
- if (!this.mXmppConnectionService.renewSymmetricKey((Conversation) conversation)) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not set symmetric key");
- cancel();
- }
- Conversation c = (Conversation) this.message.getConversation();
- this.file.setKeyAndIv(c.getSymmetricKey());
- pair = AbstractConnectionManager.createInputStream(this.file, false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, true, this.ftVersion);
- } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- this.file.setKey(mXmppAxolotlMessage.getInnerKey());
- this.file.setIv(mXmppAxolotlMessage.getIV());
- pair = AbstractConnectionManager.createInputStream(this.file, true);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
- } else {
- pair = AbstractConnectionManager.createInputStream(this.file, false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false, this.ftVersion);
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ Conversational conversation = this.message.getConversation();
+ if (!this.mXmppConnectionService.renewSymmetricKey((Conversation) conversation)) {
+ Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not set symmetric key");
+ cancel();
}
+ Conversation c = (Conversation) this.message.getConversation();
+ this.file.setKeyAndIv(c.getSymmetricKey());
+ this.file.setExpectedSize((file.getSize() / 16 + 1) * 16);
+ content.setFileOffer(this.file, true, this.ftVersion);
+ } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ this.file.setKey(mXmppAxolotlMessage.getInnerKey());
+ this.file.setIv(mXmppAxolotlMessage.getIV());
+ this.file.setExpectedSize(file.getSize() + 16);
+ content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
+ } else {
+ this.file.setExpectedSize(file.getSize());
+ content.setFileOffer(this.file, false, this.ftVersion);
+ }
+ message.resetFileParams();
+ try {
+ this.mFileInputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
- cancel();
+ abort();
return;
}
- message.resetFileParams();
- this.mFileInputStream = pair.first;
content.setTransportId(this.transportId);
content.socks5transport().setChildren(getCandidatesAsElements());
packet.setContent(content);
@@ -893,7 +886,7 @@ public class JingleConnection implements Transferable {
this.mJingleStatus = JINGLE_STATUS_FINISHED;
this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_RECEIVED);
this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ if (this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
this.message.setTransferable(null);
@@ -904,8 +897,13 @@ public class JingleConnection implements Transferable {
}
public void cancel() {
+ this.cancelled = true;
+ abort();
+ }
+
+ public void abort() {
this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ if (this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
this.sendCancel();
@@ -917,7 +915,7 @@ public class JingleConnection implements Transferable {
}
this.mJingleConnectionManager.updateConversationUi(true);
} else {
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED);
+ this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : null);
this.message.setTransferable(null);
}
}
@@ -929,7 +927,7 @@ public class JingleConnection implements Transferable {
private void fail(String errorMessage) {
this.mJingleStatus = JINGLE_STATUS_FAILED;
this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ if (this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
FileBackend.close(mFileInputStream);
@@ -944,7 +942,7 @@ public class JingleConnection implements Transferable {
} else {
this.mXmppConnectionService.markMessage(this.message,
Message.STATUS_SEND_FAILED,
- errorMessage);
+ cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage);
this.message.setTransferable(null);
}
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
index de0ce1ea0..bef7a000d 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
@@ -167,7 +167,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
public void cancelInTransmission() {
for (JingleConnection connection : this.connections) {
if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
- connection.cancel();
+ connection.abort();
}
}
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
index 151e9409f..df8b962c4 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
@@ -14,6 +14,7 @@ import de.pixart.messenger.Config;
import de.pixart.messenger.entities.Account;
import de.pixart.messenger.entities.DownloadableFile;
import de.pixart.messenger.persistance.FileBackend;
+import de.pixart.messenger.services.AbstractConnectionManager;
import de.pixart.messenger.xml.Element;
import de.pixart.messenger.xmpp.OnIqPacketReceived;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
@@ -35,6 +36,7 @@ public class JingleInbandTransport extends JingleTransport {
private JingleConnection connection;
private InputStream fileInputStream = null;
+ private InputStream innerInputStream = null;
private OutputStream fileOutputStream = null;
private long remainingSize = 0;
private long fileSize = 0;
@@ -129,10 +131,11 @@ public class JingleInbandTransport extends JingleTransport {
callback.onFileTransferAborted();
return;
}
+ innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream, false);
if (this.connected) {
this.sendNextBlock();
}
- } catch (NoSuchAlgorithmException e) {
+ } catch (Exception e) {
callback.onFileTransferAborted();
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + e.getMessage());
}
@@ -141,26 +144,14 @@ public class JingleInbandTransport extends JingleTransport {
@Override
public void disconnect() {
this.connected = false;
- if (this.fileOutputStream != null) {
- try {
- this.fileOutputStream.close();
- } catch (IOException e) {
-
- }
- }
- if (this.fileInputStream != null) {
- try {
- this.fileInputStream.close();
- } catch (IOException e) {
-
- }
- }
+ FileBackend.close(fileOutputStream);
+ FileBackend.close(fileInputStream);
}
private void sendNextBlock() {
byte[] buffer = new byte[this.blockSize];
try {
- int count = fileInputStream.read(buffer);
+ int count = innerInputStream.read(buffer);
if (count == -1) {
sendClose();
file.setSha1Sum(digest.digest());
@@ -168,7 +159,7 @@ public class JingleInbandTransport extends JingleTransport {
fileInputStream.close();
return;
} else if (count != buffer.length) {
- int rem = fileInputStream.read(buffer, count, buffer.length - count);
+ int rem = innerInputStream.read(buffer, count, buffer.length - count);
if (rem > 0) {
count += rem;
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
index 374e02750..35498664f 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
@@ -15,6 +15,7 @@ import java.security.NoSuchAlgorithmException;
import de.pixart.messenger.Config;
import de.pixart.messenger.entities.DownloadableFile;
import de.pixart.messenger.persistance.FileBackend;
+import de.pixart.messenger.services.AbstractConnectionManager;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.SocksSocketFactory;
import de.pixart.messenger.utils.WakeLockHelper;
@@ -94,11 +95,12 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted();
return;
}
+ final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream, false);
long size = file.getExpectedSize();
long transmitted = 0;
int count;
byte[] buffer = new byte[8192];
- while ((count = fileInputStream.read(buffer)) > 0) {
+ while ((count = innerInputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count);
digest.update(buffer, 0, count);
transmitted += count;
diff --git a/src/main/res/drawable-hdpi/ic_android_grey600_48dp.png b/src/main/res/drawable-hdpi/ic_android_grey600_48dp.png
new file mode 100644
index 000000000..3a39d9cc4
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_android_grey600_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_delete_black_24dp.png b/src/main/res/drawable-hdpi/ic_delete_black_24dp.png
new file mode 100644
index 000000000..789d96d42
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_delete_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_menu_white_24dp.png b/src/main/res/drawable-hdpi/ic_menu_white_24dp.png
new file mode 100644
index 000000000..aeb1e31a0
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_menu_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_qrcode_grey600_24dp.png b/src/main/res/drawable-hdpi/ic_qrcode_grey600_24dp.png
new file mode 100644
index 000000000..5114bbd0d
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_qrcode_grey600_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_qrcode_scan_white_24dp.png b/src/main/res/drawable-hdpi/ic_qrcode_scan_white_24dp.png
new file mode 100644
index 000000000..50aa91fca
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_qrcode_scan_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_qrcode_white_24dp.png b/src/main/res/drawable-hdpi/ic_qrcode_white_24dp.png
new file mode 100644
index 000000000..cf8d6a8c9
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_qrcode_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_send_attachment_online.png b/src/main/res/drawable-hdpi/ic_send_attachment_online.png
index c6c00dfa9..aa35e764d 100644
--- a/src/main/res/drawable-hdpi/ic_send_attachment_online.png
+++ b/src/main/res/drawable-hdpi/ic_send_attachment_online.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_share_black_24dp.png b/src/main/res/drawable-hdpi/ic_share_black_24dp.png
new file mode 100644
index 000000000..7331f79b3
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_video_black_24dp.png b/src/main/res/drawable-hdpi/ic_video_black_24dp.png
new file mode 100644
index 000000000..afd308697
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_video_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_video_white_24dp.png b/src/main/res/drawable-hdpi/ic_video_white_24dp.png
new file mode 100644
index 000000000..ca414b356
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_video_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_android_grey600_48dp.png b/src/main/res/drawable-mdpi/ic_android_grey600_48dp.png
new file mode 100644
index 000000000..5040f8ef2
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_android_grey600_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_delete_black_24dp.png b/src/main/res/drawable-mdpi/ic_delete_black_24dp.png
new file mode 100644
index 000000000..7981a3def
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_delete_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_menu_white_24dp.png b/src/main/res/drawable-mdpi/ic_menu_white_24dp.png
new file mode 100644
index 000000000..60e4398a3
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_menu_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_qrcode_grey600_24dp.png b/src/main/res/drawable-mdpi/ic_qrcode_grey600_24dp.png
new file mode 100644
index 000000000..24cb66d42
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_qrcode_grey600_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_qrcode_scan_white_24dp.png b/src/main/res/drawable-mdpi/ic_qrcode_scan_white_24dp.png
new file mode 100644
index 000000000..ad5be5cdf
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_qrcode_scan_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_qrcode_white_24dp.png b/src/main/res/drawable-mdpi/ic_qrcode_white_24dp.png
new file mode 100644
index 000000000..7e28f95dc
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_qrcode_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_send_attachment_online.png b/src/main/res/drawable-mdpi/ic_send_attachment_online.png
index 7b2157825..921f79ffa 100644
--- a/src/main/res/drawable-mdpi/ic_send_attachment_online.png
+++ b/src/main/res/drawable-mdpi/ic_send_attachment_online.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_share_black_24dp.png b/src/main/res/drawable-mdpi/ic_share_black_24dp.png
new file mode 100644
index 000000000..d4ebebd06
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_video_black_24dp.png b/src/main/res/drawable-mdpi/ic_video_black_24dp.png
new file mode 100644
index 000000000..c4be08a55
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_video_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_video_white_24dp.png b/src/main/res/drawable-mdpi/ic_video_white_24dp.png
new file mode 100644
index 000000000..60cc35c42
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_video_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_android_grey600_48dp.png b/src/main/res/drawable-xhdpi/ic_android_grey600_48dp.png
new file mode 100644
index 000000000..eeefeda38
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_android_grey600_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_delete_black_24dp.png b/src/main/res/drawable-xhdpi/ic_delete_black_24dp.png
new file mode 100644
index 000000000..16e6e7055
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_delete_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_menu_white_24dp.png b/src/main/res/drawable-xhdpi/ic_menu_white_24dp.png
new file mode 100644
index 000000000..5b7f7480b
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_menu_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_qrcode_grey600_24dp.png b/src/main/res/drawable-xhdpi/ic_qrcode_grey600_24dp.png
new file mode 100644
index 000000000..3a100a3ed
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_qrcode_grey600_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_qrcode_scan_white_24dp.png b/src/main/res/drawable-xhdpi/ic_qrcode_scan_white_24dp.png
new file mode 100644
index 000000000..f22df8ea7
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_qrcode_scan_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_qrcode_white_24dp.png b/src/main/res/drawable-xhdpi/ic_qrcode_white_24dp.png
new file mode 100644
index 000000000..fcb2de2c3
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_qrcode_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_attachment_online.png b/src/main/res/drawable-xhdpi/ic_send_attachment_online.png
index 5ec947bf6..d1ba3d988 100644
--- a/src/main/res/drawable-xhdpi/ic_send_attachment_online.png
+++ b/src/main/res/drawable-xhdpi/ic_send_attachment_online.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_share_black_24dp.png b/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
new file mode 100644
index 000000000..5c058d4eb
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_video_black_24dp.png b/src/main/res/drawable-xhdpi/ic_video_black_24dp.png
new file mode 100644
index 000000000..f695100bb
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_video_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_video_white_24dp.png b/src/main/res/drawable-xhdpi/ic_video_white_24dp.png
new file mode 100644
index 000000000..2c764cbb5
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_video_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_android_grey600_48dp.png b/src/main/res/drawable-xxhdpi/ic_android_grey600_48dp.png
new file mode 100644
index 000000000..42395613f
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_android_grey600_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_delete_black_24dp.png b/src/main/res/drawable-xxhdpi/ic_delete_black_24dp.png
new file mode 100644
index 000000000..d118ae77b
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_delete_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_menu_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_menu_white_24dp.png
new file mode 100644
index 000000000..dcbdc6ac9
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_menu_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_qrcode_grey600_24dp.png b/src/main/res/drawable-xxhdpi/ic_qrcode_grey600_24dp.png
new file mode 100644
index 000000000..1ff70a5c8
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_qrcode_grey600_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_qrcode_scan_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_qrcode_scan_white_24dp.png
new file mode 100644
index 000000000..796e477ae
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_qrcode_scan_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_qrcode_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_qrcode_white_24dp.png
new file mode 100644
index 000000000..2928df951
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_qrcode_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_attachment_online.png b/src/main/res/drawable-xxhdpi/ic_send_attachment_online.png
index 34791812f..19e90728f 100644
--- a/src/main/res/drawable-xxhdpi/ic_send_attachment_online.png
+++ b/src/main/res/drawable-xxhdpi/ic_send_attachment_online.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png b/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
new file mode 100644
index 000000000..f52683b7f
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_video_black_24dp.png b/src/main/res/drawable-xxhdpi/ic_video_black_24dp.png
new file mode 100644
index 000000000..2cb46bf1a
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_video_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_video_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_video_white_24dp.png
new file mode 100644
index 000000000..77ef5ec76
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_video_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_android_grey600_48dp.png b/src/main/res/drawable-xxxhdpi/ic_android_grey600_48dp.png
new file mode 100644
index 000000000..7e9d48b6a
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_android_grey600_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_delete_black_24dp.png b/src/main/res/drawable-xxxhdpi/ic_delete_black_24dp.png
new file mode 100644
index 000000000..c49da5b4e
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_delete_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_menu_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_menu_white_24dp.png
new file mode 100644
index 000000000..0966d1f16
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_menu_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_qrcode_grey600_24dp.png b/src/main/res/drawable-xxxhdpi/ic_qrcode_grey600_24dp.png
new file mode 100644
index 000000000..ccd1ac6b7
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_qrcode_grey600_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_qrcode_scan_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_qrcode_scan_white_24dp.png
new file mode 100644
index 000000000..8b90377f5
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_qrcode_scan_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_qrcode_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_qrcode_white_24dp.png
new file mode 100644
index 000000000..3ec200f7a
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_qrcode_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_attachment_online.png b/src/main/res/drawable-xxxhdpi/ic_send_attachment_online.png
index 50764c450..319709583 100644
--- a/src/main/res/drawable-xxxhdpi/ic_send_attachment_online.png
+++ b/src/main/res/drawable-xxxhdpi/ic_send_attachment_online.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png b/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png
new file mode 100644
index 000000000..484b6d474
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_video_black_24dp.png b/src/main/res/drawable-xxxhdpi/ic_video_black_24dp.png
new file mode 100644
index 000000000..bd76e9db6
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_video_black_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_video_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_video_white_24dp.png
new file mode 100644
index 000000000..d8ab46d9f
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_video_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable/date_bubble.xml b/src/main/res/drawable/date_bubble.xml
index 0ea7b0761..3edf349e9 100644
--- a/src/main/res/drawable/date_bubble.xml
+++ b/src/main/res/drawable/date_bubble.xml
@@ -11,7 +11,11 @@
android:left="6dp"
android:right="6dp"
android:top="6dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey500">
+ </stroke>
<solid
android:color="@color/lightgreen">
</solid>
-</shape>
+</shape> \ No newline at end of file
diff --git a/src/main/res/drawable/date_bubble_dark.xml b/src/main/res/drawable/date_bubble_dark.xml
index 64f5c1515..345c315c1 100644
--- a/src/main/res/drawable/date_bubble_dark.xml
+++ b/src/main/res/drawable/date_bubble_dark.xml
@@ -11,7 +11,11 @@
android:left="6dp"
android:right="6dp"
android:top="6dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey800">
+ </stroke>
<solid
android:color="@color/darkgreen">
</solid>
-</shape>
+</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 6619984b3..32e146460 100644
--- a/src/main/res/drawable/message_bubble_received_light.xml
+++ b/src/main/res/drawable/message_bubble_received_light.xml
@@ -1,29 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="top|left">
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="24dp"
- android:viewportWidth="36"
- android:viewportHeight="24" >
- <path
- android:fillColor="@color/lightwhite"
- android:pathData="m2,0 c6,0 10,6 10,10L18,0z" />
- </vector>
- </item>
- <item android:left="12dp">
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <corners
- android:topLeftRadius="0dp"
- android:topRightRadius="5dp"
- android:bottomRightRadius="5dp"
- android:bottomLeftRadius="5dp"/>
- <padding
- android:bottom="2dp"
- android:left="18dp"
- android:right="6dp"
- android:top="2dp" />
- <solid android:color="@color/lightwhite" />
- </shape>
- </item>
-</layer-list>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <corners
+ android:topLeftRadius="0dp"
+ android:topRightRadius="5dp"
+ android:bottomRightRadius="5dp"
+ android:bottomLeftRadius="5dp">
+ </corners>
+ <padding
+ android:bottom="2dp"
+ android:left="6dp"
+ android:right="6dp"
+ android:top="2dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey500">
+ </stroke>
+ <solid
+ android:color="@color/lightwhite">
+ </solid>
+</shape> \ No newline at end of file
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 9514bc757..8f9132566 100644
--- a/src/main/res/drawable/message_bubble_received_light_dark.xml
+++ b/src/main/res/drawable/message_bubble_received_light_dark.xml
@@ -1,29 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="top|left">
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="24dp"
- android:viewportWidth="36"
- android:viewportHeight="24" >
- <path
- android:fillColor="@color/darkwhite"
- android:pathData="m2,0 c6,0 10,6 10,10L18,0z" />
- </vector>
- </item>
- <item android:left="12dp">
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <corners
- android:topLeftRadius="0dp"
- android:topRightRadius="5dp"
- android:bottomRightRadius="5dp"
- android:bottomLeftRadius="5dp" />
- <padding
- android:bottom="2dp"
- android:left="18dp"
- android:right="6dp"
- android:top="2dp" />
- <solid android:color="@color/darkwhite" />
- </shape>
- </item>
-</layer-list>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <corners
+ android:topLeftRadius="0dp"
+ android:topRightRadius="5dp"
+ android:bottomRightRadius="5dp"
+ android:bottomLeftRadius="5dp">
+ </corners>
+ <padding
+ android:bottom="2dp"
+ android:left="6dp"
+ android:right="6dp"
+ android:top="2dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey700">
+ </stroke>
+ <solid
+ android:color="@color/darkwhite">
+ </solid>
+</shape> \ No newline at end of file
diff --git a/src/main/res/drawable/message_bubble_received_warning.xml b/src/main/res/drawable/message_bubble_received_warning.xml
index ae3222a9e..b3bf6d451 100644
--- a/src/main/res/drawable/message_bubble_received_warning.xml
+++ b/src/main/res/drawable/message_bubble_received_warning.xml
@@ -1,29 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="top|left">
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="24dp"
- android:viewportWidth="36"
- android:viewportHeight="24" >
- <path
- android:fillColor="@color/lightred"
- android:pathData="m2,0 c6,0 10,6 10,10L18,0z" />
- </vector>
- </item>
- <item android:left="12dp">
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <corners
- android:topLeftRadius="0dp"
- android:topRightRadius="5dp"
- android:bottomRightRadius="5dp"
- android:bottomLeftRadius="5dp" />
- <padding
- android:bottom="2dp"
- android:left="18dp"
- android:right="6dp"
- android:top="2dp" />
- <solid android:color="@color/lightred" />
- </shape>
- </item>
-</layer-list>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <corners
+ android:topLeftRadius="0dp"
+ android:topRightRadius="5dp"
+ android:bottomRightRadius="5dp"
+ android:bottomLeftRadius="5dp">
+ </corners>
+ <padding
+ android:bottom="4dp"
+ android:left="6dp"
+ android:right="6dp"
+ android:top="4dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey500">
+ </stroke>
+ <solid
+ android:color="@color/lightred">
+ </solid>
+</shape> \ No newline at end of file
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 9f8aa779e..5867f67ce 100644
--- a/src/main/res/drawable/message_bubble_received_warning_dark.xml
+++ b/src/main/res/drawable/message_bubble_received_warning_dark.xml
@@ -1,29 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="top|left">
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="24dp"
- android:viewportWidth="36"
- android:viewportHeight="24" >
- <path
- android:fillColor="@color/darkred"
- android:pathData="m2,0 c6,0 10,6 10,10L18,0z" />
- </vector>
- </item>
- <item android:left="12dp">
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <corners
- android:topLeftRadius="0dp"
- android:topRightRadius="5dp"
- android:bottomRightRadius="5dp"
- android:bottomLeftRadius="5dp" />
- <padding
- android:bottom="2dp"
- android:left="18dp"
- android:right="6dp"
- android:top="2dp" />
- <solid android:color="@color/darkred" />
- </shape>
- </item>
-</layer-list>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <corners
+ android:topLeftRadius="0dp"
+ android:topRightRadius="5dp"
+ android:bottomRightRadius="5dp"
+ android:bottomLeftRadius="5dp">
+ </corners>
+ <padding
+ android:bottom="4dp"
+ android:left="6dp"
+ android:right="6dp"
+ android:top="4dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey700">
+ </stroke>
+ <solid
+ android:color="@color/darkred">
+ </solid>
+</shape> \ No newline at end of file
diff --git a/src/main/res/drawable/message_bubble_sent_blue.xml b/src/main/res/drawable/message_bubble_sent_blue.xml
index 8e519ff6b..1ee3ad0c8 100644
--- a/src/main/res/drawable/message_bubble_sent_blue.xml
+++ b/src/main/res/drawable/message_bubble_sent_blue.xml
@@ -1,30 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="bottom|right">
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="24dp"
- android:viewportWidth="36"
- android:viewportHeight="24" >
- <path
- android:fillColor="@color/lightblue"
- android:pathData="m 34,24 c-6,0 -10,-6 -10,-10 l -6,12 z" />
-
- </vector>
- </item>
- <item android:right="12dp">
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <corners
- android:topLeftRadius="5dp"
- android:topRightRadius="5dp"
- android:bottomRightRadius="0dp"
- android:bottomLeftRadius="5dp" />
- <padding
- android:bottom="2dp"
- android:left="6dp"
- android:right="18dp"
- android:top="2dp" />
- <solid android:color="@color/lightblue" />
- </shape>
- </item>
-</layer-list>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <corners
+ android:topLeftRadius="5dp"
+ android:topRightRadius="5dp"
+ android:bottomRightRadius="0dp"
+ android:bottomLeftRadius="5dp">
+ </corners>
+ <padding
+ android:bottom="4dp"
+ android:left="6dp"
+ android:right="6dp"
+ android:top="4dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey500">
+ </stroke>
+ <solid
+ android:color="@color/lightblue">
+ </solid>
+</shape> \ No newline at end of file
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 e6f3c88c1..d9853bf74 100644
--- a/src/main/res/drawable/message_bubble_sent_blue_dark.xml
+++ b/src/main/res/drawable/message_bubble_sent_blue_dark.xml
@@ -1,29 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="bottom|right">
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="24dp"
- android:viewportWidth="36"
- android:viewportHeight="24" >
- <path
- android:fillColor="@color/darkblue"
- android:pathData="m 34,24 c-6,0 -10,-6 -10,-10 l -6,12 z" />
- </vector>
- </item>
- <item android:right="12dp">
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <corners
- android:topLeftRadius="5dp"
- android:topRightRadius="5dp"
- android:bottomRightRadius="0dp"
- android:bottomLeftRadius="5dp" />
- <padding
- android:bottom="2dp"
- android:left="6dp"
- android:right="18dp"
- android:top="2dp" />
- <solid android:color="@color/darkblue" />
- </shape>
- </item>
-</layer-list>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <corners
+ android:topLeftRadius="5dp"
+ android:topRightRadius="5dp"
+ android:bottomRightRadius="0dp"
+ android:bottomLeftRadius="5dp">
+ </corners>
+ <padding
+ android:bottom="4dp"
+ android:left="6dp"
+ android:right="6dp"
+ android:top="4dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/grey700">
+ </stroke>
+ <solid
+ android:color="@color/darkblue">
+ </solid>
+</shape> \ No newline at end of file
diff --git a/src/main/res/layout/ab_title.xml b/src/main/res/layout/ab_title.xml
index a20e9f8bc..79c9d61f7 100644
--- a/src/main/res/layout/ab_title.xml
+++ b/src/main/res/layout/ab_title.xml
@@ -5,7 +5,7 @@
android:layout_height="match_parent"
android:gravity="start|center_vertical">
- <android.support.text.emoji.widget.EmojiTextView
+ <TextView
android:id="@android:id/text1"
style="@style/Base.TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="match_parent"
@@ -19,7 +19,7 @@
android:onClick="onClick"
android:paddingTop="1dp" />
- <android.support.text.emoji.widget.EmojiTextView
+ <TextView
android:id="@android:id/text2"
style="@style/Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle"
android:layout_width="match_parent"
diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml
index 9b638172a..e56ca6a95 100644
--- a/src/main/res/layout/account_row.xml
+++ b/src/main/res/layout/account_row.xml
@@ -15,6 +15,8 @@
android:layout_alignParentLeft="true"
android:contentDescription="@string/account_image_description"
android:padding="1dp"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<LinearLayout
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index 268071fbd..58fe0c901 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -43,7 +43,7 @@
android:orientation="vertical"
android:padding="@dimen/card_padding_regular">
- <android.support.text.emoji.widget.EmojiTextView
+ <TextView
android:id="@+id/contact_display_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -114,7 +114,7 @@
android:layout_marginTop="4dp"
android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
- <android.support.text.emoji.widget.EmojiTextView
+ <TextView
android:id="@+id/status_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -124,7 +124,7 @@
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.Conversations.Body1" />
- <android.support.text.emoji.widget.EmojiTextView
+ <TextView
android:id="@+id/resource"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -253,7 +253,7 @@
<android.support.v7.widget.RecyclerView
android:id="@+id/media"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="100dp"
android:layout_marginEnd="-2dp"
android:layout_marginStart="-2dp"
android:orientation="horizontal"
diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml
index 275a1d894..b8c48a382 100644
--- a/src/main/res/layout/activity_edit_account.xml
+++ b/src/main/res/layout/activity_edit_account.xml
@@ -29,10 +29,10 @@
android:id="@+id/editor"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
android:orientation="vertical"
android:padding="@dimen/card_padding_regular"
card_view:cardBackgroundColor="?attr/color_background_secondary">
@@ -51,9 +51,11 @@
android:layout_marginBottom="@dimen/avatar_item_distance"
android:adjustViewBounds="true"
android:contentDescription="@string/account_image_description"
- android:maxHeight="384dp"
android:maxWidth="384dp"
+ android:maxHeight="384dp"
android:padding="1dp"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<LinearLayout
@@ -177,8 +179,8 @@
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
- android:layout_marginBottom="4dp"
android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
android:text="@string/show_privacy"
android:visibility="gone" />
@@ -188,8 +190,8 @@
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
- android:layout_marginBottom="4dp"
android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
android:text="@string/show_termsofuse"
android:visibility="gone" />
</LinearLayout>
@@ -200,10 +202,10 @@
android:id="@+id/os_optimization"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
android:visibility="gone"
card_view:cardBackgroundColor="?attr/color_background_secondary">
@@ -258,10 +260,10 @@
android:id="@+id/stats"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
android:background="@drawable/infocard_border"
android:orientation="vertical"
android:padding="@dimen/card_padding_regular"
@@ -554,9 +556,11 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
- android:layout_toLeftOf="@+id/action_copy_to_clipboard"
+ android:layout_toStartOf="@+id/key_actions"
+ android:layout_toLeftOf="@+id/key_actions"
android:orientation="vertical">
<TextView
@@ -574,17 +578,25 @@
</LinearLayout>
- <ImageButton
- android:id="@+id/action_copy_to_clipboard"
+ <LinearLayout
+ android:id="@+id/key_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
- android:background="?attr/selectableItemBackgroundBorderless"
- android:contentDescription="@string/copy_otr_clipboard_description"
- android:padding="@dimen/image_button_padding"
- android:src="?attr/icon_copy"
- android:visibility="visible" />
+ android:orientation="horizontal">
+
+ <ImageButton
+ android:id="@+id/action_copy_to_clipboard"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/copy_otr_clipboard_description"
+ android:padding="@dimen/image_button_padding"
+ android:src="?attr/icon_copy"
+ android:visibility="visible" />
+ </LinearLayout>
</RelativeLayout>
<RelativeLayout
@@ -596,8 +608,10 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
+ android:layout_toStartOf="@+id/axolotl_actions"
android:layout_toLeftOf="@+id/axolotl_actions"
android:orientation="vertical">
@@ -618,9 +632,10 @@
android:id="@+id/axolotl_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
- android:orientation="vertical">
+ android:orientation="horizontal">
<ImageButton
android:id="@+id/action_copy_axolotl_to_clipboard"
@@ -650,10 +665,10 @@
android:id="@+id/other_device_keys_card"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
android:visibility="gone"
card_view:cardBackgroundColor="?attr/color_background_secondary">
@@ -694,11 +709,11 @@
android:id="@+id/button_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
+ android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:layout_alignParentStart="true">
+ android:layout_alignParentBottom="true">
<Button
android:id="@+id/cancel_button"
@@ -711,8 +726,8 @@
<View
android:layout_width="1dp"
android:layout_height="fill_parent"
- android:layout_marginBottom="7dp"
- android:layout_marginTop="7dp" />
+ android:layout_marginTop="7dp"
+ android:layout_marginBottom="7dp" />
<Button
android:id="@+id/save_button"
diff --git a/src/main/res/layout/activity_fullscreen_message.xml b/src/main/res/layout/activity_media_viewer.xml
index c1a1bc619..2eaad0d04 100644
--- a/src/main/res/layout/activity_fullscreen_message.xml
+++ b/src/main/res/layout/activity_media_viewer.xml
@@ -13,7 +13,7 @@
android:layout_alignParentRight="true"
android:layout_alignParentTop="true">
- <com.github.chrisbanes.photoview.PhotoView
+ <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
android:id="@id/message_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -32,6 +32,6 @@
android:layout_height="wrap_content"
android:layout_gravity="right|top"
android:layout_margin="16dp"
- android:src="?attr/icon_share" />
+ android:src="@drawable/ic_menu_white_24dp" />
</FrameLayout>
</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml
index e1ac744ea..29400a246 100644
--- a/src/main/res/layout/activity_muc_details.xml
+++ b/src/main/res/layout/activity_muc_details.xml
@@ -26,11 +26,10 @@
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
- android:orientation="vertical"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
card_view:cardBackgroundColor="?attr/color_background_secondary">
<LinearLayout
@@ -53,10 +52,10 @@
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_alignParentLeft="true"
android:layout_toStartOf="@+id/edit_muc_name_button"
+ android:layout_toLeftOf="@+id/edit_muc_name_button"
android:orientation="vertical">
<TextView
@@ -76,8 +75,8 @@
android:id="@+id/jid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
android:layout_below="@+id/muc_subject"
+ android:layout_alignParentLeft="true"
android:text="@string/account_settings_example_jabber_id"
android:textAppearance="@style/TextAppearance.Conversations.Body1"
android:textIsSelectable="true"
@@ -89,10 +88,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_alignParentLeft="true"
android:layout_toStartOf="@+id/edit_muc_name_button"
+ android:layout_toLeftOf="@+id/edit_muc_name_button"
android:orientation="vertical"
android:visibility="gone">
@@ -130,9 +129,9 @@
android:id="@+id/edit_muc_name_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
android:alpha="?attr/icon_alpha"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="@dimen/image_button_padding"
@@ -153,10 +152,12 @@
android:layout_marginEnd="@dimen/avatar_item_distance"
android:layout_marginRight="@dimen/avatar_item_distance"
android:adjustViewBounds="true"
- android:maxHeight="384dp"
android:maxWidth="384dp"
+ android:maxHeight="384dp"
android:padding="1dp"
android:scaleType="centerCrop"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
</RelativeLayout>
@@ -279,12 +280,12 @@
<android.support.v7.widget.CardView
android:id="@+id/media_wrapper"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
card_view:cardBackgroundColor="?attr/color_background_secondary">
<LinearLayout
@@ -295,14 +296,14 @@
<android.support.v7.widget.RecyclerView
android:id="@+id/media"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="100dp"
android:layout_marginEnd="-2dp"
android:layout_marginStart="-2dp"
android:orientation="horizontal"
- android:paddingBottom="@dimen/card_padding_list"
- android:paddingEnd="@dimen/card_padding_regular"
android:paddingStart="@dimen/card_padding_regular"
- android:paddingTop="@dimen/card_padding_regular" />
+ android:paddingTop="@dimen/card_padding_regular"
+ android:paddingEnd="@dimen/card_padding_regular"
+ android:paddingBottom="@dimen/card_padding_list" />
<LinearLayout
android:layout_width="wrap_content"
@@ -327,11 +328,10 @@
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
- android:orientation="vertical"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
card_view:cardBackgroundColor="?attr/color_background_secondary">
<LinearLayout
@@ -355,10 +355,12 @@
android:id="@+id/your_photo"
android:layout_width="72dp"
android:layout_height="72dp"
- android:layout_alignParentEnd="false"
android:layout_alignParentLeft="true"
+ android:layout_alignParentEnd="false"
android:layout_alignParentRight="false"
android:padding="1dp"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<LinearLayout
@@ -369,7 +371,7 @@
android:orientation="vertical"
android:paddingLeft="@dimen/avatar_item_distance">
- <android.support.text.emoji.widget.EmojiTextView
+ <TextView
android:id="@+id/muc_your_nick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -429,11 +431,10 @@
android:id="@+id/muc_more_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
- android:orientation="vertical"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
card_view:cardBackgroundColor="?attr/color_background_secondary">
<LinearLayout
@@ -458,7 +459,7 @@
android:layout_weight="1"
android:divider="?android:dividerHorizontal"
android:orientation="vertical"
- android:showDividers="middle"></LinearLayout>
+ android:showDividers="middle" />
</LinearLayout>
</android.support.v7.widget.CardView>
diff --git a/src/main/res/layout/activity_publish_profile_picture.xml b/src/main/res/layout/activity_publish_profile_picture.xml
index 1db8a159d..0fb666365 100644
--- a/src/main/res/layout/activity_publish_profile_picture.xml
+++ b/src/main/res/layout/activity_publish_profile_picture.xml
@@ -6,80 +6,89 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/color_background_tertiary">
+ android:background="?attr/color_background_tertiary"
+ android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
- <android.support.v7.widget.CardView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/toolbar"
- android:layout_marginBottom="@dimen/activity_vertical_margin"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- card_view:cardBackgroundColor="?attr/color_background_secondary">
-
- <LinearLayout
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@id/button_bar"
+ android:layout_below="@id/toolbar">
+
+ <android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:padding="@dimen/card_padding_regular">
-
- <FrameLayout
- android:id="@+id/account_image_wrapper"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="@dimen/publish_avatar_top_margin">
-
- <com.makeramen.roundedimageview.RoundedImageView
- android:id="@+id/account_image"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ card_view:cardBackgroundColor="?attr/color_background_secondary">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:padding="@dimen/card_padding_regular">
+
+ <FrameLayout
+ android:id="@+id/account_image_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- android:maxHeight="384dp"
- android:maxWidth="384dp"
- android:padding="1dp"
- android:scaleType="centerCrop"
- app:riv_corner_radius="@dimen/rounded_image_border" />
- </FrameLayout>
-
- <TextView
- android:id="@+id/hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/touch_to_choose_picture"
- android:textAppearance="@style/TextAppearance.Conversations.Body1" />
-
- <TextView
- android:id="@+id/secondary_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/or_long_press_for_default"
- android:textAppearance="@style/TextAppearance.Conversations.Body1" />
-
- <TextView
- android:id="@+id/hint_or_warning"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="8dp"
- android:textAppearance="@style/TextAppearance.Conversations.Body1" />
-
- </LinearLayout>
- </android.support.v7.widget.CardView>
+ android:layout_marginTop="@dimen/publish_avatar_top_margin"
+ android:layout_marginBottom="8dp">
+
+ <com.makeramen.roundedimageview.RoundedImageView
+ android:id="@+id/account_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:adjustViewBounds="true"
+ android:maxWidth="384dp"
+ android:maxHeight="384dp"
+ android:padding="1dp"
+ android:scaleType="centerCrop"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
+ app:riv_corner_radius="@dimen/rounded_image_border" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/touch_to_choose_picture"
+ android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+
+ <TextView
+ android:id="@+id/secondary_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/or_long_press_for_default"
+ android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+
+ <TextView
+ android:id="@+id/hint_or_warning"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+
+ </LinearLayout>
+ </android.support.v7.widget.CardView>
+ </ScrollView>
<LinearLayout
android:id="@+id/button_bar"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true">
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true">
<Button
android:id="@+id/cancel_button"
@@ -92,8 +101,8 @@
<View
android:layout_width="1dp"
android:layout_height="fill_parent"
- android:layout_marginBottom="7dp"
android:layout_marginTop="7dp"
+ android:layout_marginBottom="7dp"
android:background="?attr/divider" />
<Button
diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml
index 9635d4c47..09e0ac9bf 100644
--- a/src/main/res/layout/contact.xml
+++ b/src/main/res/layout/contact.xml
@@ -13,6 +13,8 @@
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_alignParentLeft="true"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<LinearLayout
@@ -23,7 +25,7 @@
android:orientation="vertical"
android:paddingLeft="@dimen/avatar_item_distance">
- <android.support.text.emoji.widget.EmojiTextView
+ <TextView
android:id="@+id/contact_display_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml
index cbd884ae2..ed0e9e1cc 100644
--- a/src/main/res/layout/conversation_list_row.xml
+++ b/src/main/res/layout/conversation_list_row.xml
@@ -24,6 +24,8 @@
android:layout_alignParentLeft="true"
android:padding="1dp"
android:scaleType="centerCrop"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<RelativeLayout
@@ -144,7 +146,7 @@
android:visibility="gone"
app:backgroundColor="?attr/colorAccent" />
- <de.pixart.messenger.ui.widget.UnreadCountCustomView
+ <de.pixart.messenger.ui.widget.FailedCountCustomView
android:id="@+id/conversation_failed"
android:layout_width="?attr/IconSize"
android:layout_height="?attr/IconSize"
diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml
index eca67f91e..8917333c1 100644
--- a/src/main/res/layout/fragment_conversation.xml
+++ b/src/main/res/layout/fragment_conversation.xml
@@ -30,6 +30,7 @@
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/messages_view"
android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
android:alpha="0.85"
android:src="?attr/icon_scroll_down"
android:visibility="gone"
@@ -42,8 +43,10 @@
android:layout_width="?attr/IconSize"
android:layout_height="?attr/IconSize"
android:layout_alignEnd="@+id/scroll_to_bottom_button"
+ android:layout_alignRight="@+id/scroll_to_bottom_button"
android:layout_alignTop="@+id/scroll_to_bottom_button"
android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:elevation="8dp"
android:visibility="gone"
@@ -84,7 +87,9 @@
android:id="@+id/media_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_toLeftOf="@+id/textSendButton"
android:layout_toStartOf="@+id/textSendButton"
android:background="@drawable/message_bubble_sent_blue"
android:orientation="horizontal"
diff --git a/src/main/res/layout/message_content.xml b/src/main/res/layout/message_content.xml
index 296944565..044f8c64d 100644
--- a/src/main/res/layout/message_content.xml
+++ b/src/main/res/layout/message_content.xml
@@ -13,6 +13,8 @@
android:maxHeight="500dp"
android:maxWidth="500dp"
android:scaleType="centerCrop"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<de.pixart.messenger.ui.widget.CopyTextView
diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml
index f8b5fce95..2c6fd044a 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -16,6 +16,8 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:scaleType="fitXY"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dip"
app:riv_corner_radius="@dimen/rounded_image_border" />
<LinearLayout
@@ -23,7 +25,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
- android:layout_marginLeft="-7dp"
+ android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toRightOf="@+id/message_photo"
android:background="@drawable/message_bubble_received_light"
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 188e466d5..2526e4dd2 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -23,6 +23,8 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="fitXY"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<View
@@ -37,7 +39,7 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="4dp"
- android:layout_marginRight="-7dp"
+ android:layout_marginRight="4dp"
android:layout_toLeftOf="@+id/message_photo_box"
android:layout_toStartOf="@+id/message_photo_box"
android:background="@drawable/message_bubble_sent_blue"
diff --git a/src/main/res/layout/message_status.xml b/src/main/res/layout/message_status.xml
index bd731cbe4..d42637175 100644
--- a/src/main/res/layout/message_status.xml
+++ b/src/main/res/layout/message_status.xml
@@ -29,6 +29,8 @@
android:padding="0dp"
android:scaleType="fitXY"
android:visibility="gone"
+ app:riv_border_color="?attr/color_border"
+ app:riv_border_width="1dp"
app:riv_corner_radius="@dimen/rounded_image_border" />
<TextView
diff --git a/src/main/res/menu/contact_details.xml b/src/main/res/menu/contact_details.xml
index 50243150b..b9268e4f9 100644
--- a/src/main/res/menu/contact_details.xml
+++ b/src/main/res/menu/contact_details.xml
@@ -11,7 +11,7 @@
<item
android:id="@+id/action_share"
- android:icon="?attr/icon_share"
+ android:icon="@drawable/ic_share_white_24dp"
android:orderInCategory="15"
app:showAsAction="always"
android:title="@string/share_uri_with">
diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml
index fbc5841ea..09919b9fe 100644
--- a/src/main/res/menu/editaccount.xml
+++ b/src/main/res/menu/editaccount.xml
@@ -5,11 +5,12 @@
android:id="@+id/action_change_presence"
android:icon="@drawable/ic_new_releases_white_24dp"
android:title="@string/edit_status_message"
- app:showAsAction="always" />
+ app:showAsAction="ifRoom" />
- <item android:id="@+id/action_share"
+ <item
+ android:id="@+id/action_share"
+ android:icon="@drawable/ic_share_white_24dp"
android:title="@string/share_uri_with"
- android:icon="?attr/icon_share"
app:showAsAction="always">
<menu>
<item
@@ -21,57 +22,60 @@
<item
android:id="@+id/action_share_barcode"
android:title="@string/share_as_barcode" />
- <item
- android:id="@+id/action_show_qr_code"
- android:title="@string/show_qr_code" />
</menu>
</item>
<item
+ android:id="@+id/action_show_qr_code"
+ android:icon="@drawable/ic_qrcode_white_24dp"
+ android:title="@string/show_qr_code"
+ app:showAsAction="always" />
+
+ <item
android:id="@+id/action_show_block_list"
- app:showAsAction="always"
android:icon="@drawable/ic_speaker_notes_off_white_24dp"
- android:title="@string/show_block_list" />
+ android:title="@string/show_block_list"
+ app:showAsAction="ifRoom" />
<item
android:id="@+id/action_renew_certificate"
- app:showAsAction="never"
android:title="@string/action_renew_certificate"
- android:visible="false" />
+ android:visible="false"
+ app:showAsAction="never" />
<item
android:id="@+id/action_server_info_show_more"
android:checkable="true"
android:checked="false"
- app:showAsAction="never"
- android:title="@string/server_info_show_more" />
+ android:title="@string/server_info_show_more"
+ app:showAsAction="never" />
<item
android:id="@+id/action_mam_prefs"
android:icon="@drawable/ic_cloud_white_24dp"
- app:showAsAction="always"
- android:title="@string/mam_prefs" />
+ android:title="@string/mam_prefs"
+ app:showAsAction="ifRoom" />
<item
android:id="@+id/action_show_password"
- app:showAsAction="never"
- android:title="@string/show_password" />
+ android:title="@string/show_password"
+ app:showAsAction="never" />
<item
android:id="@+id/action_change_password_on_server"
- app:showAsAction="always"
android:icon="@drawable/ic_vpn_key_white_24dp"
- android:title="@string/change_password" />
+ android:title="@string/change_password"
+ app:showAsAction="ifRoom" />
<item
android:id="@+id/mgmt_account_reconnect"
- app:showAsAction="never"
- android:title="@string/mgmt_account_reconnect" />
+ android:title="@string/mgmt_account_reconnect"
+ app:showAsAction="never" />
<item
android:id="@+id/mgmt_account_announce_pgp"
android:title="@string/mgmt_account_publish_pgp" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
- app:showAsAction="never"
- android:title="@string/action_settings" />
+ android:title="@string/action_settings"
+ app:showAsAction="never" />
</menu> \ No newline at end of file
diff --git a/src/main/res/menu/media_viewer.xml b/src/main/res/menu/media_viewer.xml
new file mode 100644
index 000000000..c3898c79b
--- /dev/null
+++ b/src/main/res/menu/media_viewer.xml
@@ -0,0 +1,17 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/action_share"
+ android:icon="?attr/icon_share"
+ android:orderInCategory="10"
+ android:title="@string/share" />
+ <item
+ android:id="@+id/action_open"
+ android:orderInCategory="20"
+ android:title="@string/action_open" />
+ <item
+ android:id="@+id/action_delete"
+ android:icon="?attr/icon_delete"
+ android:orderInCategory="30"
+ android:title="@string/action_delete" />
+</menu> \ No newline at end of file
diff --git a/src/main/res/menu/muc_details.xml b/src/main/res/menu/muc_details.xml
index b5e74f271..9f12e7333 100644
--- a/src/main/res/menu/muc_details.xml
+++ b/src/main/res/menu/muc_details.xml
@@ -4,7 +4,7 @@
<item
android:id="@+id/action_share"
- android:icon="?attr/icon_share"
+ android:icon="@drawable/ic_share_white_24dp"
android:orderInCategory="15"
android:title="@string/share_uri_with"
app:showAsAction="always">
diff --git a/src/main/res/values-fil/strings.xml b/src/main/res/values-fil/strings.xml
index f71f50243..fe40ec093 100644
--- a/src/main/res/values-fil/strings.xml
+++ b/src/main/res/values-fil/strings.xml
@@ -317,7 +317,7 @@
<string name="delete_x_file">Burahin %s</string>
<string name="file">kikil</string>
<string name="open_x_file">Buksan %s</string>
- <string name="sending_file">pagpapadala (%1$d% nakumpleto)</string>
+ <string name="sending_file">pagpapadala (%1$d%% nakumpleto)</string>
<string name="preparing_file">Paghahanda sa kikil para sa transmisyon</string>
<string name="x_file_offered_for_download">%s inaalok para idownload</string>
<string name="cancel_transmission">Kansel transmisyon</string>
diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml
index d9528a409..9b5a71e96 100644
--- a/src/main/res/values-tr/strings.xml
+++ b/src/main/res/values-tr/strings.xml
@@ -433,7 +433,7 @@
<string name="download_failed_could_not_write_file">İndirme başarısız: Dosya yazılamadı</string>
<string name="action_check_update">Güncellemeleri kontrol et</string>
<string name="title_activity_updater">Servisi güncelle</string>
- <string name="update_available">Sürüm% 1 $ s mevcut. \ N \ nFilesize:% 2 $ s \ n \ n% 1 $ s sürümüne şimdi mi güncelleştiniz?</string>
+ <string name="update_available">Sürüm %1$s mevcut.\n\nFilesize: %2$s \n\n%1$s sürümüne şimdi mi güncelleştiniz?</string>
<string name="remind_later">sonra</string>
<string name="update">güncelle</string>
<string name="no_update_available">Güncelleme mevcut değil</string>
diff --git a/src/main/res/values-w384dp/dimens.xml b/src/main/res/values-w384dp/dimens.xml
index e9350addd..a04ce6763 100644
--- a/src/main/res/values-w384dp/dimens.xml
+++ b/src/main/res/values-w384dp/dimens.xml
@@ -2,7 +2,6 @@
<!-- 384dp is the screen width of the Nexus 4. Something like a Moto G is smaller but a Nexus 5X is larger -->
<!-- https://material.io/devices/ -->
<dimen name="fineprint_size">12sp</dimen>
- <dimen name="swipe_handle_size">48dp</dimen>
<dimen name="audio_player_width">288dp</dimen>
<dimen name="avatar_on_details_screen_size">72dp</dimen>
<dimen name="media_size">64dp</dimen> <!-- ideally not larger than avatar_on_details_screen -->
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index 672cb00fb..c537fd248 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -58,6 +58,7 @@
<attr name="icon_chat" format="reference" />
<attr name="icon_copy" format="reference" />
<attr name="icon_discard" format="reference" />
+ <attr name="icon_show_qr_code" format="reference" />
<attr name="icon_download" format="reference" />
<attr name="icon_edit" format="reference" />
<attr name="icon_edit_body" format="reference" />
@@ -74,6 +75,7 @@
<attr name="icon_secure" format="reference" />
<attr name="icon_settings" format="reference" />
<attr name="icon_share" format="reference" />
+ <attr name="icon_delete" format="reference" />
<attr name="icon_import_export" format="reference" />
<attr name="icon_scan_qr_code" format="reference" />
<attr name="icon_enable_undecided_device" format="reference" />
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index 6ea10548f..8dfec1519 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -27,7 +27,7 @@
<color name="grey900">#ff282828</color>
<color name="red800">#ffc62828</color>
<color name="red500">#fff44336</color>
- <color name="red_a700">#ffd50000</color>
+ <color name="red700">#ffd50000</color>
<color name="orange500">#ffff9800</color>
<color name="bubble">#ff2e4272</color>
<color name="realwhite">#ffffffff</color>
diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml
index e213ea773..29e560124 100644
--- a/src/main/res/values/defaults.xml
+++ b/src/main/res/values/defaults.xml
@@ -41,7 +41,7 @@
\n\nhttp://hc.apache.org/httpcomponents-client\n(Apache License, Version 2.0)
\n\nhttp://hc.apache.org/httpcomponents-core\n(Apache License, Version 2.0)
\n\nhttps://github.com/bumptech/glide\n(BSD, The MIT License (MIT) and Apache License, Version 2.0)
- \n\nhttps://github.com/chrisbanes/PhotoView\n(Apache License, Version 2.0)
+ \n\nhttps://github.com/davemorrissey/subsampling-scale-image-view\n(Apache License, Version 2.0)
\n\nhttps://github.com/rtoshiro/FullscreenVideoView\n(Apache License, Version 2.0)
\n\nhttps://github.com/mangstadt/ez-vcard\n(FreeBSD)
\n\nhttps://github.com/googlesamples/easypermissions\n(Apache License, Version 2.0)
@@ -51,7 +51,6 @@
\n\nhttp://leafletjs.com/\n(BSD 2-Clause)
\n\nhttps://www.openstreetmap.org/\n(Open Database License)
\n\nhttp://xmpp.rocks/\n(The MIT License (MIT))
- \n\nhttps://github.com/championswimmer/SimpleFingerGestures_Android_Library/\n(Apache License, Version 2.0)
</string>
<string name="default_resource" translatable="false">Phone</string>
diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml
index 526eb7d40..0c6ef41fb 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -27,5 +27,5 @@
<dimen name="scan_dot_size">8dp</dimen>
<dimen name="background_image_opacity">0.12</dimen>
- <dimen name="rounded_image_border">8dp</dimen>
+ <dimen name="rounded_image_border">5dp</dimen>
</resources>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 62b415cf3..34c227331 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -75,7 +75,7 @@
<string name="sharing_files_please_wait">Sharing files. Please wait…</string>
<string name="action_clear_history">Clear history</string>
<string name="clear_conversation_history">Clear Conversation History</string>
- <string name="clear_histor_msg">Do you want to delete all messages within this Conversation?\n\n<b>Warning:</b> This will not influence messages stored on other devices or servers.</string>
+ <string name="clear_histor_msg">Are you sure you want to delete all messages within this conversation?\n\n<b>Warning:</b> This will not delete copies of those messages that are stored on other devices or servers.</string>
<string name="delete_messages">Delete messages</string>
<string name="also_end_conversation">End this conversation afterwards</string>
<string name="choose_presence">Choose device</string>
@@ -295,8 +295,8 @@
<string name="send_again">Send again</string>
<string name="file_url">File URL</string>
<string name="url_copied_to_clipboard">URL copied to clipboard</string>
- <string name="scan_qr_code">Scan barcode</string>
- <string name="show_qr_code">Show barcode</string>
+ <string name="scan_qr_code">Scan QR code</string>
+ <string name="show_qr_code">Show QR code</string>
<string name="show_block_list">Show block list</string>
<string name="account_details">Account details</string>
<string name="verify_otr">Verify OTR</string>
@@ -611,19 +611,19 @@
<string name="this_device_has_been_verified">This device has been verified</string>
<string name="copy_fingerprint">Copy fingerprint</string>
<string name="all_omemo_keys_have_been_verified">All OMEMO keys have been verified</string>
- <string name="barcode_does_not_contain_fingerprints_for_this_conversation">Barcode does not contain fingerprints for this conversation.</string>
+ <string name="barcode_does_not_contain_fingerprints_for_this_conversation">QR code does not contain fingerprints for this conversation.</string>
<string name="verified_fingerprints">Verified fingerprints</string>
- <string name="use_camera_icon_to_scan_barcode">Use the camera to scan a contacts barcode</string>
+ <string name="use_camera_icon_to_scan_barcode">Use the camera to scan a contacts QR code</string>
<string name="please_wait_for_keys_to_be_fetched">Please wait for keys to be fetched</string>
- <string name="share_as_barcode">Share as Barcode</string>
+ <string name="share_as_barcode">Share as QR code</string>
<string name="share_as_uri">Share as XMPP URI</string>
<string name="share_as_http">Share as HTTP link</string>
<string name="blindly_trusted_omemo_keys">Blindly trusted OMEMO keys</string>
<string name="pref_blind_trust_before_verification">Blind Trust Before Verification</string>
<string name="pref_blind_trust_before_verification_summary">Automatically trust all new devices from contacts that haven’t been verified before.</string>
<string name="not_trusted">Untrusted</string>
- <string name="invalid_barcode">Invalid barcode</string>
- <string name="verify_with_qr_code">verify with barcode</string>
+ <string name="invalid_barcode">Invalid QR code</string>
+ <string name="verify_with_qr_code">verify with QR code</string>
<string name="i_followed_this_link_from_a_trusted_source">I followed this link from a trusted source</string>
<string name="verifying_omemo_keys_trusted_source">You are about to verify the OMEMO keys of %1$s after clicking a link. This is only secure if you followed this link from a trusted source where only %2$s could have published this link.</string>
<string name="verify_omemo_keys">Verify OMEMO fingerprints</string>
@@ -826,4 +826,14 @@
<string name="action_group_details">Group details</string>
<string name="view_media">View media</string>
<string name="media_browser">Media browser</string>
+ <string name="account_status_stream_opening_error">Stream opening error</string>
+ <string name="action_open">Open</string>
+ <string name="action_delete">Delete</string>
+ <string name="security_violation_not_attaching_file">File omitted due to security violation.</string>
+ <string name="delete_file_dialog">Delete file</string>
+ <string name="delete_file_dialog_msg">Are you sure you want to delete this file?\n\n<b>Warning:</b> This will not delete copies of this file that are stored on other devices or servers. </string>
+ <string name="cancelled">cancelled</string>
+ <string name="remote_server_timeout">Remote server timeout</string>
+ <string name="already_drafting_message">You are already drafting a message.</string>
+
</resources>
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index bbfaf6060..342ed8aa1 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -13,7 +13,7 @@
<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="color_warning">@color/red700</item>
<item name="TextColorOnline">@color/green500</item>
<item name="TextColorError">@color/red800</item>
@@ -80,6 +80,7 @@
<item name="icon_cancel" type="reference">@drawable/ic_cancel_black_24dp</item>
<item name="icon_chat" type="reference">@drawable/ic_action_chat</item>
<item name="icon_copy" type="reference">@drawable/ic_content_copy_grey600_24dp</item>
+ <item name="icon_show_qr_code" type="reference">@drawable/ic_qrcode_grey600_24dp</item>
<item name="icon_discard" type="reference">@drawable/ic_delete_white_24dp</item>
<item name="icon_download" type="reference">@drawable/ic_file_download_white_24dp</item>
<item name="icon_edit" type="reference">@drawable/ic_edit_white_24dp</item>
@@ -98,8 +99,9 @@
<item name="icon_settings" type="reference">@drawable/ic_settings_black_24dp</item>
<item name="icon_import_export" type="reference">@drawable/ic_import_export_white_24dp
</item>
- <item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
- <item name="icon_scan_qr_code" type="reference">@drawable/ic_barcode_scan_white_24dp</item>
+ <item name="icon_delete" type="reference">@drawable/ic_delete_black_24dp</item>
+ <item name="icon_share" type="reference">@drawable/ic_share_black_24dp</item>
+ <item name="icon_scan_qr_code" type="reference">@drawable/ic_qrcode_scan_white_24dp</item>
<item name="icon_scroll_down" type="reference">@drawable/ic_scroll_to_end_black</item>
<!-- settings-->
@@ -165,7 +167,7 @@
<item name="color_background_primary">@color/grey700</item>
<item name="color_background_secondary">@color/grey800</item>
<item name="color_background_tertiary">@color/grey900</item>
- <item name="color_warning">@color/red_a700</item>
+ <item name="color_warning">@color/red700</item>
<item name="TextColorOnline">@color/green500</item>
<item name="TextColorError">@color/red500</item>
@@ -251,6 +253,7 @@
<item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item>
<item name="icon_cancel" type="reference">@drawable/ic_cancel_white_24dp</item>
<item name="icon_copy" type="reference">@drawable/ic_content_copy_white_24dp</item>
+ <item name="icon_show_qr_code" type="reference">@drawable/ic_qrcode_white_24dp</item>
<item name="icon_discard" type="reference">@drawable/ic_delete_white_24dp</item>
<item name="icon_download" type="reference">@drawable/ic_file_download_white_24dp</item>
<item name="icon_edit" type="reference">@drawable/ic_edit_white_24dp</item>
@@ -269,8 +272,9 @@
<item name="icon_settings" type="reference">@drawable/ic_settings_white_24dp</item>
<item name="icon_import_export" type="reference">@drawable/ic_import_export_white_24dp
</item>
+ <item name="icon_delete" type="reference">@drawable/ic_delete_white_24dp</item>
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
- <item name="icon_scan_qr_code" type="reference">@drawable/ic_barcode_scan_white_24dp</item>
+ <item name="icon_scan_qr_code" type="reference">@drawable/ic_qrcode_scan_white_24dp</item>
<item name="icon_scroll_down" type="reference">@drawable/ic_scroll_to_end_white</item>
<item name="icon_notifications" type="reference">@drawable/ic_notifications_white_24dp
diff --git a/src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java b/src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java
index 3db7219c0..3e9a00a3c 100644
--- a/src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java
+++ b/src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java
@@ -1,6 +1,7 @@
package de.pixart.messenger.services;
import android.content.Intent;
+
import com.google.firebase.iid.FirebaseInstanceIdService;
public class InstanceIdService extends FirebaseInstanceIdService {
diff --git a/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java b/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java
index 9ee13c6cd..9368913bc 100644
--- a/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java
+++ b/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java
@@ -32,5 +32,6 @@ public class MaintenanceReceiver extends BroadcastReceiver {
Log.d(Config.LOGTAG, "unable to renew instance token", e);
}
}).start();
+
}
} \ No newline at end of file
diff --git a/src/standardPush/java/de/pixart/messenger/services/PushManagementService.java b/src/standardPush/java/de/pixart/messenger/services/PushManagementService.java
index b9aff036b..7daa552d4 100644
--- a/src/standardPush/java/de/pixart/messenger/services/PushManagementService.java
+++ b/src/standardPush/java/de/pixart/messenger/services/PushManagementService.java
@@ -2,7 +2,12 @@ package de.pixart.messenger.services;
import android.util.Log;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
+import com.google.firebase.iid.FirebaseInstanceId;
+
import de.pixart.messenger.Config;
+import de.pixart.messenger.R;
import de.pixart.messenger.entities.Account;
import de.pixart.messenger.utils.Namespace;
import de.pixart.messenger.utils.PhoneHelper;
@@ -14,8 +19,6 @@ import rocks.xmpp.addr.Jid;
public class PushManagementService {
- private static final Jid APP_SERVER = Jid.of("p2.siacs.eu");
-
protected final XmppConnectionService mXmppConnectionService;
PushManagementService(XmppConnectionService service) {
@@ -26,7 +29,8 @@ public class PushManagementService {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": has push support");
retrieveFcmInstanceToken(token -> {
final String androidId = PhoneHelper.getAndroidId(mXmppConnectionService);
- IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(APP_SERVER, token, androidId);
+ final Jid appServer = Jid.of(mXmppConnectionService.getString(R.string.app_server));
+ IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(appServer, token, androidId);
mXmppConnectionService.sendIqPacket(account, packet, (a, p) -> {
Element command = p.findChild("command", "http://jabber.org/protocol/commands");
if (p.getType() == IqPacket.TYPE.RESULT && command != null) {
diff --git a/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java b/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java
index d45ded454..816d11118 100644
--- a/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java
+++ b/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java
@@ -3,6 +3,9 @@ package de.pixart.messenger.services;
import android.content.Intent;
import android.util.Log;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
import java.util.Map;
import de.pixart.messenger.Config;