Allow normal attachments to be validated with plaintextHashes.

This commit is contained in:
Greyson Parrelli
2025-06-23 12:13:30 -04:00
committed by Cody Henthorne
parent 607b83d65b
commit ec5452744d
23 changed files with 319 additions and 185 deletions

View File

@@ -25,7 +25,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
@@ -77,6 +76,8 @@ 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
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.ArchiveAttachmentReconciliationJob
import org.thoughtcrime.securesms.jobs.LocalBackupJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.Util
@@ -149,9 +150,9 @@ class InternalBackupPlaygroundFragment : ComposeFragment() {
mainContent = {
Screen(
state = state,
onBackupTierSelected = { tier -> viewModel.onBackupTierSelected(tier) },
onCheckRemoteBackupStateClicked = { viewModel.checkRemoteBackupState() },
onEnqueueRemoteBackupClicked = { viewModel.triggerBackupJob() },
onEnqueueReconciliationClicked = { AppDependencies.jobManager.add(ArchiveAttachmentReconciliationJob(forced = true)) },
onHaltAllBackupJobsClicked = { viewModel.haltAllJobs() },
onValidateBackupClicked = { viewModel.validateBackup() },
onSaveEncryptedBackupToDiskClicked = {
@@ -222,7 +223,7 @@ class InternalBackupPlaygroundFragment : ComposeFragment() {
onDeleteRemoteBackup = {
MaterialAlertDialogBuilder(context)
.setTitle("Are you sure?")
.setMessage("This will delete all of your remote backup data?")
.setMessage("This will delete all of your remote backup data!")
.setPositiveButton("Delete remote data") { _, _ ->
lifecycleScope.launch {
val success = viewModel.deleteRemoteBackupData()
@@ -234,6 +235,21 @@ class InternalBackupPlaygroundFragment : ComposeFragment() {
.setNegativeButton("Cancel", null)
.show()
},
onClearLocalMediaBackupState = {
MaterialAlertDialogBuilder(context)
.setTitle("Are you sure?")
.setMessage("This will cause you to have to re-upload all of your media!")
.setPositiveButton("Clear local media state") { _, _ ->
lifecycleScope.launch {
viewModel.clearLocalMediaBackupState()
withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), "Done!", Toast.LENGTH_SHORT).show()
}
}
}
.setNegativeButton("Cancel", null)
.show()
},
onDisplayInitialBackupFailureSheet = {
BackupRepository.displayInitialBackupFailureNotification()
BackupAlertBottomSheet
@@ -312,8 +328,8 @@ fun Screen(
onImportNewStyleLocalBackupClicked: () -> Unit = {},
onCheckRemoteBackupStateClicked: () -> Unit = {},
onEnqueueRemoteBackupClicked: () -> Unit = {},
onEnqueueReconciliationClicked: () -> Unit = {},
onWipeDataAndRestoreFromRemoteClicked: () -> Unit = {},
onBackupTierSelected: (MessageBackupTier?) -> Unit = {},
onHaltAllBackupJobsClicked: () -> Unit = {},
onSavePlaintextCopyOfRemoteBackupClicked: () -> Unit = {},
onValidateBackupClicked: () -> Unit = {},
@@ -322,6 +338,7 @@ fun Screen(
onImportEncryptedBackupFromDiskClicked: () -> Unit = {},
onImportEncryptedBackupFromDiskDismissed: () -> Unit = {},
onImportEncryptedBackupFromDiskConfirmed: (aci: String, backupKey: String) -> Unit = { _, _ -> },
onClearLocalMediaBackupState: () -> Unit = {},
onDeleteRemoteBackup: () -> Unit = {},
onDisplayInitialBackupFailureSheet: () -> Unit = {}
) {
@@ -353,21 +370,6 @@ fun Screen(
.fillMaxSize()
.verticalScroll(scrollState)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Tier", fontWeight = FontWeight.Bold)
options.forEach { option ->
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(
selected = option.value == state.backupTier,
onClick = { onBackupTierSelected(option.value) }
)
Text(option.key)
}
}
}
Dividers.Default()
Rows.TextRow(
text = {
Text(
@@ -392,6 +394,12 @@ fun Screen(
onClick = onEnqueueRemoteBackupClicked
)
Rows.TextRow(
text = "Enqueue reconciliation job",
label = "Schedules a job that will ensure local and remote media state are in sync.",
onClick = onEnqueueReconciliationClicked
)
Rows.TextRow(
text = "Halt all backup jobs",
label = "Stops all backup-related jobs to the best of our ability.",
@@ -513,6 +521,12 @@ fun Screen(
onClick = onDeleteRemoteBackup
)
Rows.TextRow(
text = "Clear local media backup state",
label = "Resets local state tracking so you think you haven't uploaded any media. The media still exists on the server.",
onClick = onClearLocalMediaBackupState
)
Dividers.Default()
Rows.TextRow(

View File

@@ -292,11 +292,6 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
}
}
fun onBackupTierSelected(backupTier: MessageBackupTier?) {
SignalStore.backup.backupTier = backupTier
_state.value = _state.value.copy(backupTier = backupTier)
}
fun onImportSelected() {
_state.value = _state.value.copy(dialog = DialogState.ImportCredentials)
}
@@ -398,6 +393,10 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
return@withContext false
}
suspend fun clearLocalMediaBackupState() = withContext(Dispatchers.IO) {
SignalDatabase.attachments.clearAllArchiveData()
}
override fun onCleared() {
disposables.clear()
}

View File

@@ -55,7 +55,7 @@ fun InternalBackupStatsTab(stats: InternalBackupPlaygroundViewModel.StatsState,
Spacer(modifier = Modifier.size(16.dp))
Text(text = "Unique/archived data files: ${stats.attachmentStats.attachmentFileCount}/${stats.attachmentStats.finishedAttachmentFileCount}")
Text(text = "Unique/archived verified digest count: ${stats.attachmentStats.attachmentPlaintextHashAndKeyCount}/${stats.attachmentStats.finishedAttachmentPlaintextHashAndKeyCount}")
Text(text = "Unique/archived verified plaintextHash count: ${stats.attachmentStats.attachmentPlaintextHashAndKeyCount}/${stats.attachmentStats.finishedAttachmentPlaintextHashAndKeyCount}")
Text(text = "Unique/expected thumbnail files: ${stats.attachmentStats.thumbnailFileCount}/${stats.attachmentStats.estimatedThumbnailCount}")
Text(text = "Local Total: ${stats.attachmentStats.attachmentFileCount + stats.attachmentStats.thumbnailFileCount}")
Text(text = "Expected remote total: ${stats.attachmentStats.estimatedThumbnailCount + stats.attachmentStats.finishedAttachmentPlaintextHashAndKeyCount}")