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 4fab0772e2..4d67da2d76 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
@@ -367,6 +367,7 @@ import org.thoughtcrime.securesms.util.toMillis
import org.thoughtcrime.securesms.util.viewModel
import org.thoughtcrime.securesms.util.views.Stub
import org.thoughtcrime.securesms.util.visible
+import org.thoughtcrime.securesms.verify.VerifyAutomaticallyEducationSheet
import org.thoughtcrime.securesms.verify.VerifyIdentityActivity
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperDimLevelUtil
@@ -661,6 +662,7 @@ class ConversationFragment :
presentGroupConversationSubtitle(createGroupSubtitleString(viewModel.titleViewParticipantsSnapshot))
presentActionBarMenu()
presentStoryRing()
+ presentVerifyAutomaticallySheet()
observeConversationThread()
@@ -1414,6 +1416,12 @@ class ConversationFragment :
}
}
+ private fun presentVerifyAutomaticallySheet() {
+ if (RemoteConfig.keyTransparency && !SignalStore.uiHints.hasSeenVerifyAutomaticallySheet() && viewModel.recipientSnapshot?.isIndividual == true) {
+ VerifyAutomaticallyEducationSheet.show(parentFragmentManager)
+ }
+ }
+
private fun presentInputReadyState(inputReadyState: InputReadyState) {
presentConversationTitle(inputReadyState.conversationRecipient)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java
index 0579f8b3b0..e0724869b7 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java
@@ -31,6 +31,7 @@ public class UiHintValues extends SignalStoreValues {
private static final String HAS_SEEN_LINK_DEVICE_QR_EDUCATION_SHEET = "uihints.has_seen_link_device_qr_education_sheet";
private static final String HAS_DISMISSED_SAVE_STORAGE_WARNING = "uihints.has_dismissed_save_storage_warning";
private static final String HAS_SEEN_PINNED_MESSAGE_SHEET = "uihints.has_seen_pinned_message_sheet";
+ private static final String HAS_SEEN_VERIFY_AUTO_SHEET = "uihints.has_seen_verify_auto_sheet";
UiHintValues(@NonNull KeyValueStore store) {
super(store);
@@ -232,4 +233,12 @@ public class UiHintValues extends SignalStoreValues {
private int getSeenPinnedSheetCount() {
return getInteger(HAS_SEEN_PINNED_MESSAGE_SHEET, 0);
}
+
+ public boolean hasSeenVerifyAutomaticallySheet() {
+ return getBoolean(HAS_SEEN_VERIFY_AUTO_SHEET, false);
+ }
+
+ public void setSeenVerifyAutomaticallySheet() {
+ putBoolean(HAS_SEEN_VERIFY_AUTO_SHEET, true);
+ }
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt
index 4177802f34..89a49387f5 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt
@@ -1233,5 +1233,18 @@ object RemoteConfig {
defaultValue = false,
hotSwappable = true
)
+
+ /**
+ * Whether or not to show any UI related to key transparency
+ */
+ @JvmStatic
+ @get:JvmName("keyTransparency")
+ val keyTransparency: Boolean by remoteBoolean(
+ key = "android.keyTransparency",
+ active = false,
+ defaultValue = false,
+ hotSwappable = true
+ )
+
// endregion
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/verify/VerifyAutomaticallyEducationSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/verify/VerifyAutomaticallyEducationSheet.kt
new file mode 100644
index 0000000000..5dca3bc025
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/verify/VerifyAutomaticallyEducationSheet.kt
@@ -0,0 +1,120 @@
+package org.thoughtcrime.securesms.verify
+
+import android.content.DialogInterface
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.fragment.app.FragmentManager
+import org.signal.core.ui.compose.BottomSheets
+import org.signal.core.ui.compose.Buttons
+import org.signal.core.ui.compose.DayNightPreviews
+import org.signal.core.ui.compose.Previews
+import org.thoughtcrime.securesms.R
+import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
+import org.thoughtcrime.securesms.keyvalue.SignalStore
+import org.thoughtcrime.securesms.util.BottomSheetUtil
+
+/**
+ * Education sheet explaining that conversations now have auto verification
+ */
+class VerifyAutomaticallyEducationSheet : ComposeBottomSheetDialogFragment() {
+
+ override val peekHeightPercentage: Float = 0.75f
+
+ companion object {
+
+ @JvmStatic
+ fun show(fragmentManager: FragmentManager) {
+ VerifyAutomaticallyEducationSheet().show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
+ }
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ SignalStore.uiHints.setSeenVerifyAutomaticallySheet()
+ }
+
+ @Composable
+ override fun SheetContent() {
+ VerifyEducationSheet(
+ onVerify = {}, // TODO(michelle): Plug in to verify fragment
+ onLearnMore = {} // TODO(michelle): Update with support url
+ )
+ }
+}
+
+@Composable
+fun VerifyEducationSheet(
+ onVerify: () -> Unit = {},
+ onLearnMore: () -> Unit = {}
+) {
+ return Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ BottomSheets.Handle()
+ Icon(
+ painter = painterResource(R.drawable.image_verify_successful),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ modifier = Modifier.padding(top = 24.dp, bottom = 8.dp)
+ )
+
+ Text(
+ text = stringResource(R.string.VerifyAutomaticallyEducationSheet__title),
+ style = MaterialTheme.typography.headlineMedium,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.padding(vertical = 12.dp, horizontal = 32.dp),
+ color = MaterialTheme.colorScheme.onSurface
+ )
+ Text(
+ text = stringResource(R.string.VerifyAutomaticallyEducationSheet__body),
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.padding(horizontal = 32.dp),
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+
+ Row(
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp).padding(top = 60.dp, bottom = 28.dp)
+ ) {
+ TextButton(
+ onClick = onLearnMore
+ ) {
+ Text(
+ text = stringResource(id = R.string.VerifyAutomaticallyEducationSheet__learn_more)
+ )
+ }
+
+ Spacer(modifier = Modifier.weight(1f))
+
+ Buttons.LargeTonal(
+ onClick = onVerify
+ ) {
+ Text(stringResource(id = R.string.VerifyAutomaticallyEducationSheet__verify))
+ }
+ }
+ }
+}
+
+@DayNightPreviews
+@Composable
+fun VerifyAutomaticallyEducationSheetPreview() {
+ Previews.BottomSheetContentPreview {
+ VerifyEducationSheet()
+ }
+}
diff --git a/app/src/main/res/drawable/image_verify_successful.xml b/app/src/main/res/drawable/image_verify_successful.xml
new file mode 100644
index 0000000000..f2726b4289
--- /dev/null
+++ b/app/src/main/res/drawable/image_verify_successful.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4476ba1e49..fd2ba0f871 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -3816,6 +3816,15 @@
Mark as verified
Clear verification
+
+ Signal now auto-verifies end-to-end encryption
+
+ When you verify a safety number, Signal will automatically confirm whether the connection is secure using a process called key transparency. You can still verify connections manually using a QR code or number.
+
+ Learn more
+
+ Verify
+
Scan the QR Code on your contact\'s device.