mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-27 05:03:28 +00:00
Add support filter after backup export failure.
This commit is contained in:
committed by
Cody Henthorne
parent
eeae9579d9
commit
18f7a88d66
@@ -9,6 +9,7 @@ import android.app.PendingIntent
|
||||
import android.database.Cursor
|
||||
import android.os.Environment
|
||||
import android.os.StatFs
|
||||
import androidx.annotation.Discouraged
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.app.NotificationCompat
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -302,6 +303,37 @@ object BackupRepository {
|
||||
AppDependencies.jobManager.add(CheckRestoreMediaLeftJob(RestoreAttachmentJob.constructQueueString(RestoreAttachmentJob.RestoreOperation.MANUAL)))
|
||||
}
|
||||
|
||||
fun markBackupFailure() {
|
||||
SignalStore.backup.markMessageBackupFailure()
|
||||
ArchiveUploadProgress.onMainBackupFileUploadFailure()
|
||||
|
||||
if (!SignalStore.backup.hasBackupBeenUploaded) {
|
||||
Log.w(TAG, "Failure of initial backup. Displaying notification.")
|
||||
displayInitialBackupFailureNotification()
|
||||
}
|
||||
}
|
||||
|
||||
@Discouraged("This is only public to allow internal settings to call it directly.")
|
||||
fun displayInitialBackupFailureNotification() {
|
||||
val context = AppDependencies.application
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(context, 0, AppSettingsActivity.remoteBackups(context), cancelCurrent())
|
||||
val notification = NotificationCompat.Builder(context, NotificationChannels.getInstance().APP_ALERTS)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(context.getString(R.string.Notification_backup_failed))
|
||||
.setContentText(context.getString(R.string.Notification_an_error_occurred_and_your_backup))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
ServiceUtil.getNotificationManager(context).notify(NotificationIds.INITIAL_BACKUP_FAILED, notification)
|
||||
}
|
||||
|
||||
fun clearBackupFailure() {
|
||||
SignalStore.backup.clearMessageBackupFailure()
|
||||
ServiceUtil.getNotificationManager(AppDependencies.application).cancel(NotificationIds.INITIAL_BACKUP_FAILED)
|
||||
}
|
||||
|
||||
fun markOutOfRemoteStorageError() {
|
||||
val context = AppDependencies.application
|
||||
|
||||
|
||||
@@ -33,11 +33,17 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.withLink
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.BundleCompat
|
||||
@@ -53,6 +59,7 @@ import org.signal.core.ui.compose.theme.SignalTheme
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.billing.launchManageBackupsSubscription
|
||||
import org.thoughtcrime.securesms.components.contactsupport.ContactSupportDialogFragment
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.jobs.BackupMessagesJob
|
||||
@@ -139,7 +146,12 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() {
|
||||
is BackupAlert.DiskFull -> {
|
||||
displaySkipRestoreDialog()
|
||||
}
|
||||
BackupAlert.BackupFailed -> CommunicationActions.openBrowserLink(requireContext(), requireContext().getString(R.string.backup_failed_support_url))
|
||||
BackupAlert.BackupFailed -> {
|
||||
ContactSupportDialogFragment.create(
|
||||
subject = R.string.BackupAlertBottomSheet_network_failure_support_email,
|
||||
filter = R.string.BackupAlertBottomSheet_export_failure_filter
|
||||
).show(parentFragmentManager, null)
|
||||
}
|
||||
BackupAlert.CouldNotRedeemBackup -> CommunicationActions.openBrowserLink(requireContext(), requireContext().getString(R.string.backup_support_url)) // TODO [backups] final url
|
||||
}
|
||||
|
||||
@@ -399,8 +411,24 @@ private fun DiskFullBody(requiredSpace: String) {
|
||||
|
||||
@Composable
|
||||
private fun BackupFailedBody() {
|
||||
val context = LocalContext.current
|
||||
val text = buildAnnotatedString {
|
||||
append(stringResource(id = R.string.BackupAlertBottomSheet__an_error_occurred))
|
||||
append(" ")
|
||||
|
||||
withLink(
|
||||
LinkAnnotation.Clickable(tag = "learn-more") {
|
||||
CommunicationActions.openBrowserLink(context, context.getString(R.string.backup_failed_support_url))
|
||||
}
|
||||
) {
|
||||
withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) {
|
||||
append(stringResource(id = R.string.BackupAlertBottomSheet__learn_more))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.BackupAlertBottomSheet__an_error_occurred),
|
||||
text = text,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(bottom = 36.dp)
|
||||
@@ -463,7 +491,7 @@ private fun rememberSecondaryActionResource(backupAlert: BackupAlert): Int {
|
||||
is BackupAlert.MediaBackupsAreOff -> error("Not supported.")
|
||||
is BackupAlert.DownloadYourBackupData -> R.string.BackupAlertBottomSheet__dont_download_backup
|
||||
is BackupAlert.DiskFull -> R.string.BackupAlertBottomSheet__skip_restore
|
||||
is BackupAlert.BackupFailed -> R.string.BackupAlertBottomSheet__learn_more
|
||||
is BackupAlert.BackupFailed -> R.string.BackupAlertBottomSheet__contact_support
|
||||
BackupAlert.CouldNotRedeemBackup -> R.string.BackupAlertBottomSheet__learn_more
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.contactsupport
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.thoughtcrime.securesms.compose.ComposeDialogFragment
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
|
||||
/**
|
||||
* Three-option contact support dialog fragment.
|
||||
*/
|
||||
class ContactSupportDialogFragment : ComposeDialogFragment() {
|
||||
|
||||
companion object {
|
||||
private const val SUBJECT = "subject"
|
||||
private const val FILTER = "filter"
|
||||
|
||||
fun create(
|
||||
@StringRes subject: Int,
|
||||
@StringRes filter: Int
|
||||
): ContactSupportDialogFragment {
|
||||
return ContactSupportDialogFragment().apply {
|
||||
arguments = bundleOf(
|
||||
SUBJECT to subject,
|
||||
FILTER to filter
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val contactSupportViewModel: ContactSupportViewModel by viewModel {
|
||||
ContactSupportViewModel(
|
||||
showInitially = true
|
||||
)
|
||||
}
|
||||
|
||||
private val subject: Int by lazy { requireArguments().getInt(SUBJECT) }
|
||||
private val filter: Int by lazy { requireArguments().getInt(FILTER) }
|
||||
|
||||
@Composable
|
||||
override fun DialogContent() {
|
||||
val contactSupportState by contactSupportViewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
SendSupportEmailEffect(
|
||||
contactSupportState = contactSupportState,
|
||||
subjectRes = subject,
|
||||
filterRes = filter
|
||||
) {
|
||||
contactSupportViewModel.hideContactSupport()
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
if (contactSupportState.show) {
|
||||
ContactSupportDialog(
|
||||
showInProgress = contactSupportState.showAsProgress,
|
||||
callbacks = contactSupportViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,12 @@ import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository
|
||||
/**
|
||||
* Intended to be used to drive [ContactSupportDialog].
|
||||
*/
|
||||
class ContactSupportViewModel : ViewModel(), ContactSupportCallbacks {
|
||||
class ContactSupportViewModel(
|
||||
val showInitially: Boolean = false
|
||||
) : ViewModel(), ContactSupportCallbacks {
|
||||
private val submitDebugLogRepository: SubmitDebugLogRepository = SubmitDebugLogRepository()
|
||||
|
||||
private val store: MutableStateFlow<ContactSupportState> = MutableStateFlow(ContactSupportState())
|
||||
private val store: MutableStateFlow<ContactSupportState> = MutableStateFlow(ContactSupportState(show = showInitially))
|
||||
|
||||
val state: StateFlow<ContactSupportState> = store.asStateFlow()
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ import org.signal.core.util.getLength
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.BackupAlert
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.BackupAlertBottomSheet
|
||||
import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.DialogState
|
||||
import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.ScreenState
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
@@ -231,6 +233,12 @@ class InternalBackupPlaygroundFragment : ComposeFragment() {
|
||||
}
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show()
|
||||
},
|
||||
onDisplayInitialBackupFailureSheet = {
|
||||
BackupRepository.displayInitialBackupFailureNotification()
|
||||
BackupAlertBottomSheet
|
||||
.create(BackupAlert.BackupFailed)
|
||||
.show(parentFragmentManager, null)
|
||||
}
|
||||
)
|
||||
},
|
||||
@@ -314,7 +322,8 @@ fun Screen(
|
||||
onImportEncryptedBackupFromDiskClicked: () -> Unit = {},
|
||||
onImportEncryptedBackupFromDiskDismissed: () -> Unit = {},
|
||||
onImportEncryptedBackupFromDiskConfirmed: (aci: String, backupKey: String) -> Unit = { _, _ -> },
|
||||
onDeleteRemoteBackup: () -> Unit = {}
|
||||
onDeleteRemoteBackup: () -> Unit = {},
|
||||
onDisplayInitialBackupFailureSheet: () -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scrollState = rememberScrollState()
|
||||
@@ -506,11 +515,17 @@ fun Screen(
|
||||
|
||||
Dividers.Default()
|
||||
|
||||
Rows.TextRow(
|
||||
text = "Display initial backup failure sheet",
|
||||
label = "This will display the error sheet immediately and force the notification to display.",
|
||||
onClick = onDisplayInitialBackupFailureSheet
|
||||
)
|
||||
|
||||
Rows.TextRow(
|
||||
text = "Mark backup failure",
|
||||
label = "This will display the error sheet when returning to the chats list.",
|
||||
onClick = {
|
||||
SignalStore.backup.internalSetBackupFailedErrorState()
|
||||
BackupRepository.markBackupFailure()
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -92,8 +92,7 @@ class BackupMessagesJob private constructor(
|
||||
override fun onFailure() {
|
||||
if (!isCanceled) {
|
||||
Log.w(TAG, "Failed to backup user messages. Marking failure state.")
|
||||
SignalStore.backup.markMessageBackupFailure()
|
||||
ArchiveUploadProgress.onMainBackupFileUploadFailure()
|
||||
BackupRepository.markBackupFailure()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +199,7 @@ class BackupMessagesJob private constructor(
|
||||
ArchiveUploadProgress.onMessageBackupFinishedEarly()
|
||||
}
|
||||
|
||||
SignalStore.backup.clearMessageBackupFailure()
|
||||
BackupRepository.clearBackupFailure()
|
||||
SignalDatabase.backupMediaSnapshots.commitPendingRows()
|
||||
|
||||
AppDependencies.jobManager.add(ArchiveCommitAttachmentDeletesJob())
|
||||
|
||||
@@ -35,6 +35,7 @@ public final class NotificationIds {
|
||||
public static final int UNREGISTERED_NOTIFICATION_ID = 20230102;
|
||||
public static final int NEW_LINKED_DEVICE = 120400;
|
||||
public static final int OUT_OF_REMOTE_STORAGE = 120500;
|
||||
public static final int INITIAL_BACKUP_FAILED = 120501;
|
||||
|
||||
private NotificationIds() { }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user