From 51222738df8bf4634bdcf614e53dac16815df1fd Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 14 Jun 2023 14:58:04 -0300 Subject: [PATCH] Remove avatar color from CallLink table. --- .../securesms/jobs/PreKeysSyncJobTest.kt | 20 +++++------ .../securesms/testing/BobClient.kt | 5 +++ .../thoughtcrime/securesms/MainActivity.java | 1 + .../securesms/calls/links/CallLinks.kt | 8 +++-- .../securesms/calls/links/SignalCallRow.kt | 5 +-- .../links/create/CreateCallLinkRepository.kt | 6 ++-- .../links/create/CreateCallLinkViewModel.kt | 7 ++-- .../links/details/CallLinkDetailsFragment.kt | 3 +- .../components/webrtc/CallLinkInfoSheet.kt | 4 +-- .../webrtc/WebRtcCallViewModel.java | 9 +++-- .../components/webrtc/WebRtcControls.java | 16 ++++++--- .../securesms/database/CallLinkTable.kt | 34 +++++++++---------- .../securesms/database/RecipientTable.kt | 5 ++- .../securesms/database/ThreadTable.kt | 3 +- .../helpers/SignalDatabaseMigrations.kt | 7 +++- .../V197_DropAvatarColorFromCallLinks.kt | 18 ++++++++++ .../messages/SyncMessageProcessor.kt | 11 +----- .../securesms/recipients/LiveRecipient.java | 9 ++--- .../recipients/RecipientDetails.java | 13 +++---- .../securesms/util/CommunicationActions.java | 16 ++++----- .../securesms/util/FeatureFlags.java | 8 ++--- .../database/RecipientDatabaseTestUtils.kt | 3 +- 22 files changed, 115 insertions(+), 96 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V197_DropAvatarColorFromCallLinks.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJobTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJobTest.kt index 6b053b5bc1..52a2459645 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJobTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJobTest.kt @@ -21,8 +21,8 @@ import org.thoughtcrime.securesms.testing.assertIsNot import org.thoughtcrime.securesms.testing.parsedRequestBody import org.thoughtcrime.securesms.testing.success import org.whispersystems.signalservice.api.push.SignedPreKeyEntity +import org.whispersystems.signalservice.internal.push.OneTimePreKeyCounts import org.whispersystems.signalservice.internal.push.PreKeyState -import org.whispersystems.signalservice.internal.push.PreKeyStatus @RunWith(AndroidJUnit4::class) class PreKeysSyncJobTest { @@ -106,8 +106,8 @@ class PreKeysSyncJobTest { val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers( - Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) }, - Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(100)) } + Get("/v2/keys?identity=aci") { MockResponse().success(OneTimePreKeyCounts(100, 100)) }, + Get("/v2/keys?identity=pni") { MockResponse().success(OneTimePreKeyCounts(100, 100)) } ) // WHEN @@ -133,7 +133,7 @@ class PreKeysSyncJobTest { val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers( - Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) }, + Get("/v2/keys?identity=aci") { MockResponse().success(OneTimePreKeyCounts(100, 100)) }, Put("/v2/keys/signed?identity=pni") { MockResponse().success() } ) @@ -157,15 +157,15 @@ class PreKeysSyncJobTest { val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId - val currentNextAciPreKeyId = aciPreKeyMeta.nextOneTimePreKeyId - val currentNextPniPreKeyId = pniPreKeyMeta.nextOneTimePreKeyId + val currentNextAciPreKeyId = aciPreKeyMeta.nextEcOneTimePreKeyId + val currentNextPniPreKeyId = pniPreKeyMeta.nextEcOneTimePreKeyId lateinit var aciPreKeyStateRequest: PreKeyState lateinit var pniPreKeyStateRequest: PreKeyState InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers( - Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(5)) }, - Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(5)) }, + Get("/v2/keys?identity=aci") { MockResponse().success(OneTimePreKeyCounts(5, 5)) }, + Get("/v2/keys?identity=pni") { MockResponse().success(OneTimePreKeyCounts(5, 5)) }, Put("/v2/keys/?identity=aci") { r -> aciPreKeyStateRequest = r.parsedRequestBody() MockResponse().success() @@ -184,8 +184,8 @@ class PreKeysSyncJobTest { aciPreKeyMeta.activeSignedPreKeyId assertIsNot currentAciKeyId pniPreKeyMeta.activeSignedPreKeyId assertIsNot currentPniKeyId - aciPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextAciPreKeyId - pniPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextPniPreKeyId + aciPreKeyMeta.nextEcOneTimePreKeyId assertIsNot currentNextAciPreKeyId + pniPreKeyMeta.nextEcOneTimePreKeyId assertIsNot currentNextPniPreKeyId ApplicationDependencies.getProtocolStore().aci().identityKeyPair.publicKey.let { aciIdentityKey -> aciPreKeyStateRequest.identityKey assertIs aciIdentityKey diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt index e6085b5e43..e53cd147d2 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt @@ -32,6 +32,7 @@ import org.whispersystems.signalservice.api.push.DistributionId import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.internal.push.SignalServiceProtos +import java.lang.UnsupportedOperationException import java.util.Optional import java.util.UUID import java.util.concurrent.locks.ReentrantLock @@ -168,6 +169,10 @@ class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair: override fun getSenderKeySharedWith(distributionId: DistributionId?): MutableSet = throw UnsupportedOperationException() override fun markSenderKeySharedWith(distributionId: DistributionId?, addresses: MutableCollection?) = throw UnsupportedOperationException() override fun clearSenderKeySharedWith(addresses: MutableCollection?) = throw UnsupportedOperationException() + override fun storeLastResortKyberPreKey(kyberPreKeyId: Int, kyberPreKeyRecord: KyberPreKeyRecord) = throw UnsupportedOperationException() + override fun removeKyberPreKey(kyberPreKeyId: Int) = throw UnsupportedOperationException() + override fun loadLastResortKyberPreKeys(): List = throw UnsupportedOperationException() + override fun isMultiDevice(): Boolean = throw UnsupportedOperationException() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java index e74dcf16af..f9812a0696 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java @@ -101,6 +101,7 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot handleGroupLinkInIntent(intent); handleProxyInIntent(intent); handleSignalMeIntent(intent); + handleCallLinkInIntent(intent); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/CallLinks.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/CallLinks.kt index 50115bf725..9d046144a3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/CallLinks.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/CallLinks.kt @@ -21,11 +21,12 @@ import java.net.URLDecoder */ object CallLinks { private const val ROOT_KEY = "key" - private const val LINK_PREFIX = "https://signal.link/call/#key=" + private const val HTTPS_LINK_PREFIX = "https://signal.link/call/#key=" + private const val SNGL_LINK_PREFIX = "sgnl://signal.link/#key=" private val TAG = Log.tag(CallLinks::class.java) - fun url(linkKeyBytes: ByteArray) = "$LINK_PREFIX${CallLinkRootKey(linkKeyBytes)}" + fun url(linkKeyBytes: ByteArray) = "$HTTPS_LINK_PREFIX${CallLinkRootKey(linkKeyBytes)}" fun watchCallLink(roomId: CallLinkRoomId): Observable { return Observable.create { emitter -> @@ -52,7 +53,8 @@ object CallLinks { @JvmStatic fun parseUrl(url: String): CallLinkRootKey? { - if (!url.startsWith(LINK_PREFIX)) { + if (!url.startsWith(HTTPS_LINK_PREFIX) && !url.startsWith(SNGL_LINK_PREFIX)) { + Log.w(TAG, "Invalid url prefix.") return null } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt index bbb90f0781..d844098384 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt @@ -37,7 +37,6 @@ import org.signal.core.ui.Buttons import org.signal.core.ui.theme.SignalTheme import org.signal.ringrtc.CallLinkRootKey import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.conversation.colors.AvatarColor import org.thoughtcrime.securesms.conversation.colors.AvatarColorPair import org.thoughtcrime.securesms.database.CallLinkTable import org.thoughtcrime.securesms.recipients.RecipientId @@ -49,7 +48,6 @@ import java.time.Instant @Preview @Composable private fun SignalCallRowPreview() { - val avatarColor = remember { AvatarColor.random() } val callLink = remember { val credentials = CallLinkCredentials.generate() CallLinkTable.CallLink( @@ -61,8 +59,7 @@ private fun SignalCallRowPreview() { restrictions = org.signal.ringrtc.CallLinkState.Restrictions.NONE, expiration = Instant.MAX, revoked = false - ), - avatarColor = avatarColor + ) ) } SignalTheme(false) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt index bbecb7c436..1738c944cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt @@ -7,7 +7,6 @@ package org.thoughtcrime.securesms.calls.links.create import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers -import org.thoughtcrime.securesms.conversation.colors.AvatarColor import org.thoughtcrime.securesms.database.CallLinkTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies @@ -24,7 +23,7 @@ import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkManager class CreateCallLinkRepository( private val callLinkManager: SignalCallLinkManager = ApplicationDependencies.getSignalCallManager().callLinkManager ) { - fun ensureCallLinkCreated(credentials: CallLinkCredentials, avatarColor: AvatarColor): Single { + fun ensureCallLinkCreated(credentials: CallLinkCredentials): Single { val callLinkRecipientId = Single.fromCallable { SignalDatabase.recipients.getByCallLinkRoomId(credentials.roomId) } @@ -41,8 +40,7 @@ class CreateCallLinkRepository( recipientId = RecipientId.UNKNOWN, roomId = credentials.roomId, credentials = credentials, - state = it.state, - avatarColor = avatarColor + state = it.state ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkViewModel.kt index ecbfffa57f..039e8478ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkViewModel.kt @@ -16,7 +16,6 @@ import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.ringrtc.CallLinkState.Restrictions import org.thoughtcrime.securesms.calls.links.CallLinks import org.thoughtcrime.securesms.calls.links.UpdateCallLinkRepository -import org.thoughtcrime.securesms.conversation.colors.AvatarColorHash import org.thoughtcrime.securesms.database.CallLinkTable import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials @@ -29,7 +28,6 @@ class CreateCallLinkViewModel( private val mutationRepository: UpdateCallLinkRepository = UpdateCallLinkRepository() ) : ViewModel() { private val credentials = CallLinkCredentials.generate() - private val avatarColor = AvatarColorHash.forCallLink(credentials.linkKeyBytes) private val _callLink: MutableState = mutableStateOf( CallLinkTable.CallLink( recipientId = RecipientId.UNKNOWN, @@ -40,8 +38,7 @@ class CreateCallLinkViewModel( restrictions = Restrictions.NONE, revoked = false, expiration = Instant.MAX - ), - avatarColor = avatarColor + ) ) ) @@ -63,7 +60,7 @@ class CreateCallLinkViewModel( } fun commitCallLink(): Single { - return repository.ensureCallLinkCreated(credentials, avatarColor) + return repository.ensureCallLinkCreated(credentials) } fun setApproveAllMembers(approveAllMembers: Boolean): Single { diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt index 2d670aa4d2..b3075bf518 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt @@ -200,8 +200,7 @@ private fun CallLinkDetailsPreview() { revoked = false, restrictions = Restrictions.NONE, expiration = Instant.MAX - ), - avatarColor = avatarColor + ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallLinkInfoSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallLinkInfoSheet.kt index 09404563c8..12eae1757a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallLinkInfoSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallLinkInfoSheet.kt @@ -65,7 +65,6 @@ import org.thoughtcrime.securesms.calls.links.SignalCallRow import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsViewModel import org.thoughtcrime.securesms.components.AvatarImageView import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment -import org.thoughtcrime.securesms.conversation.colors.AvatarColor import org.thoughtcrime.securesms.database.CallLinkTable import org.thoughtcrime.securesms.events.WebRtcViewModel import org.thoughtcrime.securesms.recipients.Recipient @@ -209,8 +208,7 @@ private fun SheetPreview() { linkKeyBytes = byteArrayOf(1, 2, 3, 4, 5), adminPassBytes = byteArrayOf(1, 2, 3, 4, 5) ), - state = SignalCallLinkState(), - avatarColor = AvatarColor.random() + state = SignalCallLinkState() ), participants = listOf(Recipient.UNKNOWN).toImmutableList(), onShareLinkClicked = {}, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java index 76f50d234f..756675be98 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java @@ -265,7 +265,8 @@ public class WebRtcCallViewModel extends ViewModel { webRtcViewModel.getActiveDevice(), webRtcViewModel.getAvailableDevices(), webRtcViewModel.getRemoteDevicesCount().orElse(0), - webRtcViewModel.getParticipantLimit()); + webRtcViewModel.getParticipantLimit(), + webRtcViewModel.getRecipient().isCallLink()); if (newState.isInOutgoingRingingMode()) { cancelTimer(); @@ -339,7 +340,8 @@ public class WebRtcCallViewModel extends ViewModel { @NonNull SignalAudioManager.AudioDevice activeDevice, @NonNull Set availableDevices, long remoteDevicesCount, - @Nullable Long participantLimit) + @Nullable Long participantLimit, + boolean isCallLink) { final WebRtcControls.CallState callState; @@ -407,7 +409,8 @@ public class WebRtcCallViewModel extends ViewModel { participantLimit, WebRtcControls.FoldableState.flat(), activeDevice, - availableDevices)); + availableDevices, + isCallLink)); } private @NonNull WebRtcControls updateControlsFoldableState(@NonNull WebRtcControls.FoldableState foldableState, @NonNull WebRtcControls controls) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java index a3b3ec68a6..16e97ef81b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java @@ -27,7 +27,8 @@ public final class WebRtcControls { null, FoldableState.flat(), SignalAudioManager.AudioDevice.NONE, - emptySet()); + emptySet(), + false); private final boolean isRemoteVideoEnabled; private final boolean isLocalVideoEnabled; @@ -40,6 +41,7 @@ public final class WebRtcControls { private final FoldableState foldableState; private final SignalAudioManager.AudioDevice activeDevice; private final Set availableDevices; + private final boolean isCallLink; private WebRtcControls() { this(false, @@ -52,7 +54,8 @@ public final class WebRtcControls { null, FoldableState.flat(), SignalAudioManager.AudioDevice.NONE, - emptySet()); + emptySet(), + false); } WebRtcControls(boolean isLocalVideoEnabled, @@ -65,7 +68,8 @@ public final class WebRtcControls { @Nullable Long participantLimit, @NonNull FoldableState foldableState, @NonNull SignalAudioManager.AudioDevice activeDevice, - @NonNull Set availableDevices) + @NonNull Set availableDevices, + boolean isCallLink) { this.isLocalVideoEnabled = isLocalVideoEnabled; this.isRemoteVideoEnabled = isRemoteVideoEnabled; @@ -78,6 +82,7 @@ public final class WebRtcControls { this.foldableState = foldableState; this.activeDevice = activeDevice; this.availableDevices = availableDevices; + this.isCallLink = isCallLink; } public @NonNull WebRtcControls withFoldableState(FoldableState foldableState) { @@ -91,7 +96,8 @@ public final class WebRtcControls { participantLimit, foldableState, activeDevice, - availableDevices); + availableDevices, + isCallLink); } boolean displayErrorControls() { @@ -222,7 +228,7 @@ public final class WebRtcControls { } boolean displayRingToggle() { - return isPreJoin() && isGroupCall() && !hasAtLeastOneRemote; + return isPreJoin() && isGroupCall() && !isCallLink && !hasAtLeastOneRemote; } private boolean isError() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt index f20a9bac86..3bcdbd03ec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt @@ -18,7 +18,6 @@ import org.signal.core.util.requireInt import org.signal.core.util.requireLong import org.signal.core.util.requireNonNullBlob import org.signal.core.util.requireNonNullString -import org.signal.core.util.requireString import org.signal.core.util.select import org.signal.core.util.update import org.signal.core.util.withinTransaction @@ -53,7 +52,6 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database const val RESTRICTIONS = "restrictions" const val REVOKED = "revoked" const val EXPIRATION = "expiration" - const val AVATAR_COLOR = "avatar_color" const val RECIPIENT_ID = "recipient_id" //language=sql @@ -67,7 +65,6 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database $RESTRICTIONS INTEGER NOT NULL, $REVOKED INTEGER NOT NULL, $EXPIRATION INTEGER NOT NULL, - $AVATAR_COLOR TEXT NOT NULL, $RECIPIENT_ID INTEGER UNIQUE REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}) ON DELETE CASCADE ) """ @@ -76,7 +73,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database return contentValuesOf( NAME to name, RESTRICTIONS to restrictions.mapToInt(), - EXPIRATION to expiration.toEpochMilli(), + EXPIRATION to if (expiration == Instant.MAX) -1L else expiration.toEpochMilli(), REVOKED to revoked ) } @@ -94,7 +91,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database callLink: CallLink ) { writableDatabase.withinTransaction { db -> - val recipientId = SignalDatabase.recipients.getOrInsertFromCallLinkRoomId(callLink.roomId, callLink.avatarColor) + val recipientId = SignalDatabase.recipients.getOrInsertFromCallLinkRoomId(callLink.roomId) db .insertInto(TABLE_NAME) @@ -174,8 +171,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database linkKeyBytes = callLinkRootKey.keyBytes, adminPassBytes = null ), - state = SignalCallLinkState(), - avatarColor = AvatarColorHash.forCallLink(callLinkRootKey.keyBytes) + state = SignalCallLinkState() ) insertCallLink(link) @@ -194,8 +190,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database recipientId = RecipientId.UNKNOWN, roomId = callLinkRoomId, credentials = null, - state = SignalCallLinkState(), - avatarColor = AvatarColor.random() + state = SignalCallLinkState() ) insertCallLink(link) return getCallLinkByRoomId(callLinkRoomId)!! @@ -327,8 +322,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database RECIPIENT_ID to data.recipientId.takeIf { it != RecipientId.UNKNOWN }?.toLong(), ROOM_ID to data.roomId.serialize(), ROOT_KEY to data.credentials?.linkKeyBytes, - ADMIN_KEY to data.credentials?.adminPassBytes, - AVATAR_COLOR to data.avatarColor.serialize() + ADMIN_KEY to data.credentials?.adminPassBytes ).apply { putAll(data.state.serialize()) } @@ -356,9 +350,14 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database name = data.requireNonNullString(NAME), restrictions = data.requireInt(RESTRICTIONS).mapToRestrictions(), revoked = data.requireBoolean(REVOKED), - expiration = Instant.ofEpochMilli(data.requireLong(EXPIRATION)).truncatedTo(ChronoUnit.DAYS) - ), - avatarColor = AvatarColor.deserialize(data.requireString(AVATAR_COLOR)) + expiration = data.requireLong(EXPIRATION).let { + if (it == -1L) { + Instant.MAX + } else { + Instant.ofEpochMilli(it).truncatedTo(ChronoUnit.DAYS) + } + } + ) ) } @@ -375,9 +374,10 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database val recipientId: RecipientId, val roomId: CallLinkRoomId, val credentials: CallLinkCredentials?, - val state: SignalCallLinkState, - val avatarColor: AvatarColor - ) + val state: SignalCallLinkState + ) { + val avatarColor: AvatarColor = credentials?.let { AvatarColorHash.forCallLink(it.linkKeyBytes) } ?: AvatarColor.UNKNOWN + } override fun remapRecipient(fromId: RecipientId, toId: RecipientId) { writableDatabase.update(TABLE_NAME) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index ecafc788a6..c3746567c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -568,15 +568,14 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da ).recipientId } - fun getOrInsertFromCallLinkRoomId(callLinkRoomId: CallLinkRoomId, avatarColor: AvatarColor): RecipientId { + fun getOrInsertFromCallLinkRoomId(callLinkRoomId: CallLinkRoomId): RecipientId { return getOrInsertByColumn( CALL_LINK_ROOM_ID, callLinkRoomId.serialize(), contentValuesOf( GROUP_TYPE to GroupType.CALL_LINK.id, CALL_LINK_ROOM_ID to callLinkRoomId.serialize(), - PROFILE_SHARING to 1, - AVATAR_COLOR to avatarColor.serialize() + PROFILE_SHARING to 1 ) ).recipientId } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt index 6f86db5b99..4c94ab91ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -1794,7 +1794,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa recipientSettings, null, false, - group.isActive + group.isActive, + null ) Recipient(recipientId, details, false) } ?: Recipient.live(recipientId).get() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 90587b5938..3adac0194c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V193_BackCallLinksW import org.thoughtcrime.securesms.database.helpers.migration.V194_KyberPreKeyMigration import org.thoughtcrime.securesms.database.helpers.migration.V195_GroupMemberForeignKeyMigration import org.thoughtcrime.securesms.database.helpers.migration.V196_BackCallLinksWithRecipientV2 +import org.thoughtcrime.securesms.database.helpers.migration.V197_DropAvatarColorFromCallLinks /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -60,7 +61,7 @@ object SignalDatabaseMigrations { val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass) - const val DATABASE_VERSION = 196 + const val DATABASE_VERSION = 197 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -255,6 +256,10 @@ object SignalDatabaseMigrations { if (oldVersion < 196) { V196_BackCallLinksWithRecipientV2.migrate(context, db, oldVersion, newVersion) } + + if (oldVersion < 197) { + V197_DropAvatarColorFromCallLinks.migrate(context, db, oldVersion, newVersion) + } } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V197_DropAvatarColorFromCallLinks.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V197_DropAvatarColorFromCallLinks.kt new file mode 100644 index 0000000000..c0b8306041 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V197_DropAvatarColorFromCallLinks.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Because getting the color is a simple modulo operation, there is no need to store it in the database. + */ +object V197_DropAvatarColorFromCallLinks : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE call_link DROP COLUMN avatar_color") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index 1003f36d6a..c8d9045e31 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -8,13 +8,11 @@ import org.signal.core.util.orNull import org.signal.libsignal.protocol.util.Pair import org.signal.ringrtc.CallException import org.signal.ringrtc.CallLinkRootKey -import org.signal.ringrtc.CallLinkState import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.attachments.TombstoneAttachment import org.thoughtcrime.securesms.components.emoji.EmojiUtil import org.thoughtcrime.securesms.contactshare.Contact -import org.thoughtcrime.securesms.conversation.colors.AvatarColorHash import org.thoughtcrime.securesms.crypto.SecurityEvent import org.thoughtcrime.securesms.database.CallLinkTable import org.thoughtcrime.securesms.database.CallTable @@ -119,7 +117,6 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMe import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ViewOnceOpen import java.io.IOException -import java.time.Instant import java.util.Optional import java.util.UUID import kotlin.time.Duration.Companion.seconds @@ -1200,13 +1197,7 @@ object SyncMessageProcessor { linkKeyBytes = callLinkRootKey.keyBytes, adminPassBytes = callLinkUpdate.adminPassKey?.toByteArray() ), - state = SignalCallLinkState( - name = "", - restrictions = CallLinkState.Restrictions.UNKNOWN, - revoked = false, - expiration = Instant.MIN - ), - avatarColor = AvatarColorHash.forCallLink(callLinkRootKey.keyBytes) + state = SignalCallLinkState() ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipient.java index 484dcabf34..cf33f9eaaa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipient.java @@ -13,6 +13,7 @@ import com.annimon.stream.Stream; import org.signal.core.util.ThreadUtil; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.database.CallLinkTable; import org.thoughtcrime.securesms.database.DistributionListTables; import org.thoughtcrime.securesms.database.GroupTable; @@ -219,10 +220,10 @@ public final class LiveRecipient { avatarId = Optional.of(groupRecord.get().getAvatarId()); } - return new RecipientDetails(title, null, avatarId, false, false, record.getRegistered(), record, members, false, groupRecord.get().isActive()); + return new RecipientDetails(title, null, avatarId, false, false, record.getRegistered(), record, members, false, groupRecord.get().isActive(), null); } - return new RecipientDetails(null, null, Optional.empty(), false, false, record.getRegistered(), record, null, false, false); + return new RecipientDetails(null, null, Optional.empty(), false, false, record.getRegistered(), record, null, false, false, null); } @WorkerThread @@ -247,10 +248,10 @@ public final class LiveRecipient { if (callLink != null) { String name = callLink.getState().getName(); - return RecipientDetails.forCallLink(name, record); + return RecipientDetails.forCallLink(name, record, callLink.getAvatarColor()); } - return RecipientDetails.forCallLink(null, record); + return RecipientDetails.forCallLink(null, record, AvatarColor.UNKNOWN); } synchronized void set(@NonNull Recipient recipient) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java index 022ffa5898..c6f2c23163 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java @@ -98,7 +98,8 @@ public class RecipientDetails { @NonNull RecipientRecord record, @Nullable List participantIds, boolean isReleaseChannel, - boolean isActiveGroup) + boolean isActiveGroup, + @Nullable AvatarColor avatarColor) { this.groupAvatarId = groupAvatarId; this.systemContactPhoto = Util.uri(record.getSystemContactPhotoUri()); @@ -141,7 +142,7 @@ public class RecipientDetails { this.mentionSetting = record.getMentionSetting(); this.wallpaper = record.getWallpaper(); this.chatColors = record.getChatColors(); - this.avatarColor = record.getAvatarColor(); + this.avatarColor = avatarColor != null ? avatarColor : record.getAvatarColor(); this.about = record.getAbout(); this.aboutEmoji = record.getAboutEmoji(); this.systemProfileName = record.getSystemProfileName(); @@ -227,15 +228,15 @@ public class RecipientDetails { } } - return new RecipientDetails(null, settings.getSystemDisplayName(), Optional.empty(), systemContact, isSelf, registeredState, settings, null, isReleaseChannel, false); + return new RecipientDetails(null, settings.getSystemDisplayName(), Optional.empty(), systemContact, isSelf, registeredState, settings, null, isReleaseChannel, false, null); } public static @NonNull RecipientDetails forDistributionList(String title, @Nullable List members, @NonNull RecipientRecord record) { - return new RecipientDetails(title, null, Optional.empty(), false, false, record.getRegistered(), record, members, false, false); + return new RecipientDetails(title, null, Optional.empty(), false, false, record.getRegistered(), record, members, false, false, null); } - public static @NonNull RecipientDetails forCallLink(String name, @NonNull RecipientRecord record) { - return new RecipientDetails(name, null, Optional.empty(), false, false, record.getRegistered(), record, Collections.emptyList(), false, false); + public static @NonNull RecipientDetails forCallLink(String name, @NonNull RecipientRecord record, @NonNull AvatarColor avatarColor) { + return new RecipientDetails(name, null, Optional.empty(), false, false, record.getRegistered(), record, Collections.emptyList(), false, false, avatarColor); } public static @NonNull RecipientDetails forUnknown() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java index 8b07b5ffb4..14553491c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java @@ -26,15 +26,12 @@ import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; import org.signal.ringrtc.CallLinkRootKey; -import org.signal.ringrtc.CallLinkState; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.calls.links.CallLinks; import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery; import org.thoughtcrime.securesms.conversation.ConversationIntents; -import org.thoughtcrime.securesms.conversation.colors.AvatarColorHash; import org.thoughtcrime.securesms.database.CallLinkTable; -import org.thoughtcrime.securesms.database.CallTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.model.GroupRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; @@ -45,16 +42,12 @@ import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.proxy.ProxyBottomSheetFragment; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials; import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId; -import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkState; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; import org.whispersystems.signalservice.api.push.ServiceId; import java.io.IOException; -import java.time.Instant; import java.util.Optional; public class CommunicationActions { @@ -343,8 +336,7 @@ public class CommunicationActions { public static void handlePotentialCallLinkUrl(@NonNull FragmentActivity activity, @NonNull String potentialUrl) { CallLinkRootKey rootKey = CallLinks.parseUrl(potentialUrl); if (rootKey == null) { - Log.w(TAG, "Failed to parse root key from call link."); - // TODO [alex] -- Display a dialog informing them that the URL was invalid. + Log.w(TAG, "Failed to parse root key from call link"); return; } @@ -356,13 +348,17 @@ public class CommunicationActions { * the user's database if one does not already exist. * * @param fragment The fragment, which will be used for context and permissions routing. - * @param rootKey The root key of the call link. */ public static void startVideoCall(@NonNull Fragment fragment, @NonNull CallLinkRootKey rootKey) { startVideoCall(new FragmentCallContext(fragment), rootKey); } private static void startVideoCall(@NonNull CallContext callContext, @NonNull CallLinkRootKey rootKey) { + if (!FeatureFlags.adHocCalling()) { + Toast.makeText(callContext.getContext(), R.string.CommunicationActions_cant_join_call, Toast.LENGTH_SHORT).show(); + return; + } + SimpleTask.run(() -> { CallLinkRoomId roomId = CallLinkRoomId.fromBytes(rootKey.deriveRoomId()); CallLinkTable.CallLink callLink = SignalDatabase.callLinks().getOrCreateCallLinkByRootKey(rootKey); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 3b49fd92fa..c8591164f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -104,7 +104,7 @@ public final class FeatureFlags { private static final String PAYPAL_RECURRING_DONATIONS = "android.recurringPayPalDonations.3"; private static final String TEXT_FORMATTING = "android.textFormatting.2"; private static final String ANY_ADDRESS_PORTS_KILL_SWITCH = "android.calling.fieldTrial.anyAddressPortsKillSwitch"; - private static final String AD_HOC_CALLING = "android.calling.ad.hoc"; + private static final String AD_HOC_CALLING = "android.calling.ad.hoc.2"; private static final String EDIT_MESSAGE_RECEIVE = "android.editMessage.receive"; private static final String EDIT_MESSAGE_SEND = "android.editMessage.send"; private static final String MAX_ATTACHMENT_COUNT = "android.attachments.maxCount"; @@ -169,13 +169,13 @@ public final class FeatureFlags { EDIT_MESSAGE_RECEIVE, EDIT_MESSAGE_SEND, MAX_ATTACHMENT_COUNT, - MAX_ATTACHMENT_SIZE_MB + MAX_ATTACHMENT_SIZE_MB, + AD_HOC_CALLING ); @VisibleForTesting static final Set NOT_REMOTE_CAPABLE = SetUtil.newHashSet( - PHONE_NUMBER_PRIVACY, - AD_HOC_CALLING + PHONE_NUMBER_PRIVACY ); /** diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt b/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt index b14bfae074..d52fb5d356 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt @@ -158,7 +158,8 @@ object RecipientDatabaseTestUtils { ), participants, isReleaseChannel, - isActive + isActive, + null ), resolved )