Detailed changes
@@ -42,13 +42,13 @@ spotless {
dependencies {
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3'
implementation project(':libs:annotation')
annotationProcessor project(':libs:annotation-processor')
- implementation 'androidx.viewpager:viewpager:1.0.0'
+ implementation 'androidx.viewpager:viewpager:1.1.0'
playstoreImplementation('com.google.firebase:firebase-messaging:24.1.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
@@ -59,6 +59,8 @@ dependencies {
quicksyPlaystoreImplementation 'com.google.android.gms:play-services-auth-api-phone:18.1.0'
implementation 'com.github.open-keychain.open-keychain:openpgp-api:v5.7.1'
implementation("com.github.CanHub:Android-Image-Cropper:2.0.0")
+ implementation "androidx.sharetarget:sharetarget:1.2.0"
+
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.exifinterface:exifinterface:1.3.7'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -124,14 +124,6 @@
android:name=".services.ImportBackupService"
android:exported="false"
android:foregroundServiceType="dataSync" />
- <service
- android:name=".services.ContactChooserTargetService"
- android:exported="true"
- android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
- <intent-filter>
- <action android:name="android.service.chooser.ChooserTargetService" />
- </intent-filter>
- </service>
<service
android:name=".services.CallIntegrationConnectionService"
@@ -194,6 +186,9 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
+ <meta-data
+ android:name="android.app.shortcuts"
+ android:resource="@xml/shortcuts" />
</activity>
<activity
android:name=".ui.ConversationsActivity"
@@ -330,10 +325,9 @@
<data android:mimeType="*/*" />
</intent-filter>
- <!-- the value here needs to be the full class name; independent of the configured applicationId -->
<meta-data
android:name="android.service.chooser.chooser_target_service"
- android:value="eu.siacs.conversations.services.ContactChooserTargetService" />
+ android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
</activity>
<activity
android:name=".ui.TrustKeysActivity"
@@ -10,35 +10,7 @@ import android.os.Environment;
import android.os.SystemClock;
import android.util.Base64;
import android.util.Log;
-
import com.google.common.base.Stopwatch;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.whispersystems.libsignal.IdentityKey;
-import org.whispersystems.libsignal.IdentityKeyPair;
-import org.whispersystems.libsignal.InvalidKeyException;
-import org.whispersystems.libsignal.SignalProtocolAddress;
-import org.whispersystems.libsignal.state.PreKeyRecord;
-import org.whispersystems.libsignal.state.SessionRecord;
-import org.whispersystems.libsignal.state.SignedPreKeyRecord;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.CopyOnWriteArrayList;
-
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
@@ -60,6 +32,30 @@ import eu.siacs.conversations.utils.Resolver;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.mam.MamReference;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.whispersystems.libsignal.IdentityKey;
+import org.whispersystems.libsignal.IdentityKeyPair;
+import org.whispersystems.libsignal.InvalidKeyException;
+import org.whispersystems.libsignal.SignalProtocolAddress;
+import org.whispersystems.libsignal.state.PreKeyRecord;
+import org.whispersystems.libsignal.state.SessionRecord;
+import org.whispersystems.libsignal.state.SignedPreKeyRecord;
public class DatabaseBackend extends SQLiteOpenHelper {
@@ -68,123 +64,273 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static boolean requiresMessageIndexRebuild = false;
private static DatabaseBackend instance = null;
- private static final String CREATE_CONTATCS_STATEMENT = "create table "
- + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
- + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
- + Contact.PRESENCE_NAME + " TEXT,"
- + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
- + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
- + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
- + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
- + Contact.RTP_CAPABILITY + " TEXT,"
- + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
- + Account.TABLENAME + "(" + Account.UUID
- + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
- + Contact.JID + ") ON CONFLICT REPLACE);";
-
- private static final String CREATE_DISCOVERY_RESULTS_STATEMENT = "create table "
- + ServiceDiscoveryResult.TABLENAME + "("
- + ServiceDiscoveryResult.HASH + " TEXT, "
- + ServiceDiscoveryResult.VER + " TEXT, "
- + ServiceDiscoveryResult.RESULT + " TEXT, "
- + "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
- + ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
-
- private static final String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
- + PresenceTemplate.TABELNAME + "("
- + PresenceTemplate.UUID + " TEXT, "
- + PresenceTemplate.LAST_USED + " NUMBER,"
- + PresenceTemplate.MESSAGE + " TEXT,"
- + PresenceTemplate.STATUS + " TEXT,"
- + "UNIQUE(" + PresenceTemplate.MESSAGE + "," + PresenceTemplate.STATUS + ") ON CONFLICT REPLACE);";
-
- private static final String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.PREKEY_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.ID
- + ") ON CONFLICT REPLACE"
- + ");";
-
- private static final String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.ID
- + ") ON CONFLICT REPLACE" +
- ");";
-
- private static final String CREATE_SESSIONS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.SESSION_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.NAME + " TEXT, "
- + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.NAME + ", "
- + SQLiteAxolotlStore.DEVICE_ID
- + ") ON CONFLICT REPLACE"
- + ");";
-
- private static final String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.IDENTITIES_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.NAME + " TEXT, "
- + SQLiteAxolotlStore.OWN + " INTEGER, "
- + SQLiteAxolotlStore.FINGERPRINT + " TEXT, "
- + SQLiteAxolotlStore.CERTIFICATE + " BLOB, "
- + SQLiteAxolotlStore.TRUST + " TEXT, "
- + SQLiteAxolotlStore.ACTIVE + " NUMBER, "
- + SQLiteAxolotlStore.LAST_ACTIVATION + " NUMBER,"
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.NAME + ", "
- + SQLiteAxolotlStore.FINGERPRINT
- + ") ON CONFLICT IGNORE"
- + ");";
+ private static final String CREATE_CONTATCS_STATEMENT =
+ "create table "
+ + Contact.TABLENAME
+ + "("
+ + Contact.ACCOUNT
+ + " TEXT, "
+ + Contact.SERVERNAME
+ + " TEXT, "
+ + Contact.SYSTEMNAME
+ + " TEXT,"
+ + Contact.PRESENCE_NAME
+ + " TEXT,"
+ + Contact.JID
+ + " TEXT,"
+ + Contact.KEYS
+ + " TEXT,"
+ + Contact.PHOTOURI
+ + " TEXT,"
+ + Contact.OPTIONS
+ + " NUMBER,"
+ + Contact.SYSTEMACCOUNT
+ + " NUMBER, "
+ + Contact.AVATAR
+ + " TEXT, "
+ + Contact.LAST_PRESENCE
+ + " TEXT, "
+ + Contact.LAST_TIME
+ + " NUMBER, "
+ + Contact.RTP_CAPABILITY
+ + " TEXT,"
+ + Contact.GROUPS
+ + " TEXT, FOREIGN KEY("
+ + Contact.ACCOUNT
+ + ") REFERENCES "
+ + Account.TABLENAME
+ + "("
+ + Account.UUID
+ + ") ON DELETE CASCADE, UNIQUE("
+ + Contact.ACCOUNT
+ + ", "
+ + Contact.JID
+ + ") ON CONFLICT REPLACE);";
+
+ private static final String CREATE_DISCOVERY_RESULTS_STATEMENT =
+ "create table "
+ + ServiceDiscoveryResult.TABLENAME
+ + "("
+ + ServiceDiscoveryResult.HASH
+ + " TEXT, "
+ + ServiceDiscoveryResult.VER
+ + " TEXT, "
+ + ServiceDiscoveryResult.RESULT
+ + " TEXT, "
+ + "UNIQUE("
+ + ServiceDiscoveryResult.HASH
+ + ", "
+ + ServiceDiscoveryResult.VER
+ + ") ON CONFLICT REPLACE);";
+
+ private static final String CREATE_PRESENCE_TEMPLATES_STATEMENT =
+ "CREATE TABLE "
+ + PresenceTemplate.TABELNAME
+ + "("
+ + PresenceTemplate.UUID
+ + " TEXT, "
+ + PresenceTemplate.LAST_USED
+ + " NUMBER,"
+ + PresenceTemplate.MESSAGE
+ + " TEXT,"
+ + PresenceTemplate.STATUS
+ + " TEXT,"
+ + "UNIQUE("
+ + PresenceTemplate.MESSAGE
+ + ","
+ + PresenceTemplate.STATUS
+ + ") ON CONFLICT REPLACE);";
+
+ private static final String CREATE_PREKEYS_STATEMENT =
+ "CREATE TABLE "
+ + SQLiteAxolotlStore.PREKEY_TABLENAME
+ + "("
+ + SQLiteAxolotlStore.ACCOUNT
+ + " TEXT, "
+ + SQLiteAxolotlStore.ID
+ + " INTEGER, "
+ + SQLiteAxolotlStore.KEY
+ + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES "
+ + Account.TABLENAME
+ + "("
+ + Account.UUID
+ + ") ON DELETE CASCADE, "
+ + "UNIQUE( "
+ + SQLiteAxolotlStore.ACCOUNT
+ + ", "
+ + SQLiteAxolotlStore.ID
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static final String CREATE_SIGNED_PREKEYS_STATEMENT =
+ "CREATE TABLE "
+ + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME
+ + "("
+ + SQLiteAxolotlStore.ACCOUNT
+ + " TEXT, "
+ + SQLiteAxolotlStore.ID
+ + " INTEGER, "
+ + SQLiteAxolotlStore.KEY
+ + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES "
+ + Account.TABLENAME
+ + "("
+ + Account.UUID
+ + ") ON DELETE CASCADE, "
+ + "UNIQUE( "
+ + SQLiteAxolotlStore.ACCOUNT
+ + ", "
+ + SQLiteAxolotlStore.ID
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static final String CREATE_SESSIONS_STATEMENT =
+ "CREATE TABLE "
+ + SQLiteAxolotlStore.SESSION_TABLENAME
+ + "("
+ + SQLiteAxolotlStore.ACCOUNT
+ + " TEXT, "
+ + SQLiteAxolotlStore.NAME
+ + " TEXT, "
+ + SQLiteAxolotlStore.DEVICE_ID
+ + " INTEGER, "
+ + SQLiteAxolotlStore.KEY
+ + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES "
+ + Account.TABLENAME
+ + "("
+ + Account.UUID
+ + ") ON DELETE CASCADE, "
+ + "UNIQUE( "
+ + SQLiteAxolotlStore.ACCOUNT
+ + ", "
+ + SQLiteAxolotlStore.NAME
+ + ", "
+ + SQLiteAxolotlStore.DEVICE_ID
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static final String CREATE_IDENTITIES_STATEMENT =
+ "CREATE TABLE "
+ + SQLiteAxolotlStore.IDENTITIES_TABLENAME
+ + "("
+ + SQLiteAxolotlStore.ACCOUNT
+ + " TEXT, "
+ + SQLiteAxolotlStore.NAME
+ + " TEXT, "
+ + SQLiteAxolotlStore.OWN
+ + " INTEGER, "
+ + SQLiteAxolotlStore.FINGERPRINT
+ + " TEXT, "
+ + SQLiteAxolotlStore.CERTIFICATE
+ + " BLOB, "
+ + SQLiteAxolotlStore.TRUST
+ + " TEXT, "
+ + SQLiteAxolotlStore.ACTIVE
+ + " NUMBER, "
+ + SQLiteAxolotlStore.LAST_ACTIVATION
+ + " NUMBER,"
+ + SQLiteAxolotlStore.KEY
+ + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES "
+ + Account.TABLENAME
+ + "("
+ + Account.UUID
+ + ") ON DELETE CASCADE, "
+ + "UNIQUE( "
+ + SQLiteAxolotlStore.ACCOUNT
+ + ", "
+ + SQLiteAxolotlStore.NAME
+ + ", "
+ + SQLiteAxolotlStore.FINGERPRINT
+ + ") ON CONFLICT IGNORE"
+ + ");";
private static final String RESOLVER_RESULTS_TABLENAME = "resolver_results";
- private static final 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,"
- + "UNIQUE(" + Resolver.Result.DOMAIN + ") ON CONFLICT REPLACE"
- + ");";
-
- private static final String CREATE_MESSAGE_TIME_INDEX = "CREATE INDEX message_time_index ON " + Message.TABLENAME + "(" + Message.TIME_SENT + ")";
- private static final String CREATE_MESSAGE_CONVERSATION_INDEX = "CREATE INDEX message_conversation_index ON " + Message.TABLENAME + "(" + Message.CONVERSATION + ")";
- private static final String CREATE_MESSAGE_DELETED_INDEX = "CREATE INDEX message_deleted_index ON " + Message.TABLENAME + "(" + Message.DELETED + ")";
- private static final String CREATE_MESSAGE_RELATIVE_FILE_PATH_INDEX = "CREATE INDEX message_file_path_index ON " + Message.TABLENAME + "(" + Message.RELATIVE_FILE_PATH + ")";
- private static final String CREATE_MESSAGE_TYPE_INDEX = "CREATE INDEX message_type_index ON " + Message.TABLENAME + "(" + Message.TYPE + ")";
-
- private static final String CREATE_MESSAGE_INDEX_TABLE = "CREATE VIRTUAL TABLE messages_index USING fts4 (uuid,body,notindexed=\"uuid\",content=\"" + Message.TABLENAME + "\",tokenize='unicode61')";
- private static final String CREATE_MESSAGE_INSERT_TRIGGER = "CREATE TRIGGER after_message_insert AFTER INSERT ON " + Message.TABLENAME + " BEGIN INSERT INTO messages_index(rowid,uuid,body) VALUES(NEW.rowid,NEW.uuid,NEW.body); END;";
- private static final String CREATE_MESSAGE_UPDATE_TRIGGER = "CREATE TRIGGER after_message_update UPDATE OF uuid,body ON " + Message.TABLENAME + " BEGIN UPDATE messages_index SET body=NEW.body,uuid=NEW.uuid WHERE rowid=OLD.rowid; END;";
- private static final String CREATE_MESSAGE_DELETE_TRIGGER = "CREATE TRIGGER after_message_delete AFTER DELETE ON " + Message.TABLENAME + " BEGIN DELETE FROM messages_index WHERE rowid=OLD.rowid; END;";
- private static final String COPY_PREEXISTING_ENTRIES = "INSERT INTO messages_index(messages_index) VALUES('rebuild');";
+ private static final 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,"
+ + "UNIQUE("
+ + Resolver.Result.DOMAIN
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static final String CREATE_MESSAGE_TIME_INDEX =
+ "CREATE INDEX message_time_index ON "
+ + Message.TABLENAME
+ + "("
+ + Message.TIME_SENT
+ + ")";
+ private static final String CREATE_MESSAGE_CONVERSATION_INDEX =
+ "CREATE INDEX message_conversation_index ON "
+ + Message.TABLENAME
+ + "("
+ + Message.CONVERSATION
+ + ")";
+ private static final String CREATE_MESSAGE_DELETED_INDEX =
+ "CREATE INDEX message_deleted_index ON "
+ + Message.TABLENAME
+ + "("
+ + Message.DELETED
+ + ")";
+ private static final String CREATE_MESSAGE_RELATIVE_FILE_PATH_INDEX =
+ "CREATE INDEX message_file_path_index ON "
+ + Message.TABLENAME
+ + "("
+ + Message.RELATIVE_FILE_PATH
+ + ")";
+ private static final String CREATE_MESSAGE_TYPE_INDEX =
+ "CREATE INDEX message_type_index ON " + Message.TABLENAME + "(" + Message.TYPE + ")";
+
+ private static final String CREATE_MESSAGE_INDEX_TABLE =
+ "CREATE VIRTUAL TABLE messages_index USING fts4"
+ + " (uuid,body,notindexed=\"uuid\",content=\""
+ + Message.TABLENAME
+ + "\",tokenize='unicode61')";
+ private static final String CREATE_MESSAGE_INSERT_TRIGGER =
+ "CREATE TRIGGER after_message_insert AFTER INSERT ON "
+ + Message.TABLENAME
+ + " BEGIN INSERT INTO messages_index(rowid,uuid,body)"
+ + " VALUES(NEW.rowid,NEW.uuid,NEW.body); END;";
+ private static final String CREATE_MESSAGE_UPDATE_TRIGGER =
+ "CREATE TRIGGER after_message_update UPDATE OF uuid,body ON "
+ + Message.TABLENAME
+ + " BEGIN UPDATE messages_index SET body=NEW.body,uuid=NEW.uuid WHERE"
+ + " rowid=OLD.rowid; END;";
+ private static final String CREATE_MESSAGE_DELETE_TRIGGER =
+ "CREATE TRIGGER after_message_delete AFTER DELETE ON "
+ + Message.TABLENAME
+ + " BEGIN DELETE FROM messages_index WHERE rowid=OLD.rowid; END;";
+ private static final String COPY_PREEXISTING_ENTRIES =
+ "INSERT INTO messages_index(messages_index) VALUES('rebuild');";
private DatabaseBackend(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
- private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) {
+ private static ContentValues createFingerprintStatusContentValues(
+ FingerprintStatus.Trust trust, boolean active) {
ContentValues values = new ContentValues();
values.put(SQLiteAxolotlStore.TRUST, trust.toString());
values.put(SQLiteAxolotlStore.ACTIVE, active ? 1 : 0);
@@ -199,7 +345,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
final SQLiteDatabase db = getWritableDatabase();
final Stopwatch stopwatch = Stopwatch.createStarted();
db.execSQL(COPY_PREEXISTING_ENTRIES);
- Log.d(Config.LOGTAG,"rebuilt message index in "+ stopwatch.stop().toString());
+ Log.d(Config.LOGTAG, "rebuilt message index in " + stopwatch.stop().toString());
}
public static synchronized DatabaseBackend getInstance(Context context) {
@@ -217,57 +363,132 @@ public class DatabaseBackend extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID + " TEXT PRIMARY KEY,"
- + Account.USERNAME + " TEXT,"
- + Account.SERVER + " TEXT,"
- + Account.PASSWORD + " TEXT,"
- + Account.DISPLAY_NAME + " TEXT, "
- + Account.STATUS + " TEXT,"
- + Account.STATUS_MESSAGE + " TEXT,"
- + Account.ROSTERVERSION + " TEXT,"
- + Account.OPTIONS + " NUMBER, "
- + Account.AVATAR + " TEXT, "
- + Account.KEYS + " TEXT, "
- + Account.HOSTNAME + " TEXT, "
- + Account.RESOURCE + " TEXT,"
- + Account.PINNED_MECHANISM + " TEXT,"
- + Account.PINNED_CHANNEL_BINDING + " TEXT,"
- + Account.FAST_MECHANISM + " TEXT,"
- + Account.FAST_TOKEN + " TEXT,"
- + Account.PORT + " NUMBER DEFAULT 5222)");
- db.execSQL("create table " + Conversation.TABLENAME + " ("
- + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
- + " TEXT, " + Conversation.CONTACT + " TEXT, "
- + Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
- + " TEXT, " + Conversation.CREATED + " NUMBER, "
- + Conversation.STATUS + " NUMBER, " + Conversation.MODE
- + " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
- + Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
- + "(" + Account.UUID + ") ON DELETE CASCADE);");
- db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
- + " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
- + Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
- + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
- + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
- + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
- + Message.RELATIVE_FILE_PATH + " TEXT, "
- + Message.SERVER_MSG_ID + " TEXT, "
- + Message.FINGERPRINT + " TEXT, "
- + Message.CARBON + " INTEGER, "
- + Message.EDITED + " TEXT, "
- + Message.READ + " NUMBER DEFAULT 1, "
- + Message.OOB + " INTEGER, "
- + Message.ERROR_MESSAGE + " TEXT,"
- + Message.READ_BY_MARKERS + " TEXT,"
- + Message.MARKABLE + " NUMBER DEFAULT 0,"
- + Message.DELETED + " NUMBER DEFAULT 0,"
- + Message.BODY_LANGUAGE + " TEXT,"
- + Message.OCCUPANT_ID + " TEXT,"
- + Message.REACTIONS + " TEXT,"
- + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
- + Message.CONVERSATION + ") REFERENCES "
- + Conversation.TABLENAME + "(" + Conversation.UUID
- + ") ON DELETE CASCADE);");
+ db.execSQL(
+ "create table "
+ + Account.TABLENAME
+ + "("
+ + Account.UUID
+ + " TEXT PRIMARY KEY,"
+ + Account.USERNAME
+ + " TEXT,"
+ + Account.SERVER
+ + " TEXT,"
+ + Account.PASSWORD
+ + " TEXT,"
+ + Account.DISPLAY_NAME
+ + " TEXT, "
+ + Account.STATUS
+ + " TEXT,"
+ + Account.STATUS_MESSAGE
+ + " TEXT,"
+ + Account.ROSTERVERSION
+ + " TEXT,"
+ + Account.OPTIONS
+ + " NUMBER, "
+ + Account.AVATAR
+ + " TEXT, "
+ + Account.KEYS
+ + " TEXT, "
+ + Account.HOSTNAME
+ + " TEXT, "
+ + Account.RESOURCE
+ + " TEXT,"
+ + Account.PINNED_MECHANISM
+ + " TEXT,"
+ + Account.PINNED_CHANNEL_BINDING
+ + " TEXT,"
+ + Account.FAST_MECHANISM
+ + " TEXT,"
+ + Account.FAST_TOKEN
+ + " TEXT,"
+ + Account.PORT
+ + " NUMBER DEFAULT 5222)");
+ db.execSQL(
+ "create table "
+ + Conversation.TABLENAME
+ + " ("
+ + Conversation.UUID
+ + " TEXT PRIMARY KEY, "
+ + Conversation.NAME
+ + " TEXT, "
+ + Conversation.CONTACT
+ + " TEXT, "
+ + Conversation.ACCOUNT
+ + " TEXT, "
+ + Conversation.CONTACTJID
+ + " TEXT, "
+ + Conversation.CREATED
+ + " NUMBER, "
+ + Conversation.STATUS
+ + " NUMBER, "
+ + Conversation.MODE
+ + " NUMBER, "
+ + Conversation.ATTRIBUTES
+ + " TEXT, FOREIGN KEY("
+ + Conversation.ACCOUNT
+ + ") REFERENCES "
+ + Account.TABLENAME
+ + "("
+ + Account.UUID
+ + ") ON DELETE CASCADE);");
+ db.execSQL(
+ "create table "
+ + Message.TABLENAME
+ + "( "
+ + Message.UUID
+ + " TEXT PRIMARY KEY, "
+ + Message.CONVERSATION
+ + " TEXT, "
+ + Message.TIME_SENT
+ + " NUMBER, "
+ + Message.COUNTERPART
+ + " TEXT, "
+ + Message.TRUE_COUNTERPART
+ + " TEXT,"
+ + Message.BODY
+ + " TEXT, "
+ + Message.ENCRYPTION
+ + " NUMBER, "
+ + Message.STATUS
+ + " NUMBER,"
+ + Message.TYPE
+ + " NUMBER, "
+ + Message.RELATIVE_FILE_PATH
+ + " TEXT, "
+ + Message.SERVER_MSG_ID
+ + " TEXT, "
+ + Message.FINGERPRINT
+ + " TEXT, "
+ + Message.CARBON
+ + " INTEGER, "
+ + Message.EDITED
+ + " TEXT, "
+ + Message.READ
+ + " NUMBER DEFAULT 1, "
+ + Message.OOB
+ + " INTEGER, "
+ + Message.ERROR_MESSAGE
+ + " TEXT,"
+ + Message.READ_BY_MARKERS
+ + " TEXT,"
+ + Message.MARKABLE
+ + " NUMBER DEFAULT 0,"
+ + Message.DELETED
+ + " NUMBER DEFAULT 0,"
+ + Message.BODY_LANGUAGE
+ + " TEXT,"
+ + Message.OCCUPANT_ID
+ + " TEXT,"
+ + Message.REACTIONS
+ + " TEXT,"
+ + Message.REMOTE_MSG_ID
+ + " TEXT, FOREIGN KEY("
+ + Message.CONVERSATION
+ + ") REFERENCES "
+ + Conversation.TABLENAME
+ + "("
+ + Conversation.UUID
+ + ") ON DELETE CASCADE);");
db.execSQL(CREATE_MESSAGE_TIME_INDEX);
db.execSQL(CREATE_MESSAGE_CONVERSATION_INDEX);
db.execSQL(CREATE_MESSAGE_DELETED_INDEX);
@@ -290,54 +511,87 @@ public class DatabaseBackend extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2 && newVersion >= 2) {
- db.execSQL("update " + Account.TABLENAME + " set "
- + Account.OPTIONS + " = " + Account.OPTIONS + " | 8");
+ db.execSQL(
+ "update "
+ + Account.TABLENAME
+ + " set "
+ + Account.OPTIONS
+ + " = "
+ + Account.OPTIONS
+ + " | 8");
}
if (oldVersion < 3 && newVersion >= 3) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.TYPE + " NUMBER");
+ db.execSQL(
+ "ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.TYPE + " NUMBER");
}
if (oldVersion < 5 && newVersion >= 5) {
db.execSQL("DROP TABLE " + Contact.TABLENAME);
db.execSQL(CREATE_CONTATCS_STATEMENT);
- db.execSQL("UPDATE " + Account.TABLENAME + " SET "
- + Account.ROSTERVERSION + " = NULL");
+ db.execSQL("UPDATE " + Account.TABLENAME + " SET " + Account.ROSTERVERSION + " = NULL");
}
if (oldVersion < 6 && newVersion >= 6) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.TRUE_COUNTERPART + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.TRUE_COUNTERPART
+ + " TEXT");
}
if (oldVersion < 7 && newVersion >= 7) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.REMOTE_MSG_ID + " TEXT");
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.AVATAR + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
- + Account.AVATAR + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.REMOTE_MSG_ID
+ + " TEXT");
+ db.execSQL(
+ "ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.AVATAR + " TEXT");
+ db.execSQL(
+ "ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.AVATAR + " TEXT");
}
if (oldVersion < 8 && newVersion >= 8) {
- db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
- + Conversation.ATTRIBUTES + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Conversation.TABLENAME
+ + " ADD COLUMN "
+ + Conversation.ATTRIBUTES
+ + " TEXT");
}
if (oldVersion < 9 && newVersion >= 9) {
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.LAST_TIME + " NUMBER");
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.LAST_PRESENCE + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Contact.TABLENAME
+ + " ADD COLUMN "
+ + Contact.LAST_TIME
+ + " NUMBER");
+ db.execSQL(
+ "ALTER TABLE "
+ + Contact.TABLENAME
+ + " ADD COLUMN "
+ + Contact.LAST_PRESENCE
+ + " TEXT");
}
if (oldVersion < 10 && newVersion >= 10) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.RELATIVE_FILE_PATH + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.RELATIVE_FILE_PATH
+ + " TEXT");
}
if (oldVersion < 11 && newVersion >= 11) {
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.GROUPS + " TEXT");
+ db.execSQL(
+ "ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.GROUPS + " TEXT");
db.execSQL("delete from " + Contact.TABLENAME);
db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
}
if (oldVersion < 12 && newVersion >= 12) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.SERVER_MSG_ID + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.SERVER_MSG_ID
+ + " TEXT");
}
if (oldVersion < 13 && newVersion >= 13) {
db.execSQL("delete from " + Contact.TABLENAME);
@@ -348,26 +602,60 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
if (oldVersion < 15 && newVersion >= 15) {
recreateAxolotlDb(db);
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.FINGERPRINT + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.FINGERPRINT
+ + " TEXT");
}
if (oldVersion < 16 && newVersion >= 16) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.CARBON + " INTEGER");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.CARBON
+ + " INTEGER");
}
if (oldVersion < 19 && newVersion >= 19) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.DISPLAY_NAME + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Account.TABLENAME
+ + " ADD COLUMN "
+ + Account.DISPLAY_NAME
+ + " TEXT");
}
if (oldVersion < 20 && newVersion >= 20) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
+ db.execSQL(
+ "ALTER TABLE "
+ + Account.TABLENAME
+ + " ADD COLUMN "
+ + Account.HOSTNAME
+ + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Account.TABLENAME
+ + " ADD COLUMN "
+ + Account.PORT
+ + " NUMBER DEFAULT 5222");
}
if (oldVersion < 26 && newVersion >= 26) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
+ db.execSQL(
+ "ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Account.TABLENAME
+ + " ADD COLUMN "
+ + Account.STATUS_MESSAGE
+ + " TEXT");
}
if (oldVersion < 40 && newVersion >= 40) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.RESOURCE + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Account.TABLENAME
+ + " ADD COLUMN "
+ + Account.RESOURCE
+ + " TEXT");
}
/* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization.
@@ -375,45 +663,67 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 17 && newVersion >= 17 && newVersion < 31) {
List<Account> accounts = getAccounts(db);
for (Account account : accounts) {
- String ownDeviceIdString = account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID);
+ String ownDeviceIdString =
+ account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID);
if (ownDeviceIdString == null) {
continue;
}
int ownDeviceId = Integer.valueOf(ownDeviceIdString);
- SignalProtocolAddress ownAddress = new SignalProtocolAddress(account.getJid().asBareJid().toString(), ownDeviceId);
+ SignalProtocolAddress ownAddress =
+ new SignalProtocolAddress(
+ account.getJid().asBareJid().toString(), ownDeviceId);
deleteSession(db, account, ownAddress);
IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
if (identityKeyPair != null) {
String[] selectionArgs = {
- account.getUuid(),
- CryptoHelper.bytesToHex(identityKeyPair.getPublicKey().serialize())
+ account.getUuid(),
+ CryptoHelper.bytesToHex(identityKeyPair.getPublicKey().serialize())
};
ContentValues values = new ContentValues();
values.put(SQLiteAxolotlStore.TRUSTED, 2);
- db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
+ db.update(
+ SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ values,
+ SQLiteAxolotlStore.ACCOUNT
+ + " = ? AND "
+ + SQLiteAxolotlStore.FINGERPRINT
+ + " = ? ",
selectionArgs);
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not load own identity key pair");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": could not load own identity key pair");
}
}
}
if (oldVersion < 18 && newVersion >= 18) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.READ + " NUMBER DEFAULT 1");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.READ
+ + " NUMBER DEFAULT 1");
}
if (oldVersion < 21 && newVersion >= 21) {
List<Account> accounts = getAccounts(db);
for (Account account : accounts) {
account.unsetPgpSignature();
- db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
- + "=?", new String[]{account.getUuid()});
+ db.update(
+ Account.TABLENAME,
+ account.getContentValues(),
+ Account.UUID + "=?",
+ new String[] {account.getUuid()});
}
}
if (oldVersion >= 15 && oldVersion < 22 && newVersion >= 22) {
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
+ db.execSQL(
+ "ALTER TABLE "
+ + SQLiteAxolotlStore.IDENTITIES_TABLENAME
+ + " ADD COLUMN "
+ + SQLiteAxolotlStore.CERTIFICATE);
}
if (oldVersion < 23 && newVersion >= 23) {
@@ -421,11 +731,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
if (oldVersion < 24 && newVersion >= 24) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT");
+ db.execSQL(
+ "ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT");
}
if (oldVersion < 25 && newVersion >= 25) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
+ db.execSQL(
+ "ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
}
if (oldVersion < 26 && newVersion >= 26) {
@@ -441,51 +753,116 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
if (oldVersion < 29 && newVersion >= 29) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.ERROR_MESSAGE + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + Message.TABLENAME
+ + " ADD COLUMN "
+ + Message.ERROR_MESSAGE
+ + " TEXT");
}
if (oldVersion >= 15 && oldVersion < 31 && newVersion >= 31) {
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.TRUST + " TEXT");
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.ACTIVE + " NUMBER");
+ db.execSQL(
+ "ALTER TABLE "
+ + SQLiteAxolotlStore.IDENTITIES_TABLENAME
+ + " ADD COLUMN "
+ + SQLiteAxolotlStore.TRUST
+ + " TEXT");
+ db.execSQL(
+ "ALTER TABLE "
+ + SQLiteAxolotlStore.IDENTITIES_TABLENAME
+ + " ADD COLUMN "
+ + SQLiteAxolotlStore.ACTIVE
+ + " NUMBER");
HashMap<Integer, ContentValues> migration = new HashMap<>();
- migration.put(0, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
- migration.put(1, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
- migration.put(2, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, true));
- migration.put(3, createFingerprintStatusContentValues(FingerprintStatus.Trust.COMPROMISED, false));
- migration.put(4, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
- migration.put(5, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
- migration.put(6, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, false));
- migration.put(7, createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, true));
- migration.put(8, createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, false));
+ migration.put(
+ 0, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
+ migration.put(
+ 1, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
+ migration.put(
+ 2,
+ createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, true));
+ migration.put(
+ 3,
+ createFingerprintStatusContentValues(
+ FingerprintStatus.Trust.COMPROMISED, false));
+ migration.put(
+ 4,
+ createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
+ migration.put(
+ 5,
+ createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
+ migration.put(
+ 6,
+ createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, false));
+ migration.put(
+ 7,
+ createFingerprintStatusContentValues(
+ FingerprintStatus.Trust.VERIFIED_X509, true));
+ migration.put(
+ 8,
+ createFingerprintStatusContentValues(
+ FingerprintStatus.Trust.VERIFIED_X509, false));
for (Map.Entry<Integer, ContentValues> entry : migration.entrySet()) {
String whereClause = SQLiteAxolotlStore.TRUSTED + "=?";
String[] where = {String.valueOf(entry.getKey())};
- db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, entry.getValue(), whereClause, where);
+ db.update(
+ SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ entry.getValue(),
+ whereClause,
+ where);
}
-
}
if (oldVersion >= 15 && oldVersion < 32 && newVersion >= 32) {
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.LAST_ACTIVATION + " NUMBER");
+ db.execSQL(
+ "ALTER TABLE "
+ + SQLiteAxolotlStore.IDENTITIES_TABLENAME
+ + " ADD COLUMN "
+ + SQLiteAxolotlStore.LAST_ACTIVATION
+ + " NUMBER");
ContentValues defaults = new ContentValues();
defaults.put(SQLiteAxolotlStore.LAST_ACTIVATION, System.currentTimeMillis());
db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, defaults, null, null);
}
if (oldVersion >= 15 && oldVersion < 33 && newVersion >= 33) {
String whereClause = SQLiteAxolotlStore.OWN + "=1";
- db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED, true), whereClause, null);
+ db.update(
+ SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED, true),
+ whereClause,
+ null);
}
if (oldVersion < 34 && newVersion >= 34) {
db.execSQL(CREATE_MESSAGE_TIME_INDEX);
- final File oldPicturesDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/Conversations/");
- final File oldFilesDirectory = new File(Environment.getExternalStorageDirectory() + "/Conversations/");
- final File newFilesDirectory = new File(Environment.getExternalStorageDirectory() + "/Conversations/Media/Conversations Files/");
- final File newVideosDirectory = new File(Environment.getExternalStorageDirectory() + "/Conversations/Media/Conversations Videos/");
+ final File oldPicturesDirectory =
+ new File(
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES)
+ + "/Conversations/");
+ final File oldFilesDirectory =
+ new File(Environment.getExternalStorageDirectory() + "/Conversations/");
+ final File newFilesDirectory =
+ new File(
+ Environment.getExternalStorageDirectory()
+ + "/Conversations/Media/Conversations Files/");
+ final File newVideosDirectory =
+ new File(
+ Environment.getExternalStorageDirectory()
+ + "/Conversations/Media/Conversations Videos/");
if (oldPicturesDirectory.exists() && oldPicturesDirectory.isDirectory()) {
- final File newPicturesDirectory = new File(Environment.getExternalStorageDirectory() + "/Conversations/Media/Conversations Images/");
+ final File newPicturesDirectory =
+ new File(
+ Environment.getExternalStorageDirectory()
+ + "/Conversations/Media/Conversations Images/");
newPicturesDirectory.getParentFile().mkdirs();
if (oldPicturesDirectory.renameTo(newPicturesDirectory)) {
- Log.d(Config.LOGTAG, "moved " + oldPicturesDirectory.getAbsolutePath() + " to " + newPicturesDirectory.getAbsolutePath());
+ Log.d(
+ Config.LOGTAG,
+ "moved "
+ + oldPicturesDirectory.getAbsolutePath()
+ + " to "
+ + newPicturesDirectory.getAbsolutePath());
}
}
if (oldFilesDirectory.exists() && oldFilesDirectory.isDirectory()) {
@@ -1,117 +0,0 @@
-package eu.siacs.conversations.services;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.graphics.drawable.Icon;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.service.chooser.ChooserTarget;
-import android.service.chooser.ChooserTargetService;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.receiver.SystemEventReceiver;
-import eu.siacs.conversations.ui.ConversationsActivity;
-import eu.siacs.conversations.utils.Compatibility;
-
-@SuppressLint("Deprecated")
-@TargetApi(Build.VERSION_CODES.M)
-public class ContactChooserTargetService extends ChooserTargetService implements ServiceConnection {
-
- private final Object lock = new Object();
- private static final int MAX_TARGETS = 5;
- private XmppConnectionService mXmppConnectionService;
-
- private static boolean textOnly(IntentFilter filter) {
- for (int i = 0; i < filter.countDataTypes(); ++i) {
- if (!"text/plain".equals(filter.getDataType(i))) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public List<ChooserTarget> onGetChooserTargets(
- final ComponentName targetActivityName, final IntentFilter matchedFilter) {
- if (!SystemEventReceiver.hasEnabledAccounts(this)) {
- return Collections.emptyList();
- }
- final Intent intent = new Intent(this, XmppConnectionService.class);
- intent.setAction("contact_chooser");
- Compatibility.startService(this, intent);
- bindService(intent, this, Context.BIND_AUTO_CREATE);
- try {
- waitForService();
- if (!mXmppConnectionService.areMessagesInitialized()) {
- return Collections.emptyList();
- }
- final ArrayList<Conversation> conversations = new ArrayList<>();
- mXmppConnectionService.populateWithOrderedConversations(
- conversations, textOnly(matchedFilter));
- final ComponentName componentName =
- new ComponentName(this, ConversationsActivity.class);
- final int pixel = AvatarService.getSystemUiAvatarSize(this);
- final ArrayList<ChooserTarget> chooserTargets = new ArrayList<>();
- for (final Conversation conversation : conversations) {
- if (conversation.sentMessagesCount() == 0) {
- continue;
- }
- final String name = conversation.getName().toString();
- final Icon icon =
- Icon.createWithBitmap(
- mXmppConnectionService.getAvatarService().get(conversation, pixel));
- final float score = 1 - (1.0f / MAX_TARGETS) * chooserTargets.size();
- final Bundle extras = new Bundle();
- extras.putString(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
- chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras));
- if (chooserTargets.size() >= MAX_TARGETS) {
- return chooserTargets;
- }
- }
- return chooserTargets;
- } catch (final InterruptedException e) {
- Log.d(
- Config.LOGTAG,
- "Thread got interrupted before binding to XmppConnectionService",
- e);
- } finally {
- unbindService(this);
- }
- return Collections.emptyList();
- }
-
- @Override
- public void onServiceConnected(final ComponentName name, final IBinder service) {
- XmppConnectionService.XmppConnectionBinder binder =
- (XmppConnectionService.XmppConnectionBinder) service;
- mXmppConnectionService = binder.getService();
- synchronized (this.lock) {
- lock.notifyAll();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mXmppConnectionService = null;
- }
-
- private void waitForService() throws InterruptedException {
- if (mXmppConnectionService == null) {
- synchronized (this.lock) {
- lock.wait();
- }
- }
- }
-}
@@ -28,7 +28,6 @@ import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.util.Log;
-
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
@@ -41,7 +40,6 @@ import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.graphics.drawable.IconCompat;
-
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
@@ -49,7 +47,6 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.primitives.Ints;
-
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -70,7 +67,6 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
-
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -505,7 +501,8 @@ public class NotificationService {
Log.d(
Config.LOGTAG,
message.getConversation().getAccount().getJid().asBareJid()
- + ": suppressing failed delivery notification because conversation is open");
+ + ": suppressing failed delivery notification because conversation is"
+ + " open");
return;
}
final PendingIntent pendingIntent = createContentIntent(conversation);
@@ -631,10 +628,11 @@ public class NotificationService {
.build());
modifyIncomingCall(builder);
final Notification notification = builder.build();
- notification.audioAttributes = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
- .build();
+ notification.audioAttributes =
+ new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ .build();
notification.flags = notification.flags | Notification.FLAG_INSISTENT;
notify(INCOMING_CALL_NOTIFICATION_ID, notification);
}
@@ -708,7 +706,8 @@ public class NotificationService {
if (jingleRtpConnection == null) {
return false;
}
- final var notificationManager = mXmppConnectionService.getSystemService(NotificationManager.class);
+ final var notificationManager =
+ mXmppConnectionService.getSystemService(NotificationManager.class);
if (Iterables.any(
Arrays.asList(notificationManager.getActiveNotifications()),
n -> n.getId() == INCOMING_CALL_NOTIFICATION_ID)) {
@@ -820,7 +819,8 @@ public class NotificationService {
Log.d(
Config.LOGTAG,
conversational.getAccount().getJid().asBareJid()
- + ": dismissed missed call because call was picked up on other device");
+ + ": dismissed missed call because call was picked up on"
+ + " other device");
iterator.remove();
}
}
@@ -1345,12 +1345,15 @@ public class NotificationService {
if (systemAccount != null) {
notificationBuilder.addPerson(systemAccount.toString());
}
- info = mXmppConnectionService.getShortcutService().getShortcutInfoCompat(contact);
+ info =
+ mXmppConnectionService
+ .getShortcutService()
+ .getShortcutInfo(contact, conversation.getUuid());
} else {
info =
mXmppConnectionService
.getShortcutService()
- .getShortcutInfoCompat(conversation.getMucOptions());
+ .getShortcutInfo(conversation.getMucOptions());
}
notificationBuilder.setWhen(conversation.getLatestMessage().getTimeSent());
notificationBuilder.setSmallIcon(R.drawable.ic_app_icon_notification);
@@ -1384,16 +1387,16 @@ public class NotificationService {
}
final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
bigPictureStyle.bigPicture(bitmap);
- if (tmp.size() > 0) {
- CharSequence text = getMergedBodies(tmp);
- bigPictureStyle.setSummaryText(text);
- builder.setContentText(text);
- builder.setTicker(text);
- } else {
+ if (tmp.isEmpty()) {
final String description =
UIHelper.getFileDescriptionString(mXmppConnectionService, message);
builder.setContentText(description);
builder.setTicker(description);
+ } else {
+ final CharSequence text = getMergedBodies(tmp);
+ bigPictureStyle.setSummaryText(text);
+ builder.setContentText(text);
+ builder.setTicker(text);
}
builder.setStyle(bigPictureStyle);
} catch (final IOException e) {
@@ -2,37 +2,38 @@ package eu.siacs.conversations.services;
import android.annotation.TargetApi;
import android.content.Intent;
-import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.Bitmap;
-import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
+import android.os.PersistableBundle;
import android.util.Log;
-
import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
import androidx.core.content.pm.ShortcutInfoCompat;
+import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.MucOptions;
+import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.StartConversationActivity;
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
import eu.siacs.conversations.xmpp.Jid;
+import java.util.Collection;
+import java.util.List;
public class ShortcutService {
private final XmppConnectionService xmppConnectionService;
- private final ReplacingSerialSingleThreadExecutor replacingSerialSingleThreadExecutor = new ReplacingSerialSingleThreadExecutor(ShortcutService.class.getSimpleName());
+ private final ReplacingSerialSingleThreadExecutor replacingSerialSingleThreadExecutor =
+ new ReplacingSerialSingleThreadExecutor(ShortcutService.class.getSimpleName());
- public ShortcutService(XmppConnectionService xmppConnectionService) {
+ public ShortcutService(final XmppConnectionService xmppConnectionService) {
this.xmppConnectionService = xmppConnectionService;
}
@@ -42,12 +43,7 @@ public class ShortcutService {
public void refresh(final boolean forceUpdate) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
- final Runnable r = new Runnable() {
- @Override
- public void run() {
- refreshImpl(forceUpdate);
- }
- };
+ final Runnable r = () -> refreshImpl(forceUpdate);
replacingSerialSingleThreadExecutor.execute(r);
}
}
@@ -55,87 +51,88 @@ public class ShortcutService {
@TargetApi(25)
public void report(Contact contact) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
- ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
+ ShortcutManager shortcutManager =
+ xmppConnectionService.getSystemService(ShortcutManager.class);
shortcutManager.reportShortcutUsed(getShortcutId(contact));
}
}
@TargetApi(25)
- private void refreshImpl(boolean forceUpdate) {
- List<FrequentContact> frequentContacts = xmppConnectionService.databaseBackend.getFrequentContacts(30);
- HashMap<String,Account> accounts = new HashMap<>();
- for(Account account : xmppConnectionService.getAccounts()) {
- accounts.put(account.getUuid(),account);
- }
- List<Contact> contacts = new ArrayList<>();
- for(FrequentContact frequentContact : frequentContacts) {
- Account account = accounts.get(frequentContact.account);
+ private void refreshImpl(final boolean forceUpdate) {
+ final var frequentContacts = xmppConnectionService.databaseBackend.getFrequentContacts(30);
+ final var accounts =
+ ImmutableMap.copyOf(
+ Maps.uniqueIndex(xmppConnectionService.getAccounts(), Account::getUuid));
+ final var contactBuilder = new ImmutableMap.Builder<FrequentContact, Contact>();
+ for (final var frequentContact : frequentContacts) {
+ final Account account = accounts.get(frequentContact.account);
if (account != null) {
- contacts.add(account.getRoster().getContact(frequentContact.contact));
+ final var contact = account.getRoster().getContact(frequentContact.contact);
+ contactBuilder.put(frequentContact, contact);
}
}
- ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
- boolean needsUpdate = forceUpdate || contactsChanged(contacts,shortcutManager.getDynamicShortcuts());
+ final var contacts = contactBuilder.build();
+ final var current = ShortcutManagerCompat.getDynamicShortcuts(xmppConnectionService);
+ boolean needsUpdate = forceUpdate || contactsChanged(contacts.values(), current);
if (!needsUpdate) {
- Log.d(Config.LOGTAG,"skipping shortcut update");
+ Log.d(Config.LOGTAG, "skipping shortcut update");
return;
}
- List<ShortcutInfo> newDynamicShortCuts = new ArrayList<>();
- for (Contact contact : contacts) {
- ShortcutInfo shortcut = getShortcutInfo(contact);
- newDynamicShortCuts.add(shortcut);
+ final var newDynamicShortcuts = new ImmutableList.Builder<ShortcutInfoCompat>();
+ for (final var entry : contacts.entrySet()) {
+ final var contact = entry.getValue();
+ final var conversation = entry.getKey().conversation;
+ final var shortcut = getShortcutInfo(contact, conversation);
+ newDynamicShortcuts.add(shortcut);
}
- if (shortcutManager.setDynamicShortcuts(newDynamicShortCuts)) {
- Log.d(Config.LOGTAG,"updated dynamic shortcuts");
+ if (ShortcutManagerCompat.setDynamicShortcuts(
+ xmppConnectionService, newDynamicShortcuts.build())) {
+ Log.d(Config.LOGTAG, "updated dynamic shortcuts");
} else {
Log.d(Config.LOGTAG, "unable to update dynamic shortcuts");
}
}
- public ShortcutInfoCompat getShortcutInfoCompat(final Contact contact) {
+ public ShortcutInfoCompat getShortcutInfo(final Contact contact, final String conversation) {
final ShortcutInfoCompat.Builder builder =
new ShortcutInfoCompat.Builder(xmppConnectionService, getShortcutId(contact))
.setShortLabel(contact.getDisplayName())
.setIntent(getShortcutIntent(contact))
.setIsConversation();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- builder.setIcon(
- IconCompat.createFromIcon(
- xmppConnectionService,
- Icon.createWithBitmap(
- xmppConnectionService
- .getAvatarService()
- .getRoundedShortcut(contact))));
+ builder.setIcon(
+ IconCompat.createWithBitmap(
+ xmppConnectionService.getAvatarService().getRoundedShortcut(contact)));
+ if (conversation != null) {
+ setConversation(builder, conversation);
}
return builder.build();
}
- public ShortcutInfoCompat getShortcutInfoCompat(final MucOptions mucOptions) {
+ public ShortcutInfoCompat getShortcutInfo(final MucOptions mucOptions) {
final ShortcutInfoCompat.Builder builder =
new ShortcutInfoCompat.Builder(xmppConnectionService, getShortcutId(mucOptions))
.setShortLabel(mucOptions.getConversation().getName())
.setIntent(getShortcutIntent(mucOptions))
.setIsConversation();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- builder.setIcon(
- IconCompat.createFromIcon(
- xmppConnectionService,
- Icon.createWithBitmap(
- xmppConnectionService
- .getAvatarService()
- .getRoundedShortcut(mucOptions))));
- }
+ builder.setIcon(
+ IconCompat.createWithBitmap(
+ xmppConnectionService.getAvatarService().getRoundedShortcut(mucOptions)));
+ setConversation(builder, mucOptions.getConversation().getUuid());
return builder.build();
}
- @TargetApi(Build.VERSION_CODES.N_MR1)
- private ShortcutInfo getShortcutInfo(final Contact contact) {
- return getShortcutInfoCompat(contact).toShortcutInfo();
+ private static void setConversation(
+ final ShortcutInfoCompat.Builder builder, @NonNull final String conversation) {
+ builder.setCategories(ImmutableSet.of("eu.siacs.conversations.category.SHARE_TARGET"));
+ final var extras = new PersistableBundle();
+ extras.putString(ConversationsActivity.EXTRA_CONVERSATION, conversation);
+ builder.setExtras(extras);
}
- private static boolean contactsChanged(List<Contact> needles, List<ShortcutInfo> haystack) {
- for(Contact needle : needles) {
- if(!contactExists(needle,haystack)) {
+ private static boolean contactsChanged(
+ final Collection<Contact> needles, final List<ShortcutInfoCompat> haystack) {
+ for (final Contact needle : needles) {
+ if (!contactExists(needle, haystack)) {
return true;
}
}
@@ -143,17 +140,22 @@ public class ShortcutService {
}
@TargetApi(25)
- private static boolean contactExists(Contact needle, List<ShortcutInfo> haystack) {
- for(ShortcutInfo shortcutInfo : haystack) {
- if (getShortcutId(needle).equals(shortcutInfo.getId()) && needle.getDisplayName().equals(shortcutInfo.getShortLabel())) {
+ private static boolean contactExists(
+ final Contact needle, final List<ShortcutInfoCompat> haystack) {
+ for (final ShortcutInfoCompat shortcutInfo : haystack) {
+ final var label = shortcutInfo.getShortLabel();
+ if (getShortcutId(needle).equals(shortcutInfo.getId())
+ && needle.getDisplayName().equals(label.toString())) {
return true;
}
}
return false;
}
- private static String getShortcutId(Contact contact) {
- return contact.getAccount().getJid().asBareJid().toEscapedString()+"#"+contact.getJid().asBareJid().toEscapedString();
+ private static String getShortcutId(final Contact contact) {
+ return contact.getAccount().getJid().asBareJid().toEscapedString()
+ + "#"
+ + contact.getJid().asBareJid().toEscapedString();
}
private static String getShortcutId(final MucOptions mucOptions) {
@@ -194,12 +196,15 @@ public class ShortcutService {
}
@NonNull
- public Intent createShortcut(Contact contact, boolean legacy) {
+ public Intent createShortcut(final Contact contact, final boolean legacy) {
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !legacy) {
- ShortcutInfo shortcut = getShortcutInfo(contact);
- ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
- intent = shortcutManager.createShortcutResultIntent(shortcut);
+ final var conversation = xmppConnectionService.find(contact);
+ final var uuid = conversation == null ? null : conversation.getUuid();
+ final var shortcut = getShortcutInfo(contact, uuid);
+ intent =
+ ShortcutManagerCompat.createShortcutResultIntent(
+ xmppConnectionService, shortcut);
} else {
intent = createShortcutResultIntent(contact);
}
@@ -207,7 +212,7 @@ public class ShortcutService {
}
@NonNull
- private Intent createShortcutResultIntent(Contact contact) {
+ private Intent createShortcutResultIntent(final Contact contact) {
AvatarService avatarService = xmppConnectionService.getAvatarService();
Bitmap icon = avatarService.getRoundedShortcutWithIcon(contact);
Intent intent = new Intent();
@@ -218,13 +223,14 @@ public class ShortcutService {
}
public static class FrequentContact {
+ private final String conversation;
private final String account;
private final Jid contact;
- public FrequentContact(String account, Jid contact) {
+ public FrequentContact(final String conversation, final String account, final Jid contact) {
+ this.conversation = conversation;
this.account = account;
this.contact = contact;
}
}
-
}
@@ -55,8 +55,10 @@ import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -147,7 +149,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -253,12 +254,13 @@ public class XmppConnectionService extends Service {
private final MessageGenerator mMessageGenerator = new MessageGenerator(this);
public OnContactStatusChanged onContactStatusChanged =
(contact, online) -> {
- Conversation conversation = find(getConversations(), contact);
- if (conversation != null) {
- if (online) {
- if (contact.getPresences().size() == 1) {
- sendUnsentMessages(conversation);
- }
+ final var conversation = find(contact);
+ if (conversation == null) {
+ return;
+ }
+ if (online) {
+ if (contact.getPresences().size() == 1) {
+ sendUnsentMessages(conversation);
}
}
};
@@ -998,16 +1000,17 @@ public class XmppConnectionService extends Service {
}
if (pingNow) {
for (final Account account : pingCandidates) {
+ final var connection = account.getXmppConnection();
final boolean lowTimeout = isInLowPingTimeoutMode(account);
- account.getXmppConnection().sendPing();
+ final var delta =
+ (SystemClock.elapsedRealtime() - connection.getLastPacketReceived())
+ / 1000L;
+ connection.sendPing();
Log.d(
Config.LOGTAG,
- account.getJid().asBareJid()
- + " send ping (action="
- + action
- + ",lowTimeout="
- + lowTimeout
- + ")");
+ String.format(
+ "%s: send ping (action=%s,lowTimeout=%s,interval=%s)",
+ account.getJid().asBareJid(), action, lowTimeout, delta));
scheduleWakeUpCall(
lowTimeout ? Config.LOW_PING_TIMEOUT : Config.PING_TIMEOUT,
account.getUuid().hashCode());
@@ -1485,7 +1488,7 @@ public class XmppConnectionService extends Service {
ContextCompat.RECEIVER_EXPORTED);
mForceDuringOnCreate.set(false);
toggleForegroundService();
- internalPingExecutor.scheduleAtFixedRate(
+ internalPingExecutor.scheduleWithFixedDelay(
this::manageAccountConnectionStatesInternal, 10, 10, TimeUnit.SECONDS);
final SharedPreferences sharedPreferences =
androidx.preference.PreferenceManager.getDefaultSharedPreferences(this);
@@ -2428,10 +2431,8 @@ public class XmppConnectionService extends Service {
private void restoreFromDatabase() {
synchronized (this.conversations) {
- final Map<String, Account> accountLookupTable = new Hashtable<>();
- for (Account account : this.accounts) {
- accountLookupTable.put(account.getUuid(), account);
- }
+ final Map<String, Account> accountLookupTable =
+ ImmutableMap.copyOf(Maps.uniqueIndex(this.accounts, Account::getUuid));
Log.d(Config.LOGTAG, "restoring conversations...");
final long startTimeConversationsRestore = SystemClock.elapsedRealtime();
this.conversations.addAll(
@@ -2735,8 +2736,8 @@ public class XmppConnectionService extends Service {
return results;
}
- public Conversation find(final Iterable<Conversation> haystack, final Contact contact) {
- for (final Conversation conversation : haystack) {
+ public Conversation find(final Contact contact) {
+ for (final Conversation conversation : this.conversations) {
if (conversation.getContact() == contact) {
return conversation;
}
@@ -2798,27 +2799,19 @@ public class XmppConnectionService extends Service {
final MessageArchiveService.Query query,
final boolean async) {
synchronized (this.conversations) {
- Conversation conversation = find(account, jid);
- if (conversation != null) {
- return conversation;
+ final var cached = find(account, jid);
+ if (cached != null) {
+ return cached;
}
- conversation = databaseBackend.findConversation(account, jid);
+ final var existing = databaseBackend.findConversation(account, jid);
+ final Conversation conversation;
final boolean loadMessagesFromDb;
- if (conversation != null) {
- conversation.setStatus(Conversation.STATUS_AVAILABLE);
- conversation.setAccount(account);
- if (muc) {
- conversation.setMode(Conversation.MODE_MULTI);
- conversation.setContactJid(jid);
- } else {
- conversation.setMode(Conversation.MODE_SINGLE);
- conversation.setContactJid(jid.asBareJid());
- }
- databaseBackend.updateConversation(conversation);
- loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true, false);
+ if (existing != null) {
+ conversation = existing;
+ loadMessagesFromDb = restoreFromArchive(conversation, jid, muc);
} else {
String conversationName;
- Contact contact = account.getRoster().getContact(jid);
+ final Contact contact = account.getRoster().getContact(jid);
if (contact != null) {
conversationName = contact.getDisplayName();
} else {
@@ -2839,35 +2832,13 @@ public class XmppConnectionService extends Service {
this.databaseBackend.createConversation(conversation);
loadMessagesFromDb = false;
}
- final Conversation c = conversation;
- final Runnable runnable =
- () -> {
- if (loadMessagesFromDb) {
- c.addAll(0, databaseBackend.getMessages(c, Config.PAGE_SIZE));
- updateConversationUi();
- c.messagesLoaded.set(true);
- }
- if (account.getXmppConnection() != null
- && !c.getContact().isBlocked()
- && account.getXmppConnection().getFeatures().mam()
- && !muc) {
- if (query == null) {
- mMessageArchiveService.query(c);
- } else {
- if (query.getConversation() == null) {
- mMessageArchiveService.query(
- c, query.getStart(), query.isCatchup());
- }
- }
- }
- if (joinAfterCreate) {
- joinMuc(c);
- }
- };
if (async) {
- mDatabaseReaderExecutor.execute(runnable);
+ mDatabaseReaderExecutor.execute(
+ () ->
+ postProcessConversation(
+ conversation, loadMessagesFromDb, joinAfterCreate, query));
} else {
- runnable.run();
+ postProcessConversation(conversation, loadMessagesFromDb, joinAfterCreate, query);
}
this.conversations.add(conversation);
updateConversationUi();
@@ -2875,6 +2846,84 @@ public class XmppConnectionService extends Service {
}
}
+ public Conversation findConversationByUuidReliable(final String uuid) {
+ final var cached = findConversationByUuid(uuid);
+ if (cached != null) {
+ return cached;
+ }
+ final var existing = databaseBackend.findConversation(uuid);
+ if (existing == null) {
+ return null;
+ }
+ Log.d(
+ Config.LOGTAG,
+ existing.getJid().asBareJid()
+ + ": restoring conversation with "
+ + existing.getJid()
+ + " from DB");
+ final Map<String, Account> accounts =
+ ImmutableMap.copyOf(Maps.uniqueIndex(this.accounts, Account::getUuid));
+ existing.setAccount(accounts.get(existing.getAccountUuid()));
+ final var loadMessagesFromDb = restoreFromArchive(existing);
+ mDatabaseReaderExecutor.execute(
+ () ->
+ postProcessConversation(
+ existing,
+ loadMessagesFromDb,
+ existing.getMode() == Conversational.MODE_MULTI,
+ null));
+ this.conversations.add(existing);
+ updateConversationUi();
+ return existing;
+ }
+
+ private boolean restoreFromArchive(
+ final Conversation conversation, final Jid jid, final boolean muc) {
+ if (muc) {
+ conversation.setMode(Conversation.MODE_MULTI);
+ conversation.setContactJid(jid);
+ } else {
+ conversation.setMode(Conversation.MODE_SINGLE);
+ conversation.setContactJid(jid.asBareJid());
+ }
+ return restoreFromArchive(conversation);
+ }
+
+ private boolean restoreFromArchive(final Conversation conversation) {
+ conversation.setStatus(Conversation.STATUS_AVAILABLE);
+ databaseBackend.updateConversation(conversation);
+ return conversation.messagesLoaded.compareAndSet(true, false);
+ }
+
+ private void postProcessConversation(
+ final Conversation c,
+ final boolean loadMessagesFromDb,
+ final boolean joinAfterCreate,
+ final MessageArchiveService.Query query) {
+ final var singleMode = c.getMode() == Conversational.MODE_SINGLE;
+ final var account = c.getAccount();
+ if (loadMessagesFromDb) {
+ c.addAll(0, databaseBackend.getMessages(c, Config.PAGE_SIZE));
+ updateConversationUi();
+ c.messagesLoaded.set(true);
+ }
+ if (account.getXmppConnection() != null
+ && !c.getContact().isBlocked()
+ && account.getXmppConnection().getFeatures().mam()
+ && singleMode) {
+ if (query == null) {
+ mMessageArchiveService.query(c);
+ } else {
+ if (query.getConversation() == null) {
+ mMessageArchiveService.query(c, query.getStart(), query.isCatchup());
+ }
+ }
+ }
+ if (joinAfterCreate) {
+ joinMuc(c);
+ }
+ }
+
public void archiveConversation(Conversation conversation) {
archiveConversation(conversation, true);
}
@@ -29,7 +29,6 @@
package eu.siacs.conversations.ui;
-
import static eu.siacs.conversations.ui.ConversationFragment.REQUEST_DECRYPT_PGP;
import android.Manifest;
@@ -51,22 +50,13 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
-
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.databinding.DataBindingUtil;
-
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import org.openintents.openpgp.util.OpenPgpApi;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OmemoSetting;
@@ -90,8 +80,22 @@ import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.openintents.openpgp.util.OpenPgpApi;
-public class ConversationsActivity extends XmppActivity implements OnConversationSelected, OnConversationArchived, OnConversationsListItemUpdated, OnConversationRead, XmppConnectionService.OnAccountUpdate, XmppConnectionService.OnConversationUpdate, XmppConnectionService.OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnAffiliationChanged {
+public class ConversationsActivity extends XmppActivity
+ implements OnConversationSelected,
+ OnConversationArchived,
+ OnConversationsListItemUpdated,
+ OnConversationRead,
+ XmppConnectionService.OnAccountUpdate,
+ XmppConnectionService.OnConversationUpdate,
+ XmppConnectionService.OnRosterUpdate,
+ OnUpdateBlocklist,
+ XmppConnectionService.OnShowErrorToast,
+ XmppConnectionService.OnAffiliationChanged {
public static final String ACTION_VIEW_CONVERSATION = "eu.siacs.conversations.action.VIEW";
public static final String EXTRA_CONVERSATION = "conversationUuid";
@@ -104,19 +108,18 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
public static final String POST_ACTION_RECORD_VOICE = "record_voice";
public static final String EXTRA_TYPE = "type";
- private static final List<String> VIEW_AND_SHARE_ACTIONS = Arrays.asList(
- ACTION_VIEW_CONVERSATION,
- Intent.ACTION_SEND,
- Intent.ACTION_SEND_MULTIPLE
- );
+ private static final List<String> VIEW_AND_SHARE_ACTIONS =
+ Arrays.asList(
+ ACTION_VIEW_CONVERSATION, Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE);
public static final int REQUEST_OPEN_MESSAGE = 0x9876;
public static final int REQUEST_PLAY_PAUSE = 0x5432;
-
- //secondary fragment (when holding the conversation, must be initialized before refreshing the overview fragment
- private static final @IdRes
- int[] FRAGMENT_ID_NOTIFICATION_ORDER = {R.id.secondary_fragment, R.id.main_fragment};
+ // secondary fragment (when holding the conversation, must be initialized before refreshing the
+ // overview fragment
+ private static final @IdRes int[] FRAGMENT_ID_NOTIFICATION_ORDER = {
+ R.id.secondary_fragment, R.id.main_fragment
+ };
private final PendingItem<Intent> pendingViewIntent = new PendingItem<>();
private final PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
private ActivityConversationsBinding binding;
@@ -125,7 +128,9 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
private static boolean isViewOrShareIntent(Intent i) {
Log.d(Config.LOGTAG, "action: " + (i == null ? null : i.getAction()));
- return i != null && VIEW_AND_SHARE_ACTIONS.contains(i.getAction()) && i.hasExtra(EXTRA_CONVERSATION);
+ return i != null
+ && VIEW_AND_SHARE_ACTIONS.contains(i.getAction())
+ && i.hasExtra(EXTRA_CONVERSATION);
}
private static Intent createLauncherIntent(Context context) {
@@ -169,7 +174,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
invalidateActionBarTitle();
- if (binding.secondaryFragment != null && ConversationFragment.getConversation(this) == null) {
+ if (binding.secondaryFragment != null
+ && ConversationFragment.getConversation(this) == null) {
Conversation conversation = ConversationsOverviewFragment.getSuggestion(this);
if (conversation != null) {
openConversation(conversation, null);
@@ -182,7 +188,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
return performRedirectIfNecessary(null, noAnimation);
}
- private boolean performRedirectIfNecessary(final Conversation ignore, final boolean noAnimation) {
+ private boolean performRedirectIfNecessary(
+ final Conversation ignore, final boolean noAnimation) {
if (xmppConnectionService == null) {
return false;
}
@@ -192,12 +199,13 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (noAnimation) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
}
- runOnUiThread(() -> {
- startActivity(intent);
- if (noAnimation) {
- overridePendingTransition(0, 0);
- }
- });
+ runOnUiThread(
+ () -> {
+ startActivity(intent);
+ if (noAnimation) {
+ overridePendingTransition(0, 0);
+ }
+ });
}
return mRedirectInProcess.get();
}
@@ -219,7 +227,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
private String getBatteryOptimizationPreferenceKey() {
- @SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
+ @SuppressLint("HardwareIds")
+ String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
return "show_battery_optimization" + (device == null ? "" : device);
}
@@ -228,20 +237,31 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
private boolean openBatteryOptimizationDialogIfNeeded() {
- if (isOptimizingBattery() && getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true)) {
+ if (isOptimizingBattery()
+ && getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true)) {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.battery_optimizations_enabled);
- builder.setMessage(getString(R.string.battery_optimizations_enabled_dialog, getString(R.string.app_name)));
- builder.setPositiveButton(R.string.next, (dialog, which) -> {
- final Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- final Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- try {
- startActivityForResult(intent, REQUEST_BATTERY_OP);
- } catch (final ActivityNotFoundException e) {
- Toast.makeText(this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
- }
- });
+ builder.setMessage(
+ getString(
+ R.string.battery_optimizations_enabled_dialog,
+ getString(R.string.app_name)));
+ builder.setPositiveButton(
+ R.string.next,
+ (dialog, which) -> {
+ final Intent intent =
+ new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ final Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (final ActivityNotFoundException e) {
+ Toast.makeText(
+ this,
+ R.string.device_does_not_support_battery_op,
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+ });
builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
@@ -252,8 +272,12 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
private void requestNotificationPermissionIfNeeded() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQUEST_POST_NOTIFICATION);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(
+ new String[] {Manifest.permission.POST_NOTIFICATIONS},
+ REQUEST_POST_NOTIFICATION);
}
}
@@ -271,9 +295,10 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
}
- private boolean processViewIntent(Intent intent) {
+ private boolean processViewIntent(final Intent intent) {
final String uuid = intent.getStringExtra(EXTRA_CONVERSATION);
- final Conversation conversation = uuid != null ? xmppConnectionService.findConversationByUuid(uuid) : null;
+ final Conversation conversation =
+ uuid != null ? xmppConnectionService.findConversationByUuidReliable(uuid) : null;
if (conversation == null) {
Log.d(Config.LOGTAG, "unable to view conversation with uuid:" + uuid);
return false;
@@ -283,7 +308,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);
if (grantResults.length > 0) {
@@ -397,8 +423,9 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (qrCodeScanMenuItem != null) {
if (isCameraFeatureAvailable()) {
Fragment fragment = getFragmentManager().findFragmentById(R.id.main_fragment);
- boolean visible = getResources().getBoolean(R.bool.show_qr_code_scan)
- && fragment instanceof ConversationsOverviewFragment;
+ boolean visible =
+ getResources().getBoolean(R.bool.show_qr_code_scan)
+ && fragment instanceof ConversationsOverviewFragment;
qrCodeScanMenuItem.setVisible(visible);
} else {
qrCodeScanMenuItem.setVisible(false);
@@ -411,7 +438,9 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
public void onConversationSelected(Conversation conversation) {
clearPendingViewIntent();
if (ConversationFragment.getConversation(this) == conversation) {
- Log.d(Config.LOGTAG, "ignore onConversationSelected() because conversation is already open");
+ Log.d(
+ Config.LOGTAG,
+ "ignore onConversationSelected() because conversation is already open");
return;
}
openConversation(conversation, null);
@@ -424,13 +453,12 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
private void displayToast(final String msg) {
- runOnUiThread(() -> Toast.makeText(ConversationsActivity.this, msg, Toast.LENGTH_SHORT).show());
+ runOnUiThread(
+ () -> Toast.makeText(ConversationsActivity.this, msg, Toast.LENGTH_SHORT).show());
}
@Override
- public void onAffiliationChangedSuccessful(Jid jid) {
-
- }
+ public void onAffiliationChangedSuccessful(Jid jid) {}
@Override
public void onAffiliationChangeFailed(Jid jid, int resId) {
@@ -440,7 +468,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
private void openConversation(Conversation conversation, Bundle extras) {
final FragmentManager fragmentManager = getFragmentManager();
executePendingTransactions(fragmentManager);
- ConversationFragment conversationFragment = (ConversationFragment) fragmentManager.findFragmentById(R.id.secondary_fragment);
+ ConversationFragment conversationFragment =
+ (ConversationFragment) fragmentManager.findFragmentById(R.id.secondary_fragment);
final boolean mainNeedsRefresh;
if (conversationFragment == null) {
mainNeedsRefresh = false;
@@ -456,7 +485,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
fragmentTransaction.commit();
} catch (IllegalStateException e) {
Log.w(Config.LOGTAG, "sate loss while opening conversation", e);
- //allowing state loss is probably fine since view intents et all are already stored and a click can probably be 'ignored'
+ // allowing state loss is probably fine since view intents et all are already
+ // stored and a click can probably be 'ignored'
return;
}
}
@@ -474,14 +504,15 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
try {
fragmentManager.executePendingTransactions();
} catch (final Exception e) {
- Log.e(Config.LOGTAG,"unable to execute pending fragment transactions");
+ Log.e(Config.LOGTAG, "unable to execute pending fragment transactions");
}
}
public boolean onXmppUriClicked(Uri uri) {
XmppUri xmppUri = new XmppUri(uri);
if (xmppUri.isValidJid() && !xmppUri.hasFingerprints()) {
- final Conversation conversation = xmppConnectionService.findUniqueConversationByJid(xmppUri);
+ final Conversation conversation =
+ xmppConnectionService.findUniqueConversationByJid(xmppUri);
if (conversation != null) {
openConversation(conversation, null);
return true;
@@ -540,7 +571,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
@Override
public void onSaveInstanceState(final Bundle savedInstanceState) {
final Intent pendingIntent = pendingViewIntent.peek();
- savedInstanceState.putParcelable("intent", pendingIntent != null ? pendingIntent : getIntent());
+ savedInstanceState.putParcelable(
+ "intent", pendingIntent != null ? pendingIntent : getIntent());
super.onSaveInstanceState(savedInstanceState);
}
@@ -580,7 +612,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
final FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.main_fragment);
- final Fragment secondaryFragment = fragmentManager.findFragmentById(R.id.secondary_fragment);
+ final Fragment secondaryFragment =
+ fragmentManager.findFragmentById(R.id.secondary_fragment);
if (mainFragment != null) {
if (binding.secondaryFragment != null) {
if (mainFragment instanceof ConversationFragment) {
@@ -628,13 +661,12 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
actionBar.setTitle(conversation.getName());
actionBar.setDisplayHomeAsUpEnabled(true);
ToolbarUtils.setActionBarOnClickListener(
- binding.toolbar,
- (v) -> openConversationDetails(conversation)
- );
+ binding.toolbar, (v) -> openConversationDetails(conversation));
return;
}
}
- final Fragment secondaryFragment = fragmentManager.findFragmentById(R.id.secondary_fragment);
+ final Fragment secondaryFragment =
+ fragmentManager.findFragmentById(R.id.secondary_fragment);
if (secondaryFragment instanceof ConversationFragment conversationFragment) {
final Conversation conversation = conversationFragment.getConversation();
if (conversation != null) {
@@ -673,15 +705,21 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
try {
fragmentManager.popBackStack();
} catch (final IllegalStateException e) {
- Log.w(Config.LOGTAG, "state loss while popping back state after archiving conversation", e);
- //this usually means activity is no longer active; meaning on the next open we will run through this again
+ Log.w(
+ Config.LOGTAG,
+ "state loss while popping back state after archiving conversation",
+ e);
+ // this usually means activity is no longer active; meaning on the next open we will
+ // run through this again
}
return;
}
- final Fragment secondaryFragment = fragmentManager.findFragmentById(R.id.secondary_fragment);
+ final Fragment secondaryFragment =
+ fragmentManager.findFragmentById(R.id.secondary_fragment);
if (secondaryFragment instanceof ConversationFragment) {
if (((ConversationFragment) secondaryFragment).getConversation() == conversation) {
- Conversation suggestion = ConversationsOverviewFragment.getSuggestion(this, conversation);
+ Conversation suggestion =
+ ConversationsOverviewFragment.getSuggestion(this, conversation);
if (suggestion != null) {
openConversation(suggestion, null);
}
@@ -8,11 +8,11 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
-
import androidx.annotation.NonNull;
+import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
-
+import com.google.common.collect.Iterables;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityShareWithBinding;
@@ -21,7 +21,6 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.xmpp.Jid;
-
import java.util.ArrayList;
import java.util.List;
@@ -112,7 +111,34 @@ public class ShareWithActivity extends XmppActivity
new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
binding.chooseConversationList.setAdapter(mAdapter);
mAdapter.setConversationClickListener((view, conversation) -> share(conversation));
+ final var intent = getIntent();
+ final var shortcutId = intent.getStringExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID);
this.share = new Share();
+ if (shortcutId != null) {
+ final var conversation = shortcutIdToConversation(shortcutId);
+ if (conversation != null) {
+ // we have everything we need. Jump into chat
+ populateShare(intent);
+ share(conversation);
+ }
+ }
+ }
+
+ private String shortcutIdToConversation(final String shortcutId) {
+ final var shortcut =
+ Iterables.tryFind(
+ ShortcutManagerCompat.getDynamicShortcuts(this),
+ si -> si.getId().equals(shortcutId));
+ if (shortcut.isPresent()) {
+ final var extras = shortcut.get().getExtras();
+ if (extras == null) {
+ return null;
+ } else {
+ return extras.getString(ConversationsActivity.EXTRA_CONVERSATION);
+ }
+ } else {
+ return null;
+ }
}
@Override
@@ -137,10 +163,18 @@ public class ShareWithActivity extends XmppActivity
@Override
public void onStart() {
super.onStart();
- Intent intent = getIntent();
+ final Intent intent = getIntent();
if (intent == null) {
return;
}
+ populateShare(intent);
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.populateWithOrderedConversations(
+ mConversations, this.share.uris.isEmpty(), false);
+ }
+ }
+
+ private void populateShare(final Intent intent) {
final String type = intent.getType();
final String action = intent.getAction();
final Uri data = intent.getData();
@@ -165,10 +199,6 @@ public class ShareWithActivity extends XmppActivity
final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
this.share.uris = uris == null ? new ArrayList<>() : uris;
}
- if (xmppConnectionServiceBound) {
- xmppConnectionService.populateWithOrderedConversations(
- mConversations, this.share.uris.isEmpty(), false);
- }
}
@Override
@@ -209,8 +239,12 @@ public class ShareWithActivity extends XmppActivity
mPendingConversation = conversation;
return;
}
+ share(conversation.getUuid());
+ }
+
+ private void share(final String conversation) {
final Intent intent = new Intent(this, ConversationsActivity.class);
- intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
+ intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation);
if (!share.uris.isEmpty()) {
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, share.uris);
@@ -225,7 +259,7 @@ public class ShareWithActivity extends XmppActivity
}
try {
startActivity(intent);
- } catch (SecurityException e) {
+ } catch (final SecurityException e) {
Toast.makeText(
this,
R.string.sharing_application_not_grant_permission,
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+ <share-target android:targetClass="eu.siacs.conversations.ui.ShareWithActivity">
+ <data android:mimeType="*/*" />
+ <category android:name="eu.siacs.conversations.category.SHARE_TARGET" />
+ </share-target>
+</shortcuts>