diff --git a/build.gradle b/build.gradle
index 07a2b292a9..bf4c2a5328 100644
--- a/build.gradle
+++ b/build.gradle
@@ -182,7 +182,7 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.2'
}
-def canonicalVersionCode = 111
+def canonicalVersionCode = 115
def canonicalVersionName = "1.6.2"
def postFixSize = 10
diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java
index 29a668776d..44531d4c4b 100644
--- a/src/org/thoughtcrime/securesms/ApplicationContext.java
+++ b/src/org/thoughtcrime/securesms/ApplicationContext.java
@@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
@@ -305,7 +306,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
- publicChatAPI = new PublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB);
+ GroupDatabase groupDB = DatabaseFactory.getGroupDatabase(this);
+ publicChatAPI = new PublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB, groupDB);
return publicChatAPI;
}
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
index 886d603999..12c8e337ce 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
@@ -213,6 +213,7 @@ import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.ExpirationUtil;
+import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
@@ -227,6 +228,7 @@ import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
+import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
import org.whispersystems.signalservice.loki.protocol.mentions.Mention;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
@@ -458,7 +460,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
if (publicChat != null) {
- ApplicationContext.getInstance(this).getPublicChatAPI().getChannelInfo(publicChat.getChannel(), publicChat.getServer()).success(displayName -> {
+ PublicChatAPI publicChatAPI = ApplicationContext.getInstance(this).getPublicChatAPI();
+ publicChatAPI.getChannelInfo(publicChat.getChannel(), publicChat.getServer()).success(info -> {
+ String groupId = GroupUtil.getEncodedOpenGroupId(publicChat.getId().getBytes());
+
+ publicChatAPI.updateProfileIfNeeded(
+ publicChat.getChannel(),
+ publicChat.getServer(),
+ groupId,
+ info,
+ false);
+
runOnUiThread(ConversationActivity.this::updateSubtitleTextView);
return Unit.INSTANCE;
});
diff --git a/src/org/thoughtcrime/securesms/database/GroupDatabase.java b/src/org/thoughtcrime/securesms/database/GroupDatabase.java
index c14aff87d5..c5bd65e903 100644
--- a/src/org/thoughtcrime/securesms/database/GroupDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/GroupDatabase.java
@@ -6,9 +6,10 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.text.TextUtils;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import android.text.TextUtils;
import com.annimon.stream.Stream;
@@ -21,6 +22,7 @@ import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
+import org.whispersystems.signalservice.loki.database.LokiOpenGroupDatabaseProtocol;
import java.io.Closeable;
import java.io.IOException;
@@ -29,7 +31,7 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-public class GroupDatabase extends Database {
+public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProtocol {
@SuppressWarnings("unused")
private static final String TAG = GroupDatabase.class.getSimpleName();
@@ -240,35 +242,37 @@ public class GroupDatabase extends Database {
notifyConversationListListeners();
}
- public void updateTitle(String groupId, String title) {
+ @Override
+ public void updateTitle(String groupID, String newValue) {
ContentValues contentValues = new ContentValues();
- contentValues.put(TITLE, title);
+ contentValues.put(TITLE, newValue);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
- new String[] {groupId});
+ new String[] {groupID});
- Recipient recipient = Recipient.from(context, Address.fromSerialized(groupId), false);
- recipient.setName(title);
+ Recipient recipient = Recipient.from(context, Address.fromSerialized(groupID), false);
+ recipient.setName(newValue);
}
- public void updateAvatar(String groupId, Bitmap avatar) {
- updateAvatar(groupId, BitmapUtil.toByteArray(avatar));
+ public void updateProfilePicture(String groupID, Bitmap newValue) {
+ updateProfilePicture(groupID, BitmapUtil.toByteArray(newValue));
}
- public void updateAvatar(String groupId, byte[] avatar) {
+ @Override
+ public void updateProfilePicture(String groupID, byte[] newValue) {
long avatarId;
- if (avatar != null) avatarId = Math.abs(new SecureRandom().nextLong());
+ if (newValue != null) avatarId = Math.abs(new SecureRandom().nextLong());
else avatarId = 0;
ContentValues contentValues = new ContentValues(2);
- contentValues.put(AVATAR, avatar);
+ contentValues.put(AVATAR, newValue);
contentValues.put(AVATAR_ID, avatarId);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
- new String[] {groupId});
+ new String[] {groupID});
- Recipient.applyCached(Address.fromSerialized(groupId), recipient -> recipient.setGroupAvatarId(avatarId == 0 ? null : avatarId));
+ Recipient.applyCached(Address.fromSerialized(groupID), recipient -> recipient.setGroupAvatarId(avatarId == 0 ? null : avatarId));
}
public void updateMembers(String groupId, List
members) {
diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
index de227acdc1..f1e7ff8c0d 100644
--- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
+++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
@@ -92,9 +92,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV13 = 34;
private static final int lokiV14_BACKUP_FILES = 35;
private static final int lokiV15 = 36;
- private static final int lokiV16_AUDIO_ATTACHMENT_EXTRAS = 37;
+ private static final int lokiV16 = 37;
- private static final int DATABASE_VERSION = lokiV16_AUDIO_ATTACHMENT_EXTRAS;
+ private static final int DATABASE_VERSION = lokiV16;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -156,6 +156,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreateSessionRequestSentTimestampTableCommand());
db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand());
db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand());
+ db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand());
@@ -633,9 +634,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(SharedSenderKeysDatabase.getCreateOldClosedGroupRatchetTableCommand());
}
- if (oldVersion < lokiV16_AUDIO_ATTACHMENT_EXTRAS) {
- db.execSQL("ALTER TABLE part ADD COLUMN audio_visual_samples BLOB");
- db.execSQL("ALTER TABLE part ADD COLUMN audio_duration INTEGER");
+ if (oldVersion < lokiV16) {
+ db.execSQL("ALTER TABLE part ADD COLUMN audio_visual_samples BLOB");
+ db.execSQL("ALTER TABLE part ADD COLUMN audio_duration INTEGER");
+ db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
}
db.setTransactionSuccessful();
diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
index 520761b536..a7449ca58d 100644
--- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
@@ -159,6 +159,7 @@ public class SignalCommunicationModule {
DatabaseFactory.getLokiPreKeyBundleDatabase(context),
new SessionResetImplementation(context),
DatabaseFactory.getLokiUserDatabase(context),
+ DatabaseFactory.getGroupDatabase(context),
((ApplicationContext)context.getApplicationContext()).broadcaster);
} else {
this.messageSender.setMessagePipe(IncomingMessageObserver.getPipe(), IncomingMessageObserver.getUnidentifiedPipe());
diff --git a/src/org/thoughtcrime/securesms/groups/GroupManager.java b/src/org/thoughtcrime/securesms/groups/GroupManager.java
index 81f6feec33..694627f629 100644
--- a/src/org/thoughtcrime/securesms/groups/GroupManager.java
+++ b/src/org/thoughtcrime/securesms/groups/GroupManager.java
@@ -85,7 +85,7 @@ public class GroupManager {
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses));
if (!mms) {
- groupDatabase.updateAvatar(groupId, avatarBytes);
+ groupDatabase.updateProfilePicture(groupId, avatarBytes);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
} else {
@@ -125,7 +125,7 @@ public class GroupManager {
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>());
- groupDatabase.updateAvatar(groupId, avatarBytes);
+ groupDatabase.updateProfilePicture(groupId, avatarBytes);
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadID);
@@ -148,7 +148,7 @@ public class GroupManager {
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateAdmins(groupId, new LinkedList<>(adminAddresses));
groupDatabase.updateTitle(groupId, name);
- groupDatabase.updateAvatar(groupId, avatarBytes);
+ groupDatabase.updateProfilePicture(groupId, avatarBytes);
if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
diff --git a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java
index 0b2db43157..f2b95a46f0 100644
--- a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java
@@ -95,7 +95,7 @@ public class AvatarDownloadJob extends BaseJob implements InjectableType {
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500);
- database.updateAvatar(groupId, avatar);
+ database.updateProfilePicture(groupId, avatar);
inputStream.close();
}
} catch (BitmapDecodingException | NonSuccessfulResponseCodeException | InvalidMessageException e) {
diff --git a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
index b23313bb0e..8a5cb4ae5c 100644
--- a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
@@ -342,20 +342,18 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val threadID = thread.threadId
val recipient = thread.recipient
val threadDB = DatabaseFactory.getThreadDatabase(this)
- val deleteThread = object : Runnable {
-
- override fun run() {
- AsyncTask.execute {
- val publicChat = DatabaseFactory.getLokiThreadDatabase(this@HomeActivity).getPublicChat(threadID)
- if (publicChat != null) {
- val apiDB = DatabaseFactory.getLokiAPIDatabase(this@HomeActivity)
- apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server)
- apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server)
- ApplicationContext.getInstance(this@HomeActivity).publicChatAPI!!.leave(publicChat.channel, publicChat.server)
- }
- threadDB.deleteConversation(threadID)
- ApplicationContext.getInstance(this@HomeActivity).messageNotifier.updateNotification(this@HomeActivity)
+ val deleteThread = Runnable {
+ AsyncTask.execute {
+ val publicChat = DatabaseFactory.getLokiThreadDatabase(this@HomeActivity).getPublicChat(threadID)
+ if (publicChat != null) {
+ val apiDB = DatabaseFactory.getLokiAPIDatabase(this@HomeActivity)
+ apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server)
+ apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server)
+ apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server)
+ ApplicationContext.getInstance(this@HomeActivity).publicChatAPI!!.leave(publicChat.channel, publicChat.server)
}
+ threadDB.deleteConversation(threadID)
+ ApplicationContext.getInstance(this@HomeActivity).messageNotifier.updateNotification(this@HomeActivity)
}
}
val dialogMessage = if (recipient.isGroupRecipient) R.string.activity_home_leave_group_dialog_message else R.string.activity_home_delete_conversation_dialog_message
diff --git a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt
index fa96d6b2a1..9de15d852b 100644
--- a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt
@@ -82,7 +82,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
private fun update(isAnimated: Boolean) {
pathRowsContainer.removeAllViews()
- if (OnionRequestAPI.paths.count() >= OnionRequestAPI.pathCount) {
+ if (OnionRequestAPI.paths.isNotEmpty()) {
val path = OnionRequestAPI.paths.firstOrNull() ?: return finish()
val dotAnimationRepeatInterval = path.count().toLong() * 1000 + 1000
val pathRows = path.mapIndexed { index, snode ->
diff --git a/src/org/thoughtcrime/securesms/loki/api/BackgroundPollJob.kt b/src/org/thoughtcrime/securesms/loki/api/BackgroundPollJob.kt
index 1cf97b3c64..7081cefe46 100644
--- a/src/org/thoughtcrime/securesms/loki/api/BackgroundPollJob.kt
+++ b/src/org/thoughtcrime/securesms/loki/api/BackgroundPollJob.kt
@@ -50,13 +50,16 @@ class BackgroundPollJob private constructor(parameters: Parameters) : BaseJob(pa
Log.d("Loki", "Performing background poll.")
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val promises = mutableListOf>()
- val promise = SnodeAPI.shared.getMessages(userPublicKey).map { envelopes ->
- envelopes.forEach {
- PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false)
+ if (!TextSecurePreferences.isUsingFCM(context)) {
+ Log.d("Loki", "Not using FCM; polling for contacts and closed groups.")
+ val promise = SnodeAPI.shared.getMessages(userPublicKey).map { envelopes ->
+ envelopes.forEach {
+ PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false)
+ }
}
+ promises.add(promise)
+ promises.addAll(ClosedGroupPoller.shared.pollOnce())
}
- promises.add(promise)
- promises.addAll(ClosedGroupPoller.shared.pollOnce())
val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { it.value }
for (openGroup in openGroups) {
val poller = PublicChatPoller(context, openGroup)
diff --git a/src/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt b/src/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt
index 8095731318..6b710c5ea4 100644
--- a/src/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt
+++ b/src/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki.api
import android.content.Context
import android.database.ContentObserver
+import android.graphics.Bitmap
import android.text.TextUtils
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.bind
@@ -10,8 +11,10 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseContentProviders
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager
+import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
+import org.whispersystems.signalservice.loki.api.opengroups.PublicChatInfo
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
class PublicChatManager(private val context: Context) {
@@ -24,8 +27,8 @@ class PublicChatManager(private val context: Context) {
var areAllCaughtUp = true
refreshChatsAndPollers()
for ((threadID, chat) in chats) {
- val poller = pollers[threadID] ?: PublicChatPoller(context, chat)
- areAllCaughtUp = areAllCaughtUp && poller.isCaughtUp
+ val poller = pollers[threadID]
+ areAllCaughtUp = if (poller != null) areAllCaughtUp && poller.isCaughtUp else true
}
return areAllCaughtUp
}
@@ -56,7 +59,8 @@ class PublicChatManager(private val context: Context) {
}
public fun addChat(server: String, channel: Long): Promise {
- val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI ?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
+ val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI
+ ?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
return groupChatAPI.getAuthToken(server).bind {
groupChatAPI.getChannelInfo(channel, server)
}.map {
@@ -64,12 +68,18 @@ class PublicChatManager(private val context: Context) {
}
}
- public fun addChat(server: String, channel: Long, name: String): PublicChat {
- val chat = PublicChat(channel, server, name, true)
+ public fun addChat(server: String, channel: Long, info: PublicChatInfo): PublicChat {
+ val chat = PublicChat(channel, server, info.displayName, true)
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
+ var profilePicture: Bitmap? = null
// Create the group if we don't have one
if (threadID < 0) {
- val result = GroupManager.createOpenGroup(chat.id, context, null, chat.displayName)
+ if (info.profilePictureURL.isNotEmpty()) {
+ val profilePictureAsByteArray = ApplicationContext.getInstance(context).publicChatAPI
+ ?.downloadOpenGroupProfilePicture(server, info.profilePictureURL)
+ profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray)
+ }
+ val result = GroupManager.createOpenGroup(chat.id, context, profilePicture, chat.displayName)
threadID = result.threadId
}
DatabaseFactory.getLokiThreadDatabase(context).setPublicChat(chat, threadID)
diff --git a/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt
index 374f8b68c6..ca73a77b3e 100644
--- a/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt
+++ b/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt
@@ -46,7 +46,8 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context)
- PublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase)
+ val openGroupDatabase = DatabaseFactory.getGroupDatabase(context)
+ PublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase, openGroupDatabase)
}()
// endregion
diff --git a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt
index 18fae93b87..fc7c0b44e8 100644
--- a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt
@@ -6,7 +6,6 @@ import android.util.Log
import org.thoughtcrime.securesms.database.Database
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.utilities.*
-import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.Snode
import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink
@@ -71,6 +70,10 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
// Open group public keys
private val openGroupPublicKeyTable = "open_group_public_keys"
@JvmStatic val createOpenGroupPublicKeyTableCommand = "CREATE TABLE $openGroupPublicKeyTable ($server STRING PRIMARY KEY, $publicKey INTEGER DEFAULT 0);"
+ // Open group profile picture cache
+ private val openGroupProfilePictureTable = "open_group_avatar_cache"
+ private val openGroupProfilePicture = "open_group_avatar"
+ @JvmStatic val createOpenGroupProfilePictureTableCommand = "CREATE TABLE $openGroupProfilePictureTable ($publicChatID STRING PRIMARY KEY, $openGroupProfilePicture TEXT NULLABLE DEFAULT NULL);"
// region Deprecated
private val deviceLinkCache = "loki_pairing_authorisation_cache"
@@ -114,6 +117,30 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(snodePoolTable, row, "${Companion.dummyKey} = ?", wrap("dummy_key"))
}
+ override fun setOnionRequestPaths(newValue: List>) {
+ // FIXME: This approach assumes either 1 or 2 paths of length 3 each. We should do better than this.
+ val database = databaseHelper.writableDatabase
+ fun set(indexPath: String, snode: Snode) {
+ var snodeAsString = "${snode.address}-${snode.port}"
+ val keySet = snode.publicKeySet
+ if (keySet != null) {
+ snodeAsString += "-${keySet.ed25519Key}-${keySet.x25519Key}"
+ }
+ val row = wrap(mapOf( Companion.indexPath to indexPath, Companion.snode to snodeAsString ))
+ database.insertOrUpdate(onionRequestPathTable, row, "${Companion.indexPath} = ?", wrap(indexPath))
+ }
+ Log.d("Loki", "Persisting onion request paths to database.")
+ clearOnionRequestPaths()
+ if (newValue.count() < 1) { return }
+ val path0 = newValue[0]
+ if (path0.count() != 3) { return }
+ set("0-0", path0[0]); set("0-1", path0[1]); set("0-2", path0[2])
+ if (newValue.count() < 2) { return }
+ val path1 = newValue[1]
+ if (path1.count() != 3) { return }
+ set("1-0", path1[0]); set("1-1", path1[1]); set("1-2", path1[2])
+ }
+
override fun getOnionRequestPaths(): List> {
val database = databaseHelper.readableDatabase
fun get(indexPath: String): Snode? {
@@ -131,10 +158,16 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}
}
}
- val path0Snode0 = get("0-0") ?: return listOf(); val path0Snode1 = get("0-1") ?: return listOf()
- val path0Snode2 = get("0-2") ?: return listOf(); val path1Snode0 = get("1-0") ?: return listOf()
- val path1Snode1 = get("1-1") ?: return listOf(); val path1Snode2 = get("1-2") ?: return listOf()
- return listOf( listOf( path0Snode0, path0Snode1, path0Snode2 ), listOf( path1Snode0, path1Snode1, path1Snode2 ) )
+ val result = mutableListOf>()
+ val path0Snode0 = get("0-0"); val path0Snode1 = get("0-1"); val path0Snode2 = get("0-2")
+ if (path0Snode0 != null && path0Snode1 != null && path0Snode2 != null) {
+ result.add(listOf( path0Snode0, path0Snode1, path0Snode2 ))
+ }
+ val path1Snode0 = get("1-0"); val path1Snode1 = get("1-1"); val path1Snode2 = get("1-2")
+ if (path1Snode0 != null && path1Snode1 != null && path1Snode2 != null) {
+ result.add(listOf( path1Snode0, path1Snode1, path1Snode2 ))
+ }
+ return result
}
override fun clearOnionRequestPaths() {
@@ -147,28 +180,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
delete("1-1"); delete("1-2")
}
- override fun setOnionRequestPaths(newValue: List>) {
- // TODO: Make this work with arbitrary paths
- if (newValue.count() != 2) { return }
- val path0 = newValue[0]
- val path1 = newValue[1]
- if (path0.count() != 3 || path1.count() != 3) { return }
- Log.d("Loki", "Persisting onion request paths to database.")
- val database = databaseHelper.writableDatabase
- fun set(indexPath: String, snode: Snode) {
- var snodeAsString = "${snode.address}-${snode.port}"
- val keySet = snode.publicKeySet
- if (keySet != null) {
- snodeAsString += "-${keySet.ed25519Key}-${keySet.x25519Key}"
- }
- val row = wrap(mapOf( Companion.indexPath to indexPath, Companion.snode to snodeAsString ))
- database.insertOrUpdate(onionRequestPathTable, row, "${Companion.indexPath} = ?", wrap(indexPath))
- }
- set("0-0", path0[0]); set("0-1", path0[1])
- set("0-2", path0[2]); set("1-0", path1[0])
- set("1-1", path1[1]); set("1-2", path1[2])
- }
-
override fun getSwarm(publicKey: String): Set? {
val database = databaseHelper.readableDatabase
return database.get(swarmTable, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) { cursor ->
@@ -343,6 +354,27 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(openGroupPublicKeyTable, row, "${LokiAPIDatabase.server} = ?", wrap(server))
}
+ override fun getOpenGroupProfilePictureURL(group: Long, server: String): String? {
+ val database = databaseHelper.readableDatabase
+ val index = "$server.$group"
+ return database.get(openGroupProfilePictureTable, "$publicChatID = ?", wrap(index)) { cursor ->
+ cursor.getString(openGroupProfilePicture)
+ }?.toString()
+ }
+
+ override fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String) {
+ val database = databaseHelper.writableDatabase
+ val index = "$server.$group"
+ val row = wrap(mapOf(publicChatID to index, openGroupProfilePicture to newValue))
+ database.insertOrUpdate(openGroupProfilePictureTable, row, "$publicChatID = ?", wrap(index))
+ }
+
+ fun clearOpenGroupProfilePictureURL(group: Long, server: String): Boolean {
+ val database = databaseHelper.writableDatabase
+ val index = "$server.$group"
+ return database.delete(openGroupProfilePictureTable, "$publicChatID = ?", arrayOf(index)) > 0
+ }
+
// region Deprecated
override fun getDeviceLinks(publicKey: String): Set {
return setOf()
diff --git a/src/org/thoughtcrime/securesms/loki/views/PathStatusView.kt b/src/org/thoughtcrime/securesms/loki/views/PathStatusView.kt
index e65630c144..77cfbe2505 100644
--- a/src/org/thoughtcrime/securesms/loki/views/PathStatusView.kt
+++ b/src/org/thoughtcrime/securesms/loki/views/PathStatusView.kt
@@ -85,7 +85,7 @@ class PathStatusView : View {
private fun handlePathsBuiltEvent() { update() }
private fun update() {
- if (OnionRequestAPI.paths.count() >= OnionRequestAPI.pathCount) {
+ if (OnionRequestAPI.paths.isNotEmpty()) {
setBackgroundResource(R.drawable.accent_dot)
mainColor = resources.getColorWithID(R.color.accent, context.theme)
sessionShadowColor = resources.getColorWithID(R.color.accent, context.theme)
diff --git a/src/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt b/src/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt
index 311739d44d..85105609c8 100644
--- a/src/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt
+++ b/src/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt
@@ -68,32 +68,30 @@ class ProfilePictureView : RelativeLayout {
return result ?: publicKey
}
}
- if (recipient.isGroupRecipient) {
- if ("Session Public Chat" == recipient.name) {
- publicKey = ""
- displayName = ""
- additionalPublicKey = null
- isRSSFeed = true
- } else {
- val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toMutableList() ?: mutableListOf()
- users.remove(TextSecurePreferences.getLocalNumber(context))
- val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
- if (masterPublicKey != null) {
- users.remove(masterPublicKey)
- }
- val randomUsers = users.sorted().toMutableList() // Sort to provide a level of stability
- if (users.count() == 1) {
- val userPublicKey = TextSecurePreferences.getLocalNumber(context)
- randomUsers.add(0, userPublicKey) // Ensure the current user is at the back visually
- }
- val pk = randomUsers.getOrNull(0) ?: ""
- publicKey = pk
- displayName = getUserDisplayName(pk)
- val apk = randomUsers.getOrNull(1) ?: ""
- additionalPublicKey = apk
- additionalDisplayName = getUserDisplayName(apk)
- isRSSFeed = recipient.name == "Loki News" || recipient.name == "Session Updates"
+ fun isOpenGroupWithProfilePicture(recipient: Recipient): Boolean {
+ return recipient.isOpenGroupRecipient && recipient.groupAvatarId != null
+ }
+ if (recipient.isGroupRecipient && !isOpenGroupWithProfilePicture(recipient)) {
+ val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toMutableList() ?: mutableListOf()
+ users.remove(TextSecurePreferences.getLocalNumber(context))
+ val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
+ if (masterPublicKey != null) {
+ users.remove(masterPublicKey)
}
+ val randomUsers = users.sorted().toMutableList() // Sort to provide a level of stability
+ if (users.count() == 1) {
+ val userPublicKey = TextSecurePreferences.getLocalNumber(context)
+ randomUsers.add(0, userPublicKey) // Ensure the current user is at the back visually
+ }
+ val pk = randomUsers.getOrNull(0) ?: ""
+ publicKey = pk
+ displayName = getUserDisplayName(pk)
+ val apk = randomUsers.getOrNull(1) ?: ""
+ additionalPublicKey = apk
+ additionalDisplayName = getUserDisplayName(apk)
+ isRSSFeed = recipient.name == "Loki News" ||
+ recipient.name == "Session Updates" ||
+ recipient.name == "Session Public Chat"
} else {
publicKey = recipient.address.toString()
displayName = getUserDisplayName(publicKey)
diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java
index 7fd5b3517c..7237cf9795 100644
--- a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java
+++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java
@@ -84,13 +84,17 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
ContactPhoto contactPhoto = recipient.getContactPhoto();
if (contactPhoto != null) {
try {
- setLargeIcon(GlideApp.with(context.getApplicationContext())
- .load(contactPhoto)
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .circleCrop()
- .submit(context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
- context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height))
- .get());
+ // AC: For some reason, if not use ".asBitmap()" method, the returned BitmapDrawable
+ // wraps a recycled bitmap and leads to a crash.
+ Bitmap iconBitmap = GlideApp.with(context.getApplicationContext())
+ .asBitmap()
+ .load(contactPhoto)
+ .diskCacheStrategy(DiskCacheStrategy.ALL)
+ .circleCrop()
+ .submit(context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
+ context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height))
+ .get();
+ setLargeIcon(iconBitmap);
} catch (InterruptedException | ExecutionException e) {
Log.w(TAG, e);
setLargeIcon(getPlaceholderDrawable(context, recipient));
diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java
index 530aa6ecb1..24c44b19ac 100644
--- a/src/org/thoughtcrime/securesms/recipients/Recipient.java
+++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java
@@ -419,6 +419,10 @@ public class Recipient implements RecipientModifiedListener {
return address.isGroup();
}
+ public boolean isOpenGroupRecipient() {
+ return address.isOpenGroup();
+ }
+
public boolean isMmsGroupRecipient() {
return address.isMmsGroup();
}
@@ -505,6 +509,11 @@ public class Recipient implements RecipientModifiedListener {
if (notify) notifyListeners();
}
+ @Nullable
+ public synchronized Long getGroupAvatarId() {
+ return groupAvatarId;
+ }
+
public synchronized @Nullable Uri getMessageRingtone() {
if (messageRingtone != null && messageRingtone.getScheme() != null && messageRingtone.getScheme().startsWith("file")) {
return null;