Add education sheet for key transparency.

This commit is contained in:
Michelle Tang
2026-01-12 11:59:37 -05:00
parent 6a423cb18b
commit 5759609a11
6 changed files with 227 additions and 0 deletions

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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()
}
}

View File

@@ -0,0 +1,68 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="196dp"
android:height="132dp"
android:viewportWidth="196"
android:viewportHeight="132">
<path
android:pathData="M125.73,18.17L155.64,32.12A11,11 116.76,0 1,160.96 46.73L130.29,112.51A11,11 76.55,0 1,115.67 117.83L85.77,103.88A11,11 67.29,0 1,80.44 89.26L111.12,23.49A11,11 68.68,0 1,125.73 18.17z"
android:strokeWidth="3"
android:fillColor="#ffffff"
android:strokeColor="#757575"/>
<group>
<clip-path
android:pathData="M116.61,38l29.91,13.95l-13.95,29.91l-29.91,-13.95z"/>
<path
android:pathData="M132.7,57.81C133.43,57.36 133.65,56.4 133.2,55.68C132.74,54.95 131.79,54.73 131.06,55.19L122.47,60.55L121.65,56.94C121.46,56.1 120.63,55.58 119.79,55.77C118.96,55.96 118.44,56.79 118.63,57.63L119.94,63.36C120.05,63.84 120.39,64.25 120.86,64.44C121.32,64.63 121.84,64.59 122.27,64.32L132.7,57.81Z"
android:fillColor="#4655FF"/>
<path
android:pathData="M116,71.43C116.96,73.4 119.18,74.44 121.31,73.9L127.56,72.33C131.2,71.42 134.31,69.07 136.2,65.83L139.98,59.33C141.07,57.47 140.73,55.11 139.16,53.63L133.47,48.23C131.93,46.78 129.96,45.86 127.85,45.61L120.06,44.72C117.92,44.47 115.89,45.73 115.16,47.76L112.61,54.83C111.34,58.36 111.55,62.26 113.19,65.63L116,71.43ZM120.56,70.9C119.84,71.08 119.11,70.73 118.79,70.08L115.97,64.28C114.7,61.65 114.54,58.63 115.52,55.88L118.07,48.8C118.32,48.13 118.99,47.71 119.7,47.79L127.5,48.69C128.94,48.85 130.29,49.48 131.34,50.48L137.04,55.87C137.56,56.37 137.67,57.15 137.31,57.77L133.53,64.27C132.06,66.8 129.63,68.62 126.8,69.33L120.56,70.9Z"
android:fillColor="#4655FF"
android:fillType="evenOdd"/>
</group>
<path
android:pathData="M99.94,73.49L104.92,75.81"
android:strokeWidth="4.125"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M108.92,86.78L113.9,89.1"
android:strokeWidth="4.125"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M96.45,80.96L101.44,83.29"
android:strokeWidth="4.125"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M105.43,94.25L110.41,96.58"
android:strokeWidth="4.125"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M92.97,88.44L97.95,90.77"
android:strokeWidth="4.125"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M40.97,31.95L70.88,18A11,11 0,0 1,85.5 23.32L116.32,89.43A11,11 121.56,0 1,111 104.05L81.1,118A11,11 111.72,0 1,66.48 112.68L35.65,46.57A11,11 111.72,0 1,40.97 31.95z"
android:strokeWidth="3"
android:fillColor="#ffffff"
android:strokeColor="#757575"/>
<group>
<clip-path
android:pathData="M50.27,51.75l29.91,-13.95l13.95,29.91l-29.91,13.95z"/>
<path
android:pathData="M75.79,52.16C75.91,51.31 75.32,50.53 74.47,50.41C73.62,50.29 72.84,50.88 72.72,51.72L71.31,61.75L68.01,60.06C67.25,59.67 66.32,59.97 65.93,60.73C65.54,61.49 65.84,62.43 66.6,62.81L71.83,65.5C72.28,65.72 72.81,65.72 73.25,65.49C73.69,65.26 74,64.83 74.07,64.33L75.79,52.16Z"
android:fillColor="#4655FF"/>
<path
android:pathData="M75.48,73.7C77.61,74.23 79.83,73.2 80.79,71.23L83.6,65.43C85.24,62.05 85.45,58.16 84.18,54.63L81.63,47.55C80.9,45.53 78.87,44.27 76.73,44.52L68.94,45.41C66.83,45.66 64.86,46.57 63.32,48.03L57.63,53.43C56.06,54.91 55.72,57.27 56.81,59.13L60.59,65.63C62.48,68.87 65.59,71.22 69.23,72.13L75.48,73.7ZM78,69.87C77.68,70.53 76.95,70.88 76.24,70.7L69.99,69.13C67.16,68.42 64.73,66.6 63.26,64.07L59.48,57.57C59.12,56.95 59.23,56.17 59.76,55.67L65.45,50.28C66.5,49.28 67.85,48.65 69.29,48.49L77.09,47.59C77.8,47.51 78.48,47.93 78.72,48.6L81.27,55.68C82.26,58.42 82.09,61.45 80.82,64.08L78,69.87Z"
android:fillColor="#4655FF"
android:fillType="evenOdd"/>
</group>
</vector>

View File

@@ -3816,6 +3816,15 @@
<string name="verify_display_fragment__mark_as_verified">Mark as verified</string>
<string name="verify_display_fragment__clear_verification">Clear verification</string>
<!-- Title for auto verification education sheet -->
<string name="VerifyAutomaticallyEducationSheet__title">Signal now auto-verifies end-to-end encryption</string>
<!-- Body for auto verification education sheet -->
<string name="VerifyAutomaticallyEducationSheet__body">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.</string>
<!-- Button to learn more about automatic verification -->
<string name="VerifyAutomaticallyEducationSheet__learn_more">Learn more</string>
<!-- Button to go to verification page -->
<string name="VerifyAutomaticallyEducationSheet__verify">Verify</string>
<!-- verity_scan_fragment -->
<string name="verify_scan_fragment__scan_the_qr_code_on_your_contact">Scan the QR Code on your contact\'s device.</string>