mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-24 02:39:55 +01:00
Improve display and management of backup progress.
This commit is contained in:
committed by
Cody Henthorne
parent
5b18f05aa8
commit
dd1697de41
@@ -11,7 +11,8 @@ import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
@@ -64,6 +65,7 @@ import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
@@ -108,7 +110,6 @@ import org.thoughtcrime.securesms.util.viewModel
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
@@ -137,7 +138,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state.collectAsState()
|
||||
val backupProgress by ArchiveUploadProgress.progress.collectAsState(initial = null)
|
||||
val backupProgress by ArchiveUploadProgress.progress.collectAsStateWithLifecycle(initialValue = null)
|
||||
val restoreState by viewModel.restoreState.collectAsState()
|
||||
val callbacks = remember { Callbacks() }
|
||||
|
||||
@@ -378,36 +379,34 @@ private fun RemoteBackupsSettingsContent(
|
||||
}
|
||||
|
||||
item {
|
||||
AnimatedContent(backupState, label = "backup-state-block") { state ->
|
||||
when (state) {
|
||||
is RemoteBackupsSettingsState.BackupState.Loading -> {
|
||||
LoadingCard()
|
||||
}
|
||||
when (backupState) {
|
||||
is RemoteBackupsSettingsState.BackupState.Loading -> {
|
||||
LoadingCard()
|
||||
}
|
||||
|
||||
is RemoteBackupsSettingsState.BackupState.Error -> {
|
||||
ErrorCard()
|
||||
}
|
||||
is RemoteBackupsSettingsState.BackupState.Error -> {
|
||||
ErrorCard()
|
||||
}
|
||||
|
||||
is RemoteBackupsSettingsState.BackupState.Pending -> {
|
||||
PendingCard(state.price)
|
||||
}
|
||||
is RemoteBackupsSettingsState.BackupState.Pending -> {
|
||||
PendingCard(backupState.price)
|
||||
}
|
||||
|
||||
is RemoteBackupsSettingsState.BackupState.SubscriptionMismatchMissingGooglePlay -> {
|
||||
SubscriptionMismatchMissingGooglePlayCard(
|
||||
state = state,
|
||||
onLearnMoreClick = contentCallbacks::onLearnMoreAboutLostSubscription,
|
||||
onRenewClick = contentCallbacks::onRenewLostSubscription
|
||||
)
|
||||
}
|
||||
is RemoteBackupsSettingsState.BackupState.SubscriptionMismatchMissingGooglePlay -> {
|
||||
SubscriptionMismatchMissingGooglePlayCard(
|
||||
state = backupState,
|
||||
onLearnMoreClick = contentCallbacks::onLearnMoreAboutLostSubscription,
|
||||
onRenewClick = contentCallbacks::onRenewLostSubscription
|
||||
)
|
||||
}
|
||||
|
||||
RemoteBackupsSettingsState.BackupState.None -> Unit
|
||||
RemoteBackupsSettingsState.BackupState.None -> Unit
|
||||
|
||||
is RemoteBackupsSettingsState.BackupState.WithTypeAndRenewalTime -> {
|
||||
BackupCard(
|
||||
backupState = state,
|
||||
onBackupTypeActionButtonClicked = contentCallbacks::onBackupTypeActionClick
|
||||
)
|
||||
}
|
||||
is RemoteBackupsSettingsState.BackupState.WithTypeAndRenewalTime -> {
|
||||
BackupCard(
|
||||
backupState = backupState,
|
||||
onBackupTypeActionButtonClicked = contentCallbacks::onBackupTypeActionClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -993,14 +992,26 @@ private fun InProgressBackupRow(
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
val backupProgress = getBackupProgress(archiveUploadProgressState)
|
||||
if (backupProgress.total == 0L) {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
} else {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
progress = { backupProgress.progress }
|
||||
)
|
||||
when (archiveUploadProgressState.state) {
|
||||
ArchiveUploadProgressState.State.None -> {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
ArchiveUploadProgressState.State.Export -> {
|
||||
val progressValue by animateFloatAsState(targetValue = archiveUploadProgressState.frameExportProgress(), animationSpec = tween(durationMillis = 250))
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
progress = { progressValue },
|
||||
drawStopIndicator = {}
|
||||
)
|
||||
}
|
||||
ArchiveUploadProgressState.State.UploadBackupFile, ArchiveUploadProgressState.State.UploadMedia -> {
|
||||
val progressValue by animateFloatAsState(targetValue = archiveUploadProgressState.uploadProgress(), animationSpec = tween(durationMillis = 250))
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
progress = { progressValue },
|
||||
drawStopIndicator = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
@@ -1012,33 +1023,26 @@ private fun InProgressBackupRow(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBackupProgress(state: ArchiveUploadProgressState): BackupProgress {
|
||||
val approximateMessageCount = max(state.completedAttachments, state.totalAttachments)
|
||||
return BackupProgress(state.completedAttachments, approximateMessageCount)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getProgressStateMessage(archiveUploadProgressState: ArchiveUploadProgressState): String {
|
||||
return when (archiveUploadProgressState.state) {
|
||||
ArchiveUploadProgressState.State.None -> stringResource(R.string.RemoteBackupsSettingsFragment__processing_backup)
|
||||
ArchiveUploadProgressState.State.BackingUpMessages -> getBackupPhaseMessage(archiveUploadProgressState)
|
||||
ArchiveUploadProgressState.State.UploadingMessages -> getUploadingMessages(archiveUploadProgressState)
|
||||
ArchiveUploadProgressState.State.UploadingAttachments -> getUploadingAttachmentsMessage(archiveUploadProgressState)
|
||||
ArchiveUploadProgressState.State.Export -> getBackupExportPhaseProgressString(archiveUploadProgressState)
|
||||
ArchiveUploadProgressState.State.UploadBackupFile, ArchiveUploadProgressState.State.UploadMedia -> getBackupUploadPhaseProgressString(archiveUploadProgressState)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getBackupPhaseMessage(state: ArchiveUploadProgressState): String {
|
||||
private fun getBackupExportPhaseProgressString(state: ArchiveUploadProgressState): String {
|
||||
return when (state.backupPhase) {
|
||||
ArchiveUploadProgressState.BackupPhase.BackupPhaseNone -> stringResource(R.string.RemoteBackupsSettingsFragment__processing_backup)
|
||||
ArchiveUploadProgressState.BackupPhase.Message -> {
|
||||
val progress = getBackupProgress(state)
|
||||
pluralStringResource(
|
||||
R.plurals.RemoteBackupsSettingsFragment__processing_d_of_d_d_messages,
|
||||
progress.total.toInt(),
|
||||
"%,d".format(progress.completed.toInt()),
|
||||
"%,d".format(progress.total.toInt()),
|
||||
(progress.progress * 100).toInt()
|
||||
R.plurals.RemoteBackupsSettingsFragment__processing_messages_progress_text,
|
||||
state.frameTotalCount.toInt(),
|
||||
"%,d".format(state.frameExportCount),
|
||||
"%,d".format(state.frameTotalCount),
|
||||
(state.frameExportProgress() * 100).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1047,25 +1051,12 @@ private fun getBackupPhaseMessage(state: ArchiveUploadProgressState): String {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getUploadingMessages(state: ArchiveUploadProgressState): String {
|
||||
val formattedCompleted = state.completedAttachments.bytes.toUnitString()
|
||||
val formattedTotal = state.totalAttachments.bytes.toUnitString()
|
||||
val percent = if (state.totalAttachments == 0L) {
|
||||
0
|
||||
} else {
|
||||
((state.completedAttachments / state.totalAttachments.toFloat()) * 100).toInt()
|
||||
}
|
||||
private fun getBackupUploadPhaseProgressString(state: ArchiveUploadProgressState): String {
|
||||
val formattedTotalBytes = state.uploadBytesTotal.bytes.toUnitString()
|
||||
val formattedUploadedBytes = state.uploadBytesUploaded.bytes.toUnitString()
|
||||
val percent = (state.uploadProgress() * 100).toInt()
|
||||
|
||||
return stringResource(R.string.RemoteBackupsSettingsFragment__uploading_s_of_s_d, formattedCompleted, formattedTotal, percent)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getUploadingAttachmentsMessage(state: ArchiveUploadProgressState): String {
|
||||
return if (state.totalAttachments == 0L) {
|
||||
stringResource(R.string.RemoteBackupsSettingsFragment__processing_backup)
|
||||
} else {
|
||||
stringResource(R.string.RemoteBackupsSettingsFragment__d_slash_d, state.completedAttachments, state.totalAttachments)
|
||||
}
|
||||
return stringResource(R.string.RemoteBackupsSettingsFragment__uploading_s_of_s_d, formattedUploadedBytes, formattedTotalBytes, percent)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1475,70 +1466,82 @@ private fun InProgressRowPreview() {
|
||||
InProgressBackupRow(archiveUploadProgressState = ArchiveUploadProgressState())
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.BackupPhaseNone
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Account
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Call
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Sticker
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Recipient
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Thread
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Message,
|
||||
completedAttachments = 1,
|
||||
totalAttachments = 1
|
||||
frameExportCount = 1,
|
||||
frameTotalCount = 1
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Message,
|
||||
completedAttachments = 1000,
|
||||
totalAttachments = 100_000
|
||||
frameExportCount = 1000,
|
||||
frameTotalCount = 100_000
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.BackingUpMessages,
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Message,
|
||||
completedAttachments = 1_000_000,
|
||||
totalAttachments = 100_000
|
||||
frameExportCount = 1_000_000,
|
||||
frameTotalCount = 100_000
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.UploadingMessages,
|
||||
state = ArchiveUploadProgressState.State.UploadBackupFile,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.BackupPhaseNone,
|
||||
completedAttachments = 1.gibiBytes.inWholeBytes + 100.mebiBytes.inWholeBytes,
|
||||
totalAttachments = 12.gibiBytes.inWholeBytes
|
||||
backupFileUploadedBytes = 10.mebiBytes.inWholeBytes,
|
||||
backupFileTotalBytes = 50.mebiBytes.inWholeBytes,
|
||||
mediaUploadedBytes = 0,
|
||||
mediaTotalBytes = 0
|
||||
)
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.UploadMedia,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.BackupPhaseNone,
|
||||
backupFileUploadedBytes = 10.mebiBytes.inWholeBytes,
|
||||
backupFileTotalBytes = 50.mebiBytes.inWholeBytes,
|
||||
mediaUploadedBytes = 100.mebiBytes.inWholeBytes,
|
||||
mediaTotalBytes = 1.gibiBytes.inWholeBytes
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1614,3 +1617,28 @@ private data class BackupProgress(
|
||||
) {
|
||||
val progress: Float = if (total > 0) completed / total.toFloat() else 0f
|
||||
}
|
||||
|
||||
private fun ArchiveUploadProgressState.frameExportProgress(): Float {
|
||||
return if (this.frameTotalCount == 0L) {
|
||||
0f
|
||||
} else {
|
||||
this.frameExportCount / this.frameTotalCount.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArchiveUploadProgressState.uploadProgress(): Float {
|
||||
val current = this.backupFileUploadedBytes + this.mediaUploadedBytes
|
||||
val total = this.backupFileTotalBytes + this.mediaTotalBytes
|
||||
|
||||
return if (total == 0L) {
|
||||
0f
|
||||
} else {
|
||||
current / total.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private val ArchiveUploadProgressState.uploadBytesTotal: Long
|
||||
get() = this.backupFileTotalBytes + this.mediaTotalBytes
|
||||
|
||||
private val ArchiveUploadProgressState.uploadBytesUploaded: Long
|
||||
get() = this.backupFileUploadedBytes + this.mediaUploadedBytes
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.signal.core.util.bytes
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.thoughtcrime.securesms.backup.ArchiveUploadProgress
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
@@ -41,6 +42,7 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.BackupMessagesJob
|
||||
import org.thoughtcrime.securesms.jobs.RestoreOptimizedMediaJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
|
||||
import org.thoughtcrime.securesms.service.MessageBackupListener
|
||||
import java.util.Currency
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
@@ -103,6 +105,20 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
delay(1.seconds)
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
var previous: ArchiveUploadProgressState.State? = null
|
||||
ArchiveUploadProgress.progress
|
||||
.collect { current ->
|
||||
if (previous != null && current.state == ArchiveUploadProgressState.State.None) {
|
||||
_state.update {
|
||||
it.copy(lastBackupTimestamp = SignalStore.backup.lastBackupTime)
|
||||
}
|
||||
refreshState(null)
|
||||
}
|
||||
previous = current.state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setCanBackUpUsingCellular(canBackUpUsingCellular: Boolean) {
|
||||
@@ -154,6 +170,42 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun turnOffAndDeleteBackups() {
|
||||
viewModelScope.launch {
|
||||
Log.d(TAG, "Beginning to turn off and delete backup.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.PROGRESS_SPINNER)
|
||||
|
||||
val hasMediaBackupUploaded = SignalStore.backup.backsUpMedia && SignalStore.backup.hasBackupBeenUploaded
|
||||
|
||||
val succeeded = withContext(Dispatchers.IO) {
|
||||
BackupRepository.turnOffAndDisableBackups()
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
if (succeeded) {
|
||||
if (hasMediaBackupUploaded && SignalStore.backup.optimizeStorage) {
|
||||
Log.d(TAG, "User has optimized storage, downloading.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.DOWNLOADING_YOUR_BACKUP)
|
||||
|
||||
SignalStore.backup.optimizeStorage = false
|
||||
RestoreOptimizedMediaJob.enqueue()
|
||||
} else {
|
||||
Log.d(TAG, "User does not have optimized storage, finished.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.NONE)
|
||||
}
|
||||
refresh()
|
||||
} else {
|
||||
Log.d(TAG, "Failed to disable backups.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.TURN_OFF_FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onBackupNowClick() {
|
||||
BackupMessagesJob.enqueue()
|
||||
}
|
||||
|
||||
private suspend fun refreshState(lastPurchase: InAppPaymentTable.InAppPayment?) {
|
||||
val tier = SignalStore.backup.latestBackupTier
|
||||
|
||||
@@ -307,39 +359,6 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun turnOffAndDeleteBackups() {
|
||||
viewModelScope.launch {
|
||||
Log.d(TAG, "Beginning to turn off and delete backup.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.PROGRESS_SPINNER)
|
||||
|
||||
val hasMediaBackupUploaded = SignalStore.backup.backsUpMedia && SignalStore.backup.hasBackupBeenUploaded
|
||||
|
||||
val succeeded = withContext(Dispatchers.IO) {
|
||||
BackupRepository.turnOffAndDisableBackups()
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
if (succeeded) {
|
||||
if (hasMediaBackupUploaded && SignalStore.backup.optimizeStorage) {
|
||||
Log.d(TAG, "User has optimized storage, downloading.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.DOWNLOADING_YOUR_BACKUP)
|
||||
|
||||
SignalStore.backup.optimizeStorage = false
|
||||
RestoreOptimizedMediaJob.enqueue()
|
||||
} else {
|
||||
Log.d(TAG, "User does not have optimized storage, finished.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.NONE)
|
||||
}
|
||||
refresh()
|
||||
} else {
|
||||
Log.d(TAG, "Failed to disable backups.")
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.TURN_OFF_FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onBackupNowClick() {
|
||||
BackupMessagesJob.enqueue()
|
||||
private fun refreshLocalState() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.components.settings.conversation
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.text.TextUtils
|
||||
import android.widget.Toast
|
||||
@@ -16,11 +17,14 @@ import org.signal.core.util.withinTransaction
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.thoughtcrime.securesms.MainActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.attachments.Attachment
|
||||
import org.thoughtcrime.securesms.attachments.UriAttachment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.database.MessageType
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
||||
@@ -31,14 +35,18 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mms.IncomingMessage
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import java.util.Objects
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.nanoseconds
|
||||
import kotlin.time.DurationUnit
|
||||
|
||||
@@ -267,40 +275,71 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Add 1,000 dummy messages"),
|
||||
summary = DSLSettingsText.from("Just adds 1,000 random messages to the chat. Text-only, nothing complicated."),
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("Are you sure?")
|
||||
.setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val messageCount = 1000
|
||||
val startTime = System.currentTimeMillis() - messageCount
|
||||
SignalDatabase.rawDatabase.withinTransaction {
|
||||
val targetThread = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
|
||||
for (i in 1..messageCount) {
|
||||
val time = startTime + i
|
||||
if (Math.random() > 0.5) {
|
||||
if (!recipient.isGroup) {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Add 1,000 dummy messages"),
|
||||
summary = DSLSettingsText.from("Just adds 1,000 random messages to the chat. Text-only, nothing complicated."),
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("Are you sure?")
|
||||
.setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val messageCount = 1000
|
||||
val startTime = System.currentTimeMillis() - messageCount
|
||||
SignalDatabase.rawDatabase.withinTransaction {
|
||||
val targetThread = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
|
||||
for (i in 1..messageCount) {
|
||||
val time = startTime + i
|
||||
if (Math.random() > 0.5) {
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(
|
||||
message = OutgoingMessage(threadRecipient = recipient, sentTimeMillis = time, body = "Outgoing: $i"),
|
||||
threadId = targetThread
|
||||
)
|
||||
SignalDatabase.messages.markAsSent(id, true)
|
||||
} else {
|
||||
SignalDatabase.messages.insertMessageInbox(
|
||||
retrieved = IncomingMessage(type = MessageType.NORMAL, from = recipient.id, sentTimeMillis = time, serverTimeMillis = time, receivedTimeMillis = System.currentTimeMillis(), body = "Incoming: $i"),
|
||||
candidateThreadId = targetThread
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Toast.makeText(context, "Done!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Add 10 dummy messages with attachments"),
|
||||
summary = DSLSettingsText.from("Adds 10 random messages to the chat with attachments of a random image. Attachments are not uploaded."),
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("Are you sure?")
|
||||
.setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val messageCount = 10
|
||||
val startTime = System.currentTimeMillis() - messageCount
|
||||
SignalDatabase.rawDatabase.withinTransaction {
|
||||
val targetThread = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
|
||||
for (i in 1..messageCount) {
|
||||
val time = startTime + i
|
||||
val attachment = makeDummyAttachment()
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(
|
||||
message = OutgoingMessage(threadRecipient = recipient, sentTimeMillis = time, body = "Outgoing: $i"),
|
||||
message = OutgoingMessage(threadRecipient = recipient, sentTimeMillis = time, body = "Outgoing: $i", attachments = listOf(attachment)),
|
||||
threadId = targetThread
|
||||
)
|
||||
SignalDatabase.messages.markAsSent(id, true)
|
||||
} else {
|
||||
SignalDatabase.messages.insertMessageInbox(
|
||||
retrieved = IncomingMessage(type = MessageType.NORMAL, from = recipient.id, sentTimeMillis = time, serverTimeMillis = time, receivedTimeMillis = System.currentTimeMillis(), body = "Incoming: $i"),
|
||||
candidateThreadId = targetThread
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Toast.makeText(context, "Done!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
)
|
||||
Toast.makeText(context, "Done!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (recipient.isSelf) {
|
||||
sectionHeaderPref(DSLSettingsText.from("Donations"))
|
||||
@@ -399,6 +438,37 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeDummyAttachment(): Attachment {
|
||||
val bitmapDimens = 1024
|
||||
val bitmap = Bitmap.createBitmap(
|
||||
IntArray(bitmapDimens * bitmapDimens) { Random.nextInt(0xFFFFFF) },
|
||||
0,
|
||||
bitmapDimens,
|
||||
bitmapDimens,
|
||||
bitmapDimens,
|
||||
Bitmap.Config.RGB_565
|
||||
)
|
||||
val stream = BitmapUtil.toCompressedJpeg(bitmap)
|
||||
val bytes = stream.readBytes()
|
||||
val uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionOnDisk(requireContext())
|
||||
return UriAttachment(
|
||||
uri = uri,
|
||||
contentType = MediaUtil.IMAGE_JPEG,
|
||||
transferState = AttachmentTable.TRANSFER_PROGRESS_DONE,
|
||||
size = bytes.size.toLong(),
|
||||
fileName = null,
|
||||
voiceNote = false,
|
||||
borderless = false,
|
||||
videoGif = false,
|
||||
quote = false,
|
||||
caption = null,
|
||||
stickerLocator = null,
|
||||
blurHash = null,
|
||||
audioHash = null,
|
||||
transformProperties = null
|
||||
)
|
||||
}
|
||||
|
||||
private fun copyToClipboard(text: String) {
|
||||
Util.copyToClipboard(requireContext(), text)
|
||||
Toast.makeText(requireContext(), "Copied to clipboard", Toast.LENGTH_SHORT).show()
|
||||
|
||||
Reference in New Issue
Block a user