From 75f4fed9ced48d7c34fa15a78369682f1efc1cbc Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Tue, 19 Nov 2024 14:17:00 -0800 Subject: [PATCH] Update link+sync ui. --- .../linkdevice/AddLinkDeviceFragment.kt | 3 +- .../linkdevice/LinkDeviceQrScanScreen.kt | 8 +- .../linkdevice/LinkDeviceSettingsState.kt | 2 +- .../linkdevice/LinkDeviceSyncBottomSheet.kt | 127 ++++++++++++++++++ .../linkdevice/LinkDeviceViewModel.kt | 13 +- .../recipients/ui/about/AboutSheet.kt | 2 +- .../{chat_x.xml => symbol_chat_x.xml} | 0 .../app_settings_with_change_number.xml | 6 + app/src/main/res/values/strings.xml | 10 ++ 9 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSyncBottomSheet.kt rename app/src/main/res/drawable/{chat_x.xml => symbol_chat_x.xml} (100%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/AddLinkDeviceFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/AddLinkDeviceFragment.kt index c13e9555cf..706e8d4235 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/AddLinkDeviceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/AddLinkDeviceFragment.kt @@ -62,7 +62,7 @@ class AddLinkDeviceFragment : ComposeFragment() { onQrCodeScanned = { data -> viewModel.onQrCodeScanned(data) }, onQrCodeApproved = { navController.popBackStack() - viewModel.addDevice() + viewModel.addDevice(shouldSync = false) }, onQrCodeDismissed = { viewModel.onQrCodeDismissed() }, onQrCodeRetry = { viewModel.onQrCodeScanned(state.linkUri.toString()) }, @@ -125,6 +125,7 @@ private fun MainScreen( linkDeviceResult = state.linkDeviceResult, onLinkDeviceSuccess = onLinkDeviceSuccess, onLinkDeviceFailure = onLinkDeviceFailure, + navController = navController, modifier = Modifier.padding(contentPadding) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceQrScanScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceQrScanScreen.kt index f4f68be868..5773d6fbcf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceQrScanScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceQrScanScreen.kt @@ -12,12 +12,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource +import androidx.navigation.NavController import org.signal.core.ui.Dialogs import org.signal.qr.QrScannerView import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.linkdevice.LinkDeviceRepository.LinkDeviceResult import org.thoughtcrime.securesms.mediasend.camerax.CameraXModelBlocklist import org.thoughtcrime.securesms.qr.QrScanScreens +import org.thoughtcrime.securesms.util.navigation.safeNavigate import java.util.concurrent.TimeUnit /** @@ -36,6 +38,7 @@ fun LinkDeviceQrScanScreen( linkDeviceResult: LinkDeviceResult, onLinkDeviceSuccess: () -> Unit, onLinkDeviceFailure: () -> Unit, + navController: NavController?, modifier: Modifier = Modifier ) { val lifecycleOwner = LocalLifecycleOwner.current @@ -45,7 +48,10 @@ fun LinkDeviceQrScanScreen( LinkDeviceSettingsState.QrCodeState.NONE -> { Unit } - LinkDeviceSettingsState.QrCodeState.VALID -> { + LinkDeviceSettingsState.QrCodeState.VALID_WITH_SYNC -> { + navController?.safeNavigate(R.id.action_addLinkDeviceFragment_to_linkDeviceSyncBottomSheet) + } + LinkDeviceSettingsState.QrCodeState.VALID_WITHOUT_SYNC -> { Dialogs.SimpleAlertDialog( title = stringResource(id = R.string.DeviceProvisioningActivity_link_this_device), body = stringResource(id = R.string.AddLinkDeviceFragment__this_device_will_see_your_groups_contacts), 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 bea16668b7..c97795cf38 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt @@ -40,6 +40,6 @@ data class LinkDeviceSettingsState( } enum class QrCodeState { - NONE, VALID, INVALID + NONE, VALID_WITH_SYNC, VALID_WITHOUT_SYNC, INVALID } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSyncBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSyncBottomSheet.kt new file mode 100644 index 0000000000..d44f475f48 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSyncBottomSheet.kt @@ -0,0 +1,127 @@ +package org.thoughtcrime.securesms.linkdevice + +import android.content.DialogInterface +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +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.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.findNavController +import org.signal.core.ui.BottomSheets +import org.signal.core.ui.Previews +import org.signal.core.ui.SignalPreview +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment + +/** + * Bottom sheet dialog allowing users to choose whether to transfer their message history + */ +class LinkDeviceSyncBottomSheet : ComposeBottomSheetDialogFragment() { + + private val viewModel: LinkDeviceViewModel by activityViewModels() + + @Composable + override fun SheetContent() { + SyncSheet( + onLink = { shouldSync -> + viewModel.addDevice(shouldSync) + findNavController().popBackStack(R.id.linkDeviceFragment, false) + } + ) + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + viewModel.onQrCodeDismissed() + } +} + +@Composable +fun SyncSheet( + onLink: (Boolean) -> Unit +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 24.dp) + ) { + BottomSheets.Handle() + + Spacer(modifier = Modifier.size(12.dp)) + SheetOption( + painterResource(R.drawable.symbol_chat_check), + stringResource(R.string.LinkDeviceSyncBottomSheet_transfer), + stringResource(R.string.LinkDeviceSyncBottomSheet_transfer_your_text) + ) { onLink(true) } + + SheetOption( + painterResource(R.drawable.symbol_chat_x), + stringResource(R.string.LinkDeviceSyncBottomSheet_dont_transfer), + stringResource(R.string.LinkDeviceSyncBottomSheet_no_old_messages) + ) { onLink(false) } + + Spacer(modifier = Modifier.size(60.dp)) + } +} + +@Composable +private fun SheetOption( + icon: Painter, + title: String, + description: String, + onLink: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + .defaultMinSize(minHeight = 96.dp) + .background(color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(18.dp)) + .clickable { onLink() }, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(start = 24.dp, end = 16.dp).size(44.dp) + ) + Column( + modifier = Modifier.padding(end = 24.dp) + ) { + Text( + text = title, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = description, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } +} + +@SignalPreview +@Composable +fun SyncSheetSheetSheetPreview() { + Previews.BottomSheetPreview { + SyncSheet(onLink = {}) + } +} 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 0257728ae8..6937d83afd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt @@ -116,7 +116,7 @@ class LinkDeviceViewModel : ViewModel() { if (LinkDeviceRepository.isValidQr(uri)) { _state.update { it.copy( - qrCodeState = QrCodeState.VALID, + qrCodeState = if (uri.supportsLinkAndSync() && RemoteConfig.linkAndSync) QrCodeState.VALID_WITH_SYNC else QrCodeState.VALID_WITHOUT_SYNC, linkUri = uri, showFrontCamera = null ) @@ -140,7 +140,7 @@ class LinkDeviceViewModel : ViewModel() { } } - fun addDevice() = viewModelScope.launch(Dispatchers.IO) { + fun addDevice(shouldSync: Boolean) = viewModelScope.launch(Dispatchers.IO) { val linkUri: Uri = _state.value.linkUri!! _state.update { @@ -151,11 +151,11 @@ class LinkDeviceViewModel : ViewModel() { ) } - if (linkUri.supportsLinkAndSync() && RemoteConfig.linkAndSync) { - Log.i(TAG, "Link+Sync supported.") + if (shouldSync) { + Log.i(TAG, "Adding device with sync.") addDeviceWithSync(linkUri) } else { - Log.i(TAG, "Link+Sync not supported. (uri: ${linkUri.supportsLinkAndSync()}, remoteConfig: ${RemoteConfig.linkAndSync})") + Log.i(TAG, "Adding device without sync. (uri: ${linkUri.supportsLinkAndSync()}, remoteConfig: ${RemoteConfig.linkAndSync})") addDeviceWithoutSync(linkUri) } } @@ -210,6 +210,7 @@ class LinkDeviceViewModel : ViewModel() { } if (result !is LinkDeviceResult.Success) { + Log.w(TAG, "Unable to link device $result") return } @@ -244,6 +245,7 @@ class LinkDeviceViewModel : ViewModel() { dialogState = DialogState.None ) } + loadDevices() } is LinkDeviceRepository.LinkUploadArchiveResult.BackupCreationFailure, is LinkDeviceRepository.LinkUploadArchiveResult.BadRequest, @@ -269,6 +271,7 @@ class LinkDeviceViewModel : ViewModel() { } if (result !is LinkDeviceResult.Success) { + Log.w(TAG, "Unable to link device $result") return } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt index 3e41e53287..e804de036f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt @@ -238,7 +238,7 @@ private fun Content( ) } else { AboutRow( - startIcon = painterResource(id = R.drawable.chat_x), + startIcon = painterResource(id = R.drawable.symbol_chat_x), text = stringResource(id = R.string.AboutSheet__no_direct_message, model.shortName), modifier = Modifier.align(alignment = Alignment.Start), onClick = onClickSignalConnections diff --git a/app/src/main/res/drawable/chat_x.xml b/app/src/main/res/drawable/symbol_chat_x.xml similarity index 100% rename from app/src/main/res/drawable/chat_x.xml rename to app/src/main/res/drawable/symbol_chat_x.xml 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 06e667385f..b105eff295 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 @@ -271,10 +271,16 @@ + + Retry + + + Transfer message history + + Transfer your text messages and recent media to your desktop + + Don\'t transfer + + No old messages or media will be transferred to your desktop + Unlink \"%s\"? By unlinking this device, it will no longer be able to send or receive messages.