diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceEducationSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceEducationSheet.kt
new file mode 100644
index 0000000000..411dfeb2cd
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceEducationSheet.kt
@@ -0,0 +1,96 @@
+package org.thoughtcrime.securesms.linkdevice
+
+import android.content.DialogInterface
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.defaultMinSize
+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.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.activityViewModels
+import org.signal.core.ui.BottomSheets
+import org.signal.core.ui.Buttons
+import org.signal.core.ui.Previews
+import org.signal.core.ui.SignalPreview
+import org.thoughtcrime.securesms.R
+import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
+
+/**
+ * Education sheet shown before biometrics when linking a device
+ */
+class LinkDeviceEducationSheet : ComposeBottomSheetDialogFragment() {
+
+ override val peekHeightPercentage: Float = 0.67f
+
+ private val viewModel: LinkDeviceViewModel by activityViewModels()
+
+ @Composable
+ override fun SheetContent() {
+ DeviceEducationSheet(this::onDismiss)
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ viewModel.markEducationSheetSeen(true)
+ super.onCancel(dialog)
+ }
+
+ fun onDismiss() {
+ viewModel.markEducationSheetSeen(true)
+ dismissAllowingStateLoss()
+ }
+}
+
+@Composable
+private fun DeviceEducationSheet(onClick: () -> Unit) {
+ return Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ BottomSheets.Handle()
+ Icon(
+ painter = painterResource(R.drawable.ic_phone_lock),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ modifier = Modifier.padding(top = 24.dp)
+ )
+
+ Text(
+ text = stringResource(R.string.LinkDeviceFragment__before_linking),
+ style = MaterialTheme.typography.titleLarge,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.padding(top = 20.dp, bottom = 8.dp),
+ color = MaterialTheme.colorScheme.onSurface
+ )
+ Text(
+ text = stringResource(R.string.LinkDeviceFragment__tap_continue_and_enter_phone),
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.padding(horizontal = 44.dp),
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+
+ Buttons.LargeTonal(
+ onClick = onClick,
+ modifier = Modifier.defaultMinSize(minWidth = 220.dp).padding(top = 28.dp, bottom = 56.dp)
+ ) {
+ Text(stringResource(id = R.string.LinkDeviceFragment__continue))
+ }
+ }
+}
+
+@SignalPreview
+@Composable
+fun DeviceEducationSheetPreview() {
+ Previews.BottomSheetPreview {
+ DeviceEducationSheet(onClick = {})
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt
index f0f42a0265..cd2fd0fbb2 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt
@@ -133,6 +133,13 @@ class LinkDeviceFragment : ComposeFragment() {
}
}
+ LaunchedEffect(state.seenEducationSheet) {
+ if (state.seenEducationSheet) {
+ biometricAuth.authenticate(requireContext(), true) { biometricDeviceLockLauncher.launch(getString(R.string.LinkDeviceFragment__unlock_to_link)) }
+ viewModel.markEducationSheetSeen(false)
+ }
+ }
+
Scaffolds.Settings(
title = stringResource(id = R.string.preferences__linked_devices),
onNavigationClick = { navController.popOrFinish() },
@@ -146,7 +153,7 @@ class LinkDeviceFragment : ComposeFragment() {
onLearnMore = { navController.safeNavigate(R.id.action_linkDeviceFragment_to_linkDeviceLearnMoreBottomSheet) },
onLinkDevice = {
if (biometricAuth.canAuthenticate()) {
- biometricAuth.authenticate(requireContext(), true) { biometricDeviceLockLauncher.launch(getString(R.string.LinkDeviceFragment__unlock_to_link)) }
+ navController.safeNavigate(R.id.action_linkDeviceFragment_to_linkDeviceEducationSheet)
} else {
navController.safeNavigate(R.id.action_linkDeviceFragment_to_addLinkDeviceFragment)
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt
index a6168fe67b..ba840b7ecd 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt
@@ -17,5 +17,6 @@ data class LinkDeviceSettingsState(
val linkDeviceResult: LinkDeviceRepository.LinkDeviceResult = LinkDeviceRepository.LinkDeviceResult.UNKNOWN,
val showFinishedSheet: Boolean = false,
val seenIntroSheet: Boolean = false,
- val pendingNewDevice: Boolean = false
+ val pendingNewDevice: Boolean = false,
+ val seenEducationSheet: Boolean = false
)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt
index 2797815860..7eaadc258e 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt
@@ -191,4 +191,12 @@ class LinkDeviceViewModel : ViewModel() {
)
}
}
+
+ fun markEducationSheetSeen(seen: Boolean) {
+ _state.update {
+ it.copy(
+ seenEducationSheet = seen
+ )
+ }
+ }
}
diff --git a/app/src/main/res/drawable-night/ic_phone_lock.xml b/app/src/main/res/drawable-night/ic_phone_lock.xml
new file mode 100644
index 0000000000..dcae314fe1
--- /dev/null
+++ b/app/src/main/res/drawable-night/ic_phone_lock.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_phone_lock.xml b/app/src/main/res/drawable/ic_phone_lock.xml
new file mode 100644
index 0000000000..08817858aa
--- /dev/null
+++ b/app/src/main/res/drawable/ic_phone_lock.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/navigation/app_settings_with_change_number.xml b/app/src/main/res/navigation/app_settings_with_change_number.xml
index 9669c2a659..2fcb080693 100644
--- a/app/src/main/res/navigation/app_settings_with_change_number.xml
+++ b/app/src/main/res/navigation/app_settings_with_change_number.xml
@@ -249,6 +249,9 @@
+
+
No linked devices
Unlock to link a device
+
+ Before linking, confirm it\'s you
+
+ Tap continue and enter your phone\'s lock to confirm. Do not enter your Signal PIN.
+
+ Continue