aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md8
-rw-r--r--build.gradle19
-rw-r--r--docs/encryption.md2
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--libs/xmpp-addr/build.gradle2
-rw-r--r--src/main/java/de/pixart/messenger/Config.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java2
-rw-r--r--src/main/java/de/pixart/messenger/entities/Account.java8
-rw-r--r--src/main/java/de/pixart/messenger/entities/DownloadableFile.java4
-rw-r--r--src/main/java/de/pixart/messenger/entities/Message.java2
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpUploadConnection.java5
-rw-r--r--src/main/java/de/pixart/messenger/parser/MessageParser.java1
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java52
-rw-r--r--src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java28
-rw-r--r--src/main/java/de/pixart/messenger/services/MemorizingTrustManager.java256
-rw-r--r--src/main/java/de/pixart/messenger/services/NotificationService.java7
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java10
-rw-r--r--src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java7
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java15
-rw-r--r--src/main/java/de/pixart/messenger/ui/EditAccountActivity.java3
-rw-r--r--src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java39
-rw-r--r--src/main/java/de/pixart/messenger/ui/OmemoActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/PublishGroupChatProfilePictureActivity.java18
-rw-r--r--src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java43
-rw-r--r--src/main/java/de/pixart/messenger/ui/SearchActivity.java11
-rw-r--r--src/main/java/de/pixart/messenger/ui/SetSettingsActivity.java10
-rw-r--r--src/main/java/de/pixart/messenger/ui/StartConversationActivity.java14
-rw-r--r--src/main/java/de/pixart/messenger/ui/XmppActivity.java14
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java115
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java10
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java8
-rw-r--r--src/main/java/de/pixart/messenger/utils/MimeUtils.java2
-rw-r--r--src/main/java/de/pixart/messenger/utils/Resolver.java39
-rw-r--r--src/main/java/de/pixart/messenger/utils/UIHelper.java5
-rw-r--r--src/main/java/de/pixart/messenger/xml/XmlElementReader.java19
-rw-r--r--src/main/java/de/pixart/messenger/xml/XmlReader.java3
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/XmppConnection.java15
-rw-r--r--src/main/res/drawable/list_item_background_light.xml2
-rw-r--r--src/main/res/layout/activity_contact_details.xml12
-rw-r--r--src/main/res/layout/activity_edit_account.xml16
-rw-r--r--src/main/res/layout/activity_media_viewer.xml4
-rw-r--r--src/main/res/layout/activity_muc_details.xml16
-rw-r--r--src/main/res/layout/activity_set_settings.xml4
-rw-r--r--src/main/res/layout/activity_trust_keys.xml2
-rw-r--r--src/main/res/layout/fragment_conversation.xml1
-rw-r--r--src/main/res/menu/choose_attachment.xml5
-rw-r--r--src/main/res/menu/fragment_conversation.xml5
-rw-r--r--src/main/res/menu/media_viewer.xml3
-rw-r--r--src/main/res/values/strings.xml3
-rw-r--r--src/main/res/values/styles.xml8
-rw-r--r--src/main/res/values/themes.xml6
-rw-r--r--src/main/res/xml/file_paths.xml2
53 files changed, 609 insertions, 286 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6354220b5..eea7db538 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,11 +6,13 @@
* resume download of OMEMO encrypted files
* channels now use '#' as symbol in avatar
* support for ?register and ?register;preauth XMPP uri parameters
+* update connection settings
* use ExoPlayer for video playback (PAM)
* show artist - title for audio files (PAM)
-* show PDF preview in chat (PAM)
-* UI improvements (PAM)
-* bug fixes
+* show PDF previews (PAM)
+* minor UI improvements (PAM)
+* use 12 byte IV for OMEMO
+* a lot of bug fixes
#### Version 2.3.4
* fixes for Jingle IBB file transfer
diff --git a/build.gradle b/build.gradle
index 5cfe82de8..989199663 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.3'
+ classpath 'com.android.tools.build:gradle:3.6.1'
}
}
@@ -40,12 +40,12 @@ configurations {
dependencies {
implementation project(':libs:android-transcoder')
- playstoreImplementation('com.google.firebase:firebase-messaging:20.0.1') {
+ playstoreImplementation('com.google.firebase:firebase-messaging:20.1.3') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
- playstoreImplementation 'com.android.installreferrer:installreferrer:1.1.1'
+ playstoreImplementation 'com.android.installreferrer:installreferrer:1.1.2'
implementation 'org.sufficientlysecure:openpgp-api:10.0'
implementation('com.theartofdev.edmodo:android-image-cropper:2.8.0') {
exclude group: 'com.android.support', module: 'appcompat-v7'
@@ -69,10 +69,11 @@ dependencies {
implementation 'androidx.emoji:emoji:1.0.0'
gitImplementation 'androidx.emoji:emoji-appcompat:1.0.0'
gitImplementation 'androidx.emoji:emoji-bundled:1.0.0'
- implementation 'com.google.android.material:material:1.0.0'
+ implementation 'com.google.android.material:material:1.0.0' // higher versions cause strange fab design
+ implementation 'androidx.cardview:cardview:1.0.0' // for compatibility
implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
- implementation 'com.google.android.exoplayer:exoplayer-core:2.11.1'
- implementation 'com.google.android.exoplayer:exoplayer-ui:2.11.1'
+ implementation 'com.google.android.exoplayer:exoplayer-core:2.11.3'
+ implementation 'com.google.android.exoplayer:exoplayer-ui:2.11.3'
implementation 'pub.devrel:easypermissions:3.0.0' // version >= 3.0.0 needs android X libraries
implementation 'com.wefika:flowlayout:0.4.1'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.5'
@@ -84,7 +85,7 @@ dependencies {
implementation 'org.osmdroid:osmdroid-android:6.1.5'
implementation 'com.leinardi.android:speed-dial:3.1.1' // version >= 3.0.0 needs android X libraries
implementation 'com.squareup.picasso:picasso:2.71828'
- implementation 'com.squareup.okhttp3:okhttp:3.12.8' // versions > 3.12.x don't support API level < 21 anymore
+ implementation 'com.squareup.okhttp3:okhttp:3.12.10' // versions > 3.12.x don't support API level < 21 anymore
implementation 'com.squareup.retrofit2:retrofit:2.6.4' //retrofit needs to stick with 2.6.x (https://github.com/square/retrofit/blob/master/CHANGELOG.md)
implementation 'com.squareup.retrofit2:converter-gson:2.6.4'
implementation 'com.google.guava:guava:28.2-android'
@@ -104,9 +105,9 @@ android {
minSdkVersion 16
targetSdkVersion 29
- versionCode 292
+ versionCode 297
versionName "2.3.5"
- versionNameSuffix " beta (2020-02-11)" //" beta (XXXX-XX-XX)"
+ //versionNameSuffix " beta (2020-04-01)" //" beta (XXXX-XX-XX)"
//resConfigs "en"
archivesBaseName += "-$versionName"
diff --git a/docs/encryption.md b/docs/encryption.md
index edd7e6c5e..ee17352ee 100644
--- a/docs/encryption.md
+++ b/docs/encryption.md
@@ -4,7 +4,7 @@ Pix-Art Messenger will remove the implementation of OTR encryption by 30th June
Unfortunately, it does not make sense to continue support on a rather out-dated technology, even as we see some users keep using it. For the moment, OTR (as well as OpenPGP) can be activated via the expert settings for advanced users. Pix-Art Messenger always tries to be usable for even [non-technical users](https://github.com/kriztan/Pix-Art-Messenger/issues/227), OTR however is not counted as appropriate for this.
-Please consider to use OMEMO in the future, many other clients has implemented this, have a look at [omemo.top](https://omemo.top/). However, if you really need to continue using it, please refer to e.g. [Miranda](https://www.miranda-ng.org/de/), [Pidgin](https://pidgin.im/), [Profanity](https://profanity-im.github.io/). You are also able to fork Pix-Art Messenger and continue the implementation on your own.
+Please consider to use OMEMO in the future, many other clients has implemented this, have a look at [omemo.top](https://omemo.top/). However, if you really need to continue using it, please refer to e.g. [Miranda](https://www.miranda-ng.org/de/), [Pidgin plugin](https://github.com/gkdr/lurch/), [Profanity](https://profanity-im.github.io/) or [Coy.im](https://coy.im/). You are also able to fork Pix-Art Messenger and continue the implementation on your own.
Please consider the differences between encryption protocols and also advantages of OMEMO:
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9cbdf2b53..32b31286a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
diff --git a/libs/xmpp-addr/build.gradle b/libs/xmpp-addr/build.gradle
index 2d30752c4..0675efe7d 100644
--- a/libs/xmpp-addr/build.gradle
+++ b/libs/xmpp-addr/build.gradle
@@ -7,7 +7,7 @@ repositories {
}
dependencies {
- implementation 'rocks.xmpp:precis:1.0.0'
+ implementation 'rocks.xmpp:precis:1.1.0'
}
sourceCompatibility = "8"
diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java
index bf7ade63c..daf25028a 100644
--- a/src/main/java/de/pixart/messenger/Config.java
+++ b/src/main/java/de/pixart/messenger/Config.java
@@ -110,7 +110,7 @@ public final class Config {
public static final boolean REMOVE_BROKEN_DEVICES = false;
public static final boolean OMEMO_PADDING = false;
public static final boolean PUT_AUTH_TAG_INTO_KEY = true;
- public static final boolean TWELVE_BYTE_IV = false;
+ public static final boolean TWELVE_BYTE_IV = true;
public static final int MAX_DISPLAY_MESSAGE_CHARS = 4096;
public static final int MAX_STORAGE_MESSAGE_CHARS = 2 * 1024 * 1024; //2MB
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 3bc0c2901..85a5ddd87 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
@@ -652,7 +652,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
final boolean wipe,
final boolean firstAttempt) {
final Bundle publishOptions = account.getXmppConnection().getFeatures().pepPublishOptions() ? PublishOptions.openAccess() : null;
- IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
+ final IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
preKeyRecords, getOwnDeviceId(), publishOptions);
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing...");
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 6fbf8c3dc..553de40e5 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
@@ -121,7 +121,7 @@ public class XmppAxolotlMessage {
private static byte[] generateIv() {
final SecureRandom random = new SecureRandom();
- byte[] iv = new byte[Config.TWELVE_BYTE_IV ? 12 : 16];
+ final byte[] iv = new byte[12];
random.nextBytes(iv);
return iv;
}
diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java
index c270f5b43..154f98af9 100644
--- a/src/main/java/de/pixart/messenger/entities/Account.java
+++ b/src/main/java/de/pixart/messenger/entities/Account.java
@@ -541,13 +541,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
}
}
- public boolean hasBookmarkFor(final Jid jid) {
- synchronized (this.bookmarks) {
- return this.bookmarks.containsKey(jid.asBareJid());
- }
- }
-
- Bookmark getBookmark(final Jid jid) {
+ public Bookmark getBookmark(final Jid jid) {
synchronized (this.bookmarks) {
return this.bookmarks.get(jid.asBareJid());
}
diff --git a/src/main/java/de/pixart/messenger/entities/DownloadableFile.java b/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
index 936b8dd61..1209d16eb 100644
--- a/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
+++ b/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
@@ -1,7 +1,10 @@
package de.pixart.messenger.entities;
+import android.util.Log;
+
import java.io.File;
+import de.pixart.messenger.Config;
import de.pixart.messenger.utils.MimeUtils;
public class DownloadableFile extends File {
@@ -67,6 +70,7 @@ public class DownloadableFile extends File {
this.iv = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf};
System.arraycopy(keyIvCombo, 0, aeskey, 0, 32);
}
+ Log.d(Config.LOGTAG, "using " + this.iv.length + "-byte IV for file transmission");
}
public void setKey(byte[] key) {
diff --git a/src/main/java/de/pixart/messenger/entities/Message.java b/src/main/java/de/pixart/messenger/entities/Message.java
index b2297cd08..21ab33975 100644
--- a/src/main/java/de/pixart/messenger/entities/Message.java
+++ b/src/main/java/de/pixart/messenger/entities/Message.java
@@ -637,6 +637,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
!this.isGeoUri() &&
!message.isWebUri() &&
!this.isWebUri() &&
+ !message.isOOb() &&
+ !this.isOOb() &&
!message.treatAsDownloadable() &&
!this.treatAsDownloadable() &&
!message.getBody().startsWith(ME_COMMAND) &&
diff --git a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
index 4eedfcf3f..2293cb7ea 100644
--- a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
@@ -106,11 +106,12 @@ public class HttpUploadConnection implements Transferable {
} else {
this.mime = this.file.getMimeType();
}
+ final long originalFileSize = file.getSize();
this.delayed = delay;
if (Config.ENCRYPT_ON_HTTP_UPLOADED
|| message.getEncryption() == Message.ENCRYPTION_AXOLOTL
|| message.getEncryption() == Message.ENCRYPTION_OTR) {
- this.key = new byte[48];
+ this.key = new byte[44];
mXmppConnectionService.getRNG().nextBytes(this.key);
this.file.setKeyAndIv(this.key);
}
@@ -129,7 +130,7 @@ public class HttpUploadConnection implements Transferable {
md5 = null;
}
- this.file.setExpectedSize(file.getSize() + (file.getKey() != null ? 16 : 0));
+ this.file.setExpectedSize(originalFileSize + (file.getKey() != null ? 16 : 0));
message.resetFileParams();
this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() {
@Override
diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java
index 5fd701f89..ff0c3066a 100644
--- a/src/main/java/de/pixart/messenger/parser/MessageParser.java
+++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java
@@ -551,6 +551,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (conversation.getMucOptions().isSelf(counterpart)) {
status = Message.STATUS_SEND_RECEIVED;
isCarbon = true; //not really carbon but received from another resource
+ //TODO this would be the place to change the body after something like mod_pastebin
if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status, serverMsgId)) {
return;
} else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index 36d9c945b..9ea4c5cd9 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -53,6 +53,7 @@ import de.pixart.messenger.services.ShortcutService;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.CursorUtils;
import de.pixart.messenger.utils.FtsUtils;
+import de.pixart.messenger.utils.Resolver;
import de.pixart.messenger.xmpp.InvalidJid;
import de.pixart.messenger.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
@@ -60,7 +61,7 @@ import rocks.xmpp.addr.Jid;
public class DatabaseBackend extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "history";
- public static final int DATABASE_VERSION = 50; // = Conversations DATABASE_VERSION + 4
+ public static final int DATABASE_VERSION = 51; // = Conversations DATABASE_VERSION + 5
private static DatabaseBackend instance = null;
private static String CREATE_CONTATCS_STATEMENT = "create table "
@@ -148,6 +149,20 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ ") ON CONFLICT IGNORE"
+ ");";
+ private static String RESOLVER_RESULTS_TABLENAME = "resolver_results";
+
+ private static String CREATE_RESOLVER_RESULTS_TABLE = "create table " + RESOLVER_RESULTS_TABLENAME + "("
+ + Resolver.Result.DOMAIN + " TEXT,"
+ + Resolver.Result.HOSTNAME + " TEXT,"
+ + Resolver.Result.IP + " BLOB,"
+ + Resolver.Result.PRIORITY + " NUMBER,"
+ + Resolver.Result.DIRECT_TLS + " NUMBER,"
+ + Resolver.Result.AUTHENTICATED + " NUMBER,"
+ + Resolver.Result.PORT + " NUMBER,"
+ + Resolver.Result.TIME_REQUESTED + " NUMBER,"
+ + "UNIQUE(" + Resolver.Result.DOMAIN + ") ON CONFLICT REPLACE"
+ + ");";
+
private static String CREATE_MESSAGE_TIME_INDEX = "create INDEX message_time_index ON " + Message.TABLENAME + "(" + Message.TIME_SENT + ")";
private static String CREATE_MESSAGE_CONVERSATION_INDEX = "create INDEX message_conversation_index ON " + Message.TABLENAME + "(" + Message.CONVERSATION + ")";
private static String CREATE_MESSAGE_DELETED_INDEX = "create index message_deleted_index ON " + Message.TABLENAME + "(" + Message.DELETED + ")";
@@ -246,6 +261,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
db.execSQL(CREATE_IDENTITIES_STATEMENT);
db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ db.execSQL(CREATE_RESOLVER_RESULTS_TABLE);
db.execSQL(CREATE_MESSAGE_INDEX_TABLE);
db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER);
db.execSQL(CREATE_MESSAGE_UPDATE_TRIGGER);
@@ -557,7 +573,11 @@ public class DatabaseBackend extends SQLiteOpenHelper {
Log.d(Config.LOGTAG, "deleted old edit information in " + diff + "ms");
}
- db.execSQL("DROP TABLE IF EXISTS resolver_results");
+ if (oldVersion < 51 && newVersion >= 51) {
+ // values in resolver_result are cache and not worth to store
+ db.execSQL("DROP TABLE IF EXISTS " + RESOLVER_RESULTS_TABLENAME);
+ db.execSQL(CREATE_RESOLVER_RESULTS_TABLE);
+ }
}
private boolean isColumnExisting(SQLiteDatabase db, String TableName, String ColumnName) {
@@ -694,6 +714,34 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return result;
}
+ public void saveResolverResult(String domain, Resolver.Result result) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues contentValues = result.toContentValues();
+ contentValues.put(Resolver.Result.DOMAIN, domain);
+ db.insert(RESOLVER_RESULTS_TABLENAME, null, contentValues);
+ }
+
+ public synchronized Resolver.Result findResolverResult(String domain) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String where = Resolver.Result.DOMAIN + "=?";
+ String[] whereArgs = {domain};
+ final Cursor cursor = db.query(RESOLVER_RESULTS_TABLENAME, null, where, whereArgs, null, null, null);
+ Resolver.Result result = null;
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ result = Resolver.Result.fromCursor(cursor);
+ }
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "unable to find cached resolver result in database " + e.getMessage());
+ return null;
+ } finally {
+ cursor.close();
+ }
+ }
+ return result;
+ }
+
public void insertPresenceTemplate(PresenceTemplate template) {
SQLiteDatabase db = this.getWritableDatabase();
String whereToDelete = PresenceTemplate.MESSAGE + "=?";
diff --git a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
index cba382b61..7787c8942 100644
--- a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
@@ -5,6 +5,14 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.io.CipherInputStream;
+import org.bouncycastle.crypto.io.CipherOutputStream;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -15,12 +23,7 @@ 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;
@@ -28,9 +31,6 @@ import de.pixart.messenger.entities.DownloadableFile;
import de.pixart.messenger.utils.Compatibility;
public class AbstractConnectionManager {
- private static final String KEYTYPE = "AES";
- private static final String CIPHERMODE = "AES/GCM/NoPadding";
- private static final String PROVIDER = "BC";
private static final int UI_REFRESH_THRESHOLD = Config.REFRESH_UI_INTERVAL;
private static final AtomicLong LAST_UI_UPDATE_CALL = new AtomicLong(0);
protected XmppConnectionService mXmppConnectionService;
@@ -41,10 +41,8 @@ public class AbstractConnectionManager {
public static InputStream upgrade(DownloadableFile file, InputStream is) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, NoSuchProviderException {
if (file.getKey() != null && file.getIv() != null) {
- 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);
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
return new CipherInputStream(is, cipher);
} else {
return is;
@@ -63,10 +61,8 @@ public class AbstractConnectionManager {
return null;
}
try {
- 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);
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
return new CipherOutputStream(os, cipher);
} catch (Exception e) {
Log.d(Config.LOGTAG, "unable to create cipher output stream", e);
diff --git a/src/main/java/de/pixart/messenger/services/MemorizingTrustManager.java b/src/main/java/de/pixart/messenger/services/MemorizingTrustManager.java
index f321332d7..3534b65bf 100644
--- a/src/main/java/de/pixart/messenger/services/MemorizingTrustManager.java
+++ b/src/main/java/de/pixart/messenger/services/MemorizingTrustManager.java
@@ -83,6 +83,7 @@ import javax.net.ssl.X509TrustManager;
import de.pixart.messenger.R;
import de.pixart.messenger.crypto.DomainHostnameVerifier;
import de.pixart.messenger.entities.MTMDecision;
+import de.pixart.messenger.persistance.FileBackend;
import de.pixart.messenger.ui.MemorizingActivity;
/**
@@ -98,32 +99,26 @@ import de.pixart.messenger.ui.MemorizingActivity;
public class MemorizingTrustManager {
+ final static String DECISION_INTENT = "de.duenndns.ssl.DECISION";
+ public final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId";
+ public final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert";
+ public final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId";
+ final static String DECISION_INTENT_CHOICE = DECISION_INTENT + ".decisionChoice";
+ final static String NO_TRUST_ANCHOR = "Trust anchor for certification path not found.";
private static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
private static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
private static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
private static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
private static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
-
- final static String DECISION_INTENT = "de.duenndns.ssl.DECISION";
- public final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId";
- public final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert";
- final static String DECISION_INTENT_CHOICE = DECISION_INTENT + ".decisionChoice";
-
private final static Logger LOGGER = Logger.getLogger(MemorizingTrustManager.class.getName());
- public final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId";
private final static int NOTIFICATION_ID = 100509;
-
- final static String NO_TRUST_ANCHOR = "Trust anchor for certification path not found.";
-
static String KEYSTORE_DIR = "KeyStore";
static String KEYSTORE_FILE = "KeyStore.bks";
-
+ private static int decisionId = 0;
+ private static SparseArray<MTMDecision> openDecisions = new SparseArray<MTMDecision>();
Context master;
AppCompatActivity foregroundAct;
NotificationManager notificationManager;
- private static int decisionId = 0;
- private static SparseArray<MTMDecision> openDecisions = new SparseArray<MTMDecision>();
-
Handler masterHandler;
private File keyStoreFile;
private KeyStore appKeyStore;
@@ -131,13 +126,14 @@ public class MemorizingTrustManager {
private X509TrustManager appTrustManager;
private String poshCacheDir;
- /** Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager.
- *
+ /**
+ * Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager.
+ * <p>
* You need to supply the application context. This has to be one of:
- * - Application
- * - Activity
- * - Service
- *
+ * - Application
+ * - Activity
+ * - Service
+ * <p>
* The context is used for file management, to display the dialog /
* notification and for obtaining translated strings.
*
@@ -150,13 +146,14 @@ public class MemorizingTrustManager {
this.defaultTrustManager = defaultTrustManager;
}
- /** Creates an instance of the MemorizingTrustManager class using the system X509TrustManager.
- *
+ /**
+ * Creates an instance of the MemorizingTrustManager class using the system X509TrustManager.
+ * <p>
* You need to supply the application context. This has to be one of:
- * - Application
- * - Activity
- * - Service
- *
+ * - Application
+ * - Activity
+ * - Service
+ * <p>
* The context is used for file management, to display the dialog /
* notification and for obtaining translated strings.
*
@@ -168,6 +165,78 @@ public class MemorizingTrustManager {
this.defaultTrustManager = getTrustManager(null);
}
+ /**
+ * Changes the path for the KeyStore file.
+ * <p>
+ * The actual filename relative to the app's directory will be
+ * <code>app_<i>dirname</i>/<i>filename</i></code>.
+ *
+ * @param dirname directory to store the KeyStore.
+ * @param filename file name for the KeyStore.
+ */
+ public static void setKeyStoreFile(String dirname, String filename) {
+ KEYSTORE_DIR = dirname;
+ KEYSTORE_FILE = filename;
+ }
+
+ private static boolean isIp(final String server) {
+ return server != null && (
+ PATTERN_IPV4.matcher(server).matches()
+ || PATTERN_IPV6.matcher(server).matches()
+ || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
+ || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
+ || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
+ }
+
+ private static String getBase64Hash(X509Certificate certificate, String digest) throws CertificateEncodingException {
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance(digest);
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ md.update(certificate.getEncoded());
+ return Base64.encodeToString(md.digest(), Base64.NO_WRAP);
+ }
+
+ private static String hexString(byte[] data) {
+ StringBuffer si = new StringBuffer();
+ for (int i = 0; i < data.length; i++) {
+ si.append(String.format("%02x", data[i]));
+ if (i < data.length - 1)
+ si.append(":");
+ }
+ return si.toString();
+ }
+
+ private static String certHash(final X509Certificate cert, String digest) {
+ try {
+ MessageDigest md = MessageDigest.getInstance(digest);
+ md.update(cert.getEncoded());
+ return hexString(md.digest());
+ } catch (java.security.cert.CertificateEncodingException e) {
+ return e.getMessage();
+ } catch (java.security.NoSuchAlgorithmException e) {
+ return e.getMessage();
+ }
+ }
+
+ public static void interactResult(int decisionId, int choice) {
+ MTMDecision d;
+ synchronized (openDecisions) {
+ d = openDecisions.get(decisionId);
+ openDecisions.remove(decisionId);
+ }
+ if (d == null) {
+ LOGGER.log(Level.SEVERE, "interactResult: aborting due to stale decision reference!");
+ return;
+ }
+ synchronized (d) {
+ d.state = choice;
+ d.notify();
+ }
+ }
+
void init(Context m) {
master = m;
masterHandler = new Handler(m.getMainLooper());
@@ -191,14 +260,13 @@ public class MemorizingTrustManager {
appKeyStore = loadAppKeyStore();
}
-
/**
* Binds an Activity to the MTM for displaying the query dialog.
- *
+ * <p>
* This is useful if your connection is run from a service that is
* triggered by user interaction -- in such cases the activity is
* visible and the user tends to ignore the service notification.
- *
+ * <p>
* You should never have a hidden activity bound to MTM! Use this
* function in onResume() and @see unbindDisplayActivity in onPause().
*
@@ -210,7 +278,7 @@ public class MemorizingTrustManager {
/**
* Removes an Activity from the MTM display stack.
- *
+ * <p>
* Always call this function when the Activity added with
* {@link #bindDisplayActivity(AppCompatActivity)} is hidden.
*
@@ -223,20 +291,6 @@ public class MemorizingTrustManager {
}
/**
- * Changes the path for the KeyStore file.
- *
- * The actual filename relative to the app's directory will be
- * <code>app_<i>dirname</i>/<i>filename</i></code>.
- *
- * @param dirname directory to store the KeyStore.
- * @param filename file name for the KeyStore.
- */
- public static void setKeyStoreFile(String dirname, String filename) {
- KEYSTORE_DIR = dirname;
- KEYSTORE_FILE = filename;
- }
-
- /**
* Get a list of all certificate aliases stored in MTM.
*
* @return an {@link Enumeration} of all certificates
@@ -254,7 +308,6 @@ public class MemorizingTrustManager {
* Get a certificate for a given alias.
*
* @param alias the certificate's alias as returned by {@link #getCertificates()}.
- *
* @return the certificate associated with the alias or <tt>null</tt> if none found.
*/
public Certificate getCertificate(String alias) {
@@ -275,8 +328,8 @@ public class MemorizingTrustManager {
* (b) new connections are created using TLS renegotiation, without a new cert
* check.
* </p>
- * @param alias the certificate's alias as returned by {@link #getCertificates()}.
*
+ * @param alias the certificate's alias as returned by {@link #getCertificates()}.
* @throws KeyStoreException if the certificate could not be deleted.
*/
public void deleteCertificate(String alias) throws KeyStoreException {
@@ -291,17 +344,16 @@ public class MemorizingTrustManager {
* the given instance of {@link MemorizingTrustManager}, and leverages an
* existing {@link HostnameVerifier}. The returned verifier performs the
* following steps, returning as soon as one of them succeeds:
- * </p>
- * <ol>
- * <li>Success, if the wrapped defaultVerifier accepts the certificate.</li>
- * <li>Success, if the server certificate is stored in the keystore under the given hostname.</li>
- * <li>Ask the user and return accordingly.</li>
- * <li>Failure on exception.</li>
- * </ol>
+ * /p>
+ * <ol>
+ * <li>Success, if the wrapped defaultVerifier accepts the certificate.</li>
+ * <li>Success, if the server certificate is stored in the keystore under the given hostname.</li>
+ * <li>Ask the user and return accordingly.</li>
+ * <li>Failure on exception.</li>
+ * </ol>
*
* @param defaultVerifier the {@link HostnameVerifier} that should perform the actual check
* @return a new hostname verifier using the MTM's key store
- *
* @throws IllegalArgumentException if the defaultVerifier parameter is null
*/
public DomainHostnameVerifier wrapHostnameVerifier(final HostnameVerifier defaultVerifier, final boolean interactive) {
@@ -337,13 +389,17 @@ public class MemorizingTrustManager {
LOGGER.log(Level.SEVERE, "getAppKeyStore()", e);
return null;
}
+ FileInputStream fileInputStream = null;
try {
ks.load(null, null);
- ks.load(new java.io.FileInputStream(keyStoreFile), "MTM".toCharArray());
+ fileInputStream = new FileInputStream(keyStoreFile);
+ ks.load(fileInputStream, "MTM".toCharArray());
} catch (java.io.FileNotFoundException e) {
LOGGER.log(Level.INFO, "getAppKeyStore(" + keyStoreFile + ") - file does not exist");
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "getAppKeyStore(" + keyStoreFile + ")", e);
+ } finally {
+ FileBackend.close(fileInputStream);
}
return ks;
}
@@ -574,26 +630,6 @@ public class MemorizingTrustManager {
}
}
- private static boolean isIp(final String server) {
- return server != null && (
- PATTERN_IPV4.matcher(server).matches()
- || PATTERN_IPV6.matcher(server).matches()
- || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
- || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
- || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
- }
-
- private static String getBase64Hash(X509Certificate certificate, String digest) throws CertificateEncodingException {
- MessageDigest md;
- try {
- md = MessageDigest.getInstance(digest);
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
- md.update(certificate.getEncoded());
- return Base64.encodeToString(md.digest(), Base64.NO_WRAP);
- }
-
private X509Certificate[] getAcceptedIssuers() {
LOGGER.log(Level.FINE, "getAcceptedIssuers()");
return defaultTrustManager.getAcceptedIssuers();
@@ -609,28 +645,6 @@ public class MemorizingTrustManager {
return myId;
}
- private static String hexString(byte[] data) {
- StringBuffer si = new StringBuffer();
- for (int i = 0; i < data.length; i++) {
- si.append(String.format("%02x", data[i]));
- if (i < data.length - 1)
- si.append(":");
- }
- return si.toString();
- }
-
- private static String certHash(final X509Certificate cert, String digest) {
- try {
- MessageDigest md = MessageDigest.getInstance(digest);
- md.update(cert.getEncoded());
- return hexString(md.digest());
- } catch (java.security.cert.CertificateEncodingException e) {
- return e.getMessage();
- } catch (java.security.NoSuchAlgorithmException e) {
- return e.getMessage();
- }
- }
-
private void certDetails(StringBuffer si, X509Certificate c) {
SimpleDateFormat validityDateFormater = new SimpleDateFormat("yyyy-MM-dd");
si.append("\n");
@@ -705,6 +719,7 @@ public class MemorizingTrustManager {
certDetails(si, cert);
return si.toString();
}
+
/**
* Returns the top-most entry of the activity stack.
*
@@ -715,7 +730,7 @@ public class MemorizingTrustManager {
}
int interact(final String message, final int titleId) {
- /* prepare the MTMDecision blocker object */
+ /* prepare the MTMDecision blocker object */
MTMDecision choice = new MTMDecision();
final int myId = createDecisionId(choice);
@@ -773,20 +788,20 @@ public class MemorizingTrustManager {
}
}
- public static void interactResult(int decisionId, int choice) {
- MTMDecision d;
- synchronized (openDecisions) {
- d = openDecisions.get(decisionId);
- openDecisions.remove(decisionId);
- }
- if (d == null) {
- LOGGER.log(Level.SEVERE, "interactResult: aborting due to stale decision reference!");
- return;
- }
- synchronized (d) {
- d.state = choice;
- d.notify();
- }
+ public X509TrustManager getNonInteractive(String domain) {
+ return new NonInteractiveMemorizingTrustManager(domain);
+ }
+
+ public X509TrustManager getInteractive(String domain) {
+ return new InteractiveMemorizingTrustManager(domain);
+ }
+
+ public X509TrustManager getNonInteractive() {
+ return new NonInteractiveMemorizingTrustManager(null);
+ }
+
+ public X509TrustManager getInteractive() {
+ return new InteractiveMemorizingTrustManager(null);
}
class MemorizingHostnameVerifier implements DomainHostnameVerifier {
@@ -840,23 +855,6 @@ public class MemorizingTrustManager {
}
}
-
- public X509TrustManager getNonInteractive(String domain) {
- return new NonInteractiveMemorizingTrustManager(domain);
- }
-
- public X509TrustManager getInteractive(String domain) {
- return new InteractiveMemorizingTrustManager(domain);
- }
-
- public X509TrustManager getNonInteractive() {
- return new NonInteractiveMemorizingTrustManager(null);
- }
-
- public X509TrustManager getInteractive() {
- return new InteractiveMemorizingTrustManager(null);
- }
-
private class NonInteractiveMemorizingTrustManager implements X509TrustManager {
private final String domain;
diff --git a/src/main/java/de/pixart/messenger/services/NotificationService.java b/src/main/java/de/pixart/messenger/services/NotificationService.java
index 826b5172d..fe97344c0 100644
--- a/src/main/java/de/pixart/messenger/services/NotificationService.java
+++ b/src/main/java/de/pixart/messenger/services/NotificationService.java
@@ -59,10 +59,13 @@ import de.pixart.messenger.ui.EditAccountActivity;
import de.pixart.messenger.ui.TimePreference;
import de.pixart.messenger.utils.AccountUtils;
import de.pixart.messenger.utils.Compatibility;
+import de.pixart.messenger.utils.EmojiWrapper;
import de.pixart.messenger.utils.GeoHelper;
import de.pixart.messenger.utils.UIHelper;
import de.pixart.messenger.xmpp.XmppConnection;
+import static de.pixart.messenger.ui.util.MyLinkify.replaceYoutube;
+
public class NotificationService {
public static final Object CATCHUP_LOCK = new Object();
@@ -705,14 +708,14 @@ public class NotificationService {
SpannableString styledString;
for (Message message : messages) {
final SpannableString name = UIHelper.getColoredUsername(mXmppConnectionService, message);
- styledString = new SpannableString(name + ": " + message.getBody());
+ styledString = new SpannableString(name + ": " + EmojiWrapper.transform(replaceYoutube(mXmppConnectionService, message.getBody())));
style.addLine(styledString);
}
builder.setStyle(style);
int count = messages.size();
if (count == 1) {
final SpannableString name = UIHelper.getColoredUsername(mXmppConnectionService, messages.get(0));
- styledString = new SpannableString(name + ": " + messages.get(0).getBody());
+ styledString = new SpannableString(name + ": " + EmojiWrapper.transform(replaceYoutube(mXmppConnectionService, messages.get(0).getBody())));
builder.setContentText(styledString);
builder.setTicker(styledString);
} else {
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index c72a06aae..a51dbe49f 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -3473,16 +3473,20 @@ public class XmppConnectionService extends Service {
conversation.setAttribute("accept_non_anonymous", true);
updateConversation(conversation);
}
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ if (options.containsKey("muc#roomconfig_moderatedroom")) {
+ final boolean moderated = "1".equals(options.getString("muc#roomconfig_moderatedroom"));
+ options.putString("members_by_default", moderated ? "0" : "1");
+ }
+ final IqPacket request = new IqPacket(IqPacket.TYPE.GET);
request.setTo(conversation.getJid().asBareJid());
request.query("http://jabber.org/protocol/muc#owner");
sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
- Data data = Data.parse(packet.query().findChild("x", Namespace.DATA));
+ final Data data = Data.parse(packet.query().findChild("x", Namespace.DATA));
data.submit(options);
- IqPacket set = new IqPacket(IqPacket.TYPE.SET);
+ final IqPacket set = new IqPacket(IqPacket.TYPE.SET);
set.setTo(conversation.getJid().asBareJid());
set.query("http://jabber.org/protocol/muc#owner").addChild(data);
sendIqPacket(account, set, new OnIqPacketReceived() {
diff --git a/src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java b/src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java
index 6c2a5c562..ba572e837 100644
--- a/src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ChannelDiscoveryActivity.java
@@ -36,6 +36,7 @@ import de.pixart.messenger.ui.adapter.ChannelSearchResultAdapter;
import de.pixart.messenger.ui.util.PendingItem;
import de.pixart.messenger.ui.util.SoftKeyboardUtils;
import de.pixart.messenger.utils.AccountUtils;
+import me.drakeet.support.toast.ToastCompat;
import rocks.xmpp.addr.Jid;
public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, ChannelDiscoveryService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected {
@@ -219,10 +220,12 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
@Override
public void onChannelSearchResult(final Room result) {
- List<String> accounts = AccountUtils.getEnabledAccounts(xmppConnectionService);
+ final List<String> accounts = AccountUtils.getEnabledAccounts(xmppConnectionService);
if (accounts.size() == 1) {
joinChannelSearchResult(accounts.get(0), result);
- } else if (accounts.size() > 0) {
+ } else if (accounts.size() == 0) {
+ ToastCompat.makeText(this, R.string.please_enable_an_account, ToastCompat.LENGTH_LONG).show();
+ } else {
final AtomicReference<String> account = new AtomicReference<>(accounts.get(0));
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.choose_account);
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index 9ab0f50fc..8354d6517 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -139,6 +139,7 @@ import static de.pixart.messenger.ui.SettingsActivity.WARN_UNENCRYPTED_CHAT;
import static de.pixart.messenger.ui.XmppActivity.EXTRA_ACCOUNT;
import static de.pixart.messenger.ui.XmppActivity.REQUEST_INVITE_TO_CONVERSATION;
import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard;
+import static de.pixart.messenger.utils.Compatibility.runsTwentyOne;
import static de.pixart.messenger.utils.PermissionUtils.allGranted;
import static de.pixart.messenger.utils.PermissionUtils.getFirstDenied;
import static de.pixart.messenger.utils.PermissionUtils.readGranted;
@@ -632,6 +633,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
popup.setOnMenuItemClickListener(attachmentItem -> {
switch (attachmentItem.getItemId()) {
case R.id.attach_choose_picture:
+ case R.id.attach_choose_video:
case R.id.attach_take_picture:
case R.id.attach_record_video:
case R.id.attach_choose_file:
@@ -996,6 +998,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case ATTACHMENT_CHOICE_CHOOSE_FILE:
case ATTACHMENT_CHOICE_RECORD_VIDEO:
case ATTACHMENT_CHOICE_RECORD_VOICE:
+ case ATTACHMENT_CHOICE_CHOOSE_VIDEO:
final Attachment.Type type = requestCode == ATTACHMENT_CHOICE_RECORD_VOICE ? Attachment.Type.RECORDING : Attachment.Type.FILE;
final List<Attachment> fileUris = Attachment.extractAttachments(getActivity(), data, type);
mediaPreviewAdapter.addMediaPreviews(fileUris);
@@ -1151,7 +1154,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
menuNeedHelp.setVisible(true);
menuSearchUpdates.setVisible(false);
ConversationMenuConfigurator.configureAttachmentMenu(conversation, menu, activity.xmppConnectionService.getAttachmentChoicePreference(), hasAttachments);
- ConversationMenuConfigurator.configureEncryptionMenu(conversation, menu);
+ ConversationMenuConfigurator.configureEncryptionMenu(conversation, menu, activity);
} else {
menuNeedHelp.setVisible(false);
menuSearchUpdates.setVisible(true);
@@ -1434,6 +1437,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
handleEncryptionSelection(item);
break;
case R.id.attach_choose_picture:
+ case R.id.attach_choose_video:
case R.id.attach_take_picture:
case R.id.attach_record_video:
case R.id.attach_choose_file:
@@ -1494,6 +1498,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case R.id.attach_choose_picture:
attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
break;
+ case R.id.attach_choose_video:
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_VIDEO);
+ break;
case R.id.attach_take_picture:
attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
break;
@@ -1678,7 +1685,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
private void updateChatBG() {
- if (activity.unicoloredBG()) {
+ if (activity.unicoloredBG() || !runsTwentyOne()) {
binding.conversationsFragment.setBackgroundResource(0);
binding.conversationsFragment.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_tertiary));
} else {
@@ -2141,12 +2148,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
public void onStart() {
super.onStart();
if (this.reInitRequiredOnStart && this.conversation != null) {
+ if (activity != null) {
+ updateChatBG();
+ }
final Bundle extras = pendingExtras.pop();
reInit(this.conversation, extras != null);
if (extras != null) {
processExtras(extras);
}
} else if (conversation == null && activity != null && activity.xmppConnectionService != null) {
+ updateChatBG();
final String uuid = pendingConversationsUuid.pop();
Log.d(Config.LOGTAG, "ConversationFragment.onStart() - activity was bound but no conversation loaded. uuid=" + uuid);
if (uuid != null) {
diff --git a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
index 3afcf83bb..a6ab04be2 100644
--- a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
@@ -364,7 +364,8 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
return;
}
- if (xmppConnectionService.getAccounts().size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) {
+ final List<Account> accounts = xmppConnectionService == null ? null : xmppConnectionService.getAccounts();
+ if (accounts != null && accounts.size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) {
Intent intent = SignupUtils.getSignUpIntent(this, mForceRegister != null && mForceRegister);
StartConversationActivity.addInviteUri(intent, getIntent());
startActivity(intent);
diff --git a/src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java b/src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java
index 2ed416c17..b75af44e9 100644
--- a/src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java
@@ -3,6 +3,7 @@ package de.pixart.messenger.ui;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -13,6 +14,7 @@ import androidx.databinding.DataBindingUtil;
import java.util.ArrayList;
import java.util.List;
+import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.databinding.ActivityMediaBrowserBinding;
import de.pixart.messenger.entities.Account;
@@ -38,6 +40,14 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
private String account;
private String jid;
+ @Override
+ protected void onStart() {
+ super.onStart();
+ filter(OnlyImagesVideos);
+ invalidateOptionsMenu();
+ refreshUiReal();
+ }
+
public static void launch(Context context, Contact contact) {
launch(context, contact.getAccount(), contact.getJid().asBareJid().toEscapedString());
}
@@ -79,7 +89,7 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
}
@Override
- protected void refreshUiReal() {
+ public void refreshUiReal() {
mMediaAdapter.notifyDataSetChanged();
}
@@ -145,19 +155,18 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
@Override
public void onMediaLoaded(List<Attachment> attachments) {
+ allAttachments.clear();
allAttachments.addAll(attachments);
runOnUiThread(() -> {
- if (OnlyImagesVideos) {
- filter(OnlyImagesVideos);
- } else {
- loadAttachments(allAttachments);
- }
+ filter(OnlyImagesVideos);
});
}
private void loadAttachments(List<Attachment> attachments) {
if (attachments.size() > 0) {
- mMediaAdapter.setAttachments(attachments);
+ if (mMediaAdapter.getItemCount() != attachments.size()) {
+ mMediaAdapter.setAttachments(attachments);
+ }
this.binding.noMedia.setVisibility(View.GONE);
this.binding.progressbar.setVisibility(View.GONE);
} else {
@@ -172,20 +181,28 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
}
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ filter(OnlyImagesVideos);
+ }
+
protected void filterAttachments(boolean needle) {
if (allAttachments.size() > 0) {
- final ArrayList<Attachment> attachments = new ArrayList<>(allAttachments);
- filteredAttachments.clear();
if (needle) {
+ final ArrayList<Attachment> attachments = new ArrayList<>(allAttachments);
+ filteredAttachments.clear();
for (Attachment attachment : attachments) {
if (attachment.getMime() != null && (attachment.getMime().startsWith("image/") || attachment.getMime().startsWith("video/"))) {
filteredAttachments.add(attachment);
}
}
+ loadAttachments(filteredAttachments);
} else {
- filteredAttachments.addAll(allAttachments);
+ loadAttachments(allAttachments);
}
- loadAttachments(filteredAttachments);
+ } else {
+ loadAttachments(allAttachments);
}
}
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/OmemoActivity.java b/src/main/java/de/pixart/messenger/ui/OmemoActivity.java
index 9f309ac5f..fb63a7a86 100644
--- a/src/main/java/de/pixart/messenger/ui/OmemoActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/OmemoActivity.java
@@ -76,8 +76,8 @@ public abstract class OmemoActivity extends XmppActivity {
}
@Override
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- super.onActivityResult(requestCode, requestCode, intent);
+ public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == ScanActivity.REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
XmppUri uri = new XmppUri(result == null ? "" : result);
diff --git a/src/main/java/de/pixart/messenger/ui/PublishGroupChatProfilePictureActivity.java b/src/main/java/de/pixart/messenger/ui/PublishGroupChatProfilePictureActivity.java
index 9c70640c3..fc0d34beb 100644
--- a/src/main/java/de/pixart/messenger/ui/PublishGroupChatProfilePictureActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/PublishGroupChatProfilePictureActivity.java
@@ -51,6 +51,8 @@ import de.pixart.messenger.ui.interfaces.OnAvatarPublication;
import de.pixart.messenger.ui.util.PendingItem;
import me.drakeet.support.toast.ToastCompat;
+import static de.pixart.messenger.ui.PublishProfilePictureActivity.REQUEST_CHOOSE_PICTURE;
+
public class PublishGroupChatProfilePictureActivity extends XmppActivity implements OnAvatarPublication {
private final PendingItem<String> pendingConversationUuid = new PendingItem<>();
@@ -96,7 +98,7 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
configureActionBar(getSupportActionBar());
this.binding.cancelButton.setOnClickListener((v) -> this.finish());
this.binding.secondaryHint.setVisibility(View.GONE);
- this.binding.accountImage.setOnClickListener((v) -> this.chooseAvatar());
+ this.binding.accountImage.setOnClickListener((v) -> PublishProfilePictureActivity.chooseAvatar(this));
Intent intent = getIntent();
String uuid = intent == null ? null : intent.getStringExtra("uuid");
if (uuid != null) {
@@ -116,7 +118,7 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
- CropImage.ActivityResult result = CropImage.getActivityResult(data);
+ final CropImage.ActivityResult result = CropImage.getActivityResult(data);
if (resultCode == RESULT_OK) {
this.uri = result.getUri();
if (xmppConnectionServiceBound) {
@@ -128,17 +130,13 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
ToastCompat.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
+ } else if (requestCode == REQUEST_CHOOSE_PICTURE) {
+ if (resultCode == RESULT_OK) {
+ PublishProfilePictureActivity.cropUri(this, data.getData());
+ }
}
}
- private void chooseAvatar() {
- CropImage.activity()
- .setOutputCompressFormat(Bitmap.CompressFormat.PNG)
- .setAspectRatio(1, 1)
- .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
- .start(this);
- }
-
@Override
public void onAvatarPublicationSucceeded() {
runOnUiThread(() -> {
diff --git a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
index f505bd3a3..0c634d8f3 100644
--- a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
@@ -1,8 +1,10 @@
package de.pixart.messenger.ui;
+import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -28,6 +30,8 @@ import me.drakeet.support.toast.ToastCompat;
public class PublishProfilePictureActivity extends XmppActivity implements XmppConnectionService.OnAccountUpdate, OnAvatarPublication {
+ public static final int REQUEST_CHOOSE_PICTURE = 0x1337;
+
private ImageView avatar;
private TextView hintOrWarning;
private TextView secondaryHint;
@@ -108,7 +112,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
}
finish();
});
- this.avatar.setOnClickListener(v -> chooseAvatar());
+ this.avatar.setOnClickListener(v -> chooseAvatar(this));
this.defaultUri = PhoneHelper.getProfilePictureUri(getApplicationContext());
if (savedInstanceState != null) {
this.avatarUri = savedInstanceState.getParcelable("uri");
@@ -141,15 +145,28 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
ToastCompat.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
+ } else if (requestCode == REQUEST_CHOOSE_PICTURE) {
+ if (resultCode == RESULT_OK) {
+ cropUri(this, data.getData());
+ }
}
}
- private void chooseAvatar() {
- CropImage.activity()
- .setOutputCompressFormat(Bitmap.CompressFormat.PNG)
- .setAspectRatio(1, 1)
- .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
- .start(this);
+ public static void chooseAvatar(final Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ activity.startActivityForResult(
+ Intent.createChooser(intent, activity.getString(R.string.attach_choose_picture)),
+ REQUEST_CHOOSE_PICTURE
+ );
+ } else {
+ CropImage.activity()
+ .setOutputCompressFormat(Bitmap.CompressFormat.PNG)
+ .setAspectRatio(1, 1)
+ .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
+ .start(activity);
+ }
}
@Override
@@ -183,10 +200,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
final Uri uri = intent != null ? intent.getData() : null;
if (uri != null && handledExternalUri.compareAndSet(false, true)) {
- CropImage.activity(uri).setOutputCompressFormat(Bitmap.CompressFormat.PNG)
- .setAspectRatio(1, 1)
- .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
- .start(this);
+ cropUri(this, uri);
return;
}
@@ -196,6 +210,13 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
configureActionBar(getSupportActionBar(), !this.mInitialAccountSetup && !handledExternalUri.get());
}
+ public static void cropUri(final Activity activity, final Uri uri) {
+ CropImage.activity(uri).setOutputCompressFormat(Bitmap.CompressFormat.PNG)
+ .setAspectRatio(1, 1)
+ .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
+ .start(activity);
+ }
+
protected void loadImageIntoPreview(Uri uri) {
Bitmap bm = null;
diff --git a/src/main/java/de/pixart/messenger/ui/SearchActivity.java b/src/main/java/de/pixart/messenger/ui/SearchActivity.java
index 8fd76df2e..dce7b370c 100644
--- a/src/main/java/de/pixart/messenger/ui/SearchActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SearchActivity.java
@@ -33,6 +33,7 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
@@ -47,6 +48,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.databinding.ActivitySearchBinding;
import de.pixart.messenger.entities.Contact;
@@ -64,6 +66,7 @@ import de.pixart.messenger.ui.util.ShareUtil;
import de.pixart.messenger.ui.util.StyledAttributes;
import de.pixart.messenger.utils.FtsUtils;
import de.pixart.messenger.utils.MessageUtils;
+import de.pixart.messenger.utils.UIHelper;
import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard;
import static de.pixart.messenger.ui.util.SoftKeyboardUtils.showKeyboard;
@@ -115,9 +118,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
searchField.addTextChangedListener(this);
searchField.setHint(R.string.search_messages);
searchField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
- if (term == null) {
- showKeyboard(searchField);
- }
+ showKeyboard(searchField);
return super.onCreateOptionsMenu(menu);
}
@@ -150,7 +151,8 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
@Override
public boolean onContextItemSelected(MenuItem item) {
final Message message = selectedMessageReference.get();
- final String user = selectedMessageReference.get().getConversation().getContact().getDisplayName();
+ final boolean multi = message.getConversation().getMode() == Conversational.MODE_MULTI;
+ final String user = multi ? UIHelper.getDisplayedMucCounterpart(message.getCounterpart()) : null;
if (message != null) {
switch (item.getItemId()) {
case R.id.open_conversation:
@@ -183,6 +185,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
}
private void quote(Message message, String user) {
+ Log.d(Config.LOGTAG, "Quote User: " + user);
switchToConversationAndQuote(wrap(message.getConversation()), MessageUtils.prepareQuote(message), user);
}
diff --git a/src/main/java/de/pixart/messenger/ui/SetSettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SetSettingsActivity.java
index 5c4ce2622..27119f5fd 100644
--- a/src/main/java/de/pixart/messenger/ui/SetSettingsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SetSettingsActivity.java
@@ -36,7 +36,7 @@ public class SetSettingsActivity extends XmppActivity implements XmppConnectionS
static final int CHATSTATES = 4;
static final int CONFIRMMESSAGES = 5;
static final int LASTSEEN = 6;
- static final int INVIDEOUS = 7;
+ static final int INVIDIOUS = 7;
@Override
protected void refreshUiReal() {
@@ -66,7 +66,7 @@ public class SetSettingsActivity extends XmppActivity implements XmppConnectionS
this.binding.actionInfoChatStates.setOnClickListener(string -> showInfo(CHATSTATES));
this.binding.actionInfoConfirmMessages.setOnClickListener(string -> showInfo(CONFIRMMESSAGES));
this.binding.actionInfoLastSeen.setOnClickListener(string -> showInfo(LASTSEEN));
- this.binding.actionInfoInvideous.setOnClickListener(string -> showInfo(INVIDEOUS));
+ this.binding.actionInfoInvidious.setOnClickListener(string -> showInfo(INVIDIOUS));
}
private void getDefaults() {
@@ -76,7 +76,7 @@ public class SetSettingsActivity extends XmppActivity implements XmppConnectionS
this.binding.chatStates.setChecked(getResources().getBoolean(R.bool.chat_states));
this.binding.confirmMessages.setChecked(getResources().getBoolean(R.bool.confirm_messages));
this.binding.lastSeen.setChecked(getResources().getBoolean(R.bool.last_activity));
- this.binding.invideous.setChecked(getResources().getBoolean(R.bool.use_invidious));
+ this.binding.invidious.setChecked(getResources().getBoolean(R.bool.use_invidious));
}
private void next(View view) {
@@ -122,7 +122,7 @@ public class SetSettingsActivity extends XmppActivity implements XmppConnectionS
title = getString(R.string.pref_broadcast_last_activity);
message = getString(R.string.pref_broadcast_last_activity_summary);
break;
- case INVIDEOUS:
+ case INVIDIOUS:
title = getString(R.string.pref_use_invidious);
message = getString(R.string.pref_use_invidious_summary);
break;
@@ -171,7 +171,7 @@ public class SetSettingsActivity extends XmppActivity implements XmppConnectionS
} else {
preferences.edit().putBoolean(BROADCAST_LAST_ACTIVITY, false).apply();
}
- if (this.binding.invideous.isChecked()) {
+ if (this.binding.invidious.isChecked()) {
preferences.edit().putBoolean(USE_INVIDIOUS, true).apply();
} else {
preferences.edit().putBoolean(USE_INVIDIOUS, false).apply();
diff --git a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
index 8f4e468c7..b0e880cba 100644
--- a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
@@ -1003,10 +1003,12 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
}
if (isBookmarkChecked) {
- if (account.hasBookmarkFor(conferenceJid)) {
- layout.setError(getString(R.string.bookmark_already_exists));
+ Bookmark bookmark = account.getBookmark(conferenceJid);
+ if (bookmark != null) {
+ dialog.dismiss();
+ openConversationsForBookmark(bookmark);
} else {
- final Bookmark bookmark = new Bookmark(account, conferenceJid.asBareJid());
+ bookmark = new Bookmark(account, conferenceJid.asBareJid());
bookmark.setAutojoin(getBooleanPreference("autojoin", R.bool.autojoin));
final String nick = conferenceJid.getResource();
if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) {
@@ -1100,6 +1102,10 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
final AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
if (mResContextMenu == R.menu.conference_context) {
activity.conference_context_id = acmi.position;
+ final Bookmark bookmark = (Bookmark) activity.conferences.get(acmi.position);
+ final Conversation conversation = bookmark.getConversation();
+ final MenuItem share = menu.findItem(R.id.context_share_uri);
+ share.setVisible(conversation == null || !conversation.isPrivateAndNonAnonymous());
} else if (mResContextMenu == R.menu.contact_context) {
activity.contact_context_id = acmi.position;
final Contact contact = (Contact) activity.contacts.get(acmi.position);
@@ -1110,7 +1116,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
showContactDetailsItem.setVisible(false);
}
deleteContactMenuItem.setVisible(contact.showInRoster() && !contact.getOption(Contact.Options.SYNCED_VIA_OTHER));
- XmppConnection xmpp = contact.getAccount().getXmppConnection();
+ final XmppConnection xmpp = contact.getAccount().getXmppConnection();
if (xmpp != null && xmpp.getFeatures().blocking() && !contact.isSelf()) {
if (contact.isBlocked()) {
blockUnblockItem.setTitle(R.string.unblock_contact);
diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
index 4f9cb587a..624119bb8 100644
--- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
@@ -97,6 +97,7 @@ import me.drakeet.support.toast.ToastCompat;
import pl.droidsonroids.gif.GifDrawable;
import rocks.xmpp.addr.Jid;
+import static de.pixart.messenger.ui.SettingsActivity.ENABLE_OTR_ENCRYPTION;
import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS;
import static de.pixart.messenger.ui.SettingsActivity.USE_INTERNAL_UPDATER;
@@ -111,6 +112,7 @@ public abstract class XmppActivity extends ActionBarActivity {
public static final String EXTRA_ACCOUNT = "account";
public XmppConnectionService xmppConnectionService;
+ public MediaBrowserActivity mediaBrowserActivity;
public boolean xmppConnectionServiceBound = false;
protected int mColorWarningButton;
@@ -438,6 +440,10 @@ public abstract class XmppActivity extends ActionBarActivity {
return getBooleanPreference("unicolored_chatbg", R.bool.use_unicolored_chatbg);
}
+ public boolean enableOTR() {
+ return getBooleanPreference(ENABLE_OTR_ENCRYPTION, R.bool.enable_otr);
+ }
+
public void setBubbleColor(final View v, final int backgroundColor, final int borderColor) {
GradientDrawable shape = (GradientDrawable) v.getBackground();
shape.setColor(backgroundColor);
@@ -528,10 +534,10 @@ public abstract class XmppActivity extends ActionBarActivity {
intent.putExtra(Intent.EXTRA_TEXT, text);
if (asQuote) {
intent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, true);
- intent.putExtra(ConversationsActivity.EXTRA_ACCOUNT, nick);
+ intent.putExtra(ConversationsActivity.EXTRA_USER, nick);
}
}
- if (nick != null) {
+ if (nick != null && !asQuote) {
intent.putExtra(ConversationsActivity.EXTRA_NICK, nick);
intent.putExtra(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, pm);
}
@@ -1092,8 +1098,8 @@ public abstract class XmppActivity extends ActionBarActivity {
protected String doInBackground(XmppConnection... params) {
String uri = null;
if (this.connection != null) {
- XmppConnection.Features features = connection.getFeatures();
- if (features.adhocinvite) {
+ XmppConnection.Features features = this.connection.getFeatures();
+ if (features != null && features.adhocinvite) {
int i = 0;
uri = this.connection.getAdHocInviteUrl(Jid.ofDomain(this.account.getJid().getDomain()));
try {
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java
index 778dbbb13..3dfa9b560 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java
@@ -1,34 +1,49 @@
package de.pixart.messenger.ui.adapter;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.AsyncTask;
+import android.util.Log;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.Toast;
import androidx.annotation.AttrRes;
import androidx.annotation.DimenRes;
import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.PopupMenu;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
+import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
+import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.databinding.MediaBinding;
+import de.pixart.messenger.persistance.FileBackend;
import de.pixart.messenger.services.ExportBackupService;
import de.pixart.messenger.ui.XmppActivity;
import de.pixart.messenger.ui.util.Attachment;
import de.pixart.messenger.ui.util.StyledAttributes;
import de.pixart.messenger.ui.util.ViewUtil;
+import de.pixart.messenger.utils.MimeUtils;
+import me.drakeet.support.toast.ToastCompat;
public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHolder> {
@@ -140,9 +155,105 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
loadPreview(attachment, holder.binding.media);
} else {
cancelPotentialWork(attachment, holder.binding.media);
- renderPreview(activity, attachment, holder.binding.media);
+ renderPreview(this.activity, attachment, holder.binding.media);
}
- holder.binding.getRoot().setOnClickListener(v -> ViewUtil.view(activity, attachment));
+ holder.binding.getRoot().setOnClickListener(v -> ViewUtil.view(this.activity, attachment));
+ holder.binding.getRoot().setOnLongClickListener(v -> {
+ setSelection(v);
+ final PopupMenu popupMenu = new PopupMenu(this.activity, v);
+ popupMenu.inflate(R.menu.media_viewer);
+ popupMenu.getMenu().findItem(R.id.action_delete).setVisible(isDeletableFile(new File(attachment.getUri().getPath())));
+ popupMenu.setOnMenuItemClickListener(item -> {
+ switch (item.getItemId()) {
+ case R.id.action_share:
+ share(attachment);
+ return true;
+ case R.id.action_open:
+ open(attachment);
+ return true;
+ case R.id.action_delete:
+ deleteFile(attachment);
+ return true;
+ }
+ return false;
+ });
+ popupMenu.setOnDismissListener(menu -> resetSelection(v));
+ popupMenu.show();
+ return true;
+ });
+ }
+
+ private void setSelection(final View v) {
+ v.setBackgroundColor(StyledAttributes.getColor(this.activity, R.attr.colorAccent));
+ }
+
+ private void resetSelection(final View v) {
+ v.setBackgroundColor(0);
+ }
+
+ private void share(final Attachment attachment) {
+ final Intent share = new Intent(Intent.ACTION_SEND);
+ final File file = new File(attachment.getUri().getPath());
+ share.setType(attachment.getMime());
+ share.putExtra(Intent.EXTRA_STREAM, FileBackend.getUriForFile(this.activity, file));
+ try {
+ this.activity.startActivity(Intent.createChooser(share, this.activity.getText(R.string.share_with)));
+ } catch (ActivityNotFoundException e) {
+ //This should happen only on faulty androids because normally chooser is always available
+ ToastCompat.makeText(this.activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void deleteFile(final Attachment attachment) {
+ final File file = new File(attachment.getUri().getPath());
+ final int hash = attachment.hashCode();
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this.activity);
+ 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(file)) {
+ for (int i = 0; i < attachments.size(); i++) {
+ if (hash == attachments.get(i).hashCode()) {
+ attachments.remove(i);
+ notifyDataSetChanged();
+ this.activity.refreshUi();
+ return;
+ }
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ private void open(final Attachment attachment) {
+ final File file = new File(attachment.getUri().getPath());
+ final Uri uri;
+ try {
+ uri = FileBackend.getUriForFile(this.activity, file);
+ } catch (SecurityException e) {
+ Log.d(Config.LOGTAG, "No permission to access " + file.getAbsolutePath(), e);
+ ToastCompat.makeText(this.activity, this.activity.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String mime = MimeUtils.guessMimeTypeFromUri(this.activity, uri);
+ Intent openIntent = new Intent(Intent.ACTION_VIEW);
+ openIntent.setDataAndType(uri, mime);
+ openIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ PackageManager manager = this.activity.getPackageManager();
+ List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
+ if (info.size() == 0) {
+ openIntent.setDataAndType(uri, "*/*");
+ }
+ try {
+ this.activity.startActivity(openIntent);
+ } catch (ActivityNotFoundException e) {
+ ToastCompat.makeText(this.activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private boolean isDeletableFile(File file) {
+ return (file == null || !file.toString().startsWith("/") || file.toString().contains(FileBackend.getConversationsDirectory("null")));
}
public void setAttachments(List<Attachment> attachments) {
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 0593869ca..f85670ec0 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
@@ -493,7 +493,6 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
viewHolder.richlinkview.setVisibility(View.GONE);
viewHolder.progressBar.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
-
}
private void applyQuoteSpan(SpannableStringBuilder body, int start, int end, boolean darkBackground) {
@@ -681,6 +680,12 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
private void displayOpenableMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
toggleWhisperInfo(viewHolder, message, false, darkBackground);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.audioPlayer.setVisibility(View.GONE);
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.gifImage.setVisibility(View.GONE);
+ viewHolder.richlinkview.setVisibility(View.GONE);
+ viewHolder.progressBar.setVisibility(View.GONE);
final String mimeType = message.getMimeType();
if (mimeType != null && message.getMimeType().contains("vcard")) {
try {
@@ -1299,9 +1304,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
}
private void markFileExisting(Message message) {
- Log.d(Config.LOGTAG, "Found and restored orphaned file");
+ Log.d(Config.LOGTAG, "Found and restored orphaned file " + message.getRelativeFilePath());
message.setFileDeleted(false);
activity.xmppConnectionService.updateMessage(message, false);
+ activity.xmppConnectionService.updateConversation((Conversation) message.getConversation());
}
private boolean checkFileExistence(Message message, View view, ViewHolder viewHolder) {
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 9797da56f..8a9434622 100644
--- a/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
+++ b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
@@ -29,6 +29,7 @@
package de.pixart.messenger.ui.util;
+import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.view.Menu;
@@ -42,8 +43,7 @@ import de.pixart.messenger.crypto.OmemoSetting;
import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.entities.Conversational;
import de.pixart.messenger.entities.Message;
-
-import static de.pixart.messenger.ui.SettingsActivity.ENABLE_OTR_ENCRYPTION;
+import de.pixart.messenger.ui.XmppActivity;
public class ConversationMenuConfigurator {
@@ -93,7 +93,7 @@ public class ConversationMenuConfigurator {
menu.findItem(R.id.attach_location).setVisible(locationAvailable);
}
- public static void configureEncryptionMenu(@NonNull Conversation conversation, Menu menu) {
+ public static void configureEncryptionMenu(@NonNull Conversation conversation, Menu menu, final XmppActivity activity) {
final MenuItem menuSecure = menu.findItem(R.id.action_security);
final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
if (!participating) {
@@ -130,7 +130,7 @@ public class ConversationMenuConfigurator {
menuSecure.setIcon(R.drawable.ic_lock_white_24dp);
}
- otr.setVisible(Config.supportOtr() && conversation.getBooleanAttribute(ENABLE_OTR_ENCRYPTION, false));
+ otr.setVisible(Config.supportOtr() && activity.enableOTR());
if (conversation.getMode() == Conversation.MODE_MULTI) {
otr.setVisible(false);
}
diff --git a/src/main/java/de/pixart/messenger/utils/MimeUtils.java b/src/main/java/de/pixart/messenger/utils/MimeUtils.java
index 8603a8a89..31159e27a 100644
--- a/src/main/java/de/pixart/messenger/utils/MimeUtils.java
+++ b/src/main/java/de/pixart/messenger/utils/MimeUtils.java
@@ -278,6 +278,8 @@ public final class MimeUtils {
add("image/jpeg", "jpg");
add("image/jpeg", "jpeg");
add("image/jpeg", "jpe");
+ add("image/jpeg", "jfif");
+ add("image/jpeg", "jif");
add("image/pcx", "pcx");
add("image/png", "png");
add("image/svg+xml", "svg");
diff --git a/src/main/java/de/pixart/messenger/utils/Resolver.java b/src/main/java/de/pixart/messenger/utils/Resolver.java
index ed5e62fd3..a7c286cc7 100644
--- a/src/main/java/de/pixart/messenger/utils/Resolver.java
+++ b/src/main/java/de/pixart/messenger/utils/Resolver.java
@@ -1,6 +1,7 @@
package de.pixart.messenger.utils;
import android.content.ContentValues;
+import android.database.Cursor;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -168,6 +169,7 @@ public class Resolver {
final Result result = new Result();
result.ip = InetAddress.getByName(domain);
result.port = port;
+ result.authenticated = true;
return result;
} catch (UnknownHostException e) {
e.printStackTrace();
@@ -202,18 +204,20 @@ public class Resolver {
}));
fallbackThreads.add(new Thread(() -> {
try {
- for (CNAME cname : resolveWithFallback(record.name, CNAME.class, result.isAuthenticData()).getAnswersOrEmptySet()) {
- final List<Result> ipv6s = resolveIp(record, cname.name, AAAA.class, result.isAuthenticData(), directTls);
+ ResolverResult<CNAME> cnames = resolveWithFallback(record.name, CNAME.class, result.isAuthenticData());
+ for (CNAME cname : cnames.getAnswersOrEmptySet()) {
+ final List<Result> ipv6s = resolveIp(record, cname.name, AAAA.class, cnames.isAuthenticData(), directTls);
synchronized (fallbackResults) {
fallbackResults.addAll(ipv6s);
}
- final List<Result> ipv4s = resolveIp(record, cname.name, A.class, result.isAuthenticData(), directTls);
+ final List<Result> ipv4s = resolveIp(record, cname.name, A.class, cnames.isAuthenticData(), directTls);
synchronized (results) {
fallbackResults.addAll(ipv4s);
}
}
+ Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + "cname in srv (agains RFC2782) - run slow fallback");
} catch (Throwable throwable) {
- Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + "error resolving srv cname-fallback records", throwable);
+ Log.i(Config.LOGTAG, Resolver.class.getSimpleName() + "error resolving srv cname-fallback records", throwable);
}
}));
}
@@ -358,12 +362,14 @@ public class Resolver {
}
public static class Result implements Comparable<Result>, Callable<Result> {
+ public static final String DOMAIN = "domain";
public static final String IP = "ip";
public static final String HOSTNAME = "hostname";
public static final String PORT = "port";
public static final String PRIORITY = "priority";
public static final String DIRECT_TLS = "directTls";
public static final String AUTHENTICATED = "authenticated";
+ public static final String TIME_REQUESTED = "time_requested";
private InetAddress ip;
private DNSName hostname;
@@ -371,12 +377,14 @@ public class Resolver {
private boolean directTls = false;
private boolean authenticated = false;
private int priority;
+ private long timeRequested;
private Socket socket;
private String logID = "";
static Result fromRecord(final SRV srv, final boolean directTls) {
Result result = new Result();
+ result.timeRequested = System.currentTimeMillis();
result.port = srv.port;
result.hostname = srv.name;
result.directTls = directTls;
@@ -386,6 +394,7 @@ public class Resolver {
static Result createDefault(final DNSName hostname, final InetAddress ip, final int port) {
Result result = new Result();
+ result.timeRequested = System.currentTimeMillis();
result.port = port;
result.hostname = hostname;
result.ip = ip;
@@ -430,6 +439,10 @@ public class Resolver {
return authenticated;
}
+ public boolean isOutdated() {
+ return (System.currentTimeMillis() - timeRequested) > 300_000;
+ }
+
public Socket getSocket() {
return socket;
}
@@ -506,6 +519,23 @@ public class Resolver {
throw new Exception("Resolver.Result was not possible to connect - should be catched by executor");
}
+ public static Result fromCursor(Cursor cursor) {
+ final Result result = new Result();
+ try {
+ result.ip = InetAddress.getByAddress(cursor.getBlob(cursor.getColumnIndex(IP)));
+ } catch (UnknownHostException e) {
+ result.ip = null;
+ }
+ final String hostname = cursor.getString(cursor.getColumnIndex(HOSTNAME));
+ result.hostname = hostname == null ? null : DNSName.from(hostname);
+ result.port = cursor.getInt(cursor.getColumnIndex(PORT));
+ result.directTls = cursor.getInt(cursor.getColumnIndex(DIRECT_TLS)) > 0;
+ result.authenticated = cursor.getInt(cursor.getColumnIndex(AUTHENTICATED)) > 0;
+ result.priority = cursor.getInt(cursor.getColumnIndex(PRIORITY));
+ result.timeRequested = cursor.getLong(cursor.getColumnIndex(TIME_REQUESTED));
+ return result;
+ }
+
public ContentValues toContentValues() {
final ContentValues contentValues = new ContentValues();
contentValues.put(IP, ip == null ? null : ip.getAddress());
@@ -514,6 +544,7 @@ public class Resolver {
contentValues.put(PRIORITY, priority);
contentValues.put(DIRECT_TLS, directTls ? 1 : 0);
contentValues.put(AUTHENTICATED, authenticated ? 1 : 0);
+ contentValues.put(TIME_REQUESTED, timeRequested);
return contentValues;
}
}
diff --git a/src/main/java/de/pixart/messenger/utils/UIHelper.java b/src/main/java/de/pixart/messenger/utils/UIHelper.java
index e2b3c2f2c..7a40ba6d6 100644
--- a/src/main/java/de/pixart/messenger/utils/UIHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/UIHelper.java
@@ -35,6 +35,7 @@ import de.pixart.messenger.entities.Presence;
import de.pixart.messenger.entities.Transferable;
import de.pixart.messenger.services.ExportBackupService;
import de.pixart.messenger.services.XmppConnectionService;
+import de.pixart.messenger.ui.util.MyLinkify;
import rocks.xmpp.addr.Jid;
import static de.pixart.messenger.entities.Message.DELETED_MESSAGE_BODY;
@@ -316,7 +317,7 @@ public class UIHelper {
return new Pair<>(context.getString(R.string.x_file_offered_for_download,
getFileDescriptionString(context, message)), true);
} else {
- SpannableStringBuilder styledBody = new SpannableStringBuilder(body);
+ SpannableStringBuilder styledBody = new SpannableStringBuilder(MyLinkify.replaceYoutube(context, body));
if (textColor != 0) {
StylingHelper.format(styledBody, 0, styledBody.length() - 1, textColor);
}
@@ -603,8 +604,6 @@ public class UIHelper {
return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336, 0, account);
case DND:
return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336, 0, account);
- case OFFLINE:
- return new ListItem.Tag(context.getString(R.string.presence_offline), 0xff808080, 1, account);
default:
return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24, 0, account);
}
diff --git a/src/main/java/de/pixart/messenger/xml/XmlElementReader.java b/src/main/java/de/pixart/messenger/xml/XmlElementReader.java
new file mode 100644
index 000000000..108822509
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/xml/XmlElementReader.java
@@ -0,0 +1,19 @@
+package de.pixart.messenger.xml;
+
+import com.google.common.io.ByteSource;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class XmlElementReader {
+
+ public static Element read(byte[] bytes) throws IOException {
+ return read(ByteSource.wrap(bytes).openStream());
+ }
+
+ public static Element read(InputStream inputStream) throws IOException {
+ final XmlReader xmlReader = new XmlReader();
+ xmlReader.setInputStream(inputStream);
+ return xmlReader.readElement(xmlReader.readTag());
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/xml/XmlReader.java b/src/main/java/de/pixart/messenger/xml/XmlReader.java
index 6447f12ef..ef2a33158 100644
--- a/src/main/java/de/pixart/messenger/xml/XmlReader.java
+++ b/src/main/java/de/pixart/messenger/xml/XmlReader.java
@@ -87,8 +87,7 @@ public class XmlReader implements Closeable {
return null;
}
- public Element readElement(Tag currentTag) throws XmlPullParserException,
- IOException {
+ public Element readElement(Tag currentTag) throws IOException {
Element element = new Element(currentTag.getName());
element.setAttributes(currentTag.getAttributes());
Tag nextTag = this.readTag();
diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
index be9fd5a3a..a41924e08 100644
--- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
@@ -317,11 +317,17 @@ public class XmppConnection implements Runnable {
}
} else {
final String domain = account.getJid().getDomain();
- final Resolver.Result result;
+ final Resolver.Result storedBackupResult = mXmppConnectionService.databaseBackend.findResolverResult(domain);
+ Resolver.Result result = null;
final boolean hardcoded = extended && !account.getHostname().isEmpty();
if (hardcoded) {
result = Resolver.fromHardCoded(account.getHostname(), account.getPort());
- } else {
+ } else if (storedBackupResult != null && !storedBackupResult.isOutdated()) {
+ storedBackupResult.connect();
+ result = storedBackupResult;
+ Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": loaded backup resolver result from db: " + storedBackupResult);
+ }
+ if (result == null || result.getSocket() == null) {
result = Resolver.resolve(domain);
}
if (result == null) {
@@ -348,6 +354,9 @@ public class XmppConnection implements Runnable {
localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000);
if (startXmpp(localSocket)) {
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);
+ }
// successfully connected to server that speaks xmpp
} else {
FileBackend.close(localSocket);
@@ -1994,4 +2003,4 @@ public class XmppConnection implements Runnable {
return Config.USE_BOOKMARKS2 /* || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT)*/;
}
}
-} \ No newline at end of file
+}
diff --git a/src/main/res/drawable/list_item_background_light.xml b/src/main/res/drawable/list_item_background_light.xml
index f064403da..917f85dab 100644
--- a/src/main/res/drawable/list_item_background_light.xml
+++ b/src/main/res/drawable/list_item_background_light.xml
@@ -28,5 +28,5 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/grey300" android:state_activated="true" />
+ <item android:drawable="@color/grey500" android:state_activated="true" />
</selector> \ No newline at end of file
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index cb02d53e2..09aac94dc 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_horizontal_margin"
@@ -238,9 +238,9 @@
android:visibility="visible" />
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/media_wrapper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -289,9 +289,9 @@
android:textColor="?attr/colorAccent" />
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/keys_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -350,7 +350,7 @@
android:textColor="?attr/colorAccent" />
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
</LinearLayout>
diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml
index a1646f553..efc361bf0 100644
--- a/src/main/res/layout/activity_edit_account.xml
+++ b/src/main/res/layout/activity_edit_account.xml
@@ -25,7 +25,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/editor"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -307,9 +307,9 @@
android:layout_marginTop="8dp"
android:text="@string/register_account" />
</RelativeLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/os_optimization"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -369,9 +369,9 @@
android:textColor="@color/accent" />
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/stats"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
@@ -819,9 +819,9 @@
</LinearLayout>
</RelativeLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/other_device_keys_card"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -862,7 +862,7 @@
android:layout_gravity="center_horizontal"
android:text="@string/clear_other_devices" />
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
diff --git a/src/main/res/layout/activity_media_viewer.xml b/src/main/res/layout/activity_media_viewer.xml
index 2b64fb81a..c82ed1603 100644
--- a/src/main/res/layout/activity_media_viewer.xml
+++ b/src/main/res/layout/activity_media_viewer.xml
@@ -26,6 +26,10 @@
android:id="@+id/messageVideoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ app:played_color="?attr/colorAccent"
+ app:scrubber_color="?attr/colorAccent"
+ app:show_shuffle_button="false"
+ app:show_buffering="never"
android:visibility="gone" />
<com.leinardi.android.speeddial.SpeedDialOverlayLayout
diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml
index 894649cc2..e53d22823 100644
--- a/src/main/res/layout/activity_muc_details.xml
+++ b/src/main/res/layout/activity_muc_details.xml
@@ -24,7 +24,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_horizontal_margin"
@@ -312,9 +312,9 @@
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_horizontal_margin"
@@ -408,9 +408,9 @@
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/users_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -484,9 +484,9 @@
tools:text="View n Participants" />
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
- <com.google.android.material.card.MaterialCardView
+ <androidx.cardview.widget.CardView
android:id="@+id/media_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -537,7 +537,7 @@
android:textColor="?attr/colorAccent" />
</LinearLayout>
</LinearLayout>
- </com.google.android.material.card.MaterialCardView>
+ </androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
</LinearLayout>
diff --git a/src/main/res/layout/activity_set_settings.xml b/src/main/res/layout/activity_set_settings.xml
index 99d596e0a..80d85efa0 100644
--- a/src/main/res/layout/activity_set_settings.xml
+++ b/src/main/res/layout/activity_set_settings.xml
@@ -234,7 +234,7 @@
android:textAppearance="@style/TextAppearance.Conversations.Body1" />
<ImageButton
- android:id="@+id/action_info_invideous"
+ android:id="@+id/action_info_invidious"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="?attr/icon_alpha"
@@ -244,7 +244,7 @@
android:src="?attr/icon_help" />
<androidx.appcompat.widget.AppCompatCheckBox
- android:id="@+id/invideous"
+ android:id="@+id/invidious"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical" />
diff --git a/src/main/res/layout/activity_trust_keys.xml b/src/main/res/layout/activity_trust_keys.xml
index 178cd04c8..30a9d3906 100644
--- a/src/main/res/layout/activity_trust_keys.xml
+++ b/src/main/res/layout/activity_trust_keys.xml
@@ -92,7 +92,7 @@
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:text="@string/disable_encryption"
- android:textColor="@color/accent" />
+ android:textColor="?attr/colorAccent" />
</LinearLayout>
</LinearLayout>
diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml
index d7f177514..9748a6b22 100644
--- a/src/main/res/layout/fragment_conversation.xml
+++ b/src/main/res/layout/fragment_conversation.xml
@@ -6,7 +6,6 @@
android:id="@+id/conversations_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/chatbg"
android:clickable="false">
<ListView
diff --git a/src/main/res/menu/choose_attachment.xml b/src/main/res/menu/choose_attachment.xml
index a9a57404d..96e540f59 100644
--- a/src/main/res/menu/choose_attachment.xml
+++ b/src/main/res/menu/choose_attachment.xml
@@ -12,6 +12,11 @@
android:title="@string/attach_choose_picture" />
<item
+ android:id="@+id/attach_choose_video"
+ android:icon="?attr/ic_attach_video"
+ android:title="@string/attach_choose_video" />
+
+ <item
android:id="@+id/attach_take_picture"
android:icon="?attr/ic_attach_camera"
android:title="@string/action_take_photo" />
diff --git a/src/main/res/menu/fragment_conversation.xml b/src/main/res/menu/fragment_conversation.xml
index 8858d941d..ac7d8a1d2 100644
--- a/src/main/res/menu/fragment_conversation.xml
+++ b/src/main/res/menu/fragment_conversation.xml
@@ -43,6 +43,11 @@
android:title="@string/attach_choose_picture" />
<item
+ android:id="@+id/attach_choose_video"
+ android:icon="?attr/ic_attach_video"
+ android:title="@string/attach_choose_video" />
+
+ <item
android:id="@+id/attach_take_picture"
android:icon="?attr/ic_attach_camera"
android:title="@string/action_take_photo" />
diff --git a/src/main/res/menu/media_viewer.xml b/src/main/res/menu/media_viewer.xml
index c14181d24..e30f56cd1 100644
--- a/src/main/res/menu/media_viewer.xml
+++ b/src/main/res/menu/media_viewer.xml
@@ -14,6 +14,5 @@
android:id="@+id/action_delete"
android:icon="@drawable/ic_delete_white_24dp"
android:orderInCategory="20"
- android:title="@string/action_delete"
- android:visible="false" />
+ android:title="@string/action_delete" />
</menu> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 45ba43d61..15a0ff7ac 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -215,7 +215,6 @@
<string name="channel_bare_jid_example">channel@conference.example.com</string>
<string name="save_as_bookmark">Save as bookmark</string>
<string name="delete_bookmark">Delete bookmark</string>
- <string name="bookmark_already_exists">This bookmark already exists</string>
<string name="topic">Topic</string>
<string name="joining_conference">Joining group chat…</string>
<string name="leave">Leave</string>
@@ -1005,4 +1004,6 @@
<string name="show_videos_images_only">Only images/videos</string>
<string name="show_avatar">Show avatar</string>
<string name="action_delete">Delete</string>
+ <string name="attach_choose_video">Choose video</string>
+ <string name="please_enable_an_account">Please enable an account</string>
</resources>
diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml
index 185699519..0b5fdc3ac 100644
--- a/src/main/res/values/styles.xml
+++ b/src/main/res/values/styles.xml
@@ -154,4 +154,12 @@
<style name="Conversations.Dialog" parent="ThemeOverlay.MaterialComponents.Dialog.Alert" >
<item name="android:buttonStyle">@style/Widget.Conversations.Button.Borderless</item>
</style>
+
+ <style name="ExoMediaButton.Previous">
+ <item name="android:visibility">gone</item>
+ </style>
+
+ <style name="ExoMediaButton.Next">
+ <item name="android:visibility">gone</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index d20d1c304..dfb07ab52 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -336,6 +336,9 @@
<item name="color_bubble_warning">@color/lightred</item>
<item name="chat_bg">@drawable/bg_light_orange</item>
+
+ <item name="windowActionModeOverlay">true</item>
+ <item name="android:actionModeBackground">@color/accent_orange</item>
</style>
<style name="ConversationsTheme.Orange.Dark" parent="ConversationsTheme.Dark">
@@ -351,6 +354,9 @@
<item name="color_bubble_warning">@color/darkred</item>
<item name="chat_bg">@drawable/bg_dark_orange</item>
+
+ <item name="windowActionModeOverlay">true</item>
+ <item name="android:actionModeBackground">@color/accent_orange</item>
</style>
<style name="ConversationsTheme.Dialog" parent="Theme.MaterialComponents.Light.Dialog">
diff --git a/src/main/res/xml/file_paths.xml b/src/main/res/xml/file_paths.xml
index 847faf68f..0cc514a45 100644
--- a/src/main/res/xml/file_paths.xml
+++ b/src/main/res/xml/file_paths.xml
@@ -11,7 +11,7 @@
path="Images/" />
<files-path
name="videos"
- path="Videos" />
+ path="Videos/" />
<files-path
name="files"
path="Files/" />