From 7086709082d4fcdd262beefc4ff629bce27abe8d Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Fri, 7 Jun 2024 09:34:44 -0700 Subject: [PATCH] Update devices screen after linking a new device. --- .../MultiDeviceConfigurationUpdateJob.java | 5 +- .../linkdevice/LinkDeviceFragment.kt | 58 ++++++++----------- .../linkdevice/LinkDeviceSettingsState.kt | 3 +- .../linkdevice/LinkDeviceViewModel.kt | 46 ++++++++++++++- app/src/main/res/navigation/app_settings.xml | 13 +++++ .../app_settings_with_change_number_v2.xml | 13 +++++ app/src/main/res/values/strings.xml | 4 ++ 7 files changed, 103 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java index 623facd24c..67756f7cb5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java @@ -26,7 +26,8 @@ import java.util.Optional; public class MultiDeviceConfigurationUpdateJob extends BaseJob { - public static final String KEY = "MultiDeviceConfigurationUpdateJob"; + public static final String KEY = "MultiDeviceConfigurationUpdateJob"; + public static final String QUEUE = "__MULTI_DEVICE_CONFIGURATION_UPDATE_JOB__"; private static final String TAG = Log.tag(MultiDeviceConfigurationUpdateJob.class); @@ -46,7 +47,7 @@ public class MultiDeviceConfigurationUpdateJob extends BaseJob { boolean linkPreviewsEnabled) { this(new Job.Parameters.Builder() - .setQueue("__MULTI_DEVICE_CONFIGURATION_UPDATE_JOB__") + .setQueue(QUEUE) .addConstraint(NetworkConstraint.KEY) .setMaxAttempts(10) .build(), 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 68e97044b4..ab5902326f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.linkdevice +import android.os.Bundle +import android.view.View import android.widget.Toast import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -23,6 +25,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -36,6 +40,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.fragment.app.activityViewModels +import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import org.signal.core.ui.Buttons import org.signal.core.ui.Dialogs @@ -45,7 +50,6 @@ import org.signal.core.ui.Scaffolds import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.compose.ComposeFragment -import org.thoughtcrime.securesms.util.BottomSheetUtil import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.navigation.safeNavigate import java.util.Locale @@ -57,69 +61,54 @@ class LinkDeviceFragment : ComposeFragment() { private val viewModel: LinkDeviceViewModel by activityViewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.initialize(requireContext()) + } + @Composable override fun FragmentContent() { val state by viewModel.state.collectAsState() + val navController: NavController by remember { mutableStateOf(findNavController()) } LaunchedEffect(state.toastDialog) { if (state.toastDialog.isNotEmpty()) { Toast.makeText(requireContext(), state.toastDialog, Toast.LENGTH_LONG).show() + viewModel.clearToast() } } LaunchedEffect(state.showFinishedSheet) { if (state.showFinishedSheet) { - onShowFinishedSheet() + findNavController().safeNavigate(R.id.action_linkDeviceFragment_to_linkDeviceFinishedSheet) + viewModel.markFinishedSheetSeen() } } Scaffolds.Settings( title = stringResource(id = R.string.preferences__linked_devices), - onNavigationClick = { findNavController().popBackStack() }, + onNavigationClick = { navController.popBackStack() }, navigationIconPainter = painterResource(id = R.drawable.ic_arrow_left_24), navigationContentDescription = stringResource(id = R.string.Material3SearchToolbar__close) ) { contentPadding: PaddingValues -> DeviceDescriptionScreen( state = state, + navController = navController, modifier = Modifier.padding(contentPadding), - onLearnMore = this::openLearnMore, - onLinkDevice = this::openLinkNewDevice, - setDeviceToRemove = this::setDeviceToRemove, - onRemoveDevice = this::onRemoveDevice + onLearnMore = { navController.safeNavigate(R.id.action_linkDeviceFragment_to_linkDeviceLearnMoreBottomSheet) }, + onLinkDevice = { navController.safeNavigate(R.id.action_linkDeviceFragment_to_addLinkDeviceFragment) }, + setDeviceToRemove = { device -> viewModel.setDeviceToRemove(device) }, + onRemoveDevice = { device -> viewModel.removeDevice(requireContext(), device) } ) } } - - override fun onResume() { - super.onResume() - viewModel.loadDevices(requireContext()) - } - - private fun openLearnMore() { - LinkDeviceLearnMoreBottomSheetFragment().show(childFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) - } - - private fun openLinkNewDevice() { - findNavController().safeNavigate(R.id.action_linkDeviceFragment_to_addLinkDeviceFragment) - } - - private fun setDeviceToRemove(device: Device?) { - viewModel.setDeviceToRemove(device) - } - - private fun onRemoveDevice(device: Device) { - viewModel.removeDevice(requireContext(), device) - } - - private fun onShowFinishedSheet() { - LinkDeviceFinishedSheet().show(childFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) - viewModel.markFinishedSheetSeen() - } } @Composable fun DeviceDescriptionScreen( state: LinkDeviceSettingsState, + navController: NavController? = null, modifier: Modifier = Modifier, onLearnMore: () -> Unit = {}, onLinkDevice: () -> Unit = {}, @@ -127,6 +116,9 @@ fun DeviceDescriptionScreen( onRemoveDevice: (Device) -> Unit = {} ) { if (state.progressDialogMessage != -1) { + if (navController?.currentDestination?.id == R.id.linkDeviceFinishedSheet) { + navController?.popBackStack() + } Dialogs.IndeterminateProgressDialog(stringResource(id = state.progressDialogMessage)) } if (state.deviceToRemove != null) { 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 be89a52504..e554b37fd8 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 pendingBiometrics: Boolean = false + val pendingBiometrics: Boolean = false, + val pendingNewDevice: 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 310d848028..b8dffe9707 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt @@ -14,7 +14,11 @@ import kotlinx.coroutines.launch import org.signal.core.util.toOptional import org.signal.qr.QrProcessor import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.JobTracker import org.thoughtcrime.securesms.jobs.LinkedDeviceInactiveCheckJob +import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob /** * Maintains the state of the [LinkDeviceFragment] @@ -24,6 +28,26 @@ class LinkDeviceViewModel : ViewModel() { private val _state = MutableStateFlow(LinkDeviceSettingsState()) val state = _state.asStateFlow() + private lateinit var listener: JobTracker.JobListener + + fun initialize(context: Context) { + listener = JobTracker.JobListener { _, jobState -> + if (jobState.isComplete) { + loadDevices(context = context, isPotentialNewDevice = true) + } + } + AppDependencies.jobManager.addListener( + { job: Job -> job.parameters.queue?.startsWith(MultiDeviceConfigurationUpdateJob.QUEUE) ?: false }, + listener + ) + loadDevices(context) + } + + override fun onCleared() { + super.onCleared() + AppDependencies.jobManager.removeListener(listener) + } + fun setDeviceToRemove(device: Device?) { _state.update { it.copy(deviceToRemove = device) } } @@ -47,7 +71,14 @@ class LinkDeviceViewModel : ViewModel() { } } - fun loadDevices(context: Context) { + private fun loadDevices(context: Context, isPotentialNewDevice: Boolean = false) { + if (isPotentialNewDevice && !_state.value.pendingNewDevice) { + return + } + _state.value = _state.value.copy( + progressDialogMessage = if (isPotentialNewDevice) R.string.LinkDeviceFragment__linking_device else -1, + pendingNewDevice = if (isPotentialNewDevice) false else _state.value.pendingNewDevice + ) viewModelScope.launch(Dispatchers.IO) { val devices = LinkDeviceRepository.loadDevices() if (devices == null) { @@ -58,7 +89,7 @@ class LinkDeviceViewModel : ViewModel() { } else { _state.update { it.copy( - toastDialog = "", + toastDialog = if (isPotentialNewDevice) context.getString(R.string.LinkDeviceFragment__device_approved) else "", devices = devices, progressDialogMessage = -1 ) @@ -160,7 +191,8 @@ class LinkDeviceViewModel : ViewModel() { it.copy( showFinishedSheet = showSheet, linkDeviceResult = LinkDeviceRepository.LinkDeviceResult.UNKNOWN, - toastDialog = "" + toastDialog = "", + pendingNewDevice = true ) } } @@ -192,4 +224,12 @@ class LinkDeviceViewModel : ViewModel() { } } } + + fun clearToast() { + _state.update { + it.copy( + toastDialog = "" + ) + } + } } diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 1a33105e6c..ec211644c2 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -243,7 +243,20 @@ app:exitAnim="@anim/fragment_open_exit" app:popEnterAnim="@anim/fragment_close_enter" app:popExitAnim="@anim/fragment_close_exit" /> + + + + + + + + + + Unlink %s unlinked + + Linking device… + + Device approved