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