diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt
index 92d11c54fc..123c17183f 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt
@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier
import org.thoughtcrime.securesms.util.CommunicationActions
+import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.viewModel
/**
@@ -152,6 +153,17 @@ class AdvancedPrivacySettingsFragment : ComposeFragment() {
getString(R.string.AdvancedPrivacySettingsFragment__sealed_sender_link)
)
}
+
+ override fun onAllowAutomaticVerificationChanged(enabled: Boolean) {
+ viewModel.setAllowAutomaticVerification(enabled)
+ }
+
+ override fun onAutomaticVerificationLearnMoreClick() {
+ CommunicationActions.openBrowserLink(
+ requireContext(),
+ getString(R.string.verify_display_fragment__link)
+ )
+ }
}
}
@@ -163,6 +175,8 @@ private interface AdvancedPrivacySettingsCallbacks {
fun onShowStatusIconForSealedSenderChanged(enabled: Boolean) = Unit
fun onAllowSealedSenderFromAnyoneChanged(enabled: Boolean) = Unit
fun onSealedSenderLearnMoreClick() = Unit
+ fun onAutomaticVerificationLearnMoreClick() = Unit
+ fun onAllowAutomaticVerificationChanged(enabled: Boolean) = Unit
object Empty : AdvancedPrivacySettingsCallbacks
}
@@ -284,6 +298,33 @@ private fun AdvancedPrivacySettingsScreen(
text = sealedSenderSummary
)
}
+
+ if (RemoteConfig.keyTransparency) {
+ item {
+ Dividers.Default()
+ }
+
+ item {
+ val label = buildAnnotatedString {
+ append(stringResource(R.string.preferences_automatic_key_verification_body))
+ append(" ")
+ withLink(
+ LinkAnnotation.Clickable("learn-more", linkInteractionListener = {
+ callbacks.onAutomaticVerificationLearnMoreClick()
+ })
+ ) {
+ append(stringResource(R.string.LearnMoreTextView_learn_more))
+ }
+ }
+
+ Rows.ToggleRow(
+ checked = state.allowAutomaticKeyVerification,
+ text = AnnotatedString(stringResource(R.string.preferences_automatic_key_verification)),
+ label = label,
+ onCheckChanged = callbacks::onAllowAutomaticVerificationChanged
+ )
+ }
+ }
}
}
}
@@ -300,7 +341,8 @@ private fun AdvancedPrivacySettingsScreenPreview() {
censorshipCircumventionEnabled = false,
showSealedSenderStatusIcon = false,
allowSealedSenderFromAnyone = false,
- showProgressSpinner = false
+ showProgressSpinner = false,
+ allowAutomaticKeyVerification = false
),
callbacks = AdvancedPrivacySettingsCallbacks.Empty
)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsState.kt
index 3f5692d2b7..063b44c358 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsState.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsState.kt
@@ -7,7 +7,8 @@ data class AdvancedPrivacySettingsState(
val censorshipCircumventionEnabled: Boolean,
val showSealedSenderStatusIcon: Boolean,
val allowSealedSenderFromAnyone: Boolean,
- val showProgressSpinner: Boolean
+ val showProgressSpinner: Boolean,
+ val allowAutomaticKeyVerification: Boolean
)
enum class CensorshipCircumventionState(val available: Boolean) {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsViewModel.kt
index fef62e3700..be0a9130ca 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsViewModel.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsViewModel.kt
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components.settings.app.privacy.advanced
import android.content.SharedPreferences
import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -9,12 +10,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import org.signal.core.util.concurrent.SignalDispatchers
+import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
import org.thoughtcrime.securesms.keyvalue.SettingsValues
import org.thoughtcrime.securesms.keyvalue.SignalStore
+import org.thoughtcrime.securesms.recipients.Recipient
+import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.SignalE164Util
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
@@ -63,6 +69,18 @@ class AdvancedPrivacySettingsViewModel(
refresh()
}
+ fun setAllowAutomaticVerification(enabled: Boolean) {
+ SignalStore.settings.automaticVerificationEnabled = enabled
+ refresh()
+ viewModelScope.launch(SignalDispatchers.IO) {
+ if (!enabled) {
+ SignalDatabase.recipients.clearAllKeyTransparencyData()
+ }
+ SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
+ StorageSyncHelper.scheduleSyncForDataChange()
+ }
+ }
+
fun refresh() {
store.update { getState().copy(showProgressSpinner = it.showProgressSpinner) }
}
@@ -85,7 +103,8 @@ class AdvancedPrivacySettingsViewModel(
allowSealedSenderFromAnyone = TextSecurePreferences.isUniversalUnidentifiedAccess(
AppDependencies.application
),
- false
+ showProgressSpinner = false,
+ allowAutomaticKeyVerification = SignalStore.settings.automaticVerificationEnabled
)
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt
index 65a3482f6d..a877a57f35 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt
@@ -1418,7 +1418,7 @@ class ConversationFragment :
}
private fun presentVerifyAutomaticallySheet() {
- if (RemoteConfig.keyTransparency && !SignalStore.uiHints.hasSeenVerifyAutomaticallySheet() && viewModel.recipientSnapshot?.isIndividual == true) {
+ if (RemoteConfig.keyTransparency && SignalStore.settings.automaticVerificationEnabled && !SignalStore.uiHints.hasSeenVerifyAutomaticallySheet() && viewModel.recipientSnapshot?.isIndividual == true) {
VerifyAutomaticallyEducationSheet.show(parentFragmentManager)
}
}
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 8905842e30..edd588cc72 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt
@@ -4048,6 +4048,14 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.run()
}
+ fun clearAllKeyTransparencyData() {
+ writableDatabase
+ .update(TABLE_NAME)
+ .values(KEY_TRANSPARENCY_DATA to null)
+ .where("$KEY_TRANSPARENCY_DATA IS NOT NULL")
+ .run()
+ }
+
/**
* Will update the database with the content values you specified. It will make an intelligent
* query such that this will only return true if a row was *actually* updated.
diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java
index 53bf403012..6616ad7115 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java
@@ -75,6 +75,7 @@ public final class SettingsValues extends SignalStoreValues {
private static final String PASSPHRASE_TIMEOUT = "settings.passphrase.timeout";
private static final String SCREEN_LOCK_ENABLED = "settings.screen.lock.enabled";
private static final String SCREEN_LOCK_TIMEOUT = "settings.screen.lock.timeout";
+ private static final String AUTOMATIC_VERIFICATION_ENABLED = "settings.automatic.verification.enabled";
public static final int BACKUP_DEFAULT_HOUR = 2;
public static final int BACKUP_DEFAULT_MINUTE = 0;
@@ -560,6 +561,14 @@ public final class SettingsValues extends SignalStoreValues {
return getLong(SCREEN_LOCK_TIMEOUT, 0);
}
+ public boolean getAutomaticVerificationEnabled() {
+ return getBoolean(AUTOMATIC_VERIFICATION_ENABLED, true);
+ }
+
+ public void setAutomaticVerificationEnabled(boolean enabled) {
+ putBoolean(AUTOMATIC_VERIFICATION_ENABLED, enabled);
+ }
+
private @Nullable Uri getUri(@NonNull String key) {
String uri = getString(key, "");
diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt
index 6a4d0825a4..5651faf602 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt
@@ -138,6 +138,7 @@ class AccountRecordProcessor(
usernameLink = remote.proto.usernameLink
notificationProfileManualOverride = remote.proto.notificationProfileManualOverride
backupTier = local.proto.backupTier ?: remote.proto.backupTier
+ automaticKeyVerificationDisabled = remote.proto.automaticKeyVerificationDisabled
safeSetPayments(payments?.enabled == true, payments?.entropy?.toByteArray())
safeSetSubscriber(donationSubscriberId, donationSubscriberCurrencyCode)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt
index 67c7a4020d..8378c33d68 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt
@@ -195,6 +195,7 @@ object StorageSyncHelper {
}
safeSetPayments(SignalStore.payments.mobileCoinPaymentsEnabled(), Optional.ofNullable(SignalStore.payments.paymentsEntropy).map { obj: Entropy -> obj.bytes }.orElse(null))
+ automaticKeyVerificationDisabled = !SignalStore.settings.automaticVerificationEnabled
}
return accountRecord.toSignalAccountRecord(StorageId.forAccount(storageId)).toSignalStorageRecord()
@@ -258,6 +259,11 @@ object StorageSyncHelper {
SignalStore.story.userHasSeenGroupStoryEducationSheet = update.new.proto.hasSeenGroupStoryEducationSheet
SignalStore.uiHints.setHasCompletedUsernameOnboarding(update.new.proto.hasCompletedUsernameOnboarding)
+ if (SignalStore.settings.automaticVerificationEnabled && update.new.proto.automaticKeyVerificationDisabled) {
+ SignalDatabase.recipients.clearAllKeyTransparencyData()
+ }
+ SignalStore.settings.automaticVerificationEnabled = !update.new.proto.automaticKeyVerificationDisabled
+
if (update.new.proto.storyViewReceiptsEnabled == OptionalBool.UNSET) {
SignalStore.story.viewedReceiptsEnabled = update.new.proto.readReceipts
} else {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/verify/VerifyDisplayFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/verify/VerifyDisplayFragment.kt
index 380025eeaf..1cf1722a2a 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/verify/VerifyDisplayFragment.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/verify/VerifyDisplayFragment.kt
@@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.components.ViewBinderDelegate
import org.thoughtcrime.securesms.components.verify.SafetyNumberQrView.Companion.getSegments
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable
import org.thoughtcrime.securesms.databinding.VerifyDisplayFragmentBinding
+import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.RemoteConfig
@@ -74,7 +75,7 @@ class VerifyDisplayFragment : Fragment() {
updateVerifyButton(requireArguments().getBoolean(VERIFIED_STATE, false), false)
- binding.automaticVerification.visible = RemoteConfig.keyTransparency
+ binding.automaticVerification.visible = RemoteConfig.keyTransparency && SignalStore.settings.automaticVerificationEnabled
binding.safetyQrView.verifyButton.setOnClickListener { updateVerifyButton(!currentVerifiedState, true) }
binding.toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
binding.toolbar.setTitle(R.string.AndroidManifest__verify_safety_number)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1fa1abb6fb..ec055207e0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4247,6 +4247,10 @@
Compact
The app will restart to apply the new language setting.
+
+ Automatic key verification
+
+ When enabled, Signal will attempt to automatically verify the encryption of 1:1 chats.
Customize option
diff --git a/lib/libsignal-service/src/main/protowire/StorageService.proto b/lib/libsignal-service/src/main/protowire/StorageService.proto
index f49fa2dc68..95ec8454d6 100644
--- a/lib/libsignal-service/src/main/protowire/StorageService.proto
+++ b/lib/libsignal-service/src/main/protowire/StorageService.proto
@@ -294,6 +294,8 @@ message AccountRecord {
optional AvatarColor avatarColor = 42;
BackupTierHistory backupTierHistory = 43;
NotificationProfileManualOverride notificationProfileManualOverride = 44;
+ bool notificationProfileSyncDisabled = 45;
+ bool automaticKeyVerificationDisabled = 46;
}
message StoryDistributionListRecord {