diff --git a/build.gradle b/build.gradle index e09c04f460..f7ce368535 100644 --- a/build.gradle +++ b/build.gradle @@ -182,8 +182,8 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.2' } -def canonicalVersionCode = 110 -def canonicalVersionName = "1.6.1" +def canonicalVersionCode = 111 +def canonicalVersionName = "1.6.2" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, diff --git a/res/layout-sw400dp/activity_restore.xml b/res/layout-sw400dp/activity_restore.xml index bb241b9abf..6f3535ede0 100644 --- a/res/layout-sw400dp/activity_restore.xml +++ b/res/layout-sw400dp/activity_restore.xml @@ -34,12 +34,15 @@ style="@style/SessionEditText" android:id="@+id/mnemonicEditText" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="28dp" - android:paddingBottom="28dp" + android:layout_height="80dp" android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginTop="12dp" android:layout_marginRight="@dimen/very_large_spacing" + android:paddingTop="0dp" + android:paddingBottom="0dp" + android:gravity="center_vertical" + android:inputType="textMultiLine" + android:maxLines="2" android:hint="@string/activity_restore_seed_edit_text_hint" /> oldClosedGroupRatchetTable + ClosedGroupRatchetCollectionType.Current -> currentClosedGroupRatchetTable + } + } + // region Ratchets & Sender Keys - override fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String): ClosedGroupRatchet? { + override fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, collection: ClosedGroupRatchetCollectionType): ClosedGroupRatchet? { val database = databaseHelper.readableDatabase val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" - return database.get(closedGroupRatchetTable, query, arrayOf( groupPublicKey, senderPublicKey )) { cursor -> + return database.get(getTable(collection), query, arrayOf( groupPublicKey, senderPublicKey )) { cursor -> val chainKey = cursor.getString(Companion.chainKey) val keyIndex = cursor.getInt(Companion.keyIndex) val messageKeys = cursor.getString(Companion.messageKeys).split("-") @@ -44,7 +57,7 @@ class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) : } } - override fun setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet) { + override fun setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, collection: ClosedGroupRatchetCollectionType) { val database = databaseHelper.writableDatabase val values = ContentValues() values.put(Companion.closedGroupPublicKey, groupPublicKey) @@ -53,23 +66,33 @@ class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) : values.put(Companion.keyIndex, ratchet.keyIndex) values.put(Companion.messageKeys, ratchet.messageKeys.joinToString("-")) val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" - database.insertOrUpdate(closedGroupRatchetTable, values, query, arrayOf( groupPublicKey, senderPublicKey )) + database.insertOrUpdate(getTable(collection), values, query, arrayOf( groupPublicKey, senderPublicKey )) } - override fun removeAllClosedGroupRatchets(groupPublicKey: String) { + override fun removeAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType) { val database = databaseHelper.writableDatabase val query = "${Companion.closedGroupPublicKey} = ?" - database.delete(closedGroupRatchetTable, query, arrayOf( groupPublicKey )) + database.delete(getTable(collection), query, arrayOf( groupPublicKey )) } - override fun getAllClosedGroupSenderKeys(groupPublicKey: String): Set { + override fun getAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set> { val database = databaseHelper.readableDatabase val query = "${Companion.closedGroupPublicKey} = ?" - return database.getAll(closedGroupRatchetTable, query, arrayOf( groupPublicKey )) { cursor -> + return database.getAll(getTable(collection), query, arrayOf( groupPublicKey )) { cursor -> val chainKey = cursor.getString(Companion.chainKey) val keyIndex = cursor.getInt(Companion.keyIndex) + val messageKeys = cursor.getString(Companion.messageKeys).split("-") val senderPublicKey = cursor.getString(Companion.senderPublicKey) - ClosedGroupSenderKey(Hex.fromStringCondensed(chainKey), keyIndex, Hex.fromStringCondensed(senderPublicKey)) + val ratchet = ClosedGroupRatchet(chainKey, keyIndex, messageKeys) + Pair(senderPublicKey, ratchet) + }.toSet() + } + + override fun getAllClosedGroupSenderKeys(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set { + return getAllClosedGroupRatchets(groupPublicKey, collection).map { pair -> + val senderPublicKey = pair.first + val ratchet = pair.second + ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(senderPublicKey)) }.toSet() } // endregion diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index 9050e39a80..bed24db122 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -27,6 +27,7 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext import org.whispersystems.signalservice.loki.api.SnodeAPI import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchet +import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupSenderKey import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysImplementation import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey @@ -151,8 +152,15 @@ object ClosedGroupsProtocol { job.setContext(context) job.onRun() // Run the job immediately } + val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + for (pair in allOldRatchets) { + val senderPublicKey = pair.first + val ratchet = pair.second + val collection = ClosedGroupRatchetCollectionType.Old + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection) + } // Delete all ratchets (it's important that this happens * after * sending out the update) - sskDatabase.removeAllClosedGroupRatchets(groupPublicKey) + sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) // Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and // send it out to all members (minus the removed ones) using established channels. if (isUserLeaving) { @@ -197,7 +205,7 @@ object ClosedGroupsProtocol { // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, newMembers) // Send closed group update messages to the new members using established channels - var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey); + var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) allSenderKeys = allSenderKeys.union(newSenderKeys) for (member in newMembers) { @Suppress("NAME_SHADOWING") @@ -208,7 +216,7 @@ object ClosedGroupsProtocol { ApplicationContext.getInstance(context).jobManager.add(job) } } else { - val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey); + val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, allSenderKeys, membersAsData, adminsAsData) val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) @@ -287,7 +295,7 @@ object ClosedGroupsProtocol { senderKeys.forEach { senderKey -> if (!members.contains(senderKey.publicKey.toHexString())) { return@forEach } val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet, ClosedGroupRatchetCollectionType.Current) } // Sort out any discrepancies between the provided sender keys and what's required val missingSenderKeys = members.toSet().subtract(senderKeys.map { Hex.toStringCondensed(it.publicKey) }) @@ -357,7 +365,7 @@ object ClosedGroupsProtocol { // Store the ratchets for any new members (it's important that this happens before the code below) senderKeys.forEach { senderKey -> val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet, ClosedGroupRatchetCollectionType.Current) } // Delete all ratchets and either: // • Send out the user's new ratchet using established channels if other members of the group left or were removed @@ -366,7 +374,14 @@ object ClosedGroupsProtocol { val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() val wasSenderRemoved = !members.contains(senderPublicKey) if (wasAnyUserRemoved) { - sskDatabase.removeAllClosedGroupRatchets(groupPublicKey) + val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + for (pair in allOldRatchets) { + @Suppress("NAME_SHADOWING") val senderPublicKey = pair.first + val ratchet = pair.second + val collection = ClosedGroupRatchetCollectionType.Old + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection) + } + sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) if (wasCurrentUserRemoved) { sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) groupDB.setActive(groupID, false) @@ -416,7 +431,7 @@ object ClosedGroupsProtocol { // Respond to the request Log.d("Loki", "Responding to sender key request from: $senderPublicKey.") ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) - val userRatchet = DatabaseFactory.getSSKDatabase(context).getClosedGroupRatchet(groupPublicKey, userPublicKey) + val userRatchet = DatabaseFactory.getSSKDatabase(context).getClosedGroupRatchet(groupPublicKey, userPublicKey, ClosedGroupRatchetCollectionType.Current) ?: SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) @@ -441,7 +456,7 @@ object ClosedGroupsProtocol { // Store the sender key Log.d("Loki", "Received a sender key from: $senderPublicKey.") val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet) + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, ClosedGroupRatchetCollectionType.Current) } @JvmStatic diff --git a/src/org/thoughtcrime/securesms/loki/views/FakeChatView.kt b/src/org/thoughtcrime/securesms/loki/views/FakeChatView.kt index 017f55d353..5f84df4913 100644 --- a/src/org/thoughtcrime/securesms/loki/views/FakeChatView.kt +++ b/src/org/thoughtcrime/securesms/loki/views/FakeChatView.kt @@ -18,8 +18,8 @@ class FakeChatView : ScrollView { // region Settings private val spacing = context.resources.getDimension(R.dimen.medium_spacing) - private val startDelay: Long = 2000 - private val delayBetweenMessages: Long = 3000 + private val startDelay: Long = 1000 + private val delayBetweenMessages: Long = 1500 private val animationDuration: Long = 400 // endregion