Add education sheet to linked device biometrics.

This commit is contained in:
Michelle Tang
2024-09-04 10:55:48 -07:00
committed by Cody Henthorne
parent d59985c7b1
commit 93c8cd133d
8 changed files with 180 additions and 2 deletions

View File

@@ -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 = {})
}
}

View File

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

View File

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

View File

@@ -191,4 +191,12 @@ class LinkDeviceViewModel : ViewModel() {
)
}
}
fun markEducationSheetSeen(seen: Boolean) {
_state.update {
it.copy(
seenEducationSheet = seen
)
}
}
}

View File

@@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="112dp"
android:height="112dp"
android:viewportWidth="112"
android:viewportHeight="112">
<path
android:pathData="M28,24C28,16.27 34.27,10 42,10H70C77.73,10 84,16.27 84,24V88C84,95.73 77.73,102 70,102H42C34.27,102 28,95.73 28,88V24Z"
android:fillColor="#E3E8FE"/>
<path
android:pathData="M84,63.05V88.13C84,95.86 77.73,102.13 70,102.13H42C34.27,102.13 28,95.86 28,88.13V63.48C36.12,58.1 45.86,54.96 56.33,54.96C66.52,54.96 76.02,57.93 84,63.05Z"
android:fillColor="#C1C7FE"
android:fillType="evenOdd"/>
<path
android:pathData="M27,24C27,15.72 33.72,9 42,9H70C78.28,9 85,15.72 85,24V88C85,96.28 78.28,103 70,103H42C33.72,103 27,96.28 27,88V24ZM42,11C34.82,11 29,16.82 29,24V88C29,95.18 34.82,101 42,101H70C77.18,101 83,95.18 83,88V24C83,16.82 77.18,11 70,11H42Z"
android:fillColor="#3B45FD"
android:fillType="evenOdd"/>
<path
android:pathData="M41,60.6C41,57.24 41,55.56 41.65,54.28C42.23,53.15 43.15,52.23 44.28,51.65C45.56,51 47.24,51 50.6,51H61.4C64.76,51 66.44,51 67.72,51.65C68.85,52.23 69.77,53.15 70.35,54.28C71,55.56 71,57.24 71,60.6V66.4C71,69.76 71,71.44 70.35,72.72C69.77,73.85 68.85,74.77 67.72,75.35C66.44,76 64.76,76 61.4,76H50.6C47.24,76 45.56,76 44.28,75.35C43.15,74.77 42.23,73.85 41.65,72.72C41,71.44 41,69.76 41,66.4V60.6Z"
android:fillColor="#E3E8FE"/>
<path
android:pathData="M56,33C49.65,33 44.5,38.15 44.5,44.5V50.48C44.27,50.56 44.04,50.65 43.82,50.76C42.5,51.43 41.43,52.5 40.76,53.82C40.35,54.63 40.17,55.52 40.09,56.58C40,57.62 40,58.91 40,60.56V66.44C40,68.09 40,69.38 40.09,70.42C40.17,71.48 40.35,72.37 40.76,73.18C41.43,74.5 42.5,75.57 43.82,76.24C44.63,76.65 45.52,76.83 46.58,76.91C47.62,77 48.91,77 50.56,77H61.44C63.09,77 64.38,77 65.42,76.91C66.48,76.83 67.37,76.65 68.18,76.24C69.5,75.57 70.57,74.5 71.24,73.18C71.65,72.37 71.83,71.48 71.91,70.42C72,69.38 72,68.09 72,66.44V60.56C72,58.91 72,57.62 71.91,56.58C71.83,55.52 71.65,54.63 71.24,53.82C70.57,52.5 69.5,51.43 68.18,50.76C67.96,50.65 67.73,50.56 67.5,50.48V44.5C67.5,38.15 62.35,33 56,33ZM65.5,50.09V44.5C65.5,39.25 61.25,35 56,35C50.75,35 46.5,39.25 46.5,44.5V50.09C46.53,50.09 46.55,50.09 46.58,50.09C47.62,50 48.91,50 50.56,50H61.44C63.09,50 64.38,50 65.42,50.09C65.45,50.09 65.47,50.09 65.5,50.09ZM44.73,52.54C45.21,52.3 45.8,52.16 46.74,52.08C47.69,52 48.9,52 50.6,52H61.4C63.1,52 64.31,52 65.26,52.08C66.2,52.16 66.79,52.3 67.27,52.54C68.21,53.02 68.98,53.79 69.46,54.73C69.7,55.21 69.84,55.8 69.92,56.74C70,57.69 70,58.9 70,60.6V66.4C70,68.1 70,69.31 69.92,70.26C69.84,71.2 69.7,71.79 69.46,72.27C68.98,73.21 68.21,73.98 67.27,74.46C66.79,74.7 66.2,74.84 65.26,74.92C64.31,75 63.1,75 61.4,75H50.6C48.9,75 47.69,75 46.74,74.92C45.8,74.84 45.21,74.7 44.73,74.46C43.79,73.98 43.02,73.21 42.54,72.27C42.3,71.79 42.16,71.2 42.08,70.26C42,69.31 42,68.1 42,66.4V60.6C42,58.9 42,57.69 42.08,56.74C42.16,55.8 42.3,55.21 42.54,54.73C43.02,53.79 43.79,53.02 44.73,52.54Z"
android:fillColor="#3B45FD"
android:fillType="evenOdd"/>
<path
android:pathData="M53.25,61.25C53.25,59.73 54.48,58.5 56,58.5C57.52,58.5 58.75,59.73 58.75,61.25C58.75,62.42 58.02,63.41 57,63.81V67.5C57,68.05 56.55,68.5 56,68.5C55.45,68.5 55,68.05 55,67.5V63.81C53.98,63.41 53.25,62.42 53.25,61.25Z"
android:fillColor="#3B45FD"/>
</vector>

View File

@@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="112dp"
android:height="112dp"
android:viewportWidth="112"
android:viewportHeight="112">
<path
android:pathData="M28,24C28,16.27 34.27,10 42,10H70C77.73,10 84,16.27 84,24V88C84,95.73 77.73,102 70,102H42C34.27,102 28,95.73 28,88V24Z"
android:fillColor="#E3E8FE"/>
<path
android:pathData="M84,63.05V88.13C84,95.86 77.73,102.13 70,102.13H42C34.27,102.13 28,95.86 28,88.13V63.48C36.12,58.1 45.86,54.96 56.33,54.96C66.52,54.96 76.02,57.93 84,63.05Z"
android:fillColor="#C1C7FE"
android:fillType="evenOdd"/>
<path
android:pathData="M27,24C27,15.72 33.72,9 42,9H70C78.28,9 85,15.72 85,24V88C85,96.28 78.28,103 70,103H42C33.72,103 27,96.28 27,88V24ZM42,11C34.82,11 29,16.82 29,24V88C29,95.18 34.82,101 42,101H70C77.18,101 83,95.18 83,88V24C83,16.82 77.18,11 70,11H42Z"
android:fillColor="#020BAC"
android:fillType="evenOdd"/>
<path
android:pathData="M41,60.6C41,57.24 41,55.56 41.65,54.28C42.23,53.15 43.15,52.23 44.28,51.65C45.56,51 47.24,51 50.6,51H61.4C64.76,51 66.44,51 67.72,51.65C68.85,52.23 69.77,53.15 70.35,54.28C71,55.56 71,57.24 71,60.6V66.4C71,69.76 71,71.44 70.35,72.72C69.77,73.85 68.85,74.77 67.72,75.35C66.44,76 64.76,76 61.4,76H50.6C47.24,76 45.56,76 44.28,75.35C43.15,74.77 42.23,73.85 41.65,72.72C41,71.44 41,69.76 41,66.4V60.6Z"
android:fillColor="#E3E8FE"/>
<path
android:pathData="M56,33C49.65,33 44.5,38.15 44.5,44.5V50.48C44.27,50.56 44.04,50.65 43.82,50.76C42.5,51.43 41.43,52.5 40.76,53.82C40.35,54.63 40.17,55.52 40.09,56.58C40,57.62 40,58.91 40,60.56V66.44C40,68.09 40,69.38 40.09,70.42C40.17,71.48 40.35,72.37 40.76,73.18C41.43,74.5 42.5,75.57 43.82,76.24C44.63,76.65 45.52,76.83 46.58,76.91C47.62,77 48.91,77 50.56,77H61.44C63.09,77 64.38,77 65.42,76.91C66.48,76.83 67.37,76.65 68.18,76.24C69.5,75.57 70.57,74.5 71.24,73.18C71.65,72.37 71.83,71.48 71.91,70.42C72,69.38 72,68.09 72,66.44V60.56C72,58.91 72,57.62 71.91,56.58C71.83,55.52 71.65,54.63 71.24,53.82C70.57,52.5 69.5,51.43 68.18,50.76C67.96,50.65 67.73,50.56 67.5,50.48V44.5C67.5,38.15 62.35,33 56,33ZM65.5,50.09V44.5C65.5,39.25 61.25,35 56,35C50.75,35 46.5,39.25 46.5,44.5V50.09C46.53,50.09 46.55,50.09 46.58,50.09C47.62,50 48.91,50 50.56,50H61.44C63.09,50 64.38,50 65.42,50.09C65.45,50.09 65.47,50.09 65.5,50.09ZM44.73,52.54C45.21,52.3 45.8,52.16 46.74,52.08C47.69,52 48.9,52 50.6,52H61.4C63.1,52 64.31,52 65.26,52.08C66.2,52.16 66.79,52.3 67.27,52.54C68.21,53.02 68.98,53.79 69.46,54.73C69.7,55.21 69.84,55.8 69.92,56.74C70,57.69 70,58.9 70,60.6V66.4C70,68.1 70,69.31 69.92,70.26C69.84,71.2 69.7,71.79 69.46,72.27C68.98,73.21 68.21,73.98 67.27,74.46C66.79,74.7 66.2,74.84 65.26,74.92C64.31,75 63.1,75 61.4,75H50.6C48.9,75 47.69,75 46.74,74.92C45.8,74.84 45.21,74.7 44.73,74.46C43.79,73.98 43.02,73.21 42.54,72.27C42.3,71.79 42.16,71.2 42.08,70.26C42,69.31 42,68.1 42,66.4V60.6C42,58.9 42,57.69 42.08,56.74C42.16,55.8 42.3,55.21 42.54,54.73C43.02,53.79 43.79,53.02 44.73,52.54Z"
android:fillColor="#020BAC"
android:fillType="evenOdd"/>
<path
android:pathData="M53.25,61.25C53.25,59.73 54.48,58.5 56,58.5C57.52,58.5 58.75,59.73 58.75,61.25C58.75,62.42 58.02,63.41 57,63.81V67.5C57,68.05 56.55,68.5 56,68.5C55.45,68.5 55,68.05 55,67.5V63.81C53.98,63.41 53.25,62.42 53.25,61.25Z"
android:fillColor="#020BAC"/>
</vector>

View File

@@ -249,6 +249,9 @@
<action
android:id="@+id/action_linkDeviceFragment_to_linkDeviceLearnMoreBottomSheet"
app:destination="@id/linkDeviceLearnMoreBottomSheet" />
<action
android:id="@+id/action_linkDeviceFragment_to_linkDeviceEducationSheet"
app:destination="@id/linkDeviceEducationSheet" />
</fragment>
<dialog
android:id="@+id/linkDeviceFinishedSheet"
@@ -256,6 +259,9 @@
<dialog
android:id="@+id/linkDeviceLearnMoreBottomSheet"
android:name="org.thoughtcrime.securesms.linkdevice.LinkDeviceLearnMoreBottomSheetFragment" />
<dialog
android:id="@+id/linkDeviceEducationSheet"
android:name="org.thoughtcrime.securesms.linkdevice.LinkDeviceEducationSheet" />
<fragment
android:id="@+id/addLinkDeviceFragment"

View File

@@ -972,6 +972,12 @@
<string name="LinkDeviceFragment__no_linked_devices">No linked devices</string>
<!-- Title on biometrics prompt explaining what biometrics are being used for -->
<string name="LinkDeviceFragment__unlock_to_link">Unlock to link a device</string>
<!-- Title on bottom sheet explaining our usage of biometrics to link a device -->
<string name="LinkDeviceFragment__before_linking">Before linking, confirm it\'s you</string>
<!-- Body of bottom sheet explaining that users should use their device pin or biometrics and not their Signal pin -->
<string name="LinkDeviceFragment__tap_continue_and_enter_phone">Tap continue and enter your phone\'s lock to confirm. Do not enter your Signal PIN.</string>
<!-- Button that dismisses the bottom sheet -->
<string name="LinkDeviceFragment__continue">Continue</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->