From d81182633dcd8e6c898fd0205d5bcda8c4e5a041 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 24 Sep 2024 10:23:10 -0400 Subject: [PATCH 01/53] Fix group call update backup import/export. --- .../database/CallLinkTableBackupExtensions.kt | 4 +- .../v2/database/ChatItemExportIterator.kt | 214 +++++++++--------- .../v2/database/ChatItemImportInserter.kt | 27 ++- 3 files changed, 128 insertions(+), 117 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt index 9af0f228ee..fdf2dbe61d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt @@ -76,7 +76,9 @@ class BackupCallLinkIterator(private val cursor: Cursor) : Iterator { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.AUDIO_CALL, - state = IndividualCall.State.MISSED, - direction = IndividualCall.Direction.INCOMING - ) + if (call != null) { + return call.toCallUpdate(this) + } + + return when { + MessageTypes.isMissedAudioCall(this.type) -> { + ChatUpdateMessage( + individualCall = IndividualCall( + type = IndividualCall.Type.AUDIO_CALL, + state = IndividualCall.State.MISSED, + direction = IndividualCall.Direction.INCOMING ) - } - MessageTypes.isMissedVideoCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.VIDEO_CALL, - state = IndividualCall.State.MISSED, - direction = IndividualCall.Direction.INCOMING - ) + ) + } + MessageTypes.isMissedVideoCall(this.type) -> { + ChatUpdateMessage( + individualCall = IndividualCall( + type = IndividualCall.Type.VIDEO_CALL, + state = IndividualCall.State.MISSED, + direction = IndividualCall.Direction.INCOMING ) - } - MessageTypes.isIncomingAudioCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.AUDIO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.INCOMING - ) + ) + } + MessageTypes.isIncomingAudioCall(this.type) -> { + ChatUpdateMessage( + individualCall = IndividualCall( + type = IndividualCall.Type.AUDIO_CALL, + state = IndividualCall.State.ACCEPTED, + direction = IndividualCall.Direction.INCOMING ) - } - MessageTypes.isIncomingVideoCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.VIDEO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.INCOMING - ) + ) + } + MessageTypes.isIncomingVideoCall(this.type) -> { + ChatUpdateMessage( + individualCall = IndividualCall( + type = IndividualCall.Type.VIDEO_CALL, + state = IndividualCall.State.ACCEPTED, + direction = IndividualCall.Direction.INCOMING ) - } - MessageTypes.isOutgoingAudioCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.AUDIO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.OUTGOING - ) + ) + } + MessageTypes.isOutgoingAudioCall(this.type) -> { + ChatUpdateMessage( + individualCall = IndividualCall( + type = IndividualCall.Type.AUDIO_CALL, + state = IndividualCall.State.ACCEPTED, + direction = IndividualCall.Direction.OUTGOING ) - } - MessageTypes.isOutgoingVideoCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.VIDEO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.OUTGOING - ) + ) + } + MessageTypes.isOutgoingVideoCall(this.type) -> { + ChatUpdateMessage( + individualCall = IndividualCall( + type = IndividualCall.Type.VIDEO_CALL, + state = IndividualCall.State.ACCEPTED, + direction = IndividualCall.Direction.OUTGOING ) - } - MessageTypes.isGroupCall(this.type) -> { - try { - val groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(this.body) - ChatUpdateMessage( - groupCall = GroupCall( - state = GroupCall.State.GENERIC, - startedCallRecipientId = UuidUtil.parseOrNull(groupCallUpdateDetails.startedCallUuid)?.let { recipients.getByAci(ACI.from(it)).getOrNull()?.toLong() }, - startedCallTimestamp = groupCallUpdateDetails.startedCallTimestamp, - endedCallTimestamp = groupCallUpdateDetails.endedCallTimestamp - ) - ) - } catch (exception: IOException) { - null - } - } - else -> { - null - } + ) + } + else -> { + null } } } - private fun CallTable.Call.toCallUpdate(): ChatUpdateMessage? { - return if (this.type == CallTable.Type.GROUP_CALL) { - ChatUpdateMessage( - groupCall = GroupCall( - callId = this.messageId, - state = when (this.event) { - CallTable.Event.MISSED -> GroupCall.State.MISSED - CallTable.Event.ONGOING -> GroupCall.State.GENERIC - CallTable.Event.ACCEPTED -> GroupCall.State.ACCEPTED - CallTable.Event.NOT_ACCEPTED -> GroupCall.State.GENERIC - CallTable.Event.MISSED_NOTIFICATION_PROFILE -> GroupCall.State.MISSED_NOTIFICATION_PROFILE - CallTable.Event.GENERIC_GROUP_CALL -> GroupCall.State.GENERIC - CallTable.Event.JOINED -> GroupCall.State.JOINED - CallTable.Event.RINGING -> GroupCall.State.RINGING - CallTable.Event.DECLINED -> GroupCall.State.DECLINED - CallTable.Event.OUTGOING_RING -> GroupCall.State.OUTGOING_RING - CallTable.Event.DELETE -> return null - }, - ringerRecipientId = this.ringerRecipient?.toLong(), - startedCallRecipientId = this.ringerRecipient?.toLong(), - startedCallTimestamp = this.timestamp + private fun CallTable.Call.toCallUpdate(messageRecord: BackupMessageRecord): ChatUpdateMessage? { + return when (this.type) { + CallTable.Type.GROUP_CALL -> { + val groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(messageRecord.body) + + ChatUpdateMessage( + groupCall = GroupCall( + callId = this.callId, + state = when (this.event) { + CallTable.Event.MISSED -> GroupCall.State.MISSED + CallTable.Event.ONGOING -> GroupCall.State.GENERIC + CallTable.Event.ACCEPTED -> GroupCall.State.ACCEPTED + CallTable.Event.NOT_ACCEPTED -> GroupCall.State.GENERIC + CallTable.Event.MISSED_NOTIFICATION_PROFILE -> GroupCall.State.MISSED_NOTIFICATION_PROFILE + CallTable.Event.GENERIC_GROUP_CALL -> GroupCall.State.GENERIC + CallTable.Event.JOINED -> GroupCall.State.JOINED + CallTable.Event.RINGING -> GroupCall.State.RINGING + CallTable.Event.DECLINED -> GroupCall.State.DECLINED + CallTable.Event.OUTGOING_RING -> GroupCall.State.OUTGOING_RING + CallTable.Event.DELETE -> return null + }, + ringerRecipientId = this.ringerRecipient?.toLong(), + startedCallRecipientId = ACI.parseOrNull(groupCallUpdateDetails.startedCallUuid)?.let { recipients.getByAci(it).getOrNull()?.toLong() }, + startedCallTimestamp = this.timestamp, + endedCallTimestamp = groupCallUpdateDetails.endedCallTimestamp, + read = messageRecord.read + ) ) - ) - } else if (this.type != CallTable.Type.AD_HOC_CALL) { - ChatUpdateMessage( - individualCall = IndividualCall( - callId = this.callId, - type = if (this.type == CallTable.Type.VIDEO_CALL) IndividualCall.Type.VIDEO_CALL else IndividualCall.Type.AUDIO_CALL, - direction = if (this.direction == CallTable.Direction.INCOMING) IndividualCall.Direction.INCOMING else IndividualCall.Direction.OUTGOING, - state = when (this.event) { - CallTable.Event.MISSED -> IndividualCall.State.MISSED - CallTable.Event.MISSED_NOTIFICATION_PROFILE -> IndividualCall.State.MISSED_NOTIFICATION_PROFILE - CallTable.Event.ACCEPTED -> IndividualCall.State.ACCEPTED - CallTable.Event.NOT_ACCEPTED -> IndividualCall.State.NOT_ACCEPTED - else -> IndividualCall.State.UNKNOWN_STATE - }, - startedCallTimestamp = this.timestamp + } + + CallTable.Type.AD_HOC_CALL -> { + ChatUpdateMessage( + individualCall = IndividualCall( + callId = this.callId, + type = if (this.type == CallTable.Type.VIDEO_CALL) IndividualCall.Type.VIDEO_CALL else IndividualCall.Type.AUDIO_CALL, + direction = if (this.direction == CallTable.Direction.INCOMING) IndividualCall.Direction.INCOMING else IndividualCall.Direction.OUTGOING, + state = when (this.event) { + CallTable.Event.MISSED -> IndividualCall.State.MISSED + CallTable.Event.MISSED_NOTIFICATION_PROFILE -> IndividualCall.State.MISSED_NOTIFICATION_PROFILE + CallTable.Event.ACCEPTED -> IndividualCall.State.ACCEPTED + CallTable.Event.NOT_ACCEPTED -> IndividualCall.State.NOT_ACCEPTED + else -> IndividualCall.State.UNKNOWN_STATE + }, + startedCallTimestamp = this.timestamp + ) ) - ) - } else { - null + } + + else -> null } } @@ -1074,7 +1067,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: MessageTypes.isReportedSpam(this) || MessageTypes.isMessageRequestAccepted(this) || MessageTypes.isBlocked(this) || - MessageTypes.isUnblocked(this) + MessageTypes.isUnblocked(this) || + MessageTypes.isGroupCall(this) } private fun String.e164ToLong(): Long? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index c82c8e3c14..68dcaf4e60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -262,12 +262,15 @@ class ChatItemImportInserter( } } else if (this.updateMessage.groupCall != null && this.updateMessage.groupCall.callId != null) { followUp = { messageRowId -> + val ringer: RecipientId? = this.updateMessage.groupCall.ringerRecipientId?.let { importState.remoteToLocalRecipientId[it] } + val values = contentValuesOf( CallTable.CALL_ID to updateMessage.groupCall.callId, CallTable.MESSAGE_ID to messageRowId, CallTable.PEER to chatRecipientId.serialize(), + CallTable.RINGER to ringer?.serialize(), CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.GROUP_CALL), - CallTable.DIRECTION to CallTable.Direction.serialize(if (importState.remoteToLocalRecipientId[updateMessage.groupCall.ringerRecipientId] == selfId) CallTable.Direction.OUTGOING else CallTable.Direction.INCOMING), + CallTable.DIRECTION to CallTable.Direction.serialize(if (ringer == selfId) CallTable.Direction.OUTGOING else CallTable.Direction.INCOMING), CallTable.EVENT to CallTable.Event.serialize( when (updateMessage.groupCall.state) { GroupCall.State.ACCEPTED -> CallTable.Event.ACCEPTED @@ -673,15 +676,26 @@ class ChatItemImportInserter( } updateMessage.individualCall != null -> { if (updateMessage.individualCall.state == IndividualCall.State.MISSED || updateMessage.individualCall.state == IndividualCall.State.MISSED_NOTIFICATION_PROFILE) { - typeFlags = if (updateMessage.individualCall.type == IndividualCall.Type.AUDIO_CALL) MessageTypes.MISSED_AUDIO_CALL_TYPE else MessageTypes.MISSED_VIDEO_CALL_TYPE + typeFlags = if (updateMessage.individualCall.type == IndividualCall.Type.AUDIO_CALL) { + MessageTypes.MISSED_AUDIO_CALL_TYPE + } else { + MessageTypes.MISSED_VIDEO_CALL_TYPE + } } else { typeFlags = if (updateMessage.individualCall.direction == IndividualCall.Direction.OUTGOING) { - if (updateMessage.individualCall.type == IndividualCall.Type.AUDIO_CALL) MessageTypes.OUTGOING_AUDIO_CALL_TYPE else MessageTypes.OUTGOING_VIDEO_CALL_TYPE + if (updateMessage.individualCall.type == IndividualCall.Type.AUDIO_CALL) { + MessageTypes.OUTGOING_AUDIO_CALL_TYPE + } else { + MessageTypes.OUTGOING_VIDEO_CALL_TYPE + } } else { - if (updateMessage.individualCall.type == IndividualCall.Type.AUDIO_CALL) MessageTypes.INCOMING_AUDIO_CALL_TYPE else MessageTypes.INCOMING_VIDEO_CALL_TYPE + if (updateMessage.individualCall.type == IndividualCall.Type.AUDIO_CALL) { + MessageTypes.INCOMING_AUDIO_CALL_TYPE + } else { + MessageTypes.INCOMING_VIDEO_CALL_TYPE + } } } - this.put(MessageTable.TYPE, typeFlags) } updateMessage.groupCall != null -> { val startedCallRecipientId = if (updateMessage.groupCall.startedCallRecipientId != null) { @@ -695,7 +709,8 @@ class ChatItemImportInserter( null } this.put(MessageTable.BODY, GroupCallUpdateDetailsUtil.createBodyFromBackup(updateMessage.groupCall, startedCall)) - this.put(MessageTable.TYPE, MessageTypes.GROUP_CALL_TYPE) + this.put(MessageTable.READ, updateMessage.groupCall.read.toInt()) + typeFlags = MessageTypes.GROUP_CALL_TYPE } updateMessage.groupChange != null -> { put(MessageTable.BODY, "") From 5552455c2e9327a4a15db462336595ecf87a8cde Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 24 Sep 2024 11:41:50 -0400 Subject: [PATCH 02/53] Check for restorable previously optimized media and restore if necessary. --- .../securesms/ApplicationContext.java | 2 ++ .../jobs/RestoreOptimizedMediaJob.kt | 21 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 5792953542..27c1f7cd13 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -69,6 +69,7 @@ import org.thoughtcrime.securesms.jobs.PreKeysSyncJob; import org.thoughtcrime.securesms.jobs.ProfileUploadJob; import org.thoughtcrime.securesms.jobs.RefreshSvrCredentialsJob; +import org.thoughtcrime.securesms.jobs.RestoreOptimizedMediaJob; import org.thoughtcrime.securesms.jobs.RetrieveProfileJob; import org.thoughtcrime.securesms.jobs.RetrieveRemoteAnnouncementsJob; import org.thoughtcrime.securesms.jobs.StoryOnboardingDownloadJob; @@ -223,6 +224,7 @@ public void onCreate() { .addPostRender(LinkedDeviceInactiveCheckJob::enqueueIfNecessary) .addPostRender(() -> ActiveCallManager.clearNotifications(this)) .addPostRender(() -> GroupSendEndorsementInternalNotifier.init()) + .addPostRender(RestoreOptimizedMediaJob::enqueueIfNecessary) .execute(); Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt index e5a97f033f..5591ae4ff8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt @@ -23,6 +23,13 @@ class RestoreOptimizedMediaJob private constructor(parameters: Parameters) : Job val job = RestoreOptimizedMediaJob() AppDependencies.jobManager.add(job) } + + @JvmStatic + fun enqueueIfNecessary() { + if (SignalStore.backup.backsUpMedia && !SignalStore.backup.optimizeStorage) { + AppDependencies.jobManager.add(RestoreOptimizedMediaJob()) + } + } } private constructor() : this( @@ -34,11 +41,19 @@ class RestoreOptimizedMediaJob private constructor(parameters: Parameters) : Job ) override fun run(): Result { + if (SignalStore.backup.optimizeStorage) { + return Result.success() + } + + val restorableAttachments = SignalDatabase.attachments.getRestorableOptimizedAttachments() + + if (restorableAttachments.isEmpty()) { + return Result.success() + } + val jobManager = AppDependencies.jobManager - SignalDatabase - .attachments - .getRestorableOptimizedAttachments() + restorableAttachments .forEach { val job = RestoreAttachmentJob.forOffloadedRestore( messageId = it.mmsId, From eaf81e56d6ee7e51861f0d78c70874a1b94eef03 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 24 Sep 2024 14:04:47 -0300 Subject: [PATCH 03/53] Add turn on your video tooltip to call screen v2. --- .../components/webrtc/v2/CallActivity.kt | 7 ++ .../components/webrtc/v2/CallControls.kt | 18 +++- .../components/webrtc/v2/CallScreen.kt | 1 + .../components/webrtc/v2/CallScreenState.kt | 6 +- .../webrtc/v2/CallScreenTooltipBox.kt | 87 +++++++++++++++++++ .../components/webrtc/v2/CallViewModel.kt | 45 +++++++--- core-util-jvm/build.gradle.kts | 4 + 7 files changed, 153 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenTooltipBox.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt index c7e3aba5a8..c8e463b122 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt @@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.events.WebRtcViewModel import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet import org.thoughtcrime.securesms.util.FullscreenHelper import org.thoughtcrime.securesms.util.VibrateUtil import org.thoughtcrime.securesms.util.viewModel @@ -101,6 +102,8 @@ class CallActivity : BaseActivity(), CallControlsCallback { viewModel.callActions.collect { when (it) { CallViewModel.Action.EnableVideo -> onVideoToggleClick(true) + is CallViewModel.Action.ShowGroupCallSafetyNumberChangeDialog -> SafetyNumberBottomSheet.forGroupCall(it.untrustedIdentities).show(supportFragmentManager) + CallViewModel.Action.SwitchToSpeaker -> Unit // TODO - Switch user to speaker view. } } } @@ -324,6 +327,10 @@ class CallActivity : BaseActivity(), CallControlsCallback { viewModel.hangup() } + override fun onVideoTooltipDismissed() { + viewModel.onVideoTooltipDismissed() + } + private fun observeCallEvents() { webRtcCallViewModel.events.observe(this) { event -> viewModel.onCallEvent(event) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallControls.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallControls.kt index 314263da10..942ea732d6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallControls.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallControls.kt @@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.util.RemoteConfig */ @Composable fun CallControls( + displayVideoTooltip: Boolean, callControlsState: CallControlsState, callControlsCallback: CallControlsCallback, modifier: Modifier = Modifier @@ -96,10 +97,16 @@ fun CallControls( val hasCameraPermission = ContextCompat.checkSelfPermission(LocalContext.current, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED if (callControlsState.displayVideoToggle) { - ToggleVideoButton( - isVideoEnabled = callControlsState.isVideoEnabled && hasCameraPermission, - onChange = callControlsCallback::onVideoToggleClick - ) + CallScreenTooltipBox( + text = stringResource(R.string.WebRtcCallActivity__tap_here_to_turn_on_your_video), + displayTooltip = displayVideoTooltip, + onTooltipDismissed = callControlsCallback::onVideoTooltipDismissed + ) { + ToggleVideoButton( + isVideoEnabled = callControlsState.isVideoEnabled && hasCameraPermission, + onChange = callControlsCallback::onVideoToggleClick + ) + } } val hasRecordAudioPermission = ContextCompat.checkSelfPermission(LocalContext.current, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED @@ -162,6 +169,7 @@ fun CallControlsPreview() { startCallButtonText = R.string.WebRtcCallView__start_call, displayEndCallButton = true ), + displayVideoTooltip = false, callControlsCallback = CallControlsCallback.Empty ) } @@ -179,6 +187,7 @@ interface CallControlsCallback { fun onAdditionalActionsClick() fun onStartCallClick(isVideoCall: Boolean) fun onEndCallClick() + fun onVideoTooltipDismissed() object Empty : CallControlsCallback { override fun onAudioDeviceSheetDisplayChanged(displayed: Boolean) = Unit @@ -189,6 +198,7 @@ interface CallControlsCallback { override fun onAdditionalActionsClick() = Unit override fun onStartCallClick(isVideoCall: Boolean) = Unit override fun onEndCallClick() = Unit + override fun onVideoTooltipDismissed() = Unit } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreen.kt index 9f3015e6ad..0115efbd3e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreen.kt @@ -154,6 +154,7 @@ fun CallScreen( CallControls( callControlsState = callControlsState, callControlsCallback = callControlsCallback, + displayVideoTooltip = callScreenState.displayVideoTooltip, modifier = Modifier .fillMaxWidth() .alpha(callControlsAlpha) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenState.kt index 6dd01ee285..d88a618838 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenState.kt @@ -25,7 +25,11 @@ data class CallScreenState( val hangup: Hangup? = null, val callControlsChange: CallControlsChange? = null, val callStatus: CallString? = null, - val isDisplayingAudioToggleSheet: Boolean = false + val isDisplayingAudioToggleSheet: Boolean = false, + val displaySwitchCameraTooltip: Boolean = false, + val displayVideoTooltip: Boolean = false, + val displaySwipeToSpeakerHint: Boolean = false, + val displayWifiToCellularPopup: Boolean = false ) { data class Hangup( val hangupMessageType: HangupMessage.Type, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenTooltipBox.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenTooltipBox.kt new file mode 100644 index 0000000000..3ea9613e50 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenTooltipBox.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc.v2 + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.PlainTooltip +import androidx.compose.material3.Text +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults +import androidx.compose.material3.rememberTooltipState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import kotlinx.coroutines.flow.drop +import org.signal.core.ui.DarkPreview +import org.signal.core.ui.Previews +import org.thoughtcrime.securesms.R + +/** + * Tooltip box appropriately styled for the call screen. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CallScreenTooltipBox( + text: String, + displayTooltip: Boolean, + onTooltipDismissed: () -> Unit = {}, + content: @Composable () -> Unit +) { + val state = rememberTooltipState() + + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + state = state, + tooltip = { + PlainTooltip( + caretSize = TooltipDefaults.caretSize, + shape = TooltipDefaults.plainTooltipContainerShape, + containerColor = colorResource(R.color.signal_light_colorPrimary), + contentColor = colorResource(R.color.signal_light_colorOnPrimary) + ) { + Text(text = text) + } + }, + content = content + ) + + LaunchedEffect(displayTooltip) { + if (displayTooltip) { + state.show() + } else { + state.dismiss() + } + } + + LaunchedEffect(state) { + snapshotFlow { state.isVisible } + .drop(1) + .collect { onTooltipDismissed() } + } +} + +@DarkPreview +@Composable +fun SwitchCameraTooltipBoxPreview() { + Previews.Preview { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxSize() + ) { + CallScreenTooltipBox( + text = "Test Tooltip", + displayTooltip = true + ) { + Text(text = "Test Content") + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallViewModel.kt index 0f9d5de83a..d767445edb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallViewModel.kt @@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioDevice import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutput import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel +import org.thoughtcrime.securesms.database.model.IdentityRecord import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.events.WebRtcViewModel import org.thoughtcrime.securesms.recipients.Recipient @@ -144,17 +145,30 @@ class CallViewModel( internalDialog.update { CallScreenDialogType.NONE } } + fun onVideoTooltipDismissed() { + webRtcCallViewModel.onDismissedVideoTooltip() + internalCallScreenState.update { it.copy(displayVideoTooltip = false) } + } + fun onCallEvent(event: CallEvent) { when (event) { - CallEvent.DismissSwitchCameraTooltip -> Unit // TODO - CallEvent.DismissVideoTooltip -> Unit // TODO - is CallEvent.ShowGroupCallSafetyNumberChange -> Unit // TODO - CallEvent.ShowSwipeToSpeakerHint -> Unit // TODO - CallEvent.ShowSwitchCameraTooltip -> Unit // TODO - CallEvent.ShowVideoTooltip -> Unit // TODO - CallEvent.ShowWifiToCellularPopup -> Unit // TODO + CallEvent.DismissSwitchCameraTooltip -> internalCallScreenState.update { it.copy(displaySwitchCameraTooltip = false) } + CallEvent.DismissVideoTooltip -> internalCallScreenState.update { it.copy(displayVideoTooltip = false) } + is CallEvent.ShowGroupCallSafetyNumberChange -> { + viewModelScope.launch { + internalCallActions.emit(Action.ShowGroupCallSafetyNumberChangeDialog(event.identityRecords)) + } + } + CallEvent.ShowSwipeToSpeakerHint -> internalCallScreenState.update { it.copy(displaySwipeToSpeakerHint = true) } + CallEvent.ShowSwitchCameraTooltip -> internalCallScreenState.update { it.copy(displaySwitchCameraTooltip = true) } + CallEvent.ShowVideoTooltip -> internalCallScreenState.update { it.copy(displayVideoTooltip = true) } + CallEvent.ShowWifiToCellularPopup -> internalCallScreenState.update { it.copy(displayWifiToCellularPopup = true) } is CallEvent.StartCall -> startCall(event.isVideoCall) - CallEvent.SwitchToSpeaker -> Unit // TODO + CallEvent.SwitchToSpeaker -> { + viewModelScope.launch { + internalCallActions.emit(Action.SwitchToSpeaker) + } + } } } @@ -406,11 +420,22 @@ class CallViewModel( /** * Actions that require activity-level context (for example, to request permissions.) */ - enum class Action { + sealed interface Action { /** * Tries to enable local video via the normal toggle callback. Should display permissions * dialogs as necessary. */ - EnableVideo + data object EnableVideo : Action + + /** + * Display the safety number change dialog for the given untrusted identities. Since this dialog + * is not in compose-land, we delegate this as an action instead of embedding it in the screen state. + */ + data class ShowGroupCallSafetyNumberChangeDialog(val untrustedIdentities: List) : Action + + /** + * Immediately switch the user to speaker view + */ + data object SwitchToSpeaker : Action } } diff --git a/core-util-jvm/build.gradle.kts b/core-util-jvm/build.gradle.kts index c4cd947f82..36ab387f21 100644 --- a/core-util-jvm/build.gradle.kts +++ b/core-util-jvm/build.gradle.kts @@ -45,6 +45,10 @@ wire { } } +tasks.runKtlintCheckOverMainSourceSet { + dependsOn(":core-util-jvm:generateMainProtos") +} + dependencies { implementation(libs.kotlin.reflect) implementation(libs.kotlinx.coroutines.core) From 8030e9f7eb8bf134c7017b9cff307781bdd318ba Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 24 Sep 2024 15:04:35 -0400 Subject: [PATCH 04/53] Add job to fix digests for duplicate attachments. --- .../securesms/database/AttachmentTable.kt | 51 ++++++++ .../jobs/BackfillDigestsForDataFileJob.kt | 99 ++++++++++++++++ .../securesms/jobs/JobManagerFactories.java | 111 +++++++++--------- .../migrations/ApplicationMigrations.java | 7 +- ...ackfillDigestsForDuplicatesMigrationJob.kt | 41 +++++++ app/src/main/protowire/JobData.proto | 4 + 6 files changed, 258 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/BackfillDigestsForDataFileJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/migrations/BackfillDigestsForDuplicatesMigrationJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index 51e36fbfa3..98002b8856 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -446,6 +446,17 @@ class AttachmentTable( } } + fun getMostRecentValidAttachmentUsingDataFile(dataFile: String): DatabaseAttachment? { + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$DATA_FILE = ? AND $TRANSFER_STATE = $TRANSFER_PROGRESS_DONE", dataFile) + .orderBy("$ID DESC") + .limit(1) + .run() + .readToSingleObject { it.readAttachment() } + } + fun hasAttachment(id: AttachmentId): Boolean { return readableDatabase .exists(TABLE_NAME) @@ -1397,6 +1408,31 @@ class AttachmentTable( .readToList { AttachmentId(it.requireLong(ID)) } } + /** + * A query for a specific migration. Retrieves attachments that we'd need to create a new digest for. + * This is basically all attachments that have data and are finished downloading. + */ + fun getDataFilesWithMultipleValidAttachments(): List { + val targetDataFile = "target_data_file" + return readableDatabase + .select("DISTINCT($DATA_FILE) AS $targetDataFile") + .from(TABLE_NAME) + .where( + """ + $targetDataFile NOT NULL AND + $TRANSFER_STATE = $TRANSFER_PROGRESS_DONE AND ( + SELECT COUNT(*) + FROM $TABLE_NAME + WHERE + $DATA_FILE = $targetDataFile AND + $TRANSFER_STATE = $TRANSFER_PROGRESS_DONE + ) > 1 + """ + ) + .run() + .readToList { it.requireNonNullString(targetDataFile) } + } + /** * As part of the digest backfill process, this updates the (key, IV, digest) tuple for an attachment. */ @@ -1412,6 +1448,21 @@ class AttachmentTable( .run() } + /** + * As part of the digest backfill process, this updates the (key, IV, digest) tuple for all attachments that share a data file (and are done downloading). + */ + fun updateKeyIvDigestByDataFile(dataFile: String, key: ByteArray, iv: ByteArray, digest: ByteArray) { + writableDatabase + .update(TABLE_NAME) + .values( + REMOTE_KEY to Base64.encodeWithPadding(key), + REMOTE_IV to iv, + REMOTE_DIGEST to digest + ) + .where("$DATA_FILE = ? AND $TRANSFER_STATE = $TRANSFER_PROGRESS_DONE", dataFile) + .run() + } + /** * Inserts new attachments in the table. The [Attachment]s may or may not have data, depending on whether it's an attachment we created locally or some * inbound attachment that we haven't fetched yet. diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackfillDigestsForDataFileJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackfillDigestsForDataFileJob.kt new file mode 100644 index 0000000000..e4674f869a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackfillDigestsForDataFileJob.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.Base64 +import org.signal.core.util.copyTo +import org.signal.core.util.logging.Log +import org.signal.core.util.stream.NullOutputStream +import org.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobs.protos.BackfillDigestsForDataFileJobData +import org.thoughtcrime.securesms.util.Util +import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream +import org.whispersystems.signalservice.internal.crypto.PaddingInputStream +import java.io.IOException + +/** + * This goes through all attachments that share a data file and recalcuates their digests, ensuring that all instances share the same (key/iv/digest). + * + * This job needs to be careful to (1) minimize time in the transaction, and (2) never write partial results to disk, i.e. only write the full (key/iv/digest) + * tuple together all at once (partial writes could poison the db, preventing us from retrying properly in the event of a crash or transient error). + */ +class BackfillDigestsForDataFileJob private constructor( + private val dataFile: String, + params: Parameters +) : Job(params) { + + companion object { + private val TAG = Log.tag(BackfillDigestsForDataFileJob::class) + const val KEY = "BackfillDigestsForDataFileJob" + } + + constructor(dataFile: String) : this( + dataFile = dataFile, + params = Parameters.Builder() + .setQueue(BackfillDigestJob.QUEUE) + .setMaxAttempts(3) + .setLifespan(Parameters.IMMORTAL) + .build() + ) + + override fun serialize(): ByteArray { + return BackfillDigestsForDataFileJobData(dataFile = dataFile).encode() + } + + override fun getFactoryKey(): String = KEY + + override fun run(): Result { + val (originalKey, originalIv, decryptingStream) = SignalDatabase.rawDatabase.withinTransaction { + val attachment = SignalDatabase.attachments.getMostRecentValidAttachmentUsingDataFile(dataFile) + if (attachment == null) { + Log.w(TAG, "No attachments using file $dataFile exist anymore! Skipping.") + return Result.failure() + } + + val stream = try { + SignalDatabase.attachments.getAttachmentStream(attachment.attachmentId, offset = 0) + } catch (e: IOException) { + Log.w(TAG, "Could not open a stream for ${attachment.attachmentId}. Assuming that the file no longer exists. Skipping.", e) + return Result.failure() + } + + // In order to match the exact digest calculation, we need to use the same padding that we would use when uploading the attachment. + Triple(attachment.remoteKey?.let { Base64.decode(it) }, attachment.remoteIv, PaddingInputStream(stream, attachment.size)) + } + + val key = originalKey ?: Util.getSecretBytes(64) + val iv = originalIv ?: Util.getSecretBytes(16) + + val cipherOutputStream = AttachmentCipherOutputStream(key, iv, NullOutputStream) + decryptingStream.copyTo(cipherOutputStream) + + val digest = cipherOutputStream.transmittedDigest + + SignalDatabase.attachments.updateKeyIvDigestByDataFile( + dataFile = dataFile, + key = key, + iv = iv, + digest = digest + ) + + return Result.success() + } + + override fun onFailure() { + Log.w(TAG, "Failed to backfill digest for file $dataFile!") + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): BackfillDigestsForDataFileJob { + val dataFile = (BackfillDigestsForDataFileJobData.ADAPTER.decode(serializedData!!).dataFile) + return BackfillDigestsForDataFileJob(dataFile, parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 5bf93899fa..719bd3d3ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.migrations.AttributesMigrationJob; import org.thoughtcrime.securesms.migrations.AvatarIdRemovalMigrationJob; import org.thoughtcrime.securesms.migrations.AvatarMigrationJob; +import org.thoughtcrime.securesms.migrations.BackfillDigestsForDuplicatesMigrationJob; import org.thoughtcrime.securesms.migrations.BackfillDigestsMigrationJob; import org.thoughtcrime.securesms.migrations.BackupJitterMigrationJob; import org.thoughtcrime.securesms.migrations.BackupNotificationMigrationJob; @@ -120,6 +121,7 @@ public static Map getJobFactories(@NonNull Application appl put(AvatarGroupsV1DownloadJob.KEY, new AvatarGroupsV1DownloadJob.Factory()); put(AvatarGroupsV2DownloadJob.KEY, new AvatarGroupsV2DownloadJob.Factory()); put(BackfillDigestJob.KEY, new BackfillDigestJob.Factory()); + put(BackfillDigestsForDataFileJob.KEY, new BackfillDigestsForDataFileJob.Factory()); put(BackupMessagesJob.KEY, new BackupMessagesJob.Factory()); put(BackupRestoreJob.KEY, new BackupRestoreJob.Factory()); put(BackupRestoreMediaJob.KEY, new BackupRestoreMediaJob.Factory()); @@ -259,60 +261,61 @@ public static Map getJobFactories(@NonNull Application appl put(UploadAttachmentToArchiveJob.KEY, new UploadAttachmentToArchiveJob.Factory()); // Migrations - put(AccountConsistencyMigrationJob.KEY, new AccountConsistencyMigrationJob.Factory()); - put(AccountRecordMigrationJob.KEY, new AccountRecordMigrationJob.Factory()); - put(ApplyUnknownFieldsToSelfMigrationJob.KEY, new ApplyUnknownFieldsToSelfMigrationJob.Factory()); - put(AttachmentCleanupMigrationJob.KEY, new AttachmentCleanupMigrationJob.Factory()); - put(AttachmentHashBackfillMigrationJob.KEY, new AttachmentHashBackfillMigrationJob.Factory()); - put(AttributesMigrationJob.KEY, new AttributesMigrationJob.Factory()); - put(AvatarIdRemovalMigrationJob.KEY, new AvatarIdRemovalMigrationJob.Factory()); - put(AvatarMigrationJob.KEY, new AvatarMigrationJob.Factory()); - put(BackfillDigestsMigrationJob.KEY, new BackfillDigestsMigrationJob.Factory()); - put(BackupJitterMigrationJob.KEY, new BackupJitterMigrationJob.Factory()); - put(BackupNotificationMigrationJob.KEY, new BackupNotificationMigrationJob.Factory()); - put(BlobStorageLocationMigrationJob.KEY, new BlobStorageLocationMigrationJob.Factory()); - put(CachedAttachmentsMigrationJob.KEY, new CachedAttachmentsMigrationJob.Factory()); - put(ClearGlideCacheMigrationJob.KEY, new ClearGlideCacheMigrationJob.Factory()); - put(CopyUsernameToSignalStoreMigrationJob.KEY, new CopyUsernameToSignalStoreMigrationJob.Factory()); - put(DatabaseMigrationJob.KEY, new DatabaseMigrationJob.Factory()); - put(DeleteDeprecatedLogsMigrationJob.KEY, new DeleteDeprecatedLogsMigrationJob.Factory()); - put(DirectoryRefreshMigrationJob.KEY, new DirectoryRefreshMigrationJob.Factory()); - put(EmojiDownloadMigrationJob.KEY, new EmojiDownloadMigrationJob.Factory()); - put(EmojiSearchIndexCheckMigrationJob.KEY, new EmojiSearchIndexCheckMigrationJob.Factory()); - put(IdentityTableCleanupMigrationJob.KEY, new IdentityTableCleanupMigrationJob.Factory()); - put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory()); - put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory()); - put(OptimizeMessageSearchIndexMigrationJob.KEY,new OptimizeMessageSearchIndexMigrationJob.Factory()); - put(PinOptOutMigration.KEY, new PinOptOutMigration.Factory()); - put(PinReminderMigrationJob.KEY, new PinReminderMigrationJob.Factory()); - put(PniAccountInitializationMigrationJob.KEY, new PniAccountInitializationMigrationJob.Factory()); - put(PniMigrationJob.KEY, new PniMigrationJob.Factory()); - put(PnpLaunchMigrationJob.KEY, new PnpLaunchMigrationJob.Factory()); - put(PreKeysSyncMigrationJob.KEY, new PreKeysSyncMigrationJob.Factory()); - put(ProfileMigrationJob.KEY, new ProfileMigrationJob.Factory()); - put(ProfileSharingUpdateMigrationJob.KEY, new ProfileSharingUpdateMigrationJob.Factory()); - put(RebuildMessageSearchIndexMigrationJob.KEY, new RebuildMessageSearchIndexMigrationJob.Factory()); - put(RecheckPaymentsMigrationJob.KEY, new RecheckPaymentsMigrationJob.Factory()); - put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory()); - put(SelfRegisteredStateMigrationJob.KEY, new SelfRegisteredStateMigrationJob.Factory()); - put(StickerLaunchMigrationJob.KEY, new StickerLaunchMigrationJob.Factory()); - put(StickerAdditionMigrationJob.KEY, new StickerAdditionMigrationJob.Factory()); - put(StickerDayByDayMigrationJob.KEY, new StickerDayByDayMigrationJob.Factory()); - put(StickerMyDailyLifeMigrationJob.KEY, new StickerMyDailyLifeMigrationJob.Factory()); - put(StorageCapabilityMigrationJob.KEY, new StorageCapabilityMigrationJob.Factory()); - put(StorageFixLocalUnknownMigrationJob.KEY, new StorageFixLocalUnknownMigrationJob.Factory()); - put(StorageServiceMigrationJob.KEY, new StorageServiceMigrationJob.Factory()); - put(StorageServiceSystemNameMigrationJob.KEY, new StorageServiceSystemNameMigrationJob.Factory()); - put(StoryViewedReceiptsStateMigrationJob.KEY, new StoryViewedReceiptsStateMigrationJob.Factory()); - put(Svr2MirrorMigrationJob.KEY, new Svr2MirrorMigrationJob.Factory()); - put(SyncCallLinksMigrationJob.KEY, new SyncCallLinksMigrationJob.Factory()); - put(SyncDistributionListsMigrationJob.KEY, new SyncDistributionListsMigrationJob.Factory()); - put(SyncKeysMigrationJob.KEY, new SyncKeysMigrationJob.Factory()); - put(TrimByLengthSettingsMigrationJob.KEY, new TrimByLengthSettingsMigrationJob.Factory()); - put(UpdateSmsJobsMigrationJob.KEY, new UpdateSmsJobsMigrationJob.Factory()); - put(UserNotificationMigrationJob.KEY, new UserNotificationMigrationJob.Factory()); - put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory()); - put(WallpaperStorageMigrationJob.KEY, new WallpaperStorageMigrationJob.Factory()); + put(AccountConsistencyMigrationJob.KEY, new AccountConsistencyMigrationJob.Factory()); + put(AccountRecordMigrationJob.KEY, new AccountRecordMigrationJob.Factory()); + put(ApplyUnknownFieldsToSelfMigrationJob.KEY, new ApplyUnknownFieldsToSelfMigrationJob.Factory()); + put(AttachmentCleanupMigrationJob.KEY, new AttachmentCleanupMigrationJob.Factory()); + put(AttachmentHashBackfillMigrationJob.KEY, new AttachmentHashBackfillMigrationJob.Factory()); + put(AttributesMigrationJob.KEY, new AttributesMigrationJob.Factory()); + put(AvatarIdRemovalMigrationJob.KEY, new AvatarIdRemovalMigrationJob.Factory()); + put(AvatarMigrationJob.KEY, new AvatarMigrationJob.Factory()); + put(BackfillDigestsMigrationJob.KEY, new BackfillDigestsMigrationJob.Factory()); + put(BackfillDigestsForDuplicatesMigrationJob.KEY, new BackfillDigestsForDuplicatesMigrationJob.Factory()); + put(BackupJitterMigrationJob.KEY, new BackupJitterMigrationJob.Factory()); + put(BackupNotificationMigrationJob.KEY, new BackupNotificationMigrationJob.Factory()); + put(BlobStorageLocationMigrationJob.KEY, new BlobStorageLocationMigrationJob.Factory()); + put(CachedAttachmentsMigrationJob.KEY, new CachedAttachmentsMigrationJob.Factory()); + put(ClearGlideCacheMigrationJob.KEY, new ClearGlideCacheMigrationJob.Factory()); + put(CopyUsernameToSignalStoreMigrationJob.KEY, new CopyUsernameToSignalStoreMigrationJob.Factory()); + put(DatabaseMigrationJob.KEY, new DatabaseMigrationJob.Factory()); + put(DeleteDeprecatedLogsMigrationJob.KEY, new DeleteDeprecatedLogsMigrationJob.Factory()); + put(DirectoryRefreshMigrationJob.KEY, new DirectoryRefreshMigrationJob.Factory()); + put(EmojiDownloadMigrationJob.KEY, new EmojiDownloadMigrationJob.Factory()); + put(EmojiSearchIndexCheckMigrationJob.KEY, new EmojiSearchIndexCheckMigrationJob.Factory()); + put(IdentityTableCleanupMigrationJob.KEY, new IdentityTableCleanupMigrationJob.Factory()); + put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory()); + put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory()); + put(OptimizeMessageSearchIndexMigrationJob.KEY, new OptimizeMessageSearchIndexMigrationJob.Factory()); + put(PinOptOutMigration.KEY, new PinOptOutMigration.Factory()); + put(PinReminderMigrationJob.KEY, new PinReminderMigrationJob.Factory()); + put(PniAccountInitializationMigrationJob.KEY, new PniAccountInitializationMigrationJob.Factory()); + put(PniMigrationJob.KEY, new PniMigrationJob.Factory()); + put(PnpLaunchMigrationJob.KEY, new PnpLaunchMigrationJob.Factory()); + put(PreKeysSyncMigrationJob.KEY, new PreKeysSyncMigrationJob.Factory()); + put(ProfileMigrationJob.KEY, new ProfileMigrationJob.Factory()); + put(ProfileSharingUpdateMigrationJob.KEY, new ProfileSharingUpdateMigrationJob.Factory()); + put(RebuildMessageSearchIndexMigrationJob.KEY, new RebuildMessageSearchIndexMigrationJob.Factory()); + put(RecheckPaymentsMigrationJob.KEY, new RecheckPaymentsMigrationJob.Factory()); + put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory()); + put(SelfRegisteredStateMigrationJob.KEY, new SelfRegisteredStateMigrationJob.Factory()); + put(StickerLaunchMigrationJob.KEY, new StickerLaunchMigrationJob.Factory()); + put(StickerAdditionMigrationJob.KEY, new StickerAdditionMigrationJob.Factory()); + put(StickerDayByDayMigrationJob.KEY, new StickerDayByDayMigrationJob.Factory()); + put(StickerMyDailyLifeMigrationJob.KEY, new StickerMyDailyLifeMigrationJob.Factory()); + put(StorageCapabilityMigrationJob.KEY, new StorageCapabilityMigrationJob.Factory()); + put(StorageFixLocalUnknownMigrationJob.KEY, new StorageFixLocalUnknownMigrationJob.Factory()); + put(StorageServiceMigrationJob.KEY, new StorageServiceMigrationJob.Factory()); + put(StorageServiceSystemNameMigrationJob.KEY, new StorageServiceSystemNameMigrationJob.Factory()); + put(StoryViewedReceiptsStateMigrationJob.KEY, new StoryViewedReceiptsStateMigrationJob.Factory()); + put(Svr2MirrorMigrationJob.KEY, new Svr2MirrorMigrationJob.Factory()); + put(SyncCallLinksMigrationJob.KEY, new SyncCallLinksMigrationJob.Factory()); + put(SyncDistributionListsMigrationJob.KEY, new SyncDistributionListsMigrationJob.Factory()); + put(SyncKeysMigrationJob.KEY, new SyncKeysMigrationJob.Factory()); + put(TrimByLengthSettingsMigrationJob.KEY, new TrimByLengthSettingsMigrationJob.Factory()); + put(UpdateSmsJobsMigrationJob.KEY, new UpdateSmsJobsMigrationJob.Factory()); + put(UserNotificationMigrationJob.KEY, new UserNotificationMigrationJob.Factory()); + put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory()); + put(WallpaperStorageMigrationJob.KEY, new WallpaperStorageMigrationJob.Factory()); // Dead jobs put(FailingJob.KEY, new FailingJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index 89935c2845..e285bb8315 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -157,9 +157,10 @@ static final class Version { static final int BACKFILL_DIGESTS_V2 = 113; static final int CALL_LINK_STORAGE_SYNC = 114; static final int WALLPAPER_MIGRATION = 115; + static final int BACKFILL_DIGESTS_V3 = 116; } - public static final int CURRENT_VERSION = 115; + public static final int CURRENT_VERSION = 116; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -718,6 +719,10 @@ private static LinkedHashMap getMigrationJobs(@NonNull Co jobs.put(Version.WALLPAPER_MIGRATION, new WallpaperStorageMigrationJob()); } + if (lastSeenVersion < Version.BACKFILL_DIGESTS_V3) { + jobs.put(Version.BACKFILL_DIGESTS_V3, new BackfillDigestsForDuplicatesMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/BackfillDigestsForDuplicatesMigrationJob.kt b/app/src/main/java/org/thoughtcrime/securesms/migrations/BackfillDigestsForDuplicatesMigrationJob.kt new file mode 100644 index 0000000000..daca1c6b74 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/BackfillDigestsForDuplicatesMigrationJob.kt @@ -0,0 +1,41 @@ +package org.thoughtcrime.securesms.migrations + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobs.BackfillDigestsForDataFileJob + +/** + * Finds all attachments that share a data file and schedules a [BackfillDigestsForDataFileJob] for each. + */ +internal class BackfillDigestsForDuplicatesMigrationJob( + parameters: Parameters = Parameters.Builder().build() +) : MigrationJob(parameters) { + + companion object { + private val TAG = Log.tag(BackfillDigestsForDuplicatesMigrationJob::class.java) + const val KEY = "BackfillDigestsForDuplicatesMigrationJob" + } + + override fun getFactoryKey(): String = KEY + + override fun isUiBlocking(): Boolean = false + + override fun performMigration() { + val jobs = SignalDatabase.attachments.getDataFilesWithMultipleValidAttachments() + .map { BackfillDigestsForDataFileJob(it) } + + AppDependencies.jobManager.addAll(jobs) + + Log.i(TAG, "Enqueued ${jobs.size} backfill digest jobs for duplicate attachments.") + } + + override fun shouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): BackfillDigestsForDuplicatesMigrationJob { + return BackfillDigestsForDuplicatesMigrationJob(parameters) + } + } +} diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index 67709c1b14..c082cc67f7 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -119,6 +119,10 @@ message BackfillDigestJobData { uint64 attachmentId = 1; } +message BackfillDigestsForDataFileJobData { + string dataFile = 1; +} + message RestoreAttachmentJobData { uint64 messageId = 1; uint64 attachmentId = 2; From bf338a68357d062226146ee981bf2f0d04b9f825 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 24 Sep 2024 16:39:59 -0400 Subject: [PATCH 05/53] Keep remote fields in sync when deduping downloads. --- .../database/AttachmentTableTest_deduping.kt | 84 +++++++++++++++++++ .../securesms/database/AttachmentTable.kt | 9 +- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt index 17912fd2de..771c871b20 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt @@ -12,9 +12,12 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.signal.core.util.Base64 +import org.signal.core.util.readFully +import org.signal.core.util.stream.LimitedInputStream import org.signal.core.util.update import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.Cdn +import org.thoughtcrime.securesms.attachments.PointerAttachment import org.thoughtcrime.securesms.backup.v2.BackupRepository.getMediaName import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -30,6 +33,7 @@ import org.whispersystems.signalservice.api.attachment.AttachmentUploadResult import org.whispersystems.signalservice.api.backup.MediaId import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.internal.crypto.PaddingInputStream import java.io.File import java.util.UUID import kotlin.random.Random @@ -378,6 +382,37 @@ class AttachmentTableTest_deduping { } } + @Test + fun downloads() { + // Normal attachment download that dupes with an existing attachment + test { + val id1 = insertWithData(DATA_A) + upload(id1) + + val id2 = insertUndownloadedPlaceholder() + download(id2, DATA_A) + + assertDataFilesAreTheSame(id1, id2) + assertDataHashEndMatches(id1, id2) + assertRemoteFieldsMatch(id1, id2) + assertArchiveFieldsMatch(id1, id2) + } + + // Attachment download that dupes with an existing attachment, but has bad padding + test { + val id1 = insertWithData(DATA_A) + upload(id1) + + val id2 = insertUndownloadedPlaceholder() + download(id2, DATA_A, properPadding = false) + + assertDataFilesAreTheSame(id1, id2) + assertDataHashEndMatches(id1, id2) + assertRemoteFieldsMatch(id1, id2) + assertArchiveFieldsMatch(id1, id2) + } + } + /** * Various deletion scenarios to ensure that duped files don't deleted while there's still references. */ @@ -614,6 +649,39 @@ class AttachmentTableTest_deduping { } private class TestContext { + fun insertUndownloadedPlaceholder(): AttachmentId { + return SignalDatabase.attachments.insertAttachmentsForMessage( + mmsId = 1, + attachments = listOf( + PointerAttachment( + contentType = "image/jpeg", + transferState = AttachmentTable.TRANSFER_PROGRESS_PENDING, + size = 100, + fileName = null, + cdn = Cdn.CDN_3, + location = "somelocation", + key = Base64.encodeWithPadding(Util.getSecretBytes(64)), + iv = null, + digest = Util.getSecretBytes(64), + incrementalDigest = null, + incrementalMacChunkSize = 0, + fastPreflightId = null, + voiceNote = false, + borderless = false, + videoGif = false, + width = 100, + height = 100, + uploadTimestamp = System.currentTimeMillis(), + caption = null, + stickerLocator = null, + blurHash = null, + uuid = UUID.randomUUID() + ) + ), + quoteAttachment = emptyList() + ).values.first() + } + fun insertWithData(data: ByteArray, transformProperties: TransformProperties = TransformProperties.empty()): AttachmentId { val uri = BlobProvider.getInstance().forData(data).createForSingleSessionInMemory() @@ -675,6 +743,22 @@ class AttachmentTableTest_deduping { ) } + fun download(attachmentId: AttachmentId, data: ByteArray, properPadding: Boolean = true) { + val paddedData = if (properPadding) { + PaddingInputStream(data.inputStream(), data.size.toLong()).readFully() + } else { + val badPadding = ByteArray(16) { 42 } + data + badPadding + } + + SignalDatabase.attachments.finalizeAttachmentAfterDownload( + mmsId = 1, + attachmentId = attachmentId, + inputStream = LimitedInputStream(paddedData.inputStream(), data.size.toLong()), + iv = Util.getSecretBytes(16) + ) + } + fun delete(attachmentId: AttachmentId) { SignalDatabase.attachments.deleteAttachment(attachmentId) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index 98002b8856..da99a9e687 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -1140,8 +1140,13 @@ class AttachmentTable( values.put(TRANSFER_FILE, null as String?) values.put(TRANSFORM_PROPERTIES, TransformProperties.forSkipTransform().serialize()) values.put(ARCHIVE_TRANSFER_FILE, null as String?) + values.put(REMOTE_LOCATION, existingPlaceholder.remoteLocation) + values.put(CDN_NUMBER, existingPlaceholder.cdn.serialize()) + values.put(REMOTE_KEY, existingPlaceholder.remoteKey!!) values.put(REMOTE_IV, iv) values.put(REMOTE_DIGEST, digest) + values.put(REMOTE_INCREMENTAL_DIGEST, existingPlaceholder.incrementalDigest) + values.put(REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE, existingPlaceholder.incrementalMacChunkSize) if (digestChanged) { values.put(UPLOAD_TIMESTAMP, 0) @@ -1151,9 +1156,11 @@ class AttachmentTable( values.put(OFFLOAD_RESTORED_AT, offloadRestoredAt.inWholeMilliseconds) } + val dataFilePath = hashMatch?.file?.absolutePath ?: fileWriteResult.file.absolutePath + db.update(TABLE_NAME) .values(values) - .where("$ID = ?", attachmentId.id) + .where("$ID = ? OR $DATA_FILE = ?", attachmentId.id, dataFilePath) .run() Log.i(TAG, "[finalizeAttachmentAfterDownload] Finalized downloaded data for $attachmentId. (MessageId: $mmsId, $attachmentId)") From b6906990bc78f79a0e74f13e73ce48f69ac92160 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 25 Sep 2024 11:23:11 -0400 Subject: [PATCH 06/53] Fix individual and adhoc call backup import/export. --- .../backup/v2/ArchiveImportExportTests.kt | 34 ++++--------------- .../v2/database/CallTableBackupExtensions.kt | 27 ++++++++------- .../v2/database/ChatItemExportIterator.kt | 8 +++-- .../v2/database/ChatItemImportInserter.kt | 1 + 4 files changed, 26 insertions(+), 44 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt index 5c97448f73..207730db05 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt @@ -46,62 +46,51 @@ class ArchiveImportExportTests { val MASTER_KEY = Base64.decode("sHuBMP4ToZk4tcNU+S8eBUeCt8Am5EZnvuqTBJIR4Do") } -// @Test + @Test fun all() { runTests() } - @Test - fun temp() { - runTests { it == "chat_item_standard_message_formatted_text_03.binproto" } - } - - // Passing // @Test fun accountData() { runTests { it.startsWith("account_data_") } } - @Test +// @Test fun adHocCall() { - runTests { it.startsWith("ad_hoc_call") } + runTests { it.startsWith("ad_hoc_call_") } } - // Passing // @Test fun chat() { runTests { it.startsWith("chat_") && !it.contains("_item") } } - // Passing // @Test fun chatItemContactMessage() { runTests { it.startsWith("chat_item_contact_message_") } } - // Passing // @Test fun chatItemExpirationTimerUpdate() { runTests { it.startsWith("chat_item_expiration_timer_") } } - // Passing // @Test fun chatItemGiftBadge() { runTests { it.startsWith("chat_item_gift_badge_") } } - @Test +// @Test fun chatItemGroupCallUpdate() { runTests { it.startsWith("chat_item_group_call_update_") } } - @Test +// @Test fun chatItemIndividualCallUpdate() { runTests { it.startsWith("chat_item_individual_call_update_") } } - // Passing // @Test fun chatItemLearnedProfileUpdate() { runTests { it.startsWith("chat_item_learned_profile_update_") } @@ -112,19 +101,16 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_payment_notification_") } } - // Passing // @Test fun chatItemProfileChangeUpdate() { runTests { it.startsWith("chat_item_profile_change_update_") } } - // Passing // @Test fun chatItemRemoteDelete() { runTests { it.startsWith("chat_item_remote_delete_") } } - // Passing // @Test fun chatItemSessionSwitchoverUpdate() { runTests { it.startsWith("chat_item_session_switchover_update_") } @@ -135,7 +121,6 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_simple_updates_") } } - // Passing // @Test fun chatItemStandardMessageFormattedText() { runTests { it.startsWith("chat_item_standard_message_formatted_text_") } @@ -146,19 +131,16 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_standard_message_long_text_") } } - // Passing // @Test fun chatItemStandardMessageSpecialAttachments() { runTests { it.startsWith("chat_item_standard_message_special_attachments_") } } - // Passing // @Test fun chatItemStandardMessageStandardAttachments() { runTests { it.startsWith("chat_item_standard_message_standard_attachments_") } } - // Passing // @Test fun chatItemStandardMessageTextOnly() { runTests { it.startsWith("chat_item_standard_message_text_only_") } @@ -179,30 +161,26 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_sticker_message_") } } - // Passing // @Test fun chatItemThreadMergeUpdate() { runTests { it.startsWith("chat_item_thread_merge_update_") } } - @Test +// @Test fun recipientCallLink() { runTests { it.startsWith("recipient_call_link_") } } - // Passing // @Test fun recipientContacts() { runTests { it.startsWith("recipient_contacts_") } } - // Passing // @Test fun recipientDistributionLists() { runTests { it.startsWith("recipient_distribution_list_") } } - // Passing // @Test fun recipientGroups() { runTests { it.startsWith("recipient_groups_") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt index b3ddd898e8..3c04e9b516 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt @@ -6,8 +6,7 @@ package org.thoughtcrime.securesms.backup.v2.database import android.database.Cursor -import android.database.sqlite.SQLiteDatabase -import androidx.core.content.contentValuesOf +import org.signal.core.util.insertInto import org.signal.core.util.requireLong import org.signal.core.util.select import org.thoughtcrime.securesms.backup.v2.ImportState @@ -21,7 +20,7 @@ fun CallTable.getAdhocCallsForBackup(): CallLogIterator { readableDatabase .select() .from(CallTable.TABLE_NAME) - .where("${CallTable.TYPE}=?", CallTable.Type.AD_HOC_CALL) + .where("${CallTable.TYPE} = ?", CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL)) .run() ) } @@ -32,16 +31,18 @@ fun CallTable.restoreCallLogFromBackup(call: AdHocCall, importState: ImportState AdHocCall.State.UNKNOWN_STATE -> CallTable.Event.GENERIC_GROUP_CALL } - val values = contentValuesOf( - CallTable.CALL_ID to call.callId, - CallTable.PEER to importState.remoteToLocalRecipientId[call.recipientId]!!.serialize(), - CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL), - CallTable.DIRECTION to CallTable.Direction.serialize(CallTable.Direction.OUTGOING), - CallTable.EVENT to CallTable.Event.serialize(event), - CallTable.TIMESTAMP to call.callTimestamp - ) - - writableDatabase.insert(CallTable.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values) + val result = writableDatabase + .insertInto(CallTable.TABLE_NAME) + .values( + CallTable.CALL_ID to call.callId, + CallTable.PEER to importState.remoteToLocalRecipientId[call.recipientId]!!.serialize(), + CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL), + CallTable.DIRECTION to CallTable.Direction.serialize(CallTable.Direction.OUTGOING), + CallTable.EVENT to CallTable.Event.serialize(event), + CallTable.TIMESTAMP to call.callTimestamp + ) + .run() + return Unit } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index 8335f665a7..5f1a7a17de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -460,7 +460,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: ) } - CallTable.Type.AD_HOC_CALL -> { + CallTable.Type.AUDIO_CALL, + CallTable.Type.VIDEO_CALL -> { ChatUpdateMessage( individualCall = IndividualCall( callId = this.callId, @@ -473,12 +474,13 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: CallTable.Event.NOT_ACCEPTED -> IndividualCall.State.NOT_ACCEPTED else -> IndividualCall.State.UNKNOWN_STATE }, - startedCallTimestamp = this.timestamp + startedCallTimestamp = this.timestamp, + read = messageRecord.read ) ) } - else -> null + CallTable.Type.AD_HOC_CALL -> throw IllegalArgumentException("AdHoc calls are not update messages!") } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index 68dcaf4e60..f56e6767fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -696,6 +696,7 @@ class ChatItemImportInserter( } } } + this.put(MessageTable.READ, updateMessage.individualCall.read.toInt()) } updateMessage.groupCall != null -> { val startedCallRecipientId = if (updateMessage.groupCall.startedCallRecipientId != null) { From c80ebd565835373dcd7b9934e830123c12377db2 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 25 Sep 2024 13:57:49 -0400 Subject: [PATCH 07/53] Fix quote attachment backup import/export. --- .../backup/v2/ArchiveImportExportTests.kt | 17 +++--- .../v2/database/ChatItemExportIterator.kt | 52 +++++++++---------- .../v2/database/ChatItemImportInserter.kt | 44 ++++++---------- .../v2/util/ArchiveConverterExtensions.kt | 4 +- 4 files changed, 56 insertions(+), 61 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt index 207730db05..bef92d08ba 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt @@ -96,7 +96,7 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_learned_profile_update_") } } - @Test +// @Test fun chatItemPaymentNotification() { runTests { it.startsWith("chat_item_payment_notification_") } } @@ -116,7 +116,7 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_session_switchover_update_") } } - @Test +// @Test fun chatItemSimpleUpdates() { runTests { it.startsWith("chat_item_simple_updates_") } } @@ -126,11 +126,16 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_standard_message_formatted_text_") } } - @Test +// @Test fun chatItemStandardMessageLongText() { runTests { it.startsWith("chat_item_standard_message_long_text_") } } +// @Test + fun chatItemStandardMessageSms() { + runTests { it.startsWith("chat_item_standard_message_sms_") } + } + // @Test fun chatItemStandardMessageSpecialAttachments() { runTests { it.startsWith("chat_item_standard_message_special_attachments_") } @@ -146,17 +151,17 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_standard_message_text_only_") } } - @Test +// @Test fun chatItemStandardMessageWithEdits() { runTests { it.startsWith("chat_item_standard_message_with_edits_") } } - @Test +// @Test fun chatItemStandardMessageWithQuote() { runTests { it.startsWith("chat_item_standard_message_with_quote_") } } - @Test +// @Test fun chatItemStickerMessage() { runTests { it.startsWith("chat_item_sticker_message_") } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index 5f1a7a17de..199431f6bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -674,7 +674,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } else { Text( body = this.body, - bodyRanges = (this.bodyRanges?.toBackupBodyRanges() ?: emptyList()) + (mentions?.toBackupBodyRanges() ?: emptyList()) + bodyRanges = (this.bodyRanges?.toRemoteBodyRanges() ?: emptyList()) + (mentions?.toRemoteBodyRanges() ?: emptyList()) ) } val linkPreviews = parseLinkPreviews(attachments) @@ -697,26 +697,26 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } private fun BackupMessageRecord.toQuote(attachments: List? = null): Quote? { - return if (this.quoteTargetSentTimestamp != MessageTable.QUOTE_NOT_PRESENT_ID && this.quoteAuthor > 0) { - val type = QuoteModel.Type.fromCode(this.quoteType) - Quote( - targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID }, - authorId = this.quoteAuthor, - text = this.quoteBody?.let { body -> - Text( - body = body, - bodyRanges = this.quoteBodyRanges?.toBackupBodyRanges() ?: emptyList() - ) - }, - attachments = attachments?.toBackupQuoteAttachments() ?: emptyList(), - type = when (type) { - QuoteModel.Type.NORMAL -> Quote.Type.NORMAL - QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFTBADGE - } - ) - } else { - null + if (this.quoteTargetSentTimestamp == MessageTable.QUOTE_NOT_PRESENT_ID || this.quoteAuthor <= 0) { + return null } + + val type = QuoteModel.Type.fromCode(this.quoteType) + return Quote( + targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID }, + authorId = this.quoteAuthor, + text = this.quoteBody?.let { body -> + Text( + body = body, + bodyRanges = this.quoteBodyRanges?.toRemoteBodyRanges() ?: emptyList() + ) + }, + attachments = attachments?.toRemoteQuoteAttachments() ?: emptyList(), + type = when (type) { + QuoteModel.Type.NORMAL -> Quote.Type.NORMAL + QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFTBADGE + } + ) } private fun BackupMessageRecord.toGiftBadgeUpdate(): BackupGiftBadge { @@ -752,19 +752,19 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: ) } - private fun List.toBackupQuoteAttachments(): List { + private fun List.toRemoteQuoteAttachments(): List { return this.map { attachment -> Quote.QuotedAttachment( contentType = attachment.contentType, fileName = attachment.fileName, - thumbnail = attachment.toRemoteMessageAttachment().takeUnless { it.pointer?.invalidAttachmentLocator != null } + thumbnail = attachment.toRemoteMessageAttachment(contentTypeOverride = "image/jpeg").takeUnless { it.pointer?.invalidAttachmentLocator != null } ) } } - private fun DatabaseAttachment.toRemoteMessageAttachment(): MessageAttachment { + private fun DatabaseAttachment.toRemoteMessageAttachment(contentTypeOverride: String? = null): MessageAttachment { return MessageAttachment( - pointer = this.toRemoteFilePointer(mediaArchiveEnabled), + pointer = this.toRemoteFilePointer(mediaArchiveEnabled, contentTypeOverride), wasDownloaded = this.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE || this.transferState == AttachmentTable.TRANSFER_NEEDS_RESTORE, flag = if (this.voiceNote) { MessageAttachment.Flag.VOICE_MESSAGE @@ -827,7 +827,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } } - private fun List.toBackupBodyRanges(): List { + private fun List.toRemoteBodyRanges(): List { return this.map { BackupBodyRange( start = it.start, @@ -837,7 +837,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } } - private fun ByteArray.toBackupBodyRanges(): List { + private fun ByteArray.toRemoteBodyRanges(): List { val decoded: BodyRangeList = try { BodyRangeList.ADAPTER.decode(this) } catch (e: IOException) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index f56e6767fa..3961356674 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -856,7 +856,6 @@ class ChatItemImportInserter( this.put(MessageTable.QUOTE_BODY, quote.text?.body) this.put(MessageTable.QUOTE_TYPE, quote.type.toLocalQuoteType()) this.put(MessageTable.QUOTE_BODY_RANGES, quote.text?.bodyRanges?.toLocalBodyRanges()?.encode()) - // TODO [backup] quote attachments this.put(MessageTable.QUOTE_MISSING, (quote.targetSentTimestamp == null).toInt()) } @@ -958,21 +957,23 @@ class ChatItemImportInserter( } private fun Quote.QuotedAttachment.toLocalAttachment(): Attachment? { - val thumbnail = this.thumbnail?.toLocalAttachment(this.contentType, this.fileName) + val thumbnail = this.thumbnail?.toLocalAttachment() - return if (thumbnail != null) { - thumbnail - } else if (this.contentType == null) { - null - } else { - PointerAttachment.forPointer( - quotedAttachment = DataMessage.Quote.QuotedAttachment( - contentType = this.contentType, - fileName = this.fileName, - thumbnail = null - ) - ).orNull() + if (thumbnail != null) { + return thumbnail + } + + if (this.contentType == null) { + return null } + + return PointerAttachment.forPointer( + quotedAttachment = DataMessage.Quote.QuotedAttachment( + contentType = this.contentType, + fileName = this.fileName, + thumbnail = null + ) + ).orNull() } private fun Sticker?.toLocalAttachment(): Attachment? { @@ -1004,25 +1005,14 @@ class ChatItemImportInserter( } private fun MessageAttachment.toLocalAttachment(): Attachment? { - return this.pointer?.toLocalAttachment( - importState = importState, - voiceNote = this.flag == MessageAttachment.Flag.VOICE_MESSAGE, - gif = this.flag == MessageAttachment.Flag.GIF, - borderless = this.flag == MessageAttachment.Flag.BORDERLESS, - wasDownloaded = this.wasDownloaded, - uuid = this.clientUuid - ) - } - - private fun MessageAttachment.toLocalAttachment(contentType: String?, fileName: String?): Attachment? { return pointer?.toLocalAttachment( importState = importState, voiceNote = flag == MessageAttachment.Flag.VOICE_MESSAGE, gif = flag == MessageAttachment.Flag.GIF, borderless = flag == MessageAttachment.Flag.BORDERLESS, wasDownloaded = wasDownloaded, - contentType = contentType, - fileName = fileName, + contentType = pointer.contentType, + fileName = pointer.fileName, uuid = clientUuid ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt index e6e4b243a8..352f0b7d7e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt @@ -120,9 +120,9 @@ fun FilePointer?.toLocalAttachment( /** * @param mediaArchiveEnabled True if this user has enable media backup, otherwise false. */ -fun DatabaseAttachment.toRemoteFilePointer(mediaArchiveEnabled: Boolean): FilePointer { +fun DatabaseAttachment.toRemoteFilePointer(mediaArchiveEnabled: Boolean, contentTypeOverride: String? = null): FilePointer { val builder = FilePointer.Builder() - builder.contentType = this.contentType?.takeUnless { it.isBlank() } + builder.contentType = contentTypeOverride ?: this.contentType?.takeUnless { it.isBlank() } builder.incrementalMac = this.incrementalDigest?.toByteString() builder.incrementalMacChunkSize = this.incrementalMacChunkSize.takeIf { it > 0 } builder.fileName = this.fileName From 81d99c9d30baeaad77a9af6191f046cb64253086 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 25 Sep 2024 16:14:41 -0300 Subject: [PATCH 08/53] Implement happy path for backups subscriptions. --- .../ConfirmBackupCancellationDialog.kt | 104 ----------------- .../MessageBackupsFlowFragment.kt | 26 ++++- .../MessageBackupsFlowViewModel.kt | 109 ++++++++++-------- .../v2/ui/subscription/MessageBackupsStage.kt | 2 +- .../MessageBackupsTypeSelectionScreen.kt | 11 ++ .../subscription/InAppPaymentsRepository.kt | 3 +- .../errors/DonationErrorSource.kt | 5 + .../jobs/InAppPaymentPurchaseTokenJob.kt | 5 + .../jobs/InAppPaymentRecurringContextJob.kt | 17 ++- .../jobs/InAppPaymentRedemptionJob.kt | 7 ++ .../java/org/signal/billing/BillingApiImpl.kt | 2 + .../internal/push/PushServiceSocket.java | 2 +- 12 files changed, 135 insertions(+), 158 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/ConfirmBackupCancellationDialog.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/ConfirmBackupCancellationDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/ConfirmBackupCancellationDialog.kt deleted file mode 100644 index 3363b35a09..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/ConfirmBackupCancellationDialog.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.ui.subscription - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.AlertDialogDefaults -import androidx.compose.material3.BasicAlertDialog -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import org.signal.core.ui.Previews -import org.signal.core.ui.SignalPreview -import org.thoughtcrime.securesms.R - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun ConfirmBackupCancellationDialog( - onConfirmAndDownloadNow: () -> Unit, - onConfirmAndDownloadLater: () -> Unit, - onKeepSubscriptionClick: () -> Unit -) { - BasicAlertDialog(onDismissRequest = onKeepSubscriptionClick) { - Surface( - shape = AlertDialogDefaults.shape, - color = AlertDialogDefaults.containerColor - ) { - Column { - Text( - text = stringResource(id = R.string.ConfirmBackupCancellationDialog__confirm_cancellation), - color = AlertDialogDefaults.titleContentColor, - style = MaterialTheme.typography.headlineSmall, - modifier = Modifier - .padding(top = 24.dp) - .padding(horizontal = 24.dp) - ) - - Text( - text = stringResource(id = R.string.ConfirmBackupCancellationDialog__you_wont_be_charged_again), - color = AlertDialogDefaults.textContentColor, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier - .padding(top = 16.dp) - .padding(horizontal = 24.dp) - ) - - TextButton( - onClick = onConfirmAndDownloadNow, - modifier = Modifier - .align(Alignment.End) - .padding(end = 12.dp) - ) { - Text( - text = stringResource(id = R.string.ConfirmBackupCancellationDialog__confirm_and_download_now) - ) - } - - TextButton( - onClick = onConfirmAndDownloadLater, - modifier = Modifier - .align(Alignment.End) - .padding(end = 12.dp) - ) { - Text( - text = stringResource(id = R.string.ConfirmBackupCancellationDialog__confirm_and_download_later) - ) - } - - TextButton( - onClick = onKeepSubscriptionClick, - modifier = Modifier - .align(Alignment.End) - .padding(end = 12.dp, bottom = 12.dp) - ) { - Text( - text = stringResource(id = R.string.ConfirmBackupCancellationDialog__keep_subscription) - ) - } - } - } - } -} - -@SignalPreview -@Composable -private fun ConfirmCancellationDialogPreview() { - Previews.Preview { - ConfirmBackupCancellationDialog( - onKeepSubscriptionClick = {}, - onConfirmAndDownloadNow = {}, - onConfirmAndDownloadLater = {} - ) - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt index 9d68e3de52..ddabf9e879 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt @@ -6,6 +6,8 @@ package org.thoughtcrime.securesms.backup.v2.ui.subscription import android.app.Activity +import android.os.Bundle +import android.view.View import androidx.activity.OnBackPressedCallback import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -15,10 +17,13 @@ import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import kotlinx.coroutines.rx3.asFlowable import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.v2.MessageBackupTier +import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentCheckoutDelegate import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.compose.Nav +import org.thoughtcrime.securesms.database.InAppPaymentTable import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.viewModel @@ -26,9 +31,20 @@ import org.thoughtcrime.securesms.util.viewModel /** * Handles the selection, payment, and changing of a user's backup tier. */ -class MessageBackupsFlowFragment : ComposeFragment() { +class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelegate.ErrorHandlerCallback { private val viewModel: MessageBackupsFlowViewModel by viewModel { MessageBackupsFlowViewModel() } + private val errorHandler = InAppPaymentCheckoutDelegate.ErrorHandler() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + errorHandler.attach( + fragment = this, + errorHandlerCallback = this, + inAppPaymentIdSource = viewModel.stateFlow.asFlowable() + .filter { it.inAppPayment != null } + .map { it.inAppPayment!!.id } + ) + } @Composable override fun FragmentContent() { @@ -83,6 +99,7 @@ class MessageBackupsFlowFragment : ComposeFragment() { composable(route = MessageBackupsStage.Route.TYPE_SELECTION.name) { MessageBackupsTypeSelectionScreen( + stage = state.stage, currentBackupTier = state.currentMessageBackupTier, selectedBackupTier = state.selectedMessageBackupTier, availableBackupTypes = state.availableBackupTypes.filter { it.tier == MessageBackupTier.FREE || state.hasBackupSubscriberAvailable }, @@ -123,4 +140,11 @@ class MessageBackupsFlowFragment : ComposeFragment() { } } } + + override fun onUserLaunchedAnExternalApplication() = error("Not supported by this fragment.") + + override fun navigateToDonationPending(inAppPayment: InAppPaymentTable.InAppPayment) = error("Not supported by this fragment.") + override fun exitCheckoutFlow() { + requireActivity().finishAfterTransition() + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt index 53e6da0a50..69bb79b72c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt @@ -9,8 +9,10 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.take @@ -22,12 +24,15 @@ import kotlinx.coroutines.withContext import org.signal.core.util.billing.BillingPurchaseResult import org.signal.core.util.logging.Log import org.signal.donations.InAppPaymentType +import org.signal.donations.PaymentSourceType import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatValue import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentError +import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError +import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource import org.thoughtcrime.securesms.database.InAppPaymentTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord @@ -84,13 +89,12 @@ class MessageBackupsFlowViewModel : ViewModel() { AppDependencies.billingApi.getBillingPurchaseResults().collect { result -> when (result) { is BillingPurchaseResult.Success -> { - internalStateFlow.update { it.copy(stage = MessageBackupsStage.PROCESS_PAYMENT) } + Log.d(TAG, "Got successful purchase result for purchase at ${result.purchaseTime}") + val id = internalStateFlow.value.inAppPayment!!.id try { - handleSuccess( - result, - internalStateFlow.value.inAppPayment!!.id - ) + Log.d(TAG, "Attempting to handle successful purchase.") + handleSuccess(result, id) internalStateFlow.update { it.copy( @@ -98,6 +102,14 @@ class MessageBackupsFlowViewModel : ViewModel() { ) } } catch (e: Exception) { + Log.d(TAG, "Failed to handle purchase.", e) + InAppPaymentsRepository.handlePipelineError( + inAppPaymentId = id, + donationErrorSource = DonationErrorSource.BACKUPS, + paymentSourceType = PaymentSourceType.GooglePlayBilling, + error = e + ) + internalStateFlow.update { it.copy( stage = MessageBackupsStage.FAILURE, @@ -123,7 +135,7 @@ class MessageBackupsFlowViewModel : ViewModel() { MessageBackupsStage.BACKUP_KEY_EDUCATION -> it.copy(stage = MessageBackupsStage.BACKUP_KEY_RECORD) MessageBackupsStage.BACKUP_KEY_RECORD -> it.copy(stage = MessageBackupsStage.TYPE_SELECTION) MessageBackupsStage.TYPE_SELECTION -> validateTypeAndUpdateState(it) - MessageBackupsStage.CHECKOUT_SHEET -> validateGatewayAndUpdateState(it) + MessageBackupsStage.CHECKOUT_SHEET -> it.copy(stage = MessageBackupsStage.PROCESS_PAYMENT) MessageBackupsStage.CREATING_IN_APP_PAYMENT -> error("This is driven by an async coroutine.") MessageBackupsStage.PROCESS_PAYMENT -> error("This is driven by an async coroutine.") MessageBackupsStage.PROCESS_FREE -> error("This is driven by an async coroutine.") @@ -174,49 +186,44 @@ class MessageBackupsFlowViewModel : ViewModel() { state.copy(stage = MessageBackupsStage.COMPLETED) } - MessageBackupTier.PAID -> state.copy(stage = MessageBackupsStage.CHECKOUT_SHEET) - } - } - - private fun validateGatewayAndUpdateState(state: MessageBackupsFlowState): MessageBackupsFlowState { - check(state.selectedMessageBackupTier == MessageBackupTier.PAID) - check(state.availableBackupTypes.any { it.tier == state.selectedMessageBackupTier }) - check(state.hasBackupSubscriberAvailable) - - viewModelScope.launch(Dispatchers.IO) { - withContext(Dispatchers.Main) { - internalStateFlow.update { it.copy(inAppPayment = null) } - } - - val paidFiat = AppDependencies.billingApi.queryProduct()!!.price - - SignalDatabase.inAppPayments.clearCreated() - val id = SignalDatabase.inAppPayments.insert( - type = InAppPaymentType.RECURRING_BACKUP, - state = InAppPaymentTable.State.CREATED, - subscriberId = InAppPaymentsRepository.requireSubscriber(InAppPaymentSubscriberRecord.Type.BACKUP).subscriberId, - endOfPeriod = null, - inAppPaymentData = InAppPaymentData( - badge = null, - label = state.selectedMessageBackupTierLabel!!, - amount = paidFiat.toFiatValue(), - level = SubscriptionsConfiguration.BACKUPS_LEVEL.toLong(), - recipientId = Recipient.self().id.serialize(), - paymentMethodType = InAppPaymentData.PaymentMethodType.GOOGLE_PLAY_BILLING, - redemption = InAppPaymentData.RedemptionState( - stage = InAppPaymentData.RedemptionState.Stage.INIT + MessageBackupTier.PAID -> { + check(state.selectedMessageBackupTier == MessageBackupTier.PAID) + check(state.availableBackupTypes.any { it.tier == state.selectedMessageBackupTier }) + check(state.hasBackupSubscriberAvailable) + + viewModelScope.launch(Dispatchers.IO) { + internalStateFlow.update { it.copy(inAppPayment = null) } + + val paidFiat = AppDependencies.billingApi.queryProduct()!!.price + + SignalDatabase.inAppPayments.clearCreated() + val id = SignalDatabase.inAppPayments.insert( + type = InAppPaymentType.RECURRING_BACKUP, + state = InAppPaymentTable.State.CREATED, + subscriberId = InAppPaymentsRepository.requireSubscriber(InAppPaymentSubscriberRecord.Type.BACKUP).subscriberId, + endOfPeriod = null, + inAppPaymentData = InAppPaymentData( + badge = null, + label = state.selectedMessageBackupTierLabel!!, + amount = paidFiat.toFiatValue(), + level = SubscriptionsConfiguration.BACKUPS_LEVEL.toLong(), + recipientId = Recipient.self().id.serialize(), + paymentMethodType = InAppPaymentData.PaymentMethodType.GOOGLE_PLAY_BILLING, + redemption = InAppPaymentData.RedemptionState( + stage = InAppPaymentData.RedemptionState.Stage.INIT + ) + ) ) - ) - ) - val inAppPayment = SignalDatabase.inAppPayments.getById(id)!! + val inAppPayment = SignalDatabase.inAppPayments.getById(id)!! + internalStateFlow.update { + it.copy(inAppPayment = inAppPayment, stage = MessageBackupsStage.CHECKOUT_SHEET) + } + } - withContext(Dispatchers.Main) { - internalStateFlow.update { it.copy(inAppPayment = inAppPayment, stage = MessageBackupsStage.PROCESS_PAYMENT) } + state.copy(stage = MessageBackupsStage.CREATING_IN_APP_PAYMENT) } } - - return state.copy(stage = MessageBackupsStage.CREATING_IN_APP_PAYMENT) } /** @@ -237,6 +244,8 @@ class MessageBackupsFlowViewModel : ViewModel() { @OptIn(FlowPreview::class) private suspend fun handleSuccess(result: BillingPurchaseResult.Success, inAppPaymentId: InAppPaymentTable.InAppPaymentId) { withContext(Dispatchers.IO) { + Log.d(TAG, "Setting purchase token data on InAppPayment.") + val inAppPayment = SignalDatabase.inAppPayments.getById(inAppPaymentId)!! SignalDatabase.inAppPayments.update( inAppPayment.copy( @@ -248,20 +257,30 @@ class MessageBackupsFlowViewModel : ViewModel() { ) ) + Log.d(TAG, "Enqueueing InAppPaymentPurchaseTokenJob chain.") InAppPaymentPurchaseTokenJob.createJobChain(inAppPayment).enqueue() } val terminalInAppPayment = withContext(Dispatchers.IO) { + Log.d(TAG, "Awaiting completion of job chain for up to 10 seconds.") InAppPaymentsRepository.observeUpdates(inAppPaymentId).asFlow() .filter { it.state == InAppPaymentTable.State.END } .take(1) .timeout(10.seconds) + .catch { exception -> + if (exception is TimeoutCancellationException) { + throw DonationError.BadgeRedemptionError.TimeoutWaitingForTokenError(DonationErrorSource.BACKUPS) + } + } .first() } if (terminalInAppPayment.data.error != null) { - throw InAppPaymentError(terminalInAppPayment.data.error) + val err = InAppPaymentError(terminalInAppPayment.data.error) + Log.d(TAG, "An error occurred during the job chain!", err) + throw err } else { + Log.d(TAG, "Job chain completed successfully.") return } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsStage.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsStage.kt index ec9d00b809..817ed42505 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsStage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsStage.kt @@ -15,8 +15,8 @@ enum class MessageBackupsStage( BACKUP_KEY_EDUCATION(route = Route.BACKUP_KEY_EDUCATION), BACKUP_KEY_RECORD(route = Route.BACKUP_KEY_RECORD), TYPE_SELECTION(route = Route.TYPE_SELECTION), - CHECKOUT_SHEET(route = Route.TYPE_SELECTION), CREATING_IN_APP_PAYMENT(route = Route.TYPE_SELECTION), + CHECKOUT_SHEET(route = Route.TYPE_SELECTION), PROCESS_PAYMENT(route = Route.TYPE_SELECTION), PROCESS_FREE(route = Route.TYPE_SELECTION), COMPLETED(route = Route.TYPE_SELECTION), diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt index da73ced2d1..890ede5e27 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt @@ -45,6 +45,7 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import kotlinx.collections.immutable.persistentListOf import org.signal.core.ui.Buttons +import org.signal.core.ui.Dialogs import org.signal.core.ui.Previews import org.signal.core.ui.Scaffolds import org.signal.core.ui.SignalPreview @@ -64,6 +65,7 @@ import java.util.Currency @OptIn(ExperimentalTextApi::class) @Composable fun MessageBackupsTypeSelectionScreen( + stage: MessageBackupsStage, currentBackupTier: MessageBackupTier?, selectedBackupTier: MessageBackupTier?, availableBackupTypes: List, @@ -168,6 +170,13 @@ fun MessageBackupsTypeSelectionScreen( ) ) } + + when (stage) { + MessageBackupsStage.CREATING_IN_APP_PAYMENT -> Dialogs.IndeterminateProgressDialog() + MessageBackupsStage.PROCESS_PAYMENT -> Dialogs.IndeterminateProgressDialog() + MessageBackupsStage.PROCESS_FREE -> Dialogs.IndeterminateProgressDialog() + else -> Unit + } } } } @@ -179,6 +188,7 @@ private fun MessageBackupsTypeSelectionScreenPreview() { Previews.Preview { MessageBackupsTypeSelectionScreen( + stage = MessageBackupsStage.TYPE_SELECTION, selectedBackupTier = MessageBackupTier.FREE, availableBackupTypes = testBackupTypes(), onMessageBackupsTierSelected = { selectedBackupsType = it }, @@ -197,6 +207,7 @@ private fun MessageBackupsTypeSelectionScreenWithCurrentTierPreview() { Previews.Preview { MessageBackupsTypeSelectionScreen( + stage = MessageBackupsStage.TYPE_SELECTION, selectedBackupTier = MessageBackupTier.FREE, availableBackupTypes = testBackupTypes(), onMessageBackupsTierSelected = { selectedBackupsType = it }, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt index 67c23c08bc..1b68eef28b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt @@ -224,6 +224,7 @@ object InAppPaymentsRepository { DonationErrorSource.ONE_TIME -> InAppPaymentType.ONE_TIME_DONATION DonationErrorSource.MONTHLY -> InAppPaymentType.RECURRING_DONATION DonationErrorSource.GIFT -> InAppPaymentType.ONE_TIME_GIFT + DonationErrorSource.BACKUPS -> InAppPaymentType.RECURRING_BACKUP DonationErrorSource.GIFT_REDEMPTION -> InAppPaymentType.UNKNOWN DonationErrorSource.KEEP_ALIVE -> InAppPaymentType.UNKNOWN DonationErrorSource.UNKNOWN -> InAppPaymentType.UNKNOWN @@ -266,7 +267,7 @@ object InAppPaymentsRepository { InAppPaymentType.ONE_TIME_GIFT -> DonationErrorSource.GIFT InAppPaymentType.ONE_TIME_DONATION -> DonationErrorSource.ONE_TIME InAppPaymentType.RECURRING_DONATION -> DonationErrorSource.MONTHLY - InAppPaymentType.RECURRING_BACKUP -> DonationErrorSource.UNKNOWN // TODO [message-backups] error handling + InAppPaymentType.RECURRING_BACKUP -> DonationErrorSource.BACKUPS } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorSource.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorSource.kt index e45ade8806..55eb153500 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorSource.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationErrorSource.kt @@ -31,6 +31,11 @@ enum class DonationErrorSource(private val code: String) { */ KEEP_ALIVE("keep-alive"), + /** + * Refers to backup payments. + */ + BACKUPS("backups"), + UNKNOWN("unknown"); fun serialize(): String = code diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentPurchaseTokenJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentPurchaseTokenJob.kt index d83a873462..c6f3ad12a6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentPurchaseTokenJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentPurchaseTokenJob.kt @@ -177,6 +177,11 @@ class InAppPaymentPurchaseTokenJob private constructor( info("Scheduling retry.") throw InAppPaymentRetryException() } + + else -> { + warning("An unknown error occurred.", applicationError) + throw IOException(applicationError) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt index e8e0a3bf32..1cbb4dfdcc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRecurringContextJob.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.jobs import okio.ByteString.Companion.toByteString import org.signal.core.util.logging.Log +import org.signal.donations.InAppPaymentType import org.signal.libsignal.zkgroup.VerificationFailedException import org.signal.libsignal.zkgroup.receipts.ReceiptCredential import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation @@ -65,11 +66,17 @@ class InAppPaymentRecurringContextJob private constructor( * meaning the job will always load the freshest data it can about the payment. */ fun createJobChain(inAppPayment: InAppPaymentTable.InAppPayment, makePrimary: Boolean = false): Chain { - return AppDependencies.jobManager - .startChain(create(inAppPayment)) - .then(InAppPaymentRedemptionJob.create(inAppPayment, makePrimary)) - .then(RefreshOwnProfileJob()) - .then(MultiDeviceProfileContentUpdateJob()) + return if (inAppPayment.type == InAppPaymentType.RECURRING_BACKUP) { + AppDependencies.jobManager + .startChain(create(inAppPayment)) + .then(InAppPaymentRedemptionJob.create(inAppPayment, makePrimary)) + } else { + AppDependencies.jobManager + .startChain(create(inAppPayment)) + .then(InAppPaymentRedemptionJob.create(inAppPayment, makePrimary)) + .then(RefreshOwnProfileJob()) + .then(MultiDeviceProfileContentUpdateJob()) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt index 7cc8b218f4..fcc0929384 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt @@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.jobs import org.signal.core.util.logging.Log import org.signal.donations.InAppPaymentType import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository.requireSubscriberType import org.thoughtcrime.securesms.database.InAppPaymentTable @@ -252,6 +253,12 @@ class InAppPaymentRedemptionJob private constructor( ) ) ) + + if (inAppPayment.type == InAppPaymentType.RECURRING_BACKUP) { + Log.i(TAG, "Enabling backups and setting backup tier to PAID", true) + SignalStore.backup.areBackupsEnabled = true + SignalStore.backup.backupTier = MessageBackupTier.PAID + } } private fun verifyServiceResponse(serviceResponse: ServiceResponse, onFatalError: (Int) -> Unit = {}) { diff --git a/billing/src/main/java/org/signal/billing/BillingApiImpl.kt b/billing/src/main/java/org/signal/billing/BillingApiImpl.kt index c57994d8ba..4e67fb2212 100644 --- a/billing/src/main/java/org/signal/billing/BillingApiImpl.kt +++ b/billing/src/main/java/org/signal/billing/BillingApiImpl.kt @@ -74,8 +74,10 @@ internal class BillingApiImpl( Log.d(TAG, "purchasesUpdatedListener: ${purchases.size} purchases.") val newestPurchase = purchases.maxByOrNull { it.purchaseTime } if (newestPurchase == null) { + Log.d(TAG, "purchasesUpdatedListener: no purchase.") BillingPurchaseResult.None } else { + Log.d(TAG, "purchasesUpdatedListener: successful purchase at ${newestPurchase.purchaseTime}") BillingPurchaseResult.Success( purchaseToken = newestPurchase.purchaseToken, isAcknowledged = newestPurchase.isAcknowledged, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 2bdf5b1b3f..251cd150e4 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -1432,7 +1432,7 @@ public BankMandate getBankMandate(Locale locale, String bankTransferType) throws } public void linkPlayBillingPurchaseToken(String subscriberId, String purchaseToken) throws IOException { - makeServiceRequestWithoutAuthentication(String.format(LINK_PLAY_BILLING_PURCHASE_TOKEN, subscriberId, purchaseToken), "PUT", "", NO_HEADERS, new LinkGooglePlayBillingPurchaseTokenResponseCodeHandler()); + makeServiceRequestWithoutAuthentication(String.format(LINK_PLAY_BILLING_PURCHASE_TOKEN, subscriberId, purchaseToken), "POST", "", NO_HEADERS, new LinkGooglePlayBillingPurchaseTokenResponseCodeHandler()); } public void updateSubscriptionLevel(String subscriberId, String level, String currencyCode, String idempotencyKey) throws IOException { From e77d9d3ad68e60b94bfbb0afddb1d6ffd5a0d001 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 25 Sep 2024 16:07:38 -0400 Subject: [PATCH 09/53] Fix link preview backup import/export. --- .../backup/v2/ArchiveImportExportTests.kt | 5 ++++ .../v2/database/ChatItemExportIterator.kt | 9 ++++---- .../v2/database/ChatItemImportInserter.kt | 23 +++++++++++-------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt index bef92d08ba..78d0ceafe9 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt @@ -156,6 +156,11 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_standard_message_with_edits_") } } +// @Test + fun chatItemStandardMessageWithLinkPreview() { + runTests { it.startsWith("chat_item_standard_message_with_link_preview_") } + } + // @Test fun chatItemStandardMessageWithQuote() { runTests { it.startsWith("chat_item_standard_message_with_quote_") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index 199431f6bb..14ccd07e05 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -12,6 +12,7 @@ import org.json.JSONException import org.signal.core.util.Base64 import org.signal.core.util.Hex import org.signal.core.util.logging.Log +import org.signal.core.util.nullIfEmpty import org.signal.core.util.orNull import org.signal.core.util.requireBlob import org.signal.core.util.requireBoolean @@ -579,12 +580,12 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: return emptyList() } - private fun LinkPreview.toBackupLinkPreview(): org.thoughtcrime.securesms.backup.v2.proto.LinkPreview { + private fun LinkPreview.toRemoteLinkPreview(): org.thoughtcrime.securesms.backup.v2.proto.LinkPreview { return org.thoughtcrime.securesms.backup.v2.proto.LinkPreview( url = url, - title = title, + title = title.nullIfEmpty(), image = (thumbnail.orNull() as? DatabaseAttachment)?.toRemoteMessageAttachment()?.pointer, - description = description, + description = description.nullIfEmpty(), date = date ) } @@ -690,7 +691,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: quote = this.toQuote(quotedAttachments), text = text, attachments = messageAttachments.toBackupAttachments(), - linkPreview = linkPreviews.map { it.toBackupLinkPreview() }, + linkPreview = linkPreviews.map { it.toRemoteLinkPreview() }, longText = longTextAttachment?.toRemoteFilePointer(mediaArchiveEnabled), reactions = reactionRecords.toBackupReactions() ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index 3961356674..734827220d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -400,18 +400,21 @@ class ChatItemImportInserter( it.toLocalAttachment() } ?: emptyList() - if (attachments.isNotEmpty() || linkPreviewAttachments.isNotEmpty() || quoteAttachments.isNotEmpty() || longTextAttachments.isNotEmpty()) { + val hasAttachments = attachments.isNotEmpty() || linkPreviewAttachments.isNotEmpty() || quoteAttachments.isNotEmpty() || longTextAttachments.isNotEmpty() + + if (hasAttachments || linkPreviews.isNotEmpty()) { followUp = { messageRowId -> - val attachmentMap = SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, attachments + linkPreviewAttachments + longTextAttachments, quoteAttachments) + val attachmentMap = if (hasAttachments) { + SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, attachments + linkPreviewAttachments + longTextAttachments, quoteAttachments) + } else { + emptyMap() + } + if (linkPreviews.isNotEmpty()) { - db.update( - MessageTable.TABLE_NAME, - contentValuesOf( - MessageTable.LINK_PREVIEWS to SignalDatabase.messages.getSerializedLinkPreviews(attachmentMap, linkPreviews) - ), - "${MessageTable.ID} = ?", - SqlUtil.buildArgs(messageRowId) - ) + db.update(MessageTable.TABLE_NAME) + .values(MessageTable.LINK_PREVIEWS to SignalDatabase.messages.getSerializedLinkPreviews(attachmentMap, linkPreviews)) + .where("${MessageTable.ID} = ?", messageRowId) + .run() } } } From 181ac26caedb2ec721a5479679337e2883620bdf Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Thu, 26 Sep 2024 08:00:50 -0700 Subject: [PATCH 10/53] Update verified icon size. --- .../drawable/ic_safety_number_compact_16.xml | 12 ++++++++++++ ...ic_timer_conversation_badge_compact_16.xml | 12 ++++++++++++ .../res/layout/conversation_title_view.xml | 15 ++++++++------- .../res/layout/expiration_timer_badge.xml | 19 +++++++++---------- 4 files changed, 41 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/drawable/ic_safety_number_compact_16.xml create mode 100644 app/src/main/res/drawable/ic_timer_conversation_badge_compact_16.xml diff --git a/app/src/main/res/drawable/ic_safety_number_compact_16.xml b/app/src/main/res/drawable/ic_safety_number_compact_16.xml new file mode 100644 index 0000000000..fa9ab7bb70 --- /dev/null +++ b/app/src/main/res/drawable/ic_safety_number_compact_16.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_timer_conversation_badge_compact_16.xml b/app/src/main/res/drawable/ic_timer_conversation_badge_compact_16.xml new file mode 100644 index 0000000000..8895ee5192 --- /dev/null +++ b/app/src/main/res/drawable/ic_timer_conversation_badge_compact_16.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/conversation_title_view.xml b/app/src/main/res/layout/conversation_title_view.xml index 283be2ee9f..b45de1cef0 100644 --- a/app/src/main/res/layout/conversation_title_view.xml +++ b/app/src/main/res/layout/conversation_title_view.xml @@ -80,12 +80,13 @@ diff --git a/app/src/main/res/layout/expiration_timer_badge.xml b/app/src/main/res/layout/expiration_timer_badge.xml index 5eef9db331..3fa0dbb034 100644 --- a/app/src/main/res/layout/expiration_timer_badge.xml +++ b/app/src/main/res/layout/expiration_timer_badge.xml @@ -6,19 +6,20 @@ android:id="@+id/expiration_badge_container" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="11dp" + android:layout_marginEnd="12dp" android:gravity="center" - android:visibility="gone" + android:visibility="visible" android:orientation="horizontal"> + android:tint="@color/signal_colorOnSurfaceVariant" + app:srcCompat="@drawable/ic_timer_conversation_badge_compact_16" /> \ No newline at end of file From 588f1073008ce3b257c5fb1510cb83a24e238e13 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 27 Sep 2024 09:58:19 -0300 Subject: [PATCH 11/53] Allow stories to be unhidden from within viewer. Fixes #13710 --- .../securesms/stories/dialogs/StoryContextMenu.kt | 3 ++- .../stories/viewer/page/StoryViewerPageFragment.kt | 8 ++++++++ .../stories/viewer/page/StoryViewerPageRepository.kt | 6 ++++++ .../stories/viewer/page/StoryViewerPageViewModel.kt | 4 ++++ app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt index f87b267dcf..d4fdb78a5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt @@ -152,6 +152,7 @@ object StoryContextMenu { anchorView: View, storyViewerPageState: StoryViewerPageState, onHide: (StoryPost) -> Unit, + onUnhide: (StoryPost) -> Unit, onForward: (StoryPost) -> Unit, onShare: (StoryPost) -> Unit, onGoToChat: (StoryPost) -> Unit, @@ -171,7 +172,7 @@ object StoryContextMenu { canHide = !selectedStory.sender.shouldHideStory, callbacks = object : Callbacks { override fun onHide() = onHide(selectedStory) - override fun onUnhide() = throw NotImplementedError() + override fun onUnhide() = onUnhide(selectedStory) override fun onForward() = onForward(selectedStory) override fun onShare() = onShare(selectedStory) override fun onGoToChat() = onGoToChat(selectedStory) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt index c25b3a8821..0ae5e73538 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt @@ -35,6 +35,7 @@ import com.google.android.material.button.MaterialButton import com.google.android.material.card.MaterialCardView import com.google.android.material.progressindicator.CircularProgressIndicatorSpec import com.google.android.material.progressindicator.IndeterminateDrawable +import com.google.android.material.snackbar.Snackbar import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.kotlin.subscribeBy @@ -1159,6 +1160,13 @@ class StoryViewerPageFragment : } } }, + onUnhide = { + lifecycleDisposable += viewModel.unhideStory().subscribe { + Snackbar + .make(requireView(), R.string.StoryViewerPageFragment__story_no_longer_hidden, Snackbar.LENGTH_SHORT) + .show() + } + }, onShare = { StoryContextMenu.share(this, it.conversationMessage.messageRecord as MmsMessageRecord) }, diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt index 0c04bbcd32..13d73d9fd5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt @@ -171,6 +171,12 @@ open class StoryViewerPageRepository(context: Context, private val storyViewStat }.subscribeOn(Schedulers.io()) } + fun unhideStory(recipientId: RecipientId): Completable { + return Completable.fromAction { + SignalDatabase.recipients.setHideStory(recipientId, false) + }.subscribeOn(Schedulers.io()) + } + fun markViewed(storyPost: StoryPost) { if (!storyPost.conversationMessage.messageRecord.isOutgoing) { SignalExecutors.SERIAL.execute { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt index f696747ad6..11f12cc21f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt @@ -117,6 +117,10 @@ class StoryViewerPageViewModel( return repository.hideStory(args.recipientId) } + fun unhideStory(): Completable { + return repository.unhideStory(args.recipientId).observeOn(AndroidSchedulers.mainThread()) + } + fun markViewed(storyPost: StoryPost) { repository.markViewed(storyPost) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 72dda52c79..3087e4e1a8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5986,6 +5986,8 @@ %1$d reply %1$d replies + + Story no longer hidden Add From 5394aaa44cc5f7f526255e6338963e91f202e251 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 27 Sep 2024 10:27:08 -0300 Subject: [PATCH 12/53] Set pip auto-enter based off live view-model value. --- .../securesms/WebRtcCallActivity.java | 26 +++++++++++++------ .../webrtc/WebRtcCallViewModel.java | 7 ++--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index aa72ba0804..b5d63b0b35 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -370,7 +370,7 @@ public void onBackPressed() { private boolean enterPipModeIfPossible() { if (isSystemPipEnabledAndAvailable()) { - if (viewModel.canEnterPipMode()) { + if (Boolean.TRUE.equals(viewModel.canEnterPipMode().getValue())) { try { enterPictureInPictureMode(pipBuilderParams.build()); } catch (Exception e) { @@ -380,6 +380,7 @@ private boolean enterPipModeIfPossible() { return true; } + if (Build.VERSION.SDK_INT >= 31) { pipBuilderParams.setAutoEnterEnabled(false); } @@ -573,15 +574,24 @@ private void initializePictureInPictureParams() { pipBuilderParams = new PictureInPictureParams.Builder(); pipBuilderParams.setAspectRatio(aspectRatio); + if (Build.VERSION.SDK_INT >= 31) { - pipBuilderParams.setAutoEnterEnabled(true); + viewModel.canEnterPipMode().observe(this, canEnterPipMode -> { + pipBuilderParams.setAutoEnterEnabled(canEnterPipMode); + tryToSetPictureInPictureParams(); + }); + } else { + tryToSetPictureInPictureParams(); } - if (Build.VERSION.SDK_INT >= 26) { - try { - setPictureInPictureParams(pipBuilderParams.build()); - } catch (Exception e) { - Log.w(TAG, "System lied about having PiP available.", e); - } + } + } + + private void tryToSetPictureInPictureParams() { + if (Build.VERSION.SDK_INT >= 26) { + try { + setPictureInPictureParams(pipBuilderParams.build()); + } catch (Exception e) { + Log.w(TAG, "System lied about having PiP available.", e); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java index 03988cab52..4d15cd6f73 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java @@ -17,6 +17,7 @@ import com.annimon.stream.Stream; import org.signal.core.util.ThreadUtil; +import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.components.webrtc.v2.CallControlsState; import org.thoughtcrime.securesms.components.webrtc.v2.CallEvent; import org.thoughtcrime.securesms.database.GroupTable; @@ -74,6 +75,7 @@ public class WebRtcCallViewModel extends ViewModel { private final LiveData groupMemberCount = Transformations.map(groupMembers, List::size); private final Observable shouldShowSpeakerHint = participantsState.map(this::shouldShowSpeakerHint); private final MutableLiveData isLandscapeEnabled = new MutableLiveData<>(); + private final MutableLiveData canEnterPipMode = new MutableLiveData<>(false); private final Observer> groupMemberStateUpdater = m -> participantsState.onNext(CallParticipantsState.update(participantsState.getValue(), m)); private final MutableLiveData ephemeralState = new MutableLiveData<>(); private final BehaviorProcessor recipientId = BehaviorProcessor.createDefault(RecipientId.UNKNOWN); @@ -91,7 +93,6 @@ public class WebRtcCallViewModel extends ViewModel { private boolean wasInOutgoingRingingMode = false; private long callConnectedTime = -1; private boolean answerWithVideoAvailable = false; - private boolean canEnterPipMode = false; private List previousParticipantsList = Collections.emptyList(); private boolean callStarting = false; private boolean switchOnFirstScreenShare = true; @@ -209,7 +210,7 @@ public LiveData getEphemeralState() { return ephemeralState; } - public boolean canEnterPipMode() { + public LiveData canEnterPipMode() { return canEnterPipMode; } @@ -280,7 +281,7 @@ public void onDismissedSwitchCameraTooltip() { @MainThread public void updateFromWebRtcViewModel(@NonNull WebRtcViewModel webRtcViewModel, boolean enableVideo) { - canEnterPipMode = !webRtcViewModel.getState().isPreJoinOrNetworkUnavailable(); + canEnterPipMode.setValue(!webRtcViewModel.getState().isPreJoinOrNetworkUnavailable()); if (callStarting && webRtcViewModel.getState().isPassedPreJoin()) { callStarting = false; } From 5212088a1ba869707c8ceb20f91b5bf5b1697621 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 27 Sep 2024 10:33:52 -0300 Subject: [PATCH 13/53] Hide overflow if the group call is not in the CONNECTED state. --- .../securesms/components/webrtc/WebRtcControls.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java index d703dcac3a..0f4d9acdf8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java @@ -166,7 +166,7 @@ public boolean displayEndCall() { } public boolean displayOverflow() { - return isAtLeastOutgoing() && hasAtLeastOneRemote && isGroupCall(); + return isAtLeastOutgoing() && hasAtLeastOneRemote && isGroupCall() && groupCallState == GroupCallState.CONNECTED; } public boolean displayMuteAudio() { From ea33fa2af1c74d297e369019fe63544b4e88b8a4 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 27 Sep 2024 10:38:59 -0300 Subject: [PATCH 14/53] Add better error handling for empty story text post. --- .../securesms/stories/viewer/post/StoryPostViewModel.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt index a1389f3ace..19451ca620 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt @@ -83,7 +83,13 @@ class StoryPostViewModel(private val repository: StoryTextPostRepository) : View val text: StoryTextPost = if (record.body.isNotEmpty()) { StoryTextPost.ADAPTER.decode(Base64.decode(record.body)) } else { - throw Exception("Text post message body is empty.") + Log.w(TAG, "Failed to decode empty story text post body.") + store.update { + StoryPostState.TextPost( + loadState = StoryPostState.LoadState.FAILED + ) + } + return@subscribeBy } val linkPreview = record.linkPreviews.firstOrNull() From 5bdc7c2740a4a6b88da222a36f780f732403ef0b Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 27 Sep 2024 11:04:57 -0300 Subject: [PATCH 15/53] Implement new top-level backups settings. --- .../securesms/backup/v2/BackupRepository.kt | 6 + .../MessageBackupsFlowViewModel.kt | 1 + .../settings/app/AppSettingsFragment.kt | 11 + .../app/backups/BackupsSettingsFragment.kt | 384 ++++++++++++++++++ .../app/backups/BackupsSettingsState.kt | 46 +++ .../app/backups/BackupsSettingsViewModel.kt | 116 ++++++ .../remote}/RemoteBackupsSettingsFragment.kt | 272 +++++++------ .../remote}/RemoteBackupsSettingsState.kt | 5 +- .../remote}/RemoteBackupsSettingsViewModel.kt | 18 +- .../type/BackupsTypeSettingsFragment.kt | 7 +- .../backups/type/BackupsTypeSettingsState.kt | 2 +- .../type/BackupsTypeSettingsViewModel.kt | 2 +- .../RemoteBackupsPaymentHistoryFragment.kt | 337 --------------- .../RemoteBackupsPaymentHistoryRepository.kt | 16 - .../RemoteBackupsPaymentHistoryState.kt | 17 - .../RemoteBackupsPaymentHistoryViewModel.kt | 40 -- .../RecurringInAppPaymentRepository.kt | 33 +- .../securesms/fonts/SignalSymbols.kt | 18 + .../jobs/InAppPaymentRedemptionJob.kt | 1 + .../securesms/keyvalue/InAppPaymentValues.kt | 1 + .../securesms/keyvalue/UiHintValues.java | 11 +- .../main/res/drawable/symbol_backup_24.xml | 12 + app/src/main/res/navigation/app_settings.xml | 38 +- .../app_settings_with_change_number.xml | 46 ++- app/src/main/res/values/strings.xml | 46 ++- 25 files changed, 869 insertions(+), 617 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsState.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt rename app/src/main/java/org/thoughtcrime/securesms/components/settings/app/{chats/backups => backups/remote}/RemoteBackupsSettingsFragment.kt (76%) rename app/src/main/java/org/thoughtcrime/securesms/components/settings/app/{chats/backups => backups/remote}/RemoteBackupsSettingsState.kt (83%) rename app/src/main/java/org/thoughtcrime/securesms/components/settings/app/{chats/backups => backups/remote}/RemoteBackupsSettingsViewModel.kt (80%) rename app/src/main/java/org/thoughtcrime/securesms/components/settings/app/{chats => }/backups/type/BackupsTypeSettingsFragment.kt (95%) rename app/src/main/java/org/thoughtcrime/securesms/components/settings/app/{chats => }/backups/type/BackupsTypeSettingsState.kt (85%) rename app/src/main/java/org/thoughtcrime/securesms/components/settings/app/{chats => }/backups/type/BackupsTypeSettingsViewModel.kt (95%) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryFragment.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryRepository.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryState.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryViewModel.kt create mode 100644 app/src/main/res/drawable/symbol_backup_24.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index c8e5263678..a52a3aa410 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -105,6 +105,7 @@ object BackupRepository { 403 -> { Log.w(TAG, "Received status 403. The user is not in the media tier. Updating local state.", error.exception) SignalStore.backup.backupTier = MessageBackupTier.FREE + SignalStore.uiHints.markHasEverEnabledRemoteBackups() // TODO [backup] If the user thought they were in media tier but aren't, feels like we should have a special UX flow for this? } } @@ -846,6 +847,11 @@ object BackupRepository { Log.i(TAG, "Could not retrieve backup tier.", e) null } + + if (SignalStore.backup.backupTier != null) { + SignalStore.uiHints.markHasEverEnabledRemoteBackups() + } + return SignalStore.backup.backupTier } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt index 69bb79b72c..0a13d5ffee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt @@ -182,6 +182,7 @@ class MessageBackupsFlowViewModel : ViewModel() { MessageBackupTier.FREE -> { SignalStore.backup.areBackupsEnabled = true SignalStore.backup.backupTier = MessageBackupTier.FREE + SignalStore.uiHints.markHasEverEnabledRemoteBackups() state.copy(stage = MessageBackupsStage.COMPLETED) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt index d230b2409f..560e291f76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt @@ -201,6 +201,17 @@ class AppSettingsFragment : DSLSettingsFragment( isEnabled = state.isRegisteredAndUpToDate() ) + if (RemoteConfig.messageBackups) { + clickPref( + title = DSLSettingsText.from(R.string.preferences_chats__backups), + // TODO [message-backups] -- icon + onClick = { + findNavController().safeNavigate(R.id.action_appSettingsFragment_to_backupsSettingsFragment) + }, + isEnabled = state.isRegisteredAndUpToDate() + ) + } + clickPref( title = DSLSettingsText.from(R.string.preferences__data_and_storage), icon = DSLSettingsIcon.from(R.drawable.symbol_data_24), diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt new file mode 100644 index 0000000000..135ec715d3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt @@ -0,0 +1,384 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.backups + +import android.os.Bundle +import android.view.View +import androidx.activity.result.ActivityResultLauncher +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import org.signal.core.ui.Buttons +import org.signal.core.ui.Dividers +import org.signal.core.ui.Previews +import org.signal.core.ui.Rows +import org.signal.core.ui.Scaffolds +import org.signal.core.ui.SignalPreview +import org.signal.core.ui.Texts +import org.signal.core.util.money.FiatMoney +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType +import org.thoughtcrime.securesms.components.settings.app.subscription.MessageBackupsCheckoutLauncher.createBackupsCheckoutLauncher +import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.payments.FiatMoneyUtil +import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.navigation.safeNavigate +import java.math.BigDecimal +import java.util.Currency +import java.util.Locale +import kotlin.time.Duration.Companion.seconds + +/** + * Top-level backups settings screen. + */ +class BackupsSettingsFragment : ComposeFragment() { + + private lateinit var checkoutLauncher: ActivityResultLauncher + + private val viewModel: BackupsSettingsViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + checkoutLauncher = createBackupsCheckoutLauncher { + findNavController().safeNavigate(R.id.action_backupsSettingsFragment_to_remoteBackupsSettingsFragment) + } + } + + override fun onResume() { + super.onResume() + viewModel.refreshState() + } + + @Composable + override fun FragmentContent() { + val state by viewModel.stateFlow.collectAsState() + + BackupsSettingsContent( + backupsSettingsState = state, + onNavigationClick = { findNavController().popBackStack() }, + onBackupsRowClick = { + when (state.enabledState) { + is BackupsSettingsState.EnabledState.Active, BackupsSettingsState.EnabledState.Inactive -> { + findNavController().safeNavigate(R.id.action_backupsSettingsFragment_to_remoteBackupsSettingsFragment) + } + + BackupsSettingsState.EnabledState.Never -> { + checkoutLauncher.launch(Unit) + } + + else -> Unit + } + }, + onOnDeviceBackupsRowClick = { findNavController().safeNavigate(R.id.action_backupsSettingsFragment_to_backupsPreferenceFragment) } + ) + } +} + +@Composable +private fun BackupsSettingsContent( + backupsSettingsState: BackupsSettingsState, + onNavigationClick: () -> Unit = {}, + onBackupsRowClick: () -> Unit = {}, + onOnDeviceBackupsRowClick: () -> Unit = {} +) { + Scaffolds.Settings( + title = stringResource(R.string.preferences_chats__backups), + navigationIconPainter = painterResource(R.drawable.symbol_arrow_left_24), + onNavigationClick = onNavigationClick + ) { paddingValues -> + LazyColumn( + modifier = Modifier.padding(paddingValues) + ) { + item { + Text( + text = stringResource(R.string.RemoteBackupsSettingsFragment__back_up_your_message_history), + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.core_ui__gutter), vertical = 16.dp) + ) + } + + item { + when (backupsSettingsState.enabledState) { + BackupsSettingsState.EnabledState.Loading -> { + LoadingBackupsRow() + } + + BackupsSettingsState.EnabledState.Inactive -> { + InactiveBackupsRow( + onBackupsRowClick = onBackupsRowClick + ) + } + + is BackupsSettingsState.EnabledState.Active -> { + ActiveBackupsRow( + enabledState = backupsSettingsState.enabledState, + onBackupsRowClick = onBackupsRowClick + ) + } + + BackupsSettingsState.EnabledState.Never -> { + NeverEnabledBackupsRow( + onBackupsRowClick = onBackupsRowClick + ) + } + + BackupsSettingsState.EnabledState.Failed -> { + Text(text = "TODO") + } + } + } + + item { + Dividers.Default() + } + + item { + Texts.SectionHeader( + text = stringResource(R.string.RemoteBackupsSettingsFragment__other_ways_to_backup) + ) + + Rows.TextRow( + text = stringResource(R.string.RemoteBackupsSettingsFragment__on_device_backups), + label = stringResource(R.string.RemoteBackupsSettingsFragment__save_your_backups_to), + onClick = onOnDeviceBackupsRowClick + ) + } + } + } +} + +@Composable +private fun NeverEnabledBackupsRow( + onBackupsRowClick: () -> Unit = {} +) { + Rows.TextRow( + modifier = Modifier.height(IntrinsicSize.Min), + icon = { + Box( + modifier = Modifier + .fillMaxHeight() + .padding(top = 12.dp) + ) { + Icon( + painter = painterResource(R.drawable.symbol_backup_24), + contentDescription = null + ) + } + }, + text = { + Column { + Text( + text = stringResource(R.string.RemoteBackupsSettingsFragment__signal_backups) + ) + + Text( + text = stringResource(R.string.BackupsSettingsFragment_automatic_backups_with_signals), + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium + ) + + Buttons.MediumTonal( + onClick = onBackupsRowClick, + modifier = Modifier.padding(top = 12.dp) + ) { + Text( + text = stringResource(R.string.BackupsSettingsFragment_set_up) + ) + } + } + } + ) +} + +@Composable +private fun InactiveBackupsRow( + onBackupsRowClick: () -> Unit = {} +) { + Rows.TextRow( + text = stringResource(R.string.RemoteBackupsSettingsFragment__signal_backups), + label = stringResource(R.string.preferences_off), + icon = painterResource(R.drawable.symbol_backup_24), + onClick = onBackupsRowClick + ) +} + +@Composable +private fun ActiveBackupsRow( + enabledState: BackupsSettingsState.EnabledState.Active, + onBackupsRowClick: () -> Unit = {} +) { + Rows.TextRow( + modifier = Modifier.height(IntrinsicSize.Min), + icon = { + Box( + contentAlignment = Alignment.TopCenter, + modifier = Modifier + .fillMaxHeight() + .padding(top = 12.dp) + ) { + Icon( + painter = painterResource(R.drawable.symbol_backup_24), + contentDescription = null + ) + } + }, + text = { + Column { + Text( + text = stringResource(R.string.RemoteBackupsSettingsFragment__signal_backups) + ) + + when (enabledState.type) { + is MessageBackupsType.Paid -> { + Text( + text = stringResource( + R.string.BackupsSettingsFragment_s_month_renews_s, + FiatMoneyUtil.format(LocalContext.current.resources, enabledState.type.pricePerMonth), + DateUtils.formatDateWithYear(Locale.getDefault(), enabledState.expiresAt.inWholeMilliseconds) + ), + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium + ) + } + + is MessageBackupsType.Free -> { + Text( + text = stringResource( + R.string.RemoteBackupsSettingsFragment__your_backup_plan_is_free + ), + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium + ) + } + } + + Text( + text = stringResource( + R.string.BackupsSettingsFragment_last_backup_s, + DateUtils.getDatelessRelativeTimeSpanFormattedDate( + LocalContext.current, + Locale.getDefault(), + enabledState.lastBackupAt.inWholeMilliseconds + ).value + ), + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium + ) + } + }, + onClick = onBackupsRowClick + ) +} + +@Composable +private fun LoadingBackupsRow() { + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .padding(horizontal = dimensionResource(R.dimen.core_ui__gutter)) + ) { + CircularProgressIndicator() + } +} + +@SignalPreview +@Composable +private fun BackupsSettingsContentPreview() { + Previews.Preview { + BackupsSettingsContent( + backupsSettingsState = BackupsSettingsState( + enabledState = BackupsSettingsState.EnabledState.Active( + type = MessageBackupsType.Paid( + pricePerMonth = FiatMoney(BigDecimal.valueOf(4), Currency.getInstance("CAD")), + storageAllowanceBytes = 1_000_000 + ), + expiresAt = 0.seconds, + lastBackupAt = 0.seconds + ) + ) + ) + } +} + +@SignalPreview +@Composable +private fun InactiveBackupsRowPreview() { + Previews.Preview { + InactiveBackupsRow() + } +} + +@SignalPreview +@Composable +private fun ActivePaidBackupsRowPreview() { + Previews.Preview { + ActiveBackupsRow( + enabledState = BackupsSettingsState.EnabledState.Active( + type = MessageBackupsType.Paid( + pricePerMonth = FiatMoney(BigDecimal.valueOf(4), Currency.getInstance("CAD")), + storageAllowanceBytes = 1_000_000 + ), + expiresAt = 0.seconds, + lastBackupAt = 0.seconds + ) + ) + } +} + +@SignalPreview +@Composable +private fun ActiveFreeBackupsRowPreview() { + Previews.Preview { + ActiveBackupsRow( + enabledState = BackupsSettingsState.EnabledState.Active( + type = MessageBackupsType.Free( + mediaRetentionDays = 30 + ), + expiresAt = 0.seconds, + lastBackupAt = 0.seconds + ) + ) + } +} + +@SignalPreview +@Composable +private fun LoadingBackupsRowPreview() { + Previews.Preview { + LoadingBackupsRow() + } +} + +@SignalPreview +@Composable +private fun NeverEnabledBackupsRowPreview() { + Previews.Preview { + NeverEnabledBackupsRow() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsState.kt new file mode 100644 index 0000000000..5834dfcb2f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsState.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.backups + +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType +import kotlin.time.Duration + +/** + * Screen state for top-level backups settings screen. + */ +data class BackupsSettingsState( + val enabledState: EnabledState = EnabledState.Loading +) { + /** + * Describes the 'enabled' state of backups. + */ + sealed interface EnabledState { + /** + * Loading data for this row + */ + data object Loading : EnabledState + + /** + * Backups have never been enabled. + */ + data object Never : EnabledState + + /** + * Backups were active at one point, but have been turned off. + */ + data object Inactive : EnabledState + + /** + * Backup state couldn't be retrieved from the server for some reason + */ + data object Failed : EnabledState + + /** + * Backups are currently active. + */ + data class Active(val type: MessageBackupsType, val expiresAt: Duration, val lastBackupAt: Duration) : EnabledState + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt new file mode 100644 index 0000000000..b78a533522 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.backups + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.rx3.asFlow +import org.signal.core.util.logging.Log +import org.signal.core.util.money.FiatMoney +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType +import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository +import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.InternetConnectionObserver +import java.util.Currency +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +class BackupsSettingsViewModel : ViewModel() { + + companion object { + private val TAG = Log.tag(BackupsSettingsViewModel::class) + } + + private val internalStateFlow = MutableStateFlow(BackupsSettingsState()) + + val stateFlow: StateFlow = internalStateFlow + + init { + viewModelScope.launch(Dispatchers.Default) { + InternetConnectionObserver.observe().asFlow() + .distinctUntilChanged() + .filter { it } + .drop(1) + .collect { + refreshState() + } + } + } + + fun refreshState() { + Log.d(TAG, "Refreshing state.") + loadEnabledState() + } + + private fun loadEnabledState() { + viewModelScope.launch(Dispatchers.IO) { + val enabledState = when (SignalStore.backup.backupTier) { + MessageBackupTier.FREE -> getEnabledStateForFreeTier() + MessageBackupTier.PAID -> getEnabledStateForPaidTier() + null -> getEnabledStateForNoTier() + } + + internalStateFlow.update { it.copy(enabledState = enabledState) } + } + } + + private suspend fun getEnabledStateForFreeTier(): BackupsSettingsState.EnabledState { + return try { + BackupsSettingsState.EnabledState.Active( + expiresAt = 0.seconds, + lastBackupAt = SignalStore.backup.lastBackupTime.milliseconds, + type = BackupRepository.getBackupsType(MessageBackupTier.FREE)!! + ) + } catch (e: Exception) { + Log.w(TAG, "Failed to build enabled state.", e) + BackupsSettingsState.EnabledState.Failed + } + } + + private suspend fun getEnabledStateForPaidTier(): BackupsSettingsState.EnabledState { + return try { + val backupType = BackupRepository.getBackupsType(MessageBackupTier.PAID) as MessageBackupsType.Paid + val activeSubscription = RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).getOrThrow() + if (activeSubscription.isActive) { + BackupsSettingsState.EnabledState.Active( + expiresAt = activeSubscription.activeSubscription.endOfCurrentPeriod.seconds, + lastBackupAt = SignalStore.backup.lastBackupTime.milliseconds, + type = MessageBackupsType.Paid( + pricePerMonth = FiatMoney.fromSignalNetworkAmount( + activeSubscription.activeSubscription.amount, + Currency.getInstance(activeSubscription.activeSubscription.currency) + ), + storageAllowanceBytes = backupType.storageAllowanceBytes + ) + ) + } else { + BackupsSettingsState.EnabledState.Inactive + } + } catch (e: Exception) { + Log.w(TAG, "Failed to build enabled state.", e) + BackupsSettingsState.EnabledState.Failed + } + } + + private fun getEnabledStateForNoTier(): BackupsSettingsState.EnabledState { + return if (SignalStore.uiHints.hasEverEnabledRemoteBackups) { + BackupsSettingsState.EnabledState.Never + } else { + BackupsSettingsState.EnabledState.Inactive + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt similarity index 76% rename from app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt rename to app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt index e4f52f0a85..efeea40c24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.components.settings.app.chats.backups +package org.thoughtcrime.securesms.components.settings.app.backups.remote import android.os.Bundle import android.view.View @@ -18,9 +18,11 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -44,6 +46,7 @@ import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import androidx.fragment.app.setFragmentResultListener @@ -58,16 +61,18 @@ import org.signal.core.ui.Scaffolds import org.signal.core.ui.SignalPreview import org.signal.core.ui.Snackbars import org.signal.core.ui.Texts +import org.signal.core.ui.theme.SignalTheme import org.signal.core.util.money.FiatMoney import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.ArchiveUploadProgress import org.thoughtcrime.securesms.backup.v2.BackupFrequency +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType -import org.thoughtcrime.securesms.components.settings.app.chats.backups.type.BackupsTypeSettingsFragment +import org.thoughtcrime.securesms.components.settings.app.backups.type.BackupsTypeSettingsFragment import org.thoughtcrime.securesms.components.settings.app.subscription.MessageBackupsCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.compose.ComposeFragment -import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord -import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.fonts.SignalSymbols +import org.thoughtcrime.securesms.fonts.SignalSymbols.SignalSymbol import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.util.DateUtils @@ -75,7 +80,10 @@ import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.viewModel import java.math.BigDecimal +import java.util.Currency import java.util.Locale +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds /** * Remote backups settings fragment. @@ -105,7 +113,8 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { requestedSnackbar = state.snackbar, contentCallbacks = callbacks, backupProgress = backupProgress, - backupSize = state.backupSize + backupSize = state.backupSize, + renewalTime = state.renewalTime ) } @@ -115,18 +124,14 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { findNavController().popBackStack() } - override fun onEnableBackupsClick() { - checkoutLauncher.launch(Unit) + override fun onBackupTypeActionClick(tier: MessageBackupTier) { + // TODO [message-backups] } override fun onBackUpUsingCellularClick(canUseCellular: Boolean) { viewModel.setCanBackUpUsingCellular(canUseCellular) } - override fun onViewPaymentHistory() { - findNavController().safeNavigate(R.id.action_remoteBackupsSettingsFragment_to_remoteBackupsPaymentHistoryFragment) - } - override fun onBackupNowClick() { viewModel.onBackupNowClick() } @@ -191,10 +196,9 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { */ private interface ContentCallbacks { fun onNavigationClick() = Unit - fun onEnableBackupsClick() = Unit fun onBackupsTypeClick() = Unit + fun onBackupTypeActionClick(tier: MessageBackupTier) = Unit fun onBackUpUsingCellularClick(canUseCellular: Boolean) = Unit - fun onViewPaymentHistory() = Unit fun onBackupNowClick() = Unit fun onTurnOffAndDeleteBackupsClick() = Unit fun onChangeBackupFrequencyClick() = Unit @@ -207,6 +211,7 @@ private interface ContentCallbacks { @Composable private fun RemoteBackupsSettingsContent( messageBackupsType: MessageBackupsType?, + renewalTime: Duration, lastBackupTimestamp: Long, canBackUpUsingCellular: Boolean, backupsFrequency: BackupFrequency, @@ -232,99 +237,88 @@ private fun RemoteBackupsSettingsContent( modifier = Modifier .padding(it) ) { + if (messageBackupsType != null) { + item { + BackupTypeRow( + messageBackupsType = messageBackupsType, + renewalTime = renewalTime, + onBackupTypeActionButtonClicked = contentCallbacks::onBackupTypeActionClick + ) + } + } + item { - BackupTypeRow( - messageBackupsType = messageBackupsType, - onEnableBackupsClick = contentCallbacks::onEnableBackupsClick, - onChangeBackupsTypeClick = contentCallbacks::onBackupsTypeClick - ) + Texts.SectionHeader(text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_details)) } - if (messageBackupsType == null) { + if (backupProgress == null || backupProgress.state == ArchiveUploadProgressState.State.None) { item { - Rows.TextRow( - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__payment_history), - onClick = contentCallbacks::onViewPaymentHistory + LastBackupRow( + lastBackupTimestamp = lastBackupTimestamp, + onBackupNowClick = contentCallbacks::onBackupNowClick ) } } else { item { - Dividers.Default() - } - - item { - Texts.SectionHeader(text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_details)) + InProgressBackupRow(progress = backupProgress.completedAttachments.toInt(), totalProgress = backupProgress.totalAttachments.toInt()) } + } - if (backupProgress == null || backupProgress.state == ArchiveUploadProgressState.State.None) { - item { - LastBackupRow( - lastBackupTimestamp = lastBackupTimestamp, - onBackupNowClick = contentCallbacks::onBackupNowClick + item { + Rows.TextRow(text = { + Column { + Text( + text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_size), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = Util.getPrettyFileSize(backupSize), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant ) } - } else { - item { - InProgressBackupRow(progress = backupProgress.completedAttachments.toInt(), totalProgress = backupProgress.totalAttachments.toInt()) - } - } + }) + } - item { - Rows.TextRow(text = { + item { + Rows.TextRow( + text = { Column { Text( - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_size), + text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_frequency), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface ) Text( - text = Util.getPrettyFileSize(backupSize), + text = getTextForFrequency(backupsFrequency = backupsFrequency), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } - }) - } - - item { - Rows.TextRow( - text = { - Column { - Text( - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_frequency), - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface - ) - Text( - text = getTextForFrequency(backupsFrequency = backupsFrequency), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - }, - onClick = contentCallbacks::onChangeBackupFrequencyClick - ) - } + }, + onClick = contentCallbacks::onChangeBackupFrequencyClick + ) + } - item { - Rows.ToggleRow( - checked = canBackUpUsingCellular, - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__back_up_using_cellular), - onCheckChanged = contentCallbacks::onBackUpUsingCellularClick - ) - } + item { + Rows.ToggleRow( + checked = canBackUpUsingCellular, + text = stringResource(id = R.string.RemoteBackupsSettingsFragment__back_up_using_cellular), + onCheckChanged = contentCallbacks::onBackUpUsingCellularClick + ) + } - item { - Dividers.Default() - } + item { + Dividers.Default() + } - item { - Rows.TextRow( - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__turn_off_and_delete_backup), - foregroundTint = MaterialTheme.colorScheme.error, - onClick = contentCallbacks::onTurnOffAndDeleteBackupsClick - ) - } + item { + Rows.TextRow( + text = stringResource(id = R.string.RemoteBackupsSettingsFragment__turn_off_and_delete_backup), + foregroundTint = MaterialTheme.colorScheme.error, + onClick = contentCallbacks::onTurnOffAndDeleteBackupsClick + ) } } } @@ -383,63 +377,71 @@ private fun RemoteBackupsSettingsContent( @Composable private fun BackupTypeRow( - messageBackupsType: MessageBackupsType?, - onEnableBackupsClick: () -> Unit, - onChangeBackupsTypeClick: () -> Unit + messageBackupsType: MessageBackupsType, + renewalTime: Duration, + onBackupTypeActionButtonClicked: (MessageBackupTier) -> Unit = {} ) { - Row( + Column( modifier = Modifier .fillMaxWidth() - .clickable(enabled = messageBackupsType != null, onClick = onChangeBackupsTypeClick) - .padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter)) - .padding(top = 16.dp, bottom = 14.dp) + .padding(horizontal = 16.dp, vertical = 12.dp) + .background(color = SignalTheme.colors.colorSurface2, shape = RoundedCornerShape(12.dp)) + .padding(24.dp) ) { - Column( - modifier = Modifier.weight(1f) - ) { - Text( - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_type), - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface - ) - - if (messageBackupsType == null) { - Text( - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backups_disabled), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } else if (messageBackupsType is MessageBackupsType.Paid) { - val localResources = LocalContext.current.resources - val formattedCurrency = remember(messageBackupsType.pricePerMonth) { - FiatMoneyUtil.format(localResources, messageBackupsType.pricePerMonth, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) + Row { + Column { + val title = when (messageBackupsType) { + is MessageBackupsType.Paid -> stringResource(R.string.MessageBackupsTypeSelectionScreen__text_plus_all_your_media) + is MessageBackupsType.Free -> pluralStringResource(R.plurals.MessageBackupsTypeSelectionScreen__text_plus_d_days_of_media, messageBackupsType.mediaRetentionDays, messageBackupsType.mediaRetentionDays) } Text( - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__s_dot_s_per_month, stringResource(id = R.string.MessageBackupsTypeSelectionScreen__text_plus_all_your_media), formattedCurrency) + text = buildAnnotatedString { + SignalSymbol(SignalSymbols.Weight.REGULAR, SignalSymbols.Glyph.CHECKMARK) + append(" ") + append(title) + }, + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium ) - } else { - val retentionDays = (messageBackupsType as MessageBackupsType.Free).mediaRetentionDays - val localResources = LocalContext.current.resources - val formattedCurrency = remember { - val currency = SignalStore.inAppPayments.getSubscriptionCurrency(InAppPaymentSubscriberRecord.Type.BACKUP) - FiatMoneyUtil.format(localResources, FiatMoney(BigDecimal.ZERO, currency), FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) + + val cost = when (messageBackupsType) { + is MessageBackupsType.Paid -> stringResource(R.string.RemoteBackupsSettingsFragment__s_per_month, FiatMoneyUtil.format(LocalContext.current.resources, messageBackupsType.pricePerMonth)) + is MessageBackupsType.Free -> stringResource(R.string.RemoteBackupsSettingsFragment__your_backup_plan_is_free) } Text( - text = stringResource( - id = R.string.RemoteBackupsSettingsFragment__s_dot_s_per_month, - pluralStringResource(id = R.plurals.MessageBackupsTypeSelectionScreen__text_plus_d_days_of_media, retentionDays, retentionDays), - formattedCurrency - ) + text = cost, + modifier = Modifier.padding(top = 12.dp) ) + + if (messageBackupsType is MessageBackupsType.Paid) { + if (renewalTime > 0.seconds) { + Text( + text = stringResource(R.string.RemoteBackupsSettingsFragment__renews_s, DateUtils.formatDateWithYear(Locale.getDefault(), renewalTime.inWholeMilliseconds)) + ) + } + } } + + // Icon } - if (messageBackupsType == null) { - Buttons.Small(onClick = onEnableBackupsClick) { - Text(text = stringResource(id = R.string.RemoteBackupsSettingsFragment__enable_backups)) - } + val buttonText = when (messageBackupsType) { + is MessageBackupsType.Paid -> stringResource(R.string.RemoteBackupsSettingsFragment__manage_or_cancel) + is MessageBackupsType.Free -> stringResource(R.string.RemoteBackupsSettingsFragment__upgrade) + } + + Buttons.LargeTonal( + onClick = { onBackupTypeActionButtonClicked(messageBackupsType.tier) }, + colors = ButtonDefaults.filledTonalButtonColors().copy( + containerColor = SignalTheme.colors.colorTransparent5 + ), + modifier = Modifier.padding(top = 12.dp) + ) { + Text( + text = buttonText + ) } } } @@ -668,7 +670,7 @@ private fun getTextForFrequency(backupsFrequency: BackupFrequency): String { private fun RemoteBackupsSettingsContentPreview() { Previews.Preview { RemoteBackupsSettingsContent( - messageBackupsType = null, + messageBackupsType = MessageBackupsType.Free(mediaRetentionDays = 30), lastBackupTimestamp = -1, canBackUpUsingCellular = false, backupsFrequency = BackupFrequency.MANUAL, @@ -676,6 +678,7 @@ private fun RemoteBackupsSettingsContentPreview() { requestedSnackbar = RemoteBackupsSettingsState.Snackbar.NONE, contentCallbacks = object : ContentCallbacks {}, backupProgress = null, + renewalTime = 1727193018.seconds, backupSize = 2300000 ) } @@ -685,13 +688,22 @@ private fun RemoteBackupsSettingsContentPreview() { @Composable private fun BackupTypeRowPreview() { Previews.Preview { - BackupTypeRow( - messageBackupsType = MessageBackupsType.Free( - mediaRetentionDays = 30 - ), - onChangeBackupsTypeClick = {}, - onEnableBackupsClick = {} - ) + Column { + BackupTypeRow( + messageBackupsType = MessageBackupsType.Paid( + pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")), + storageAllowanceBytes = 100_000_000 + ), + renewalTime = 1727193018.seconds + ) + + BackupTypeRow( + messageBackupsType = MessageBackupsType.Free( + mediaRetentionDays = 30 + ), + renewalTime = 0.seconds + ) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsState.kt similarity index 83% rename from app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt rename to app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsState.kt index 1212b12057..b70706b578 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsState.kt @@ -3,10 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.components.settings.app.chats.backups +package org.thoughtcrime.securesms.components.settings.app.backups.remote import org.thoughtcrime.securesms.backup.v2.BackupFrequency import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds data class RemoteBackupsSettingsState( val messageBackupsType: MessageBackupsType? = null, @@ -14,6 +16,7 @@ data class RemoteBackupsSettingsState( val backupSize: Long = 0, val backupsFrequency: BackupFrequency = BackupFrequency.DAILY, val lastBackupTimestamp: Long = 0, + val renewalTime: Duration = 0.seconds, val dialog: Dialog = Dialog.NONE, val snackbar: Snackbar = Snackbar.NONE ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt similarity index 80% rename from app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsViewModel.kt rename to app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt index f4cf7fb644..3bf3fbd45a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.components.settings.app.chats.backups +package org.thoughtcrime.securesms.components.settings.app.backups.remote import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -17,11 +17,14 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.thoughtcrime.securesms.backup.v2.BackupFrequency import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository +import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.service.MessageBackupListener import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds /** * ViewModel for state management of RemoteBackupsSettingsFragment @@ -40,6 +43,19 @@ class RemoteBackupsSettingsViewModel : ViewModel() { init { refresh() + + viewModelScope.launch { + val activeSubscription = withContext(Dispatchers.IO) { + RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) + } + + if (activeSubscription.isSuccess) { + val subscription = activeSubscription.getOrThrow().activeSubscription + if (subscription.isActive && subscription != null) { + _state.update { it.copy(renewalTime = subscription.endOfCurrentPeriod.seconds) } + } + } + } } fun setCanBackUpUsingCellular(canBackUpUsingCellular: Boolean) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsFragment.kt similarity index 95% rename from app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt rename to app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsFragment.kt index e6f5b3404f..1e1772333f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsFragment.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.components.settings.app.chats.backups.type +package org.thoughtcrime.securesms.components.settings.app.backups.type import android.os.Bundle import android.view.View @@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.util.DateUtils -import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.viewModel import java.math.BigDecimal import java.util.Locale @@ -86,10 +85,6 @@ class BackupsTypeSettingsFragment : ComposeFragment() { findNavController().popBackStack() } - override fun onPaymentHistoryClick() { - findNavController().safeNavigate(R.id.action_backupsTypeSettingsFragment_to_remoteBackupsPaymentHistoryFragment) - } - override fun onChangeOrCancelSubscriptionClick() { checkoutLauncher.launch(Unit) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsState.kt similarity index 85% rename from app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt rename to app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsState.kt index 3c77c1e305..52dde1e5e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsState.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.components.settings.app.chats.backups.type +package org.thoughtcrime.securesms.components.settings.app.backups.type import androidx.compose.runtime.Stable import org.signal.donations.PaymentSourceType diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsViewModel.kt similarity index 95% rename from app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt rename to app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsViewModel.kt index 02c477d36c..54c16164e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsViewModel.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.components.settings.app.chats.backups.type +package org.thoughtcrime.securesms.components.settings.app.backups.type import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryFragment.kt deleted file mode 100644 index 83b16f6944..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryFragment.kt +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.chats.backups.history - -import android.content.Intent -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.fragment.app.viewModels -import androidx.navigation.NavType -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController -import androidx.navigation.fragment.findNavController -import androidx.navigation.navArgument -import kotlinx.collections.immutable.persistentMapOf -import kotlinx.collections.immutable.toPersistentList -import org.signal.core.ui.Buttons -import org.signal.core.ui.Dialogs -import org.signal.core.ui.Dividers -import org.signal.core.ui.Previews -import org.signal.core.ui.Rows -import org.signal.core.ui.Scaffolds -import org.signal.core.ui.SignalPreview -import org.signal.core.ui.Texts -import org.signal.core.util.money.FiatMoney -import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.components.settings.app.subscription.receipts.ReceiptImageRenderer -import org.thoughtcrime.securesms.compose.ComposeFragment -import org.thoughtcrime.securesms.compose.Nav -import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord -import org.thoughtcrime.securesms.payments.FiatMoneyUtil -import org.thoughtcrime.securesms.util.DateUtils -import java.math.BigDecimal -import java.util.Calendar -import java.util.Currency -import java.util.Locale - -/** - * Displays a list or detail view of in-app-payment receipts related to - * backups. - */ -class RemoteBackupsPaymentHistoryFragment : ComposeFragment() { - - private val viewModel: RemoteBackupsPaymentHistoryViewModel by viewModels() - - @Composable - override fun FragmentContent() { - val state by viewModel.state.collectAsState() - val navController = rememberNavController() - - LaunchedEffect(Unit) { - navController.setOnBackPressedDispatcher(requireActivity().onBackPressedDispatcher) - navController.enableOnBackPressed(true) - } - - val onNavigationClick = remember { - { - if (!navController.popBackStack()) { - findNavController().popBackStack() - } - } - } - - Nav.Host(navController = navController, startDestination = "list") { - composable("list") { - PaymentHistoryContent( - state = state, - onNavigationClick = onNavigationClick, - onRecordClick = { navController.navigate("detail/${it.id}") } - ) - } - - composable("detail/{recordId}", listOf(navArgument("recordId") { type = NavType.LongType })) { backStackEntry -> - val recordId = backStackEntry.arguments?.getLong("recordId")!! - val record = state.records[recordId]!! - - PaymentHistoryDetails( - record = record, - onNavigationClick = onNavigationClick, - onShareClick = this@RemoteBackupsPaymentHistoryFragment::onShareClick - ) - - if (state.displayProgressDialog) { - Dialogs.IndeterminateProgressDialog() - } - } - } - } - - private fun onShareClick(record: InAppPaymentReceiptRecord) { - viewModel.onStartRenderingBitmap() - ReceiptImageRenderer.renderPng( - requireContext(), - viewLifecycleOwner, - record, - getString(R.string.RemoteBackupsPaymentHistoryFragment__text_and_all_media_backup), - object : ReceiptImageRenderer.Callback { - override fun onBitmapRendered() { - viewModel.onEndRenderingBitmap() - } - - override fun onStartActivity(intent: Intent) { - startActivity(intent) - } - } - ) - } -} - -@Composable -private fun PaymentHistoryContent( - state: RemoteBackupsPaymentHistoryState, - onNavigationClick: () -> Unit, - onRecordClick: (InAppPaymentReceiptRecord) -> Unit -) { - Scaffolds.Settings( - title = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__payment_history), - navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24), - onNavigationClick = onNavigationClick - ) { - val itemList = remember(state.records) { state.records.values.toPersistentList() } - - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(it) - ) { - itemsIndexed( - items = itemList, - key = { _, item -> item.id } - ) { idx, item -> - val previous = itemList.getOrNull(idx - 1) - val previousYear = rememberYear(timestamp = previous?.timestamp ?: 0) - val ourYear = rememberYear(timestamp = item.timestamp) - - if (previousYear != ourYear) { - Texts.SectionHeader(text = "$ourYear") - } - - PaymentHistoryRow(item, onRecordClick) - } - } - } -} - -@Composable -private fun rememberYear(timestamp: Long): Int { - if (timestamp == 0L) { - return -1 - } - - val calendar = remember { - Calendar.getInstance() - } - - return remember(timestamp) { - calendar.timeInMillis = timestamp - calendar.get(Calendar.YEAR) - } -} - -@Composable -private fun PaymentHistoryRow( - record: InAppPaymentReceiptRecord, - onRecordClick: (InAppPaymentReceiptRecord) -> Unit -) { - val date = remember(record.timestamp) { - DateUtils.formatDateWithYear(Locale.getDefault(), record.timestamp) - } - - val onClick = remember(record) { - { onRecordClick(record) } - } - - Rows.TextRow(text = { - Column( - modifier = Modifier.weight(1f) - ) { - Text( - text = date, - style = MaterialTheme.typography.bodyLarge - ) - - Text( - text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__text_and_all_media_backup), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - - val resources = LocalContext.current.resources - val fiat = remember(record.amount) { - FiatMoneyUtil.format(resources, record.amount, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) - } - - Text(text = fiat) - }, onClick = onClick) -} - -@Composable -private fun PaymentHistoryDetails( - record: InAppPaymentReceiptRecord, - onNavigationClick: () -> Unit, - onShareClick: (InAppPaymentReceiptRecord) -> Unit -) { - Scaffolds.Settings( - title = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__payment_details), - onNavigationClick = onNavigationClick, - navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24) - ) { - Column( - modifier = Modifier - .fillMaxSize() - .padding(it) - ) { - val resources = LocalContext.current.resources - val formattedAmount = remember(record.amount) { - FiatMoneyUtil.format(resources, record.amount, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) - } - - Image( - painter = painterResource(id = R.drawable.ic_signal_logo_type), - contentDescription = null, - modifier = Modifier - .align(alignment = Alignment.CenterHorizontally) - .padding(top = 24.dp, bottom = 16.dp) - ) - - Text( - text = formattedAmount, - style = MaterialTheme.typography.displayMedium, - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() - ) - - Dividers.Default() - - Rows.TextRow( - text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__backup_type), - label = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__text_and_all_media_backup) - ) - - val formattedDate = remember(record.timestamp) { - DateUtils.formatDateWithYear(Locale.getDefault(), record.timestamp) - } - - Rows.TextRow( - text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__date_paid), - label = formattedDate - ) - - Spacer(modifier = Modifier.weight(1f)) - - Buttons.LargePrimary( - onClick = { onShareClick(record) }, - modifier = Modifier - .padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter)) - .padding(bottom = 24.dp) - .fillMaxWidth() - ) { - Text(text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__share)) - } - } - } -} - -@SignalPreview -@Composable -private fun PaymentHistoryContentPreview() { - Previews.Preview { - PaymentHistoryContent( - state = RemoteBackupsPaymentHistoryState( - records = persistentMapOf( - 1L to testRecord() - ) - ), - onNavigationClick = {}, - onRecordClick = {} - ) - } -} - -@SignalPreview -@Composable -private fun PaymentHistoryRowPreview() { - Previews.Preview { - PaymentHistoryRow( - record = testRecord(), - onRecordClick = {} - ) - } -} - -@SignalPreview -@Composable -private fun PaymentDetailsContentPreview() { - Previews.Preview { - PaymentHistoryDetails( - record = testRecord(), - onNavigationClick = {}, - onShareClick = {} - ) - } -} - -private fun testRecord(): InAppPaymentReceiptRecord { - return InAppPaymentReceiptRecord( - id = 1, - amount = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")), - timestamp = 1718739691000, - type = InAppPaymentReceiptRecord.Type.RECURRING_BACKUP, - subscriptionLevel = 201 - ) -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryRepository.kt deleted file mode 100644 index 89f1655142..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryRepository.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.chats.backups.history - -import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord - -object RemoteBackupsPaymentHistoryRepository { - - fun getReceipts(): List { - return SignalDatabase.donationReceipts.getReceipts(InAppPaymentReceiptRecord.Type.RECURRING_BACKUP) - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryState.kt deleted file mode 100644 index 3189026113..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryState.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.chats.backups.history - -import androidx.compose.runtime.Stable -import kotlinx.collections.immutable.PersistentMap -import kotlinx.collections.immutable.persistentMapOf -import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord - -@Stable -data class RemoteBackupsPaymentHistoryState( - val records: PersistentMap = persistentMapOf(), - val displayProgressDialog: Boolean = false -) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryViewModel.kt deleted file mode 100644 index 9399dfbfe7..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/history/RemoteBackupsPaymentHistoryViewModel.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.chats.backups.history - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.collections.immutable.toPersistentMap -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class RemoteBackupsPaymentHistoryViewModel : ViewModel() { - - private val internalStateFlow = MutableStateFlow(RemoteBackupsPaymentHistoryState()) - val state: StateFlow = internalStateFlow - - init { - viewModelScope.launch { - val receipts = withContext(Dispatchers.IO) { - RemoteBackupsPaymentHistoryRepository.getReceipts() - } - - internalStateFlow.update { state -> state.copy(records = receipts.associateBy { it.id }.toPersistentMap()) } - } - } - - fun onStartRenderingBitmap() { - internalStateFlow.update { it.copy(displayProgressDialog = true) } - } - - fun onEndRenderingBitmap() { - internalStateFlow.update { it.copy(displayProgressDialog = false) } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt index c562015691..da0cf658b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription import androidx.annotation.CheckResult +import androidx.annotation.WorkerThread import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers @@ -46,18 +47,26 @@ object RecurringInAppPaymentRepository { private val donationsService = AppDependencies.donationsService fun getActiveSubscription(type: InAppPaymentSubscriberRecord.Type): Single { - val localSubscription = InAppPaymentsRepository.getSubscriber(type) - return if (localSubscription != null) { - Single.fromCallable { donationsService.getSubscription(localSubscription.subscriberId) } - .subscribeOn(Schedulers.io()) - .flatMap(ServiceResponse::flattenResult) - .doOnSuccess { activeSubscription -> - if (activeSubscription.isActive && activeSubscription.activeSubscription.endOfCurrentPeriod > SignalStore.inAppPayments.getLastEndOfPeriod()) { - InAppPaymentKeepAliveJob.enqueueAndTrackTime(System.currentTimeMillis().milliseconds) - } - } - } else { - Single.just(ActiveSubscription.EMPTY) + return Single.fromCallable { + getActiveSubscriptionSync(type).getOrThrow() + }.subscribeOn(Schedulers.io()) + } + + @WorkerThread + fun getActiveSubscriptionSync(type: InAppPaymentSubscriberRecord.Type): Result { + val response = InAppPaymentsRepository.getSubscriber(type)?.let { + donationsService.getSubscription(it.subscriberId) + } ?: return Result.success(ActiveSubscription.EMPTY) + + return try { + val result = response.resultOrThrow + if (result.isActive && result.activeSubscription.endOfCurrentPeriod > SignalStore.inAppPayments.getLastEndOfPeriod()) { + InAppPaymentKeepAliveJob.enqueueAndTrackTime(System.currentTimeMillis().milliseconds) + } + + Result.success(result) + } catch (e: Exception) { + Result.failure(e) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/fonts/SignalSymbols.kt b/app/src/main/java/org/thoughtcrime/securesms/fonts/SignalSymbols.kt index 1a684905f8..fd2246bd5d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/fonts/SignalSymbols.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/fonts/SignalSymbols.kt @@ -10,6 +10,12 @@ import android.graphics.Typeface import android.text.SpannableStringBuilder import android.text.TextPaint import android.text.style.MetricAffectingSpan +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.withStyle /** * Helper object for working with the SignalSymbols font @@ -17,6 +23,7 @@ import android.text.style.MetricAffectingSpan object SignalSymbols { enum class Glyph(val unicode: Char) { + CHECKMARK('\u2713'), CHEVRON_RIGHT('\uE025'), PERSON_CIRCLE('\uE05E') } @@ -43,6 +50,17 @@ object SignalSymbols { return text } + @Composable + fun AnnotatedString.Builder.SignalSymbol(weight: Weight, glyph: Glyph) { + withStyle( + SpanStyle( + fontFamily = FontFamily(getTypeface(LocalContext.current, weight)) + ) + ) { + append(glyph.unicode.toString()) + } + } + private fun getTypeface(context: Context, weight: Weight): Typeface { return when (weight) { Weight.BOLD -> getBoldWeightedFont(context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt index fcc0929384..be9aae4020 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt @@ -258,6 +258,7 @@ class InAppPaymentRedemptionJob private constructor( Log.i(TAG, "Enabling backups and setting backup tier to PAID", true) SignalStore.backup.areBackupsEnabled = true SignalStore.backup.backupTier = MessageBackupTier.PAID + SignalStore.uiHints.markHasEverEnabledRemoteBackups() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt index e2419695ad..65ca82d2fd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt @@ -516,6 +516,7 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor SignalStore.backup.areBackupsEnabled = true SignalStore.backup.backupTier = MessageBackupTier.PAID + SignalStore.uiHints.markHasEverEnabledRemoteBackups() } val subscriber = InAppPaymentsRepository.requireSubscriber(subscriberType) diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java index 81944216bc..4ddc5a3db7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHintValues.java @@ -27,6 +27,7 @@ public class UiHintValues extends SignalStoreValues { private static final String DISMISSED_CONTACTS_PERMISSION_BANNER = "uihints.dismissed_contacts_permission_banner"; private static final String HAS_SEEN_DELETE_SYNC_EDUCATION_SHEET = "uihints.has_seen_delete_sync_education_sheet"; private static final String LAST_SUPPORT_VERSION_SEEN = "uihints.last_support_version_seen"; + private static final String HAS_EVER_ENABLED_REMOTE_BACKUPS = "uihints.has_ever_enabled_remote_backups"; UiHintValues(@NonNull KeyValueStore store) { super(store); @@ -39,7 +40,7 @@ void onFirstEverAppLaunch() { @Override @NonNull List getKeysToIncludeInBackup() { - return Arrays.asList(NEVER_DISPLAY_PULL_TO_FILTER_TIP, HAS_COMPLETED_USERNAME_ONBOARDING, HAS_SEEN_TEXT_FORMATTING_ALERT); + return Arrays.asList(NEVER_DISPLAY_PULL_TO_FILTER_TIP, HAS_COMPLETED_USERNAME_ONBOARDING, HAS_SEEN_TEXT_FORMATTING_ALERT, HAS_EVER_ENABLED_REMOTE_BACKUPS); } public void markHasSeenGroupSettingsMenuToast() { @@ -200,4 +201,12 @@ public int getLastSupportVersionSeen() { public void setLastSupportVersionSeen(int version) { putInteger(LAST_SUPPORT_VERSION_SEEN, version); } + + public void markHasEverEnabledRemoteBackups() { + putBoolean(HAS_EVER_ENABLED_REMOTE_BACKUPS, true); + } + + public boolean getHasEverEnabledRemoteBackups() { + return getBoolean(HAS_EVER_ENABLED_REMOTE_BACKUPS, false); + } } diff --git a/app/src/main/res/drawable/symbol_backup_24.xml b/app/src/main/res/drawable/symbol_backup_24.xml new file mode 100644 index 0000000000..2bf6c8968f --- /dev/null +++ b/app/src/main/res/drawable/symbol_backup_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index b8d3c3f853..ae627bd003 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -29,6 +29,13 @@ android:defaultValue="true" app:argType="boolean" /> + + android:id="@+id/backupsSettingsFragment" + android:name="org.thoughtcrime.securesms.components.settings.app.backups.BackupsSettingsFragment"> + + + + + android:name="org.thoughtcrime.securesms.components.settings.app.backups.type.BackupsTypeSettingsFragment"> - - - diff --git a/app/src/main/res/navigation/app_settings_with_change_number.xml b/app/src/main/res/navigation/app_settings_with_change_number.xml index 2fcb080693..a1b616ebfb 100644 --- a/app/src/main/res/navigation/app_settings_with_change_number.xml +++ b/app/src/main/res/navigation/app_settings_with_change_number.xml @@ -10,6 +10,13 @@ android:name="org.thoughtcrime.securesms.components.settings.app.AppSettingsFragment" android:label="app_settings_fragment" tools:layout="@layout/dsl_settings_fragment"> + + android:id="@+id/backupsSettingsFragment" + android:name="org.thoughtcrime.securesms.components.settings.app.backups.BackupsSettingsFragment"> - - - + android:id="@+id/remoteBackupsSettingsFragment" + android:name="org.thoughtcrime.securesms.components.settings.app.backups.remote.RemoteBackupsSettingsFragment"> + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3087e4e1a8..a6cfa8627c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7336,19 +7336,15 @@ OK - - - Payment history - - Text and all media backup - - Payment details - - Backup type - - Date paid - - Share + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7376,13 +7372,27 @@ Backup will be created overnight. - Backup type + Backup plan Backups disabled - - %1$s · %2$s/month - - Enable backups + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d From 62b5276c75104511863e1b7bb1ced8af99dc1e85 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 27 Sep 2024 10:46:37 -0400 Subject: [PATCH 16/53] Add support for Contact.nickname field. --- .../v2/database/ChatItemImportInserter.kt | 4 +- .../securesms/contactshare/Contact.java | 24 +++--- .../contactshare/ContactModelMapper.java | 82 +------------------ .../ContactNameEditViewModel.java | 2 +- .../securesms/contactshare/ContactUtil.java | 16 +++- .../contactshare/SharedContactRepository.java | 4 +- .../securesms/contactshare/VCardUtil.java | 3 +- .../api/SignalServiceMessageSender.java | 12 +-- .../api/messages/shared/SharedContact.java | 38 ++++----- .../src/main/protowire/SignalService.proto | 3 +- 10 files changed, 61 insertions(+), 127 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index 734827220d..d6c9f28907 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -65,7 +65,6 @@ import org.thoughtcrime.securesms.payments.Direction import org.thoughtcrime.securesms.payments.FailureReason import org.thoughtcrime.securesms.payments.State import org.thoughtcrime.securesms.payments.proto.PaymentMetaData -import org.thoughtcrime.securesms.profiles.ProfileName import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stickers.StickerLocator @@ -1021,8 +1020,7 @@ class ChatItemImportInserter( } private fun ContactAttachment.Name?.toLocal(): Contact.Name { - val displayName = ProfileName.fromParts(this?.givenName, this?.familyName).toString() - return Contact.Name(displayName, this?.givenName, this?.familyName, this?.prefix, this?.suffix, this?.middleName) + return Contact.Name(this?.givenName, this?.familyName, this?.prefix, this?.suffix, this?.middleName, null) } private fun ContactAttachment.Phone.Type?.toLocal(): Contact.Phone.Type { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java index 487fab8105..97cfffb225 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java @@ -142,9 +142,6 @@ public Contact[] newArray(int size) { public static class Name implements Parcelable { - @JsonProperty - private final String displayName; - @JsonProperty private final String givenName; @@ -160,30 +157,29 @@ public static class Name implements Parcelable { @JsonProperty private final String middleName; + @JsonProperty + private final String nickname; + public Name( - @JsonProperty("displayName") @Nullable String displayName, @JsonProperty("givenName") @Nullable String givenName, @JsonProperty("familyName") @Nullable String familyName, @JsonProperty("prefix") @Nullable String prefix, @JsonProperty("suffix") @Nullable String suffix, - @JsonProperty("middleName") @Nullable String middleName) + @JsonProperty("middleName") @Nullable String middleName, + @JsonProperty("nickname") @Nullable String nickname) { - this.displayName = displayName; this.givenName = givenName; this.familyName = familyName; this.prefix = prefix; this.suffix = suffix; this.middleName = middleName; + this.nickname = nickname; } private Name(Parcel in) { this(in.readString(), in.readString(), in.readString(), in.readString(), in.readString(), in.readString()); } - public @Nullable String getDisplayName() { - return displayName; - } - public @Nullable String getGivenName() { return givenName; } @@ -204,8 +200,12 @@ private Name(Parcel in) { return middleName; } + public @Nullable String getNickname() { + return nickname; + } + public boolean isEmpty() { - return TextUtils.isEmpty(displayName) && + return TextUtils.isEmpty(nickname) && TextUtils.isEmpty(givenName) && TextUtils.isEmpty(familyName) && TextUtils.isEmpty(prefix) && @@ -220,12 +220,12 @@ public int describeContents() { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(displayName); dest.writeString(givenName); dest.writeString(familyName); dest.writeString(prefix); dest.writeString(suffix); dest.writeString(middleName); + dest.writeString(nickname); } public static final Creator CREATOR = new Creator() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java index 8c89829a09..8ca512b920 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java @@ -59,7 +59,7 @@ public static SharedContact.Builder localToRemoteBuilder(@NonNull Contact contac .build()); } - SharedContact.Name name = new SharedContact.Name.Builder().setDisplay(contact.getName().getDisplayName()) + SharedContact.Name name = new SharedContact.Name.Builder().setNickname(contact.getName().getNickname()) .setGiven(contact.getName().getGivenName()) .setFamily(contact.getName().getFamilyName()) .setPrefix(contact.getName().getPrefix()) @@ -74,66 +74,14 @@ public static SharedContact.Builder localToRemoteBuilder(@NonNull Contact contac .withAddresses(postalAddresses); } - public static Contact remoteToLocal(@NonNull SharedContact sharedContact) { - Name name = new Name(sharedContact.getName().getDisplay().orElse(null), - sharedContact.getName().getGiven().orElse(null), - sharedContact.getName().getFamily().orElse(null), - sharedContact.getName().getPrefix().orElse(null), - sharedContact.getName().getSuffix().orElse(null), - sharedContact.getName().getMiddle().orElse(null)); - - List phoneNumbers = new LinkedList<>(); - if (sharedContact.getPhone().isPresent()) { - for (SharedContact.Phone phone : sharedContact.getPhone().get()) { - phoneNumbers.add(new Phone(phone.getValue(), - remoteToLocalType(phone.getType()), - phone.getLabel().orElse(null))); - } - } - - List emails = new LinkedList<>(); - if (sharedContact.getEmail().isPresent()) { - for (SharedContact.Email email : sharedContact.getEmail().get()) { - emails.add(new Email(email.getValue(), - remoteToLocalType(email.getType()), - email.getLabel().orElse(null))); - } - } - - List postalAddresses = new LinkedList<>(); - if (sharedContact.getAddress().isPresent()) { - for (SharedContact.PostalAddress postalAddress : sharedContact.getAddress().get()) { - postalAddresses.add(new PostalAddress(remoteToLocalType(postalAddress.getType()), - postalAddress.getLabel().orElse(null), - postalAddress.getStreet().orElse(null), - postalAddress.getPobox().orElse(null), - postalAddress.getNeighborhood().orElse(null), - postalAddress.getCity().orElse(null), - postalAddress.getRegion().orElse(null), - postalAddress.getPostcode().orElse(null), - postalAddress.getCountry().orElse(null))); - } - } - - Avatar avatar = null; - if (sharedContact.getAvatar().isPresent()) { - Attachment attachment = PointerAttachment.forPointer(Optional.of(sharedContact.getAvatar().get().getAttachment().asPointer())).get(); - boolean isProfile = sharedContact.getAvatar().get().isProfile(); - - avatar = new Avatar(null, attachment, isProfile); - } - - return new Contact(name, sharedContact.getOrganization().orElse(null), phoneNumbers, emails, postalAddresses, avatar); - } - public static Contact remoteToLocal(@NonNull DataMessage.Contact contact) { DataMessage.Contact.Name contactName = contact.name != null ? contact.name : new DataMessage.Contact.Name(); - Name name = new Name(contactName.displayName, - contactName.givenName, + Name name = new Name(contactName.givenName, contactName.familyName, contactName.prefix, contactName.suffix, - contactName.middleName); + contactName.middleName, + contactName.nickname); List phoneNumbers = new ArrayList<>(contact.number.size()); for (DataMessage.Contact.Phone phone : contact.number) { @@ -182,17 +130,6 @@ public static Contact remoteToLocal(@NonNull DataMessage.Contact contact) { return new Contact(name, contact.organization, phoneNumbers, emails, postalAddresses, avatar); } - private static Phone.Type remoteToLocalType(@Nullable SharedContact.Phone.Type type) { - if (type == null) return Phone.Type.CUSTOM; - - switch (type) { - case HOME: return Phone.Type.HOME; - case MOBILE: return Phone.Type.MOBILE; - case WORK: return Phone.Type.WORK; - default: return Phone.Type.CUSTOM; - } - } - private static Phone.Type remoteToLocalType(@Nullable DataMessage.Contact.Phone.Type type) { if (type == null) return Phone.Type.CUSTOM; @@ -204,17 +141,6 @@ private static Phone.Type remoteToLocalType(@Nullable DataMessage.Contact.Phone. } } - private static Email.Type remoteToLocalType(@Nullable SharedContact.Email.Type type) { - if (type == null) return Email.Type.CUSTOM; - - switch (type) { - case HOME: return Email.Type.HOME; - case MOBILE: return Email.Type.MOBILE; - case WORK: return Email.Type.WORK; - default: return Email.Type.CUSTOM; - } - } - private static Email.Type remoteToLocalType(@Nullable DataMessage.Contact.Email.Type type) { if (type == null) return Email.Type.CUSTOM; diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java index b1433dc36f..4690495d95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java @@ -36,7 +36,7 @@ void setName(@NonNull Name name) { } Name getName() { - return new Name(displayName.getValue(), givenName, familyName, prefix, suffix, middleName); + return new Name(givenName, familyName, prefix, suffix, middleName, null); } void updateGivenName(@NonNull String givenName) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java index 3a464785f2..9c65f87337 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java @@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; +import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.SpanUtil; @@ -65,8 +66,12 @@ public static long getContactIdFromUri(@NonNull Uri uri) { return ""; } - if (!TextUtils.isEmpty(contact.getName().getDisplayName())) { - return contact.getName().getDisplayName(); + if (!TextUtils.isEmpty(contact.getName().getNickname())) { + return contact.getName().getNickname(); + } + + if (!TextUtils.isEmpty(contact.getName().getGivenName())) { + return ProfileName.fromParts(contact.getName().getGivenName(), contact.getName().getFamilyName()).toString(); } if (!TextUtils.isEmpty(contact.getOrganization())) { @@ -152,8 +157,11 @@ public static List getRecipients(@NonNull Context context, @NonNull Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); - if (!TextUtils.isEmpty(contact.getName().getDisplayName())) { - intent.putExtra(ContactsContract.Intents.Insert.NAME, contact.getName().getDisplayName()); + if (!TextUtils.isEmpty(contact.getName().getNickname())) { + intent.putExtra(ContactsContract.Intents.Insert.NAME, contact.getName().getNickname()); + } else if (!TextUtils.isEmpty(contact.getName().getGivenName())) { + String displayName = ProfileName.fromParts(contact.getName().getGivenName(), contact.getName().getFamilyName()).toString(); + intent.putExtra(ContactsContract.Intents.Insert.NAME, displayName); } if (!TextUtils.isEmpty(contact.getOrganization())) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java index 9c0a78f8df..264e23e3e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java @@ -108,7 +108,7 @@ void getContacts(@NonNull List contactUris, @NonNull ValueCallback contactUris, @NonNull ValueCallback parseContacts(@NonNull String vCardData) { return null; } - Contact.Name name = new Contact.Name(displayName, + Contact.Name name = new Contact.Name( vName != null ? vName.getGiven() : null, vName != null ? vName.getFamily() : null, vName != null && !vName.getPrefixes().isEmpty() ? vName.getPrefixes().get(0) : null, vName != null && !vName.getSuffixes().isEmpty() ? vName.getSuffixes().get(0) : null, + null, null); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index d45b8f61e7..1a9525067e 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -1754,12 +1754,12 @@ private List createSharedContactContent(List for (SharedContact contact : contacts) { DataMessage.Contact.Name.Builder nameBuilder = new DataMessage.Contact.Name.Builder(); - if (contact.getName().getFamily().isPresent()) nameBuilder.familyName(contact.getName().getFamily().get()); - if (contact.getName().getGiven().isPresent()) nameBuilder.givenName(contact.getName().getGiven().get()); - if (contact.getName().getMiddle().isPresent()) nameBuilder.middleName(contact.getName().getMiddle().get()); - if (contact.getName().getPrefix().isPresent()) nameBuilder.prefix(contact.getName().getPrefix().get()); - if (contact.getName().getSuffix().isPresent()) nameBuilder.suffix(contact.getName().getSuffix().get()); - if (contact.getName().getDisplay().isPresent()) nameBuilder.displayName(contact.getName().getDisplay().get()); + if (contact.getName().getFamily().isPresent()) nameBuilder.familyName(contact.getName().getFamily().get()); + if (contact.getName().getGiven().isPresent()) nameBuilder.givenName(contact.getName().getGiven().get()); + if (contact.getName().getMiddle().isPresent()) nameBuilder.middleName(contact.getName().getMiddle().get()); + if (contact.getName().getPrefix().isPresent()) nameBuilder.prefix(contact.getName().getPrefix().get()); + if (contact.getName().getSuffix().isPresent()) nameBuilder.suffix(contact.getName().getSuffix().get()); + if (contact.getName().getNickname().isPresent()) nameBuilder.nickname(contact.getName().getNickname().get()); DataMessage.Contact.Builder contactBuilder = new DataMessage.Contact.Builder().name(nameBuilder.build()); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/shared/SharedContact.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/shared/SharedContact.java index 4bf284c38c..a7ad777348 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/shared/SharedContact.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/shared/SharedContact.java @@ -11,9 +11,9 @@ public class SharedContact { private final Name name; - private final Optional avatar; - private final Optional> phone; - private final Optional> email; + private final Optional avatar; + private final Optional> phone; + private final Optional> email; private final Optional> address; private final Optional organization; @@ -103,28 +103,28 @@ public Avatar build() { public static class Name { - private final Optional display; private final Optional given; private final Optional family; private final Optional prefix; private final Optional suffix; private final Optional middle; + private final Optional nickname; - public Name(Optional display, Optional given, Optional family, Optional prefix, Optional suffix, Optional middle) { - this.display = display; - this.given = given; - this.family = family; - this.prefix = prefix; - this.suffix = suffix; - this.middle = middle; + public Name(Optional given, Optional family, Optional prefix, Optional suffix, Optional middle, Optional nickname) { + this.given = given; + this.family = family; + this.prefix = prefix; + this.suffix = suffix; + this.middle = middle; + this.nickname = nickname; } public static Builder newBuilder() { return new Builder(); } - public Optional getDisplay() { - return display; + public Optional getNickname() { + return nickname; } public Optional getGiven() { @@ -148,15 +148,15 @@ public Optional getMiddle() { } public static class Builder { - private String display; + private String nickname; private String given; private String family; private String prefix; private String suffix; private String middle; - public Builder setDisplay(String display) { - this.display = display; + public Builder setNickname(String nickname) { + this.nickname = nickname; return this; } @@ -186,12 +186,12 @@ public Builder setMiddle(String middle) { } public Name build() { - return new Name(Optional.ofNullable(display), - Optional.ofNullable(given), + return new Name(Optional.ofNullable(given), Optional.ofNullable(family), Optional.ofNullable(prefix), Optional.ofNullable(suffix), - Optional.ofNullable(middle)); + Optional.ofNullable(middle), + Optional.ofNullable(nickname)); } } } diff --git a/libsignal-service/src/main/protowire/SignalService.proto b/libsignal-service/src/main/protowire/SignalService.proto index c16f9b24a0..343092d1d9 100644 --- a/libsignal-service/src/main/protowire/SignalService.proto +++ b/libsignal-service/src/main/protowire/SignalService.proto @@ -177,7 +177,8 @@ message DataMessage { optional string prefix = 3; optional string suffix = 4; optional string middleName = 5; - optional string displayName = 6; + reserved /*displayName*/ 6; + optional string nickname = 7; } message Phone { From 6678fdcbe9c8aef916a6804a014a126484a00e43 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 27 Sep 2024 12:04:01 -0300 Subject: [PATCH 17/53] Fix bad sheet behavior when returning from system pip. --- .../controls/ControlsAndInfoController.kt | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt index f8910ef955..c499cf281f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.components.webrtc.controls +import android.annotation.SuppressLint import android.content.res.ColorStateList import android.content.res.Configuration import android.graphics.Color @@ -201,22 +202,35 @@ class ControlsAndInfoController private constructor( BottomSheetBehaviorHack.setNestedScrollingChild(behavior, callInfoComposeView) behavior.addBottomSheetCallback(object : BottomSheetCallback() { + @SuppressLint("SwitchIntDef") override fun onStateChanged(bottomSheet: View, newState: Int) { overflowPopupWindow.dismiss() - if (newState == BottomSheetBehavior.STATE_COLLAPSED) { - controlsAndInfoViewModel.resetScrollState() - if (controlState.isFadeOutEnabled) { - hide(delay = HIDE_CONTROL_DELAY) + when (newState) { + BottomSheetBehavior.STATE_COLLAPSED -> { + controlsAndInfoViewModel.resetScrollState() + if (controlState.isFadeOutEnabled) { + hide(delay = HIDE_CONTROL_DELAY) + } + updateCallSheetVisibilities(0f) + } + BottomSheetBehavior.STATE_EXPANDED -> { + cancelScheduledHide() + updateCallSheetVisibilities(1f) + } + BottomSheetBehavior.STATE_DRAGGING -> { + cancelScheduledHide() + } + BottomSheetBehavior.STATE_HIDDEN -> { + controlsAndInfoViewModel.resetScrollState() + updateCallSheetVisibilities(-1f) } - } else if (newState == BottomSheetBehavior.STATE_EXPANDED || newState == BottomSheetBehavior.STATE_DRAGGING) { - cancelScheduledHide() - } else if (newState == BottomSheetBehavior.STATE_HIDDEN) { - controlsAndInfoViewModel.resetScrollState() } } override fun onSlide(bottomSheet: View, slideOffset: Float) { - updateCallSheetVisibilities(slideOffset) + if (slideOffset <= 1 || slideOffset >= -1) { + updateCallSheetVisibilities(slideOffset) + } } }) From ec3b40e2dd3e2843d1f21d50485a9b36782f24e0 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 27 Sep 2024 16:51:42 -0400 Subject: [PATCH 18/53] Update libsignal to 0.58.1 --- dependencies.gradle.kts | 2 +- gradle/verification-metadata.xml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dependencies.gradle.kts b/dependencies.gradle.kts index 2a7d06d73f..ea2aef5458 100644 --- a/dependencies.gradle.kts +++ b/dependencies.gradle.kts @@ -15,7 +15,7 @@ dependencyResolutionManagement { version("exoplayer", "2.19.0") version("glide", "4.15.1") version("kotlin", "1.9.20") - version("libsignal-client", "0.58.0") + version("libsignal-client", "0.58.1") version("mp4parser", "1.9.39") version("android-gradle-plugin", "8.4.0") version("accompanist", "0.28.0") diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 5483300670..7a0319e153 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -8997,20 +8997,20 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + - - - + + + - - + + From aa252b1733ec8845ab658ca94e53bb904b1a1443 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 27 Sep 2024 16:52:12 -0400 Subject: [PATCH 19/53] Add support for Contact.nickname in backup import/export. --- .../database/CallLinkTableBackupExtensions.kt | 4 +-- .../v2/database/ChatItemExportIterator.kt | 31 ++++++++++--------- .../v2/database/ChatItemImportInserter.kt | 2 +- app/src/main/protowire/Backup.proto | 3 +- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt index fdf2dbe61d..62c9c8fe61 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt @@ -79,7 +79,7 @@ class BackupCallLinkIterator(private val cursor: Cursor) : Iterator CallLink.Restrictions.ADMIN_APPROVAL CallLinkState.Restrictions.NONE -> CallLink.Restrictions.NONE diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index 14ccd07e05..abdbecf801 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -595,13 +595,13 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: val contacts = sharedContacts.map { ContactAttachment( - name = it.name.toBackup(), + name = it.name.toRemote(), avatar = (it.avatar?.attachment as? DatabaseAttachment)?.toRemoteMessageAttachment()?.pointer, organization = it.organization, number = it.phoneNumbers.map { phone -> ContactAttachment.Phone( value_ = phone.number, - type = phone.type.toBackup(), + type = phone.type.toRemote(), label = phone.label ) }, @@ -609,12 +609,12 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: ContactAttachment.Email( value_ = email.email, label = email.label, - type = email.type.toBackup() + type = email.type.toRemote() ) }, address = it.postalAddresses.map { address -> ContactAttachment.PostalAddress( - type = address.type.toBackup(), + type = address.type.toRemote(), label = address.label, street = address.street, pobox = address.poBox, @@ -629,21 +629,22 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } return ContactMessage( contact = contacts, - reactions = reactionRecords.toBackupReactions() + reactions = reactionRecords.toRemoteReactions() ) } - private fun Contact.Name.toBackup(): ContactAttachment.Name { + private fun Contact.Name.toRemote(): ContactAttachment.Name { return ContactAttachment.Name( givenName = givenName, familyName = familyName, prefix = prefix, suffix = suffix, - middleName = middleName + middleName = middleName, + nickname = nickname ) } - private fun Contact.Phone.Type.toBackup(): ContactAttachment.Phone.Type { + private fun Contact.Phone.Type.toRemote(): ContactAttachment.Phone.Type { return when (this) { Contact.Phone.Type.HOME -> ContactAttachment.Phone.Type.HOME Contact.Phone.Type.MOBILE -> ContactAttachment.Phone.Type.MOBILE @@ -652,7 +653,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } } - private fun Contact.Email.Type.toBackup(): ContactAttachment.Email.Type { + private fun Contact.Email.Type.toRemote(): ContactAttachment.Email.Type { return when (this) { Contact.Email.Type.HOME -> ContactAttachment.Email.Type.HOME Contact.Email.Type.MOBILE -> ContactAttachment.Email.Type.MOBILE @@ -661,7 +662,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } } - private fun Contact.PostalAddress.Type.toBackup(): ContactAttachment.PostalAddress.Type { + private fun Contact.PostalAddress.Type.toRemote(): ContactAttachment.PostalAddress.Type { return when (this) { Contact.PostalAddress.Type.HOME -> ContactAttachment.PostalAddress.Type.HOME Contact.PostalAddress.Type.WORK -> ContactAttachment.PostalAddress.Type.WORK @@ -693,7 +694,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: attachments = messageAttachments.toBackupAttachments(), linkPreview = linkPreviews.map { it.toRemoteLinkPreview() }, longText = longTextAttachment?.toRemoteFilePointer(mediaArchiveEnabled), - reactions = reactionRecords.toBackupReactions() + reactions = reactionRecords.toRemoteReactions() ) } @@ -749,7 +750,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: emoji = stickerLocator.emoji, data_ = this.toRemoteMessageAttachment().pointer ), - reactions = reactions.toBackupReactions() + reactions = reactions.toRemoteReactions() ) } @@ -796,14 +797,14 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: timestamp = this.timestamp, blockIndex = this.blockIndex, blockTimestamp = this.blockTimestamp, - mobileCoinIdentification = this.paymentMetaData.mobileCoinTxoIdentification?.toBackup(), + mobileCoinIdentification = this.paymentMetaData.mobileCoinTxoIdentification?.toRemote(), transaction = this.transaction?.toByteString(), receipt = this.receipt?.toByteString() ) ) } - private fun PaymentMetaData.MobileCoinTxoIdentification.toBackup(): PaymentNotification.TransactionDetails.MobileCoinTxoIdentification { + private fun PaymentMetaData.MobileCoinTxoIdentification.toRemote(): PaymentNotification.TransactionDetails.MobileCoinTxoIdentification { return PaymentNotification.TransactionDetails.MobileCoinTxoIdentification( publicKey = this.publicKey, keyImages = this.keyImages @@ -873,7 +874,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } } - private fun List?.toBackupReactions(): List { + private fun List?.toRemoteReactions(): List { return this ?.map { Reaction( diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index d6c9f28907..119f96510c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -1020,7 +1020,7 @@ class ChatItemImportInserter( } private fun ContactAttachment.Name?.toLocal(): Contact.Name { - return Contact.Name(this?.givenName, this?.familyName, this?.prefix, this?.suffix, this?.middleName, null) + return Contact.Name(this?.givenName, this?.familyName, this?.prefix, this?.suffix, this?.middleName, this?.nickname) } private fun ContactAttachment.Phone.Type?.toLocal(): Contact.Phone.Type { diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index aaa1aab0dd..624433d16a 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -476,6 +476,7 @@ message ContactAttachment { optional string prefix = 3; optional string suffix = 4; optional string middleName = 5; + optional string nickname = 6; } message Phone { @@ -621,7 +622,7 @@ message FilePointer { oneof locator { BackupLocator backupLocator = 1; - AttachmentLocator attachmentLocator = 2; + AttachmentLocator attachmentLocator= 2; InvalidAttachmentLocator invalidAttachmentLocator = 3; } From a3e05f4a75368aa34aca7d82b657c330b16cb8cc Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Sat, 28 Sep 2024 08:18:22 -0400 Subject: [PATCH 20/53] Ignore edit limits for note to self. Fixes #13716 --- .../securesms/conversation/v2/ConversationFragment.kt | 3 ++- .../thoughtcrime/securesms/util/MessageConstraintsUtil.kt | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index c103994b54..7b67553a0f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -2367,7 +2367,8 @@ class ConversationFragment : } private fun handleEditMessage(conversationMessage: ConversationMessage) { - if (!MessageConstraintsUtil.isWithinMaxEdits(conversationMessage.messageRecord)) { + val isNoteToSelf = viewModel.recipientSnapshot?.isSelf ?: false + if (!isNoteToSelf && !MessageConstraintsUtil.isWithinMaxEdits(conversationMessage.messageRecord)) { Log.i(TAG, "Too many edits to the message") Dialogs.showAlertDialog(requireContext(), null, resources.getQuantityString(R.plurals.ConversationActivity_edit_message_too_many_edits, MessageConstraintsUtil.MAX_EDIT_COUNT, MessageConstraintsUtil.MAX_EDIT_COUNT)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MessageConstraintsUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/MessageConstraintsUtil.kt index b91a0d6f4a..8c7ee8bd46 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MessageConstraintsUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MessageConstraintsUtil.kt @@ -62,8 +62,11 @@ object MessageConstraintsUtil { } else { targetMessage } + + val isNoteToSelf = targetMessage.toRecipient.isSelf && targetMessage.fromRecipient.isSelf + return isValidRemoteDeleteSend(originalMessage, currentTime) && - targetMessage.revisionNumber < MAX_EDIT_COUNT && + (isNoteToSelf || targetMessage.revisionNumber < MAX_EDIT_COUNT) && !targetMessage.isViewOnceMessage() && !targetMessage.hasAudio() && !targetMessage.hasSharedContact() && From c5a13b392be5dc0af2ad57bb36af155ef44658e5 Mon Sep 17 00:00:00 2001 From: Christian Stadelmann Date: Sat, 28 Sep 2024 14:26:51 +0200 Subject: [PATCH 21/53] Limit number of contacts for Android's "Quick Share" Work around a bug in Android which lead to very non recent contacts showing up in Android's "Quick Share" overlay. Also increase privacy by leaking less conversation names to the Android UI. Fixes #13398 Resolves #13718 --- .../java/org/thoughtcrime/securesms/util/ConversationUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java index 5af2866945..e8713c330a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java @@ -142,7 +142,7 @@ public static void clearShortcuts(@NonNull Context context, @NonNull Collection< } public static int getMaxShortcuts(@NonNull Context context) { - return Math.min(ShortcutManagerCompat.getMaxShortcutCountPerActivity(context), 150); + return Math.min(ShortcutManagerCompat.getMaxShortcutCountPerActivity(context), 10); } /** From 88d1e7b40dcf8fb95bb8a0bfe16f04253680b8ed Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 1 Oct 2024 09:35:49 -0300 Subject: [PATCH 22/53] Add additional checks to chat-color processing. --- .../securesms/conversation/colors/ChatColors.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt index aa564b8df0..cf674dc17d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt @@ -148,9 +148,17 @@ class ChatColors( } companion object { + /** + * Converts a network chat color into our domain model object. + * + * Has additional protection to ensure that a rogue client can't send us a malformed + * color. + */ @JvmStatic fun forChatColor(id: Id, chatColor: ChatColor): ChatColors { - assert((chatColor.singleColor != null) xor (chatColor.linearGradient != null)) + if (chatColor.singleColor == null && chatColor.linearGradient == null) { + return ChatColorsPalette.Bubbles.ULTRAMARINE + } return if (chatColor.linearGradient != null) { val linearGradient = LinearGradient( @@ -160,10 +168,12 @@ class ChatColors( ) forGradient(id, linearGradient) - } else { - val singleColor = chatColor.singleColor!!.color + } else if (chatColor.singleColor != null) { + val singleColor = chatColor.singleColor.color forColor(id, singleColor) + } else { + ChatColorsPalette.Bubbles.ULTRAMARINE } } From 813a92380bf606245c082a59a4069ded235df4c6 Mon Sep 17 00:00:00 2001 From: Jim Gustafson Date: Tue, 1 Oct 2024 08:05:25 -0700 Subject: [PATCH 23/53] Update to RingRTC v2.48.1 --- .github/workflows/diffuse.yml | 9 +++++++-- dependencies.gradle.kts | 2 +- gradle/verification-metadata.xml | 10 +++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/diffuse.yml b/.github/workflows/diffuse.yml index 99954ce8c3..a444963871 100644 --- a/.github/workflows/diffuse.yml +++ b/.github/workflows/diffuse.yml @@ -7,6 +7,9 @@ permissions: contents: read # to fetch code (actions/checkout) pull-requests: write # to comment on PR +env: + NDK_VERSION: '27.0.12077973' + jobs: assemble-base: if: ${{ github.repository != 'signalapp/Signal-Android' }} @@ -25,6 +28,9 @@ jobs: java-version: 17 cache: gradle + - name: Install NDK + run: echo "y" | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "ndk;${{ env.NDK_VERSION }}" + - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 @@ -35,7 +41,6 @@ jobs: path: diffuse-base.apk key: diffuse-${{ github.event.pull_request.base.sha }} - - name: Build with Gradle if: steps.cache-base.outputs.cache-hit != 'true' run: ./gradlew assemblePlayProdRelease @@ -70,7 +75,7 @@ jobs: - uses: peter-evans/create-or-update-comment@v3 with: body: | - Diffuse output: + Diffuse output: ${{ steps.diffuse.outputs.diff-gh-comment }} edit-mode: replace diff --git a/dependencies.gradle.kts b/dependencies.gradle.kts index ea2aef5458..39fec952b0 100644 --- a/dependencies.gradle.kts +++ b/dependencies.gradle.kts @@ -127,7 +127,7 @@ dependencyResolutionManagement { library("libsignal-client", "org.signal", "libsignal-client").versionRef("libsignal-client") library("libsignal-android", "org.signal", "libsignal-android").versionRef("libsignal-client") library("signal-aesgcmprovider", "org.signal:aesgcmprovider:0.0.3") - library("signal-ringrtc", "org.signal:ringrtc-android:2.48.0") + library("signal-ringrtc", "org.signal:ringrtc-android:2.48.1") library("signal-android-database-sqlcipher", "org.signal:sqlcipher-android:4.6.0-S1") // Third Party diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 7a0319e153..78075e5a3d 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -9013,12 +9013,12 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + From 13708e33e4071f8a98c9e553dfbfdea0a2d92b68 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 1 Oct 2024 11:12:44 -0400 Subject: [PATCH 24/53] Refactor backup exporting. --- .../securesms/backup/v2/ArchiveTypeAliases.kt | 9 + .../securesms/backup/v2/BackupRepository.kt | 6 +- .../database/CallLinkTableBackupExtensions.kt | 53 +- .../v2/database/CallTableBackupExtensions.kt | 40 +- .../v2/database/ChatItemExportIterator.kt | 1161 ----------------- .../DistributionListTablesBackupExtensions.kt | 68 +- .../database/MessageTableBackupExtensions.kt | 12 +- .../RecipientTableBackupExtensions.kt | 264 +--- .../database/ThreadTableBackupExtensions.kt | 60 +- .../AdHocCallArchiveExportIterator.kt | 41 + .../CallLinkArchiveExportIterator.kt | 59 + .../v2/exporters/ChatArchiveExportIterator.kt | 67 + .../ChatItemArchiveExportIterator.kt | 1127 ++++++++++++++++ .../exporters/ContactArchiveExportIterator.kt | 102 ++ .../DistributionListArchiveExportIterator.kt | 91 ++ .../exporters/GroupArchiveExportIterator.kt | 147 +++ ...essor.kt => AccountDataBackupProcessor.kt} | 198 +-- .../v2/processor/AdHocCallBackupProcessor.kt | 7 +- .../v2/processor/ChatBackupProcessor.kt | 3 + .../v2/processor/ChatItemBackupProcessor.kt | 8 +- .../v2/processor/RecipientBackupProcessor.kt | 39 +- .../v2/processor/StickerBackupProcessor.kt | 3 + 22 files changed, 1834 insertions(+), 1731 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExportIterator.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExportIterator.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExportIterator.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExportIterator.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExportIterator.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExportIterator.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExportIterator.kt rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/{AccountDataProcessor.kt => AccountDataBackupProcessor.kt} (65%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt new file mode 100644 index 0000000000..9655d3339e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2 + +typealias ArchiveRecipient = org.thoughtcrime.securesms.backup.v2.proto.Recipient +typealias ArchiveGroup = org.thoughtcrime.securesms.backup.v2.proto.Group diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index a52a3aa410..edb5b3fb85 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.backup.v2.database.ChatItemImportInserter import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackupRestore -import org.thoughtcrime.securesms.backup.v2.processor.AccountDataProcessor +import org.thoughtcrime.securesms.backup.v2.processor.AccountDataBackupProcessor import org.thoughtcrime.securesms.backup.v2.processor.AdHocCallBackupProcessor import org.thoughtcrime.securesms.backup.v2.processor.ChatBackupProcessor import org.thoughtcrime.securesms.backup.v2.processor.ChatItemBackupProcessor @@ -285,7 +285,7 @@ object BackupRepository { // We're using a snapshot, so the transaction is more for perf than correctness dbSnapshot.rawWritableDatabase.withinTransaction { progressEmitter?.onAccount() - AccountDataProcessor.export(dbSnapshot, signalStoreSnapshot) { + AccountDataBackupProcessor.export(dbSnapshot, signalStoreSnapshot) { writer.write(it) eventTimer.emit("account") } @@ -412,7 +412,7 @@ object BackupRepository { for (frame in frameReader) { when { frame.account != null -> { - AccountDataProcessor.import(frame.account, selfId, importState) + AccountDataBackupProcessor.import(frame.account, selfId, importState) eventTimer.emit("account") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt index 62c9c8fe61..e063e9bb82 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt @@ -5,30 +5,25 @@ package org.thoughtcrime.securesms.backup.v2.database -import android.database.Cursor -import okio.ByteString -import okio.ByteString.Companion.toByteString import org.signal.core.util.select import org.signal.ringrtc.CallLinkRootKey import org.signal.ringrtc.CallLinkState import org.thoughtcrime.securesms.backup.v2.proto.CallLink import org.thoughtcrime.securesms.database.CallLinkTable -import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkState -import java.io.Closeable import java.time.Instant -fun CallLinkTable.getCallLinksForBackup(): BackupCallLinkIterator { +fun CallLinkTable.getCallLinksForBackup(): CallLinkArchiveExportIterator { val cursor = readableDatabase .select() .from(CallLinkTable.TABLE_NAME) .run() - return BackupCallLinkIterator(cursor) + return CallLinkArchiveExportIterator(cursor) } fun CallLinkTable.restoreFromBackup(callLink: CallLink): RecipientId? { @@ -53,50 +48,6 @@ fun CallLinkTable.restoreFromBackup(callLink: CallLink): RecipientId? { ) } -/** - * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. - * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. - */ -class BackupCallLinkIterator(private val cursor: Cursor) : Iterator, Closeable { - override fun hasNext(): Boolean { - return cursor.count > 0 && !cursor.isLast - } - - override fun next(): BackupRecipient { - if (!cursor.moveToNext()) { - throw NoSuchElementException() - } - - val callLink = CallLinkTable.CallLinkDeserializer.deserialize(cursor) - return BackupRecipient( - id = callLink.recipientId.toLong(), - callLink = CallLink( - rootKey = callLink.credentials?.linkKeyBytes?.toByteString() ?: ByteString.EMPTY, - adminKey = callLink.credentials?.adminPassBytes?.toByteString(), - name = callLink.state.name, - expirationMs = try { - callLink.state.expiration.toEpochMilli() - } catch (e: ArithmeticException) { - Long.MAX_VALUE - }, - restrictions = callLink.state.restrictions.toRemote() - ) - ) - } - - override fun close() { - cursor.close() - } -} - -private fun CallLinkState.Restrictions.toRemote(): CallLink.Restrictions { - return when (this) { - CallLinkState.Restrictions.ADMIN_APPROVAL -> CallLink.Restrictions.ADMIN_APPROVAL - CallLinkState.Restrictions.NONE -> CallLink.Restrictions.NONE - CallLinkState.Restrictions.UNKNOWN -> CallLink.Restrictions.UNKNOWN - } -} - private fun CallLink.Restrictions.toLocal(): CallLinkState.Restrictions { return when (this) { CallLink.Restrictions.ADMIN_APPROVAL -> CallLinkState.Restrictions.ADMIN_APPROVAL diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt index 3c04e9b516..737f8b2fd1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt @@ -5,18 +5,14 @@ package org.thoughtcrime.securesms.backup.v2.database -import android.database.Cursor import org.signal.core.util.insertInto -import org.signal.core.util.requireLong import org.signal.core.util.select import org.thoughtcrime.securesms.backup.v2.ImportState import org.thoughtcrime.securesms.backup.v2.proto.AdHocCall import org.thoughtcrime.securesms.database.CallTable -import org.thoughtcrime.securesms.database.RecipientTable -import java.io.Closeable -fun CallTable.getAdhocCallsForBackup(): CallLogIterator { - return CallLogIterator( +fun CallTable.getAdhocCallsForBackup(): AdHocCallArchiveExportIterator { + return AdHocCallArchiveExportIterator( readableDatabase .select() .from(CallTable.TABLE_NAME) @@ -31,7 +27,7 @@ fun CallTable.restoreCallLogFromBackup(call: AdHocCall, importState: ImportState AdHocCall.State.UNKNOWN_STATE -> CallTable.Event.GENERIC_GROUP_CALL } - val result = writableDatabase + writableDatabase .insertInto(CallTable.TABLE_NAME) .values( CallTable.CALL_ID to call.callId, @@ -42,34 +38,4 @@ fun CallTable.restoreCallLogFromBackup(call: AdHocCall, importState: ImportState CallTable.TIMESTAMP to call.callTimestamp ) .run() - return Unit -} - -/** - * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. - * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. - */ -class CallLogIterator(private val cursor: Cursor) : Iterator, Closeable { - override fun hasNext(): Boolean { - return cursor.count > 0 && !cursor.isLast - } - - override fun next(): AdHocCall? { - if (!cursor.moveToNext()) { - throw NoSuchElementException() - } - - val callId = cursor.requireLong(CallTable.CALL_ID) - - return AdHocCall( - callId = callId, - recipientId = cursor.requireLong(CallTable.PEER), - state = AdHocCall.State.GENERIC, - callTimestamp = cursor.requireLong(CallTable.TIMESTAMP) - ) - } - - override fun close() { - cursor.close() - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt deleted file mode 100644 index abdbecf801..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ /dev/null @@ -1,1161 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.database - -import android.database.Cursor -import okio.ByteString.Companion.toByteString -import org.json.JSONArray -import org.json.JSONException -import org.signal.core.util.Base64 -import org.signal.core.util.Hex -import org.signal.core.util.logging.Log -import org.signal.core.util.nullIfEmpty -import org.signal.core.util.orNull -import org.signal.core.util.requireBlob -import org.signal.core.util.requireBoolean -import org.signal.core.util.requireInt -import org.signal.core.util.requireLong -import org.signal.core.util.requireLongOrNull -import org.signal.core.util.requireString -import org.thoughtcrime.securesms.attachments.AttachmentId -import org.thoughtcrime.securesms.attachments.DatabaseAttachment -import org.thoughtcrime.securesms.backup.v2.proto.ChatItem -import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage -import org.thoughtcrime.securesms.backup.v2.proto.ContactAttachment -import org.thoughtcrime.securesms.backup.v2.proto.ContactMessage -import org.thoughtcrime.securesms.backup.v2.proto.ExpirationTimerChatUpdate -import org.thoughtcrime.securesms.backup.v2.proto.GroupCall -import org.thoughtcrime.securesms.backup.v2.proto.IndividualCall -import org.thoughtcrime.securesms.backup.v2.proto.LearnedProfileChatUpdate -import org.thoughtcrime.securesms.backup.v2.proto.MessageAttachment -import org.thoughtcrime.securesms.backup.v2.proto.PaymentNotification -import org.thoughtcrime.securesms.backup.v2.proto.ProfileChangeChatUpdate -import org.thoughtcrime.securesms.backup.v2.proto.Quote -import org.thoughtcrime.securesms.backup.v2.proto.Reaction -import org.thoughtcrime.securesms.backup.v2.proto.RemoteDeletedMessage -import org.thoughtcrime.securesms.backup.v2.proto.SendStatus -import org.thoughtcrime.securesms.backup.v2.proto.SessionSwitchoverChatUpdate -import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate -import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage -import org.thoughtcrime.securesms.backup.v2.proto.Sticker -import org.thoughtcrime.securesms.backup.v2.proto.StickerMessage -import org.thoughtcrime.securesms.backup.v2.proto.Text -import org.thoughtcrime.securesms.backup.v2.proto.ThreadMergeChatUpdate -import org.thoughtcrime.securesms.backup.v2.util.toRemoteFilePointer -import org.thoughtcrime.securesms.contactshare.Contact -import org.thoughtcrime.securesms.database.AttachmentTable -import org.thoughtcrime.securesms.database.CallTable -import org.thoughtcrime.securesms.database.GroupReceiptTable -import org.thoughtcrime.securesms.database.MessageTable -import org.thoughtcrime.securesms.database.MessageTypes -import org.thoughtcrime.securesms.database.PaymentTable -import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.database.SignalDatabase.Companion.calls -import org.thoughtcrime.securesms.database.SignalDatabase.Companion.recipients -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet -import org.thoughtcrime.securesms.database.documents.NetworkFailureSet -import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil -import org.thoughtcrime.securesms.database.model.GroupsV2UpdateMessageConverter -import org.thoughtcrime.securesms.database.model.Mention -import org.thoughtcrime.securesms.database.model.ReactionRecord -import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList -import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context -import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge -import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras -import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails -import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent -import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent -import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.linkpreview.LinkPreview -import org.thoughtcrime.securesms.mms.QuoteModel -import org.thoughtcrime.securesms.payments.FailureReason -import org.thoughtcrime.securesms.payments.State -import org.thoughtcrime.securesms.payments.proto.PaymentMetaData -import org.thoughtcrime.securesms.util.JsonUtils -import org.whispersystems.signalservice.api.push.ServiceId.ACI -import org.whispersystems.signalservice.api.util.UuidUtil -import org.whispersystems.signalservice.api.util.toByteArray -import java.io.Closeable -import java.io.IOException -import java.util.HashMap -import java.util.LinkedList -import java.util.Queue -import kotlin.jvm.optionals.getOrNull -import org.thoughtcrime.securesms.backup.v2.proto.BodyRange as BackupBodyRange -import org.thoughtcrime.securesms.backup.v2.proto.GiftBadge as BackupGiftBadge - -/** - * An iterator for chat items with a clever performance twist: rather than do the extra queries one at a time (for reactions, - * attachments, etc), this will populate items in batches, doing bulk lookups to improve throughput. We keep these in a buffer - * and only do more queries when the buffer is empty. - * - * All of this complexity is hidden from the user -- they just get a normal iterator interface. - */ -class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: Int, private val mediaArchiveEnabled: Boolean) : Iterator, Closeable { - - companion object { - private val TAG = Log.tag(ChatItemExportIterator::class.java) - - const val COLUMN_BASE_TYPE = "base_type" - } - - /** - * A queue of already-parsed ChatItems. Processing in batches means that we read ahead in the cursor and put - * the pending items here. - */ - private val buffer: Queue = LinkedList() - - private val revisionMap: HashMap> = HashMap() - - override fun hasNext(): Boolean { - return buffer.isNotEmpty() || (cursor.count > 0 && !cursor.isLast && !cursor.isAfterLast) - } - - override fun next(): ChatItem? { - if (buffer.isNotEmpty()) { - return buffer.remove() - } - - val records: LinkedHashMap = linkedMapOf() - - for (i in 0 until batchSize) { - if (cursor.moveToNext()) { - val record = cursor.toBackupMessageRecord() - records[record.id] = record - } else { - break - } - } - - val reactionsById: Map> = SignalDatabase.reactions.getReactionsForMessages(records.keys).map { entry -> entry.key to entry.value.sortedBy { it.dateReceived } }.toMap() - val mentionsById: Map> = SignalDatabase.mentions.getMentionsForMessages(records.keys) - val attachmentsById: Map> = SignalDatabase.attachments.getAttachmentsForMessages(records.keys) - val groupReceiptsById: Map> = SignalDatabase.groupReceipts.getGroupReceiptInfoForMessages(records.keys) - - for ((id, record) in records) { - val builder = record.toBasicChatItemBuilder(groupReceiptsById[id]) - - when { - record.remoteDeleted -> { - builder.remoteDeletedMessage = RemoteDeletedMessage() - } - MessageTypes.isJoinedType(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.JOINED_SIGNAL) - } - MessageTypes.isIdentityUpdate(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.IDENTITY_UPDATE) - } - MessageTypes.isIdentityVerified(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.IDENTITY_VERIFIED) - } - MessageTypes.isIdentityDefault(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.IDENTITY_DEFAULT) - } - MessageTypes.isChangeNumber(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.CHANGE_NUMBER) - } - MessageTypes.isReleaseChannelDonationRequest(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST) - } - MessageTypes.isEndSessionType(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.END_SESSION) - } - MessageTypes.isChatSessionRefresh(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.CHAT_SESSION_REFRESH) - } - MessageTypes.isBadDecryptType(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.BAD_DECRYPT) - } - MessageTypes.isPaymentsActivated(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.PAYMENTS_ACTIVATED) - } - MessageTypes.isPaymentsRequestToActivate(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST) - } - MessageTypes.isUnsupportedMessageType(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE) - } - MessageTypes.isReportedSpam(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.REPORTED_SPAM) - } - MessageTypes.isMessageRequestAccepted(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.MESSAGE_REQUEST_ACCEPTED) - } - MessageTypes.isBlocked(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.BLOCKED) - } - MessageTypes.isUnblocked(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.UNBLOCKED) - } - MessageTypes.isExpirationTimerUpdate(record.type) -> { - builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn)) - builder.expiresInMs = 0 - } - MessageTypes.isProfileChange(record.type) -> { - builder.updateMessage = record.toProfileChangeUpdate() - } - MessageTypes.isSessionSwitchoverType(record.type) -> { - builder.updateMessage = record.toSessionSwitchoverUpdate() - } - MessageTypes.isThreadMergeType(record.type) -> { - builder.updateMessage = record.toThreadMergeUpdate() - } - MessageTypes.isGroupV2(record.type) && MessageTypes.isGroupUpdate(record.type) -> { - builder.updateMessage = record.toGroupUpdate() - } - MessageTypes.isCallLog(record.type) -> { - builder.updateMessage = record.toCallUpdate() - } - MessageTypes.isPaymentsNotification(record.type) -> { - builder.paymentNotification = record.toPaymentNotificationUpdate() - } - MessageTypes.isGiftBadge(record.type) -> { - builder.giftBadge = record.toGiftBadgeUpdate() - } - !record.sharedContacts.isNullOrEmpty() -> { - builder.contactMessage = record.toContactMessage(reactionsById[id], attachmentsById[id]) - } - else -> { - if (record.body == null && !attachmentsById.containsKey(record.id)) { - Log.w(TAG, "Record with ID ${record.id} missing a body and doesn't have attachments. Skipping.") - continue - } - - val attachments = attachmentsById[record.id] - val sticker = attachments?.firstOrNull { dbAttachment -> - dbAttachment.isSticker - } - - if (sticker?.stickerLocator != null) { - builder.stickerMessage = sticker.toStickerMessage(reactionsById[id]) - } else { - builder.standardMessage = record.toStandardMessage(reactionsById[id], mentions = mentionsById[id], attachments = attachmentsById[record.id]) - } - } - } - - if (record.latestRevisionId == null) { - val previousEdits = revisionMap.remove(record.id) - if (previousEdits != null) { - builder.revisions = previousEdits - } - buffer += builder.build() - } else { - var previousEdits = revisionMap[record.latestRevisionId] - if (previousEdits == null) { - previousEdits = ArrayList() - revisionMap[record.latestRevisionId] = previousEdits - } - previousEdits += builder.build() - } - } - - return if (buffer.isNotEmpty()) { - buffer.remove() - } else { - null - } - } - - override fun close() { - cursor.close() - } - - private fun simpleUpdate(type: SimpleChatUpdate.Type): ChatUpdateMessage { - return ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = type)) - } - - private fun BackupMessageRecord.toBasicChatItemBuilder(groupReceipts: List?): ChatItem.Builder { - val record = this - - return ChatItem.Builder().apply { - chatId = record.threadId - authorId = record.fromRecipientId - dateSent = record.dateSent - expireStartDate = if (record.expireStarted > 0) record.expireStarted else 0 - expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0 - revisions = emptyList() - sms = record.type.isSmsType() - if (record.type.isDirectionlessType()) { - directionless = ChatItem.DirectionlessMessageDetails() - } else if (MessageTypes.isOutgoingMessageType(record.type)) { - outgoing = ChatItem.OutgoingMessageDetails( - sendStatus = record.toRemoteSendStatus(groupReceipts) - ) - } else { - incoming = ChatItem.IncomingMessageDetails( - dateServerSent = record.dateServer, - dateReceived = record.dateReceived, - read = record.read, - sealedSender = record.sealedSender - ) - } - } - } - - private fun BackupMessageRecord.toProfileChangeUpdate(): ChatUpdateMessage? { - val profileChangeDetails = if (this.messageExtras != null) { - this.messageExtras.profileChangeDetails - } else { - Base64.decodeOrNull(this.body)?.let { ProfileChangeDetails.ADAPTER.decode(it) } - } - - return if (profileChangeDetails?.profileNameChange != null) { - ChatUpdateMessage(profileChange = ProfileChangeChatUpdate(previousName = profileChangeDetails.profileNameChange.previous, newName = profileChangeDetails.profileNameChange.newValue)) - } else if (profileChangeDetails?.learnedProfileName != null) { - ChatUpdateMessage(learnedProfileChange = LearnedProfileChatUpdate(e164 = profileChangeDetails.learnedProfileName.e164?.e164ToLong(), username = profileChangeDetails.learnedProfileName.username)) - } else { - null - } - } - - private fun BackupMessageRecord.toSessionSwitchoverUpdate(): ChatUpdateMessage { - if (this.body == null) { - return ChatUpdateMessage(sessionSwitchover = SessionSwitchoverChatUpdate()) - } - - return ChatUpdateMessage( - sessionSwitchover = try { - val event = SessionSwitchoverEvent.ADAPTER.decode(Base64.decodeOrThrow(this.body)) - SessionSwitchoverChatUpdate(event.e164.e164ToLong()!!) - } catch (e: IOException) { - SessionSwitchoverChatUpdate() - } - ) - } - - private fun BackupMessageRecord.toThreadMergeUpdate(): ChatUpdateMessage { - if (this.body == null) { - return ChatUpdateMessage(threadMerge = ThreadMergeChatUpdate()) - } - - return ChatUpdateMessage( - threadMerge = try { - val event = ThreadMergeEvent.ADAPTER.decode(Base64.decodeOrThrow(this.body)) - ThreadMergeChatUpdate(event.previousE164.e164ToLong()!!) - } catch (e: IOException) { - ThreadMergeChatUpdate() - } - ) - } - - private fun BackupMessageRecord.toGroupUpdate(): ChatUpdateMessage? { - val groupChange = this.messageExtras?.gv2UpdateDescription?.groupChangeUpdate - return if (groupChange != null) { - ChatUpdateMessage( - groupChange = groupChange - ) - } else if (this.body != null) { - try { - val decoded: ByteArray = Base64.decode(this.body) - val context = DecryptedGroupV2Context.ADAPTER.decode(decoded) - ChatUpdateMessage( - groupChange = GroupsV2UpdateMessageConverter.translateDecryptedChange(selfIds = SignalStore.account.getServiceIds(), context) - ) - } catch (e: IOException) { - null - } - } else { - null - } - } - - private fun BackupMessageRecord.toCallUpdate(): ChatUpdateMessage? { - val call = calls.getCallByMessageId(this.id) - - if (call != null) { - return call.toCallUpdate(this) - } - - return when { - MessageTypes.isMissedAudioCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.AUDIO_CALL, - state = IndividualCall.State.MISSED, - direction = IndividualCall.Direction.INCOMING - ) - ) - } - MessageTypes.isMissedVideoCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.VIDEO_CALL, - state = IndividualCall.State.MISSED, - direction = IndividualCall.Direction.INCOMING - ) - ) - } - MessageTypes.isIncomingAudioCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.AUDIO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.INCOMING - ) - ) - } - MessageTypes.isIncomingVideoCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.VIDEO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.INCOMING - ) - ) - } - MessageTypes.isOutgoingAudioCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.AUDIO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.OUTGOING - ) - ) - } - MessageTypes.isOutgoingVideoCall(this.type) -> { - ChatUpdateMessage( - individualCall = IndividualCall( - type = IndividualCall.Type.VIDEO_CALL, - state = IndividualCall.State.ACCEPTED, - direction = IndividualCall.Direction.OUTGOING - ) - ) - } - else -> { - null - } - } - } - - private fun CallTable.Call.toCallUpdate(messageRecord: BackupMessageRecord): ChatUpdateMessage? { - return when (this.type) { - CallTable.Type.GROUP_CALL -> { - val groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(messageRecord.body) - - ChatUpdateMessage( - groupCall = GroupCall( - callId = this.callId, - state = when (this.event) { - CallTable.Event.MISSED -> GroupCall.State.MISSED - CallTable.Event.ONGOING -> GroupCall.State.GENERIC - CallTable.Event.ACCEPTED -> GroupCall.State.ACCEPTED - CallTable.Event.NOT_ACCEPTED -> GroupCall.State.GENERIC - CallTable.Event.MISSED_NOTIFICATION_PROFILE -> GroupCall.State.MISSED_NOTIFICATION_PROFILE - CallTable.Event.GENERIC_GROUP_CALL -> GroupCall.State.GENERIC - CallTable.Event.JOINED -> GroupCall.State.JOINED - CallTable.Event.RINGING -> GroupCall.State.RINGING - CallTable.Event.DECLINED -> GroupCall.State.DECLINED - CallTable.Event.OUTGOING_RING -> GroupCall.State.OUTGOING_RING - CallTable.Event.DELETE -> return null - }, - ringerRecipientId = this.ringerRecipient?.toLong(), - startedCallRecipientId = ACI.parseOrNull(groupCallUpdateDetails.startedCallUuid)?.let { recipients.getByAci(it).getOrNull()?.toLong() }, - startedCallTimestamp = this.timestamp, - endedCallTimestamp = groupCallUpdateDetails.endedCallTimestamp, - read = messageRecord.read - ) - ) - } - - CallTable.Type.AUDIO_CALL, - CallTable.Type.VIDEO_CALL -> { - ChatUpdateMessage( - individualCall = IndividualCall( - callId = this.callId, - type = if (this.type == CallTable.Type.VIDEO_CALL) IndividualCall.Type.VIDEO_CALL else IndividualCall.Type.AUDIO_CALL, - direction = if (this.direction == CallTable.Direction.INCOMING) IndividualCall.Direction.INCOMING else IndividualCall.Direction.OUTGOING, - state = when (this.event) { - CallTable.Event.MISSED -> IndividualCall.State.MISSED - CallTable.Event.MISSED_NOTIFICATION_PROFILE -> IndividualCall.State.MISSED_NOTIFICATION_PROFILE - CallTable.Event.ACCEPTED -> IndividualCall.State.ACCEPTED - CallTable.Event.NOT_ACCEPTED -> IndividualCall.State.NOT_ACCEPTED - else -> IndividualCall.State.UNKNOWN_STATE - }, - startedCallTimestamp = this.timestamp, - read = messageRecord.read - ) - ) - } - - CallTable.Type.AD_HOC_CALL -> throw IllegalArgumentException("AdHoc calls are not update messages!") - } - } - - private fun BackupMessageRecord.toPaymentNotificationUpdate(): PaymentNotification { - val paymentUuid = UuidUtil.parseOrNull(this.body) - val payment = if (paymentUuid != null) { - SignalDatabase.payments.getPayment(paymentUuid) - } else { - null - } - - return if (payment == null) { - PaymentNotification() - } else { - PaymentNotification( - amountMob = payment.amount.serializeAmountString(), - feeMob = payment.fee.serializeAmountString(), - note = payment.note.takeUnless { it.isEmpty() }, - transactionDetails = payment.getTransactionDetails() - ) - } - } - - private fun BackupMessageRecord.parseSharedContacts(attachments: List?): List { - if (this.sharedContacts.isNullOrEmpty()) { - return emptyList() - } - - val attachmentIdMap: Map = attachments?.associateBy { it.attachmentId } ?: emptyMap() - - try { - val contacts: MutableList = LinkedList() - val jsonContacts = JSONArray(sharedContacts) - - for (i in 0 until jsonContacts.length()) { - val contact: Contact = Contact.deserialize(jsonContacts.getJSONObject(i).toString()) - - if (contact.avatar != null && contact.avatar!!.attachmentId != null) { - val attachment = attachmentIdMap[contact.avatar!!.attachmentId] - - val updatedAvatar = Contact.Avatar( - contact.avatar!!.attachmentId, - attachment, - contact.avatar!!.isProfile - ) - - contacts += Contact(contact, updatedAvatar) - } else { - contacts += contact - } - } - - return contacts - } catch (e: JSONException) { - Log.w(TAG, "Failed to parse shared contacts.", e) - } catch (e: IOException) { - Log.w(TAG, "Failed to parse shared contacts.", e) - } - - return emptyList() - } - - private fun BackupMessageRecord.parseLinkPreviews(attachments: List?): List { - if (linkPreview.isNullOrEmpty()) { - return emptyList() - } - val attachmentIdMap: Map = attachments?.associateBy { it.attachmentId } ?: emptyMap() - - try { - val previews: MutableList = LinkedList() - val jsonPreviews = JSONArray(linkPreview) - - for (i in 0 until jsonPreviews.length()) { - val preview = LinkPreview.deserialize(jsonPreviews.getJSONObject(i).toString()) - - if (preview.attachmentId != null) { - val attachment = attachmentIdMap[preview.attachmentId] - - if (attachment != null) { - previews += LinkPreview(preview.url, preview.title, preview.description, preview.date, attachment) - } else { - previews += preview - } - } else { - previews += preview - } - } - - return previews - } catch (e: JSONException) { - Log.w(TAG, "Failed to parse link preview", e) - } catch (e: IOException) { - Log.w(TAG, "Failed to parse shared contacts.", e) - } - - return emptyList() - } - - private fun LinkPreview.toRemoteLinkPreview(): org.thoughtcrime.securesms.backup.v2.proto.LinkPreview { - return org.thoughtcrime.securesms.backup.v2.proto.LinkPreview( - url = url, - title = title.nullIfEmpty(), - image = (thumbnail.orNull() as? DatabaseAttachment)?.toRemoteMessageAttachment()?.pointer, - description = description.nullIfEmpty(), - date = date - ) - } - - private fun BackupMessageRecord.toContactMessage(reactionRecords: List?, attachments: List?): ContactMessage { - val sharedContacts = parseSharedContacts(attachments) - - val contacts = sharedContacts.map { - ContactAttachment( - name = it.name.toRemote(), - avatar = (it.avatar?.attachment as? DatabaseAttachment)?.toRemoteMessageAttachment()?.pointer, - organization = it.organization, - number = it.phoneNumbers.map { phone -> - ContactAttachment.Phone( - value_ = phone.number, - type = phone.type.toRemote(), - label = phone.label - ) - }, - email = it.emails.map { email -> - ContactAttachment.Email( - value_ = email.email, - label = email.label, - type = email.type.toRemote() - ) - }, - address = it.postalAddresses.map { address -> - ContactAttachment.PostalAddress( - type = address.type.toRemote(), - label = address.label, - street = address.street, - pobox = address.poBox, - neighborhood = address.neighborhood, - city = address.city, - region = address.region, - postcode = address.postalCode, - country = address.country - ) - } - ) - } - return ContactMessage( - contact = contacts, - reactions = reactionRecords.toRemoteReactions() - ) - } - - private fun Contact.Name.toRemote(): ContactAttachment.Name { - return ContactAttachment.Name( - givenName = givenName, - familyName = familyName, - prefix = prefix, - suffix = suffix, - middleName = middleName, - nickname = nickname - ) - } - - private fun Contact.Phone.Type.toRemote(): ContactAttachment.Phone.Type { - return when (this) { - Contact.Phone.Type.HOME -> ContactAttachment.Phone.Type.HOME - Contact.Phone.Type.MOBILE -> ContactAttachment.Phone.Type.MOBILE - Contact.Phone.Type.WORK -> ContactAttachment.Phone.Type.WORK - Contact.Phone.Type.CUSTOM -> ContactAttachment.Phone.Type.CUSTOM - } - } - - private fun Contact.Email.Type.toRemote(): ContactAttachment.Email.Type { - return when (this) { - Contact.Email.Type.HOME -> ContactAttachment.Email.Type.HOME - Contact.Email.Type.MOBILE -> ContactAttachment.Email.Type.MOBILE - Contact.Email.Type.WORK -> ContactAttachment.Email.Type.WORK - Contact.Email.Type.CUSTOM -> ContactAttachment.Email.Type.CUSTOM - } - } - - private fun Contact.PostalAddress.Type.toRemote(): ContactAttachment.PostalAddress.Type { - return when (this) { - Contact.PostalAddress.Type.HOME -> ContactAttachment.PostalAddress.Type.HOME - Contact.PostalAddress.Type.WORK -> ContactAttachment.PostalAddress.Type.WORK - Contact.PostalAddress.Type.CUSTOM -> ContactAttachment.PostalAddress.Type.CUSTOM - } - } - - private fun BackupMessageRecord.toStandardMessage(reactionRecords: List?, mentions: List?, attachments: List?): StandardMessage { - val text = if (body == null) { - null - } else { - Text( - body = this.body, - bodyRanges = (this.bodyRanges?.toRemoteBodyRanges() ?: emptyList()) + (mentions?.toRemoteBodyRanges() ?: emptyList()) - ) - } - val linkPreviews = parseLinkPreviews(attachments) - val linkPreviewAttachments = linkPreviews.mapNotNull { it.thumbnail.orElse(null) }.toSet() - val quotedAttachments = attachments?.filter { it.quote } ?: emptyList() - val longTextAttachment = attachments?.firstOrNull { it.contentType == "text/x-signal-plain" } - val messageAttachments = attachments - ?.filterNot { it.quote } - ?.filterNot { linkPreviewAttachments.contains(it) } - ?.filterNot { it == longTextAttachment } - ?: emptyList() - return StandardMessage( - quote = this.toQuote(quotedAttachments), - text = text, - attachments = messageAttachments.toBackupAttachments(), - linkPreview = linkPreviews.map { it.toRemoteLinkPreview() }, - longText = longTextAttachment?.toRemoteFilePointer(mediaArchiveEnabled), - reactions = reactionRecords.toRemoteReactions() - ) - } - - private fun BackupMessageRecord.toQuote(attachments: List? = null): Quote? { - if (this.quoteTargetSentTimestamp == MessageTable.QUOTE_NOT_PRESENT_ID || this.quoteAuthor <= 0) { - return null - } - - val type = QuoteModel.Type.fromCode(this.quoteType) - return Quote( - targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID }, - authorId = this.quoteAuthor, - text = this.quoteBody?.let { body -> - Text( - body = body, - bodyRanges = this.quoteBodyRanges?.toRemoteBodyRanges() ?: emptyList() - ) - }, - attachments = attachments?.toRemoteQuoteAttachments() ?: emptyList(), - type = when (type) { - QuoteModel.Type.NORMAL -> Quote.Type.NORMAL - QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFTBADGE - } - ) - } - - private fun BackupMessageRecord.toGiftBadgeUpdate(): BackupGiftBadge { - val giftBadge = try { - GiftBadge.ADAPTER.decode(Base64.decode(this.body ?: "")) - } catch (e: IOException) { - Log.w(TAG, "Failed to decode GiftBadge!") - return BackupGiftBadge() - } - - return BackupGiftBadge( - receiptCredentialPresentation = giftBadge.redemptionToken, - state = when (giftBadge.redemptionState) { - GiftBadge.RedemptionState.REDEEMED -> BackupGiftBadge.State.REDEEMED - GiftBadge.RedemptionState.FAILED -> BackupGiftBadge.State.FAILED - GiftBadge.RedemptionState.PENDING -> BackupGiftBadge.State.UNOPENED - GiftBadge.RedemptionState.STARTED -> BackupGiftBadge.State.OPENED - } - ) - } - - private fun DatabaseAttachment.toStickerMessage(reactions: List?): StickerMessage { - val stickerLocator = this.stickerLocator!! - return StickerMessage( - sticker = Sticker( - packId = Hex.fromStringCondensed(stickerLocator.packId).toByteString(), - packKey = Hex.fromStringCondensed(stickerLocator.packKey).toByteString(), - stickerId = stickerLocator.stickerId, - emoji = stickerLocator.emoji, - data_ = this.toRemoteMessageAttachment().pointer - ), - reactions = reactions.toRemoteReactions() - ) - } - - private fun List.toRemoteQuoteAttachments(): List { - return this.map { attachment -> - Quote.QuotedAttachment( - contentType = attachment.contentType, - fileName = attachment.fileName, - thumbnail = attachment.toRemoteMessageAttachment(contentTypeOverride = "image/jpeg").takeUnless { it.pointer?.invalidAttachmentLocator != null } - ) - } - } - - private fun DatabaseAttachment.toRemoteMessageAttachment(contentTypeOverride: String? = null): MessageAttachment { - return MessageAttachment( - pointer = this.toRemoteFilePointer(mediaArchiveEnabled, contentTypeOverride), - wasDownloaded = this.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE || this.transferState == AttachmentTable.TRANSFER_NEEDS_RESTORE, - flag = if (this.voiceNote) { - MessageAttachment.Flag.VOICE_MESSAGE - } else if (this.videoGif) { - MessageAttachment.Flag.GIF - } else if (this.borderless) { - MessageAttachment.Flag.BORDERLESS - } else { - MessageAttachment.Flag.NONE - }, - clientUuid = this.uuid?.let { UuidUtil.toByteString(uuid) } - ) - } - - private fun List.toBackupAttachments(): List { - return this.map { attachment -> - attachment.toRemoteMessageAttachment() - } - } - - private fun PaymentTable.PaymentTransaction.getTransactionDetails(): PaymentNotification.TransactionDetails? { - if (this.failureReason != null || this.state == State.FAILED) { - return PaymentNotification.TransactionDetails(failedTransaction = PaymentNotification.TransactionDetails.FailedTransaction(reason = this.failureReason.toBackupFailureReason())) - } - return PaymentNotification.TransactionDetails( - transaction = PaymentNotification.TransactionDetails.Transaction( - status = this.state.toBackupState(), - timestamp = this.timestamp, - blockIndex = this.blockIndex, - blockTimestamp = this.blockTimestamp, - mobileCoinIdentification = this.paymentMetaData.mobileCoinTxoIdentification?.toRemote(), - transaction = this.transaction?.toByteString(), - receipt = this.receipt?.toByteString() - ) - ) - } - - private fun PaymentMetaData.MobileCoinTxoIdentification.toRemote(): PaymentNotification.TransactionDetails.MobileCoinTxoIdentification { - return PaymentNotification.TransactionDetails.MobileCoinTxoIdentification( - publicKey = this.publicKey, - keyImages = this.keyImages - ) - } - - private fun State.toBackupState(): PaymentNotification.TransactionDetails.Transaction.Status { - return when (this) { - State.INITIAL -> PaymentNotification.TransactionDetails.Transaction.Status.INITIAL - State.SUBMITTED -> PaymentNotification.TransactionDetails.Transaction.Status.SUBMITTED - State.SUCCESSFUL -> PaymentNotification.TransactionDetails.Transaction.Status.SUCCESSFUL - State.FAILED -> throw IllegalArgumentException("state cannot be failed") - } - } - - private fun FailureReason?.toBackupFailureReason(): PaymentNotification.TransactionDetails.FailedTransaction.FailureReason { - return when (this) { - FailureReason.UNKNOWN -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.GENERIC - FailureReason.INSUFFICIENT_FUNDS -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.INSUFFICIENT_FUNDS - FailureReason.NETWORK -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.NETWORK - else -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.GENERIC - } - } - - private fun List.toRemoteBodyRanges(): List { - return this.map { - BackupBodyRange( - start = it.start, - length = it.length, - mentionAci = SignalDatabase.recipients.getRecord(it.recipientId).aci?.toByteString() - ) - } - } - - private fun ByteArray.toRemoteBodyRanges(): List { - val decoded: BodyRangeList = try { - BodyRangeList.ADAPTER.decode(this) - } catch (e: IOException) { - Log.w(TAG, "Failed to decode BodyRangeList!") - return emptyList() - } - - return decoded.ranges.map { - val mention = it.mentionUuid?.let { uuid -> UuidUtil.parseOrThrow(uuid) }?.toByteArray()?.toByteString() - val style = if (mention == null) { - it.style?.toBackupBodyRangeStyle() ?: BackupBodyRange.Style.NONE - } else { - null - } - - BackupBodyRange( - start = it.start, - length = it.length, - mentionAci = mention, - style = style - ) - } - } - - private fun BodyRangeList.BodyRange.Style.toBackupBodyRangeStyle(): BackupBodyRange.Style { - return when (this) { - BodyRangeList.BodyRange.Style.BOLD -> BackupBodyRange.Style.BOLD - BodyRangeList.BodyRange.Style.ITALIC -> BackupBodyRange.Style.ITALIC - BodyRangeList.BodyRange.Style.STRIKETHROUGH -> BackupBodyRange.Style.STRIKETHROUGH - BodyRangeList.BodyRange.Style.MONOSPACE -> BackupBodyRange.Style.MONOSPACE - BodyRangeList.BodyRange.Style.SPOILER -> BackupBodyRange.Style.SPOILER - } - } - - private fun List?.toRemoteReactions(): List { - return this - ?.map { - Reaction( - emoji = it.emoji, - authorId = it.author.toLong(), - sentTimestamp = it.dateSent, - sortOrder = it.dateReceived - ) - } ?: emptyList() - } - - private fun BackupMessageRecord.toRemoteSendStatus(groupReceipts: List?): List { - if (!MessageTypes.isOutgoingMessageType(this.type)) { - return emptyList() - } - - if (!groupReceipts.isNullOrEmpty()) { - return groupReceipts.toRemoteSendStatus(this, this.networkFailureRecipientIds, this.identityMismatchRecipientIds) - } - - val statusBuilder = SendStatus.Builder() - .recipientId(this.toRecipientId) - .timestamp(this.receiptTimestamp) - - when { - this.identityMismatchRecipientIds.contains(this.toRecipientId) -> { - statusBuilder.failed = SendStatus.Failed( - reason = SendStatus.Failed.FailureReason.IDENTITY_KEY_MISMATCH - ) - } - this.networkFailureRecipientIds.contains(this.toRecipientId) -> { - statusBuilder.failed = SendStatus.Failed( - reason = SendStatus.Failed.FailureReason.NETWORK - ) - } - this.viewed -> { - statusBuilder.viewed = SendStatus.Viewed( - sealedSender = this.sealedSender - ) - } - this.hasReadReceipt -> { - statusBuilder.read = SendStatus.Read( - sealedSender = this.sealedSender - ) - } - this.hasDeliveryReceipt -> { - statusBuilder.delivered = SendStatus.Delivered( - sealedSender = this.sealedSender - ) - } - this.baseType == MessageTypes.BASE_SENT_FAILED_TYPE -> { - statusBuilder.failed = SendStatus.Failed( - reason = SendStatus.Failed.FailureReason.UNKNOWN - ) - } - this.baseType == MessageTypes.BASE_SENDING_SKIPPED_TYPE -> { - statusBuilder.skipped = SendStatus.Skipped() - } - this.baseType == MessageTypes.BASE_SENT_TYPE -> { - statusBuilder.sent = SendStatus.Sent( - sealedSender = this.sealedSender - ) - } - else -> { - statusBuilder.pending = SendStatus.Pending() - } - } - - return listOf(statusBuilder.build()) - } - - private fun List.toRemoteSendStatus(messageRecord: BackupMessageRecord, networkFailureRecipientIds: Set, identityMismatchRecipientIds: Set): List { - return this.map { - val statusBuilder = SendStatus.Builder() - .recipientId(it.recipientId.toLong()) - .timestamp(it.timestamp) - - when { - identityMismatchRecipientIds.contains(it.recipientId.toLong()) -> { - statusBuilder.failed = SendStatus.Failed( - reason = SendStatus.Failed.FailureReason.IDENTITY_KEY_MISMATCH - ) - } - networkFailureRecipientIds.contains(it.recipientId.toLong()) -> { - statusBuilder.failed = SendStatus.Failed( - reason = SendStatus.Failed.FailureReason.NETWORK - ) - } - messageRecord.baseType == MessageTypes.BASE_SENT_FAILED_TYPE -> { - statusBuilder.failed = SendStatus.Failed( - reason = SendStatus.Failed.FailureReason.UNKNOWN - ) - } - it.status == GroupReceiptTable.STATUS_UNKNOWN -> { - statusBuilder.pending = SendStatus.Pending() - } - it.status == GroupReceiptTable.STATUS_UNDELIVERED -> { - statusBuilder.sent = SendStatus.Sent( - sealedSender = it.isUnidentified - ) - } - it.status == GroupReceiptTable.STATUS_DELIVERED -> { - statusBuilder.delivered = SendStatus.Delivered( - sealedSender = it.isUnidentified - ) - } - it.status == GroupReceiptTable.STATUS_READ -> { - statusBuilder.read = SendStatus.Read( - sealedSender = it.isUnidentified - ) - } - it.status == GroupReceiptTable.STATUS_VIEWED -> { - statusBuilder.viewed = SendStatus.Viewed( - sealedSender = it.isUnidentified - ) - } - it.status == GroupReceiptTable.STATUS_SKIPPED -> { - statusBuilder.skipped = SendStatus.Skipped() - } - else -> { - statusBuilder.pending = SendStatus.Pending() - } - } - - statusBuilder.build() - } - } - - private fun String?.parseNetworkFailures(): Set { - if (this.isNullOrBlank()) { - return emptySet() - } - - return try { - JsonUtils.fromJson(this, NetworkFailureSet::class.java).items.map { it.recipientId.toLong() }.toSet() - } catch (e: IOException) { - emptySet() - } - } - - private fun String?.parseIdentityMismatches(): Set { - if (this.isNullOrBlank()) { - return emptySet() - } - - return try { - JsonUtils.fromJson(this, IdentityKeyMismatchSet::class.java).items.map { it.recipientId.toLong() }.toSet() - } catch (e: IOException) { - emptySet() - } - } - - private fun ByteArray?.parseMessageExtras(): MessageExtras? { - if (this == null) { - return null - } - return try { - MessageExtras.ADAPTER.decode(this) - } catch (e: java.lang.Exception) { - null - } - } - - private fun Long.isSmsType(): Boolean { - if (MessageTypes.isSecureType(this)) { - return false - } - - if (MessageTypes.isCallLog(this)) { - return false - } - - return MessageTypes.isOutgoingMessageType(this) || MessageTypes.isInboxType(this) - } - - private fun Long.isDirectionlessType(): Boolean { - return MessageTypes.isCallLog(this) || - MessageTypes.isExpirationTimerUpdate(this) || - MessageTypes.isThreadMergeType(this) || - MessageTypes.isSessionSwitchoverType(this) || - MessageTypes.isProfileChange(this) || - MessageTypes.isJoinedType(this) || - MessageTypes.isIdentityUpdate(this) || - MessageTypes.isIdentityVerified(this) || - MessageTypes.isIdentityDefault(this) || - MessageTypes.isReleaseChannelDonationRequest(this) || - MessageTypes.isChangeNumber(this) || - MessageTypes.isEndSessionType(this) || - MessageTypes.isChatSessionRefresh(this) || - MessageTypes.isBadDecryptType(this) || - MessageTypes.isPaymentsActivated(this) || - MessageTypes.isPaymentsRequestToActivate(this) || - MessageTypes.isUnsupportedMessageType(this) || - MessageTypes.isReportedSpam(this) || - MessageTypes.isMessageRequestAccepted(this) || - MessageTypes.isBlocked(this) || - MessageTypes.isUnblocked(this) || - MessageTypes.isGroupCall(this) - } - - private fun String.e164ToLong(): Long? { - val fixed = if (this.startsWith("+")) { - this.substring(1) - } else { - this - } - - return fixed.toLongOrNull() - } - - private fun Cursor.toBackupMessageRecord(): BackupMessageRecord { - return BackupMessageRecord( - id = this.requireLong(MessageTable.ID), - dateSent = this.requireLong(MessageTable.DATE_SENT), - dateReceived = this.requireLong(MessageTable.DATE_RECEIVED), - dateServer = this.requireLong(MessageTable.DATE_SERVER), - type = this.requireLong(MessageTable.TYPE), - threadId = this.requireLong(MessageTable.THREAD_ID), - body = this.requireString(MessageTable.BODY), - bodyRanges = this.requireBlob(MessageTable.MESSAGE_RANGES), - fromRecipientId = this.requireLong(MessageTable.FROM_RECIPIENT_ID), - toRecipientId = this.requireLong(MessageTable.TO_RECIPIENT_ID), - expiresIn = this.requireLong(MessageTable.EXPIRES_IN), - expireStarted = this.requireLong(MessageTable.EXPIRE_STARTED), - remoteDeleted = this.requireBoolean(MessageTable.REMOTE_DELETED), - sealedSender = this.requireBoolean(MessageTable.UNIDENTIFIED), - linkPreview = this.requireString(MessageTable.LINK_PREVIEWS), - sharedContacts = this.requireString(MessageTable.SHARED_CONTACTS), - quoteTargetSentTimestamp = this.requireLong(MessageTable.QUOTE_ID), - quoteAuthor = this.requireLong(MessageTable.QUOTE_AUTHOR), - quoteBody = this.requireString(MessageTable.QUOTE_BODY), - quoteMissing = this.requireBoolean(MessageTable.QUOTE_MISSING), - quoteBodyRanges = this.requireBlob(MessageTable.QUOTE_BODY_RANGES), - quoteType = this.requireInt(MessageTable.QUOTE_TYPE), - originalMessageId = this.requireLongOrNull(MessageTable.ORIGINAL_MESSAGE_ID), - latestRevisionId = this.requireLongOrNull(MessageTable.LATEST_REVISION_ID), - hasDeliveryReceipt = this.requireBoolean(MessageTable.HAS_DELIVERY_RECEIPT), - viewed = this.requireBoolean(MessageTable.VIEWED_COLUMN), - hasReadReceipt = this.requireBoolean(MessageTable.HAS_READ_RECEIPT), - read = this.requireBoolean(MessageTable.READ), - receiptTimestamp = this.requireLong(MessageTable.RECEIPT_TIMESTAMP), - networkFailureRecipientIds = this.requireString(MessageTable.NETWORK_FAILURES).parseNetworkFailures(), - identityMismatchRecipientIds = this.requireString(MessageTable.MISMATCHED_IDENTITIES).parseIdentityMismatches(), - baseType = this.requireLong(COLUMN_BASE_TYPE), - messageExtras = this.requireBlob(MessageTable.MESSAGE_EXTRAS).parseMessageExtras() - ) - } - - private class BackupMessageRecord( - val id: Long, - val dateSent: Long, - val dateReceived: Long, - val dateServer: Long, - val type: Long, - val threadId: Long, - val body: String?, - val bodyRanges: ByteArray?, - val fromRecipientId: Long, - val toRecipientId: Long, - val expiresIn: Long, - val expireStarted: Long, - val remoteDeleted: Boolean, - val sealedSender: Boolean, - val linkPreview: String?, - val sharedContacts: String?, - val quoteTargetSentTimestamp: Long, - val quoteAuthor: Long, - val quoteBody: String?, - val quoteMissing: Boolean, - val quoteBodyRanges: ByteArray?, - val quoteType: Int, - val originalMessageId: Long?, - val latestRevisionId: Long?, - val hasDeliveryReceipt: Boolean, - val hasReadReceipt: Boolean, - val viewed: Boolean, - val receiptTimestamp: Long, - val read: Boolean, - val networkFailureRecipientIds: Set, - val identityMismatchRecipientIds: Set, - val baseType: Long, - val messageExtras: MessageExtras? - ) -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt index 76aea11833..24045a63ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt @@ -5,81 +5,31 @@ package org.thoughtcrime.securesms.backup.v2.database -import okio.ByteString.Companion.toByteString import org.signal.core.util.deleteAll import org.signal.core.util.logging.Log -import org.signal.core.util.readToList -import org.signal.core.util.requireBoolean -import org.signal.core.util.requireLong -import org.signal.core.util.requireNonNullString -import org.signal.core.util.requireObject import org.signal.core.util.select import org.signal.core.util.withinTransaction import org.thoughtcrime.securesms.backup.v2.ImportState -import org.thoughtcrime.securesms.backup.v2.proto.DistributionList +import org.thoughtcrime.securesms.backup.v2.exporters.DistributionListArchiveExportIterator import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem import org.thoughtcrime.securesms.database.DistributionListTables import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.DistributionListId import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode -import org.thoughtcrime.securesms.database.model.DistributionListRecord import org.thoughtcrime.securesms.recipients.RecipientId import org.whispersystems.signalservice.api.push.DistributionId import org.whispersystems.signalservice.api.util.UuidUtil -import org.whispersystems.signalservice.api.util.toByteArray import org.thoughtcrime.securesms.backup.v2.proto.DistributionList as BackupDistributionList private val TAG = Log.tag(DistributionListTables::class.java) -data class DistributionRecipient(val id: RecipientId, val record: DistributionListRecord) - -fun DistributionListTables.getAllForBackup(): List { - val records = readableDatabase +fun DistributionListTables.getAllForBackup(): DistributionListArchiveExportIterator { + val cursor = readableDatabase .select() .from(DistributionListTables.ListTable.TABLE_NAME) .run() - .readToList { cursor -> - val id: DistributionListId = DistributionListId.from(cursor.requireLong(DistributionListTables.ListTable.ID)) - val privacyMode: DistributionListPrivacyMode = cursor.requireObject(DistributionListTables.ListTable.PRIVACY_MODE, DistributionListPrivacyMode.Serializer) - val recipientId: RecipientId = RecipientId.from(cursor.requireLong(DistributionListTables.ListTable.RECIPIENT_ID)) - DistributionRecipient( - id = recipientId, - record = DistributionListRecord( - id = id, - name = cursor.requireNonNullString(DistributionListTables.ListTable.NAME), - distributionId = DistributionId.from(cursor.requireNonNullString(DistributionListTables.ListTable.DISTRIBUTION_ID)), - allowsReplies = cursor.requireBoolean(DistributionListTables.ListTable.ALLOWS_REPLIES), - rawMembers = getRawMembers(id, privacyMode), - members = getMembersForBackup(id), - deletedAtTimestamp = cursor.requireLong(DistributionListTables.ListTable.DELETION_TIMESTAMP), - isUnknown = cursor.requireBoolean(DistributionListTables.ListTable.IS_UNKNOWN), - privacyMode = privacyMode - ) - ) - } - - return records - .map { recipient -> - BackupRecipient( - id = recipient.id.toLong(), - distributionList = if (recipient.record.deletedAtTimestamp != 0L) { - DistributionListItem( - distributionId = recipient.record.distributionId.asUuid().toByteArray().toByteString(), - deletionTimestamp = recipient.record.deletedAtTimestamp - ) - } else { - DistributionListItem( - distributionId = recipient.record.distributionId.asUuid().toByteArray().toByteString(), - distributionList = DistributionList( - name = recipient.record.name, - allowReplies = recipient.record.allowsReplies, - privacyMode = recipient.record.privacyMode.toBackupPrivacyMode(), - memberRecipientIds = recipient.record.members.map { it.toLong() } - ) - ) - } - ) - } + + return DistributionListArchiveExportIterator(cursor, this) } fun DistributionListTables.getMembersForBackup(id: DistributionListId): List { @@ -142,14 +92,6 @@ fun DistributionListTables.clearAllDataForBackupRestore() { writableDatabase.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME) } -private fun DistributionListPrivacyMode.toBackupPrivacyMode(): BackupDistributionList.PrivacyMode { - return when (this) { - DistributionListPrivacyMode.ONLY_WITH -> BackupDistributionList.PrivacyMode.ONLY_WITH - DistributionListPrivacyMode.ALL -> BackupDistributionList.PrivacyMode.ALL - DistributionListPrivacyMode.ALL_EXCEPT -> BackupDistributionList.PrivacyMode.ALL_EXCEPT - } -} - private fun BackupDistributionList.PrivacyMode.toLocalPrivacyMode(): DistributionListPrivacyMode { return when (this) { BackupDistributionList.PrivacyMode.UNKNOWN -> DistributionListPrivacyMode.ALL diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt index a97bf94211..f5e4656ab6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt @@ -6,17 +6,17 @@ package org.thoughtcrime.securesms.backup.v2.database import org.signal.core.util.SqlUtil -import org.signal.core.util.logging.Log import org.signal.core.util.select import org.thoughtcrime.securesms.backup.v2.ImportState +import org.thoughtcrime.securesms.backup.v2.exporters.ChatItemArchiveExportIterator import org.thoughtcrime.securesms.database.MessageTable import org.thoughtcrime.securesms.database.MessageTypes +import org.thoughtcrime.securesms.database.SignalDatabase import java.util.concurrent.TimeUnit -private val TAG = Log.tag(MessageTable::class.java) -private const val BASE_TYPE = "base_type" +private const val COLUMN_BASE_TYPE = "base_type" -fun MessageTable.getMessagesForBackup(backupTime: Long, mediaBackupEnabled: Boolean): ChatItemExportIterator { +fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, mediaBackupEnabled: Boolean): ChatItemArchiveExportIterator { val cursor = readableDatabase .select( MessageTable.ID, @@ -50,7 +50,7 @@ fun MessageTable.getMessagesForBackup(backupTime: Long, mediaBackupEnabled: Bool MessageTable.READ, MessageTable.NETWORK_FAILURES, MessageTable.MISMATCHED_IDENTITIES, - "${MessageTable.TYPE} & ${MessageTypes.BASE_TYPE_MASK} AS ${ChatItemExportIterator.COLUMN_BASE_TYPE}", + "${MessageTable.TYPE} & ${MessageTypes.BASE_TYPE_MASK} AS $COLUMN_BASE_TYPE", MessageTable.MESSAGE_EXTRAS ) .from(MessageTable.TABLE_NAME) @@ -66,7 +66,7 @@ fun MessageTable.getMessagesForBackup(backupTime: Long, mediaBackupEnabled: Bool .orderBy("${MessageTable.DATE_RECEIVED} ASC") .run() - return ChatItemExportIterator(cursor, 100, mediaBackupEnabled) + return ChatItemArchiveExportIterator(db, cursor, 100, mediaBackupEnabled) } fun MessageTable.createChatItemInserter(importState: ImportState): ChatItemImportInserter { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt index 9ac5820866..b4a6bd7002 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt @@ -6,20 +6,12 @@ package org.thoughtcrime.securesms.backup.v2.database import android.content.ContentValues -import android.database.Cursor import androidx.core.content.contentValuesOf -import okio.ByteString.Companion.toByteString import org.signal.core.util.Base64 import org.signal.core.util.SqlUtil import org.signal.core.util.deleteAll import org.signal.core.util.logging.Log import org.signal.core.util.nullIfBlank -import org.signal.core.util.requireBlob -import org.signal.core.util.requireBoolean -import org.signal.core.util.requireInt -import org.signal.core.util.requireLong -import org.signal.core.util.requireNonNullBlob -import org.signal.core.util.requireString import org.signal.core.util.select import org.signal.core.util.toInt import org.signal.core.util.update @@ -35,14 +27,14 @@ import org.signal.storageservice.protos.groups.local.DecryptedPendingMember import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember import org.signal.storageservice.protos.groups.local.DecryptedTimer import org.signal.storageservice.protos.groups.local.EnabledState +import org.thoughtcrime.securesms.backup.v2.exporters.ContactArchiveExportIterator +import org.thoughtcrime.securesms.backup.v2.exporters.GroupArchiveExportIterator import org.thoughtcrime.securesms.backup.v2.proto.AccountData import org.thoughtcrime.securesms.backup.v2.proto.Contact import org.thoughtcrime.securesms.backup.v2.proto.Group -import org.thoughtcrime.securesms.backup.v2.proto.Self import org.thoughtcrime.securesms.conversation.colors.AvatarColorHash import org.thoughtcrime.securesms.database.GroupTable import org.thoughtcrime.securesms.database.RecipientTable -import org.thoughtcrime.securesms.database.RecipientTableCursorUtil import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras import org.thoughtcrime.securesms.dependencies.AppDependencies @@ -58,18 +50,15 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI -import org.whispersystems.signalservice.api.util.toByteArray -import java.io.Closeable -typealias BackupRecipient = org.thoughtcrime.securesms.backup.v2.proto.Recipient -typealias BackupGroup = Group +private typealias BackupRecipient = org.thoughtcrime.securesms.backup.v2.proto.Recipient /** * Fetches all individual contacts for backups and returns the result as an iterator. * It's important to note that the iterator still needs to be closed after it's used. * It's recommended to use `.use` or a try-with-resources pattern. */ -fun RecipientTable.getContactsForBackup(selfId: Long): BackupContactIterator { +fun RecipientTable.getContactsForBackup(selfId: Long): ContactArchiveExportIterator { val cursor = readableDatabase .select( RecipientTable.ID, @@ -104,10 +93,10 @@ fun RecipientTable.getContactsForBackup(selfId: Long): BackupContactIterator { ) .run() - return BackupContactIterator(cursor, selfId) + return ContactArchiveExportIterator(cursor, selfId) } -fun RecipientTable.getGroupsForBackup(): BackupGroupIterator { +fun RecipientTable.getGroupsForBackup(): GroupArchiveExportIterator { val cursor = readableDatabase .select( "${RecipientTable.TABLE_NAME}.${RecipientTable.ID}", @@ -129,7 +118,7 @@ fun RecipientTable.getGroupsForBackup(): BackupGroupIterator { .where("${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY} IS NOT NULL") .run() - return BackupGroupIterator(cursor) + return GroupArchiveExportIterator(cursor) } /** @@ -226,7 +215,7 @@ fun RecipientTable.restoreGroupFromBackup(group: Group): RecipientId { val decryptedState = if (group.snapshot == null) { DecryptedGroup(revision = GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) } else { - group.snapshot.toDecryptedGroup(operations) + group.snapshot.toLocal(operations) } val values = ContentValues().apply { @@ -244,12 +233,30 @@ fun RecipientTable.restoreGroupFromBackup(group: Group): RecipientId { val recipientId = writableDatabase.insert(RecipientTable.TABLE_NAME, null, values) val restoredId = SignalDatabase.groups.create(masterKey, decryptedState, groupSendEndorsements = null) if (restoredId != null) { - SignalDatabase.groups.setShowAsStoryState(restoredId, group.storySendMode.toGroupShowAsStoryState()) + SignalDatabase.groups.setShowAsStoryState(restoredId, group.storySendMode.toLocal()) } return RecipientId.from(recipientId) } +private fun Group.StorySendMode.toLocal(): GroupTable.ShowAsStoryState { + return when (this) { + Group.StorySendMode.ENABLED -> GroupTable.ShowAsStoryState.ALWAYS + Group.StorySendMode.DISABLED -> GroupTable.ShowAsStoryState.NEVER + Group.StorySendMode.DEFAULT -> GroupTable.ShowAsStoryState.IF_ACTIVE + } +} + +private fun Group.MemberPendingProfileKey.toLocal(operations: GroupsV2Operations.GroupOperations): DecryptedPendingMember { + return DecryptedPendingMember( + serviceIdBytes = member!!.userId, + role = member.role.toLocal(), + addedByAci = addedByUserId, + timestamp = timestamp, + serviceIdCipherText = operations.encryptServiceId(ServiceId.Companion.parseOrNull(member.userId)) + ) +} + private fun Contact.Visibility.toLocal(): Recipient.HiddenState { return when (this) { Contact.Visibility.VISIBLE -> Recipient.HiddenState.NOT_HIDDEN @@ -280,78 +287,10 @@ private fun Group.Member.Role.toLocal(): Member.Role { } } -private fun AccessControl.AccessRequired.toSnapshot(): Group.AccessControl.AccessRequired { - return when (this) { - AccessControl.AccessRequired.UNKNOWN -> Group.AccessControl.AccessRequired.UNKNOWN - AccessControl.AccessRequired.ANY -> Group.AccessControl.AccessRequired.ANY - AccessControl.AccessRequired.MEMBER -> Group.AccessControl.AccessRequired.MEMBER - AccessControl.AccessRequired.ADMINISTRATOR -> Group.AccessControl.AccessRequired.ADMINISTRATOR - AccessControl.AccessRequired.UNSATISFIABLE -> Group.AccessControl.AccessRequired.UNSATISFIABLE - } -} - -private fun AccessControl.toSnapshot(): Group.AccessControl { - return Group.AccessControl(members = members.toSnapshot(), attributes = attributes.toSnapshot(), addFromInviteLink = addFromInviteLink.toSnapshot()) -} - -private fun Member.Role.toSnapshot(): Group.Member.Role { - return when (this) { - Member.Role.UNKNOWN -> Group.Member.Role.UNKNOWN - Member.Role.DEFAULT -> Group.Member.Role.DEFAULT - Member.Role.ADMINISTRATOR -> Group.Member.Role.ADMINISTRATOR - } -} - -private fun DecryptedGroup.toSnapshot(): Group.GroupSnapshot? { - if (this.revision == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION || this.revision == GroupsV2StateProcessor.PLACEHOLDER_REVISION) { - return null - } - - return Group.GroupSnapshot( - title = Group.GroupAttributeBlob(title = this.title), - avatarUrl = this.avatar, - disappearingMessagesTimer = this.disappearingMessagesTimer?.takeIf { it.duration > 0 }?.let { Group.GroupAttributeBlob(disappearingMessagesDuration = it.duration) }, - accessControl = this.accessControl?.toSnapshot(), - version = this.revision, - members = this.members.map { it.toSnapshot() }, - membersPendingProfileKey = this.pendingMembers.map { it.toSnapshot() }, - membersPendingAdminApproval = this.requestingMembers.map { it.toSnapshot() }, - inviteLinkPassword = this.inviteLinkPassword, - description = this.description.takeUnless { it.isBlank() }?.let { Group.GroupAttributeBlob(descriptionText = it) }, - announcements_only = this.isAnnouncementGroup == EnabledState.ENABLED, - members_banned = this.bannedMembers.map { it.toSnapshot() } - ) -} - private fun Group.Member.toLocal(): DecryptedMember { return DecryptedMember(aciBytes = userId, role = role.toLocal(), joinedAtRevision = joinedAtVersion) } -private fun DecryptedMember.toSnapshot(): Group.Member { - return Group.Member(userId = aciBytes, role = role.toSnapshot(), joinedAtVersion = joinedAtRevision) -} - -private fun Group.MemberPendingProfileKey.toLocal(operations: GroupsV2Operations.GroupOperations): DecryptedPendingMember { - return DecryptedPendingMember( - serviceIdBytes = member!!.userId, - role = member.role.toLocal(), - addedByAci = addedByUserId, - timestamp = timestamp, - serviceIdCipherText = operations.encryptServiceId(ServiceId.Companion.parseOrNull(member.userId)) - ) -} - -private fun DecryptedPendingMember.toSnapshot(): Group.MemberPendingProfileKey { - return Group.MemberPendingProfileKey( - member = Group.Member( - userId = this.serviceIdBytes, - role = this.role.toSnapshot() - ), - addedByUserId = this.addedByAci, - timestamp = this.timestamp - ) -} - private fun Group.MemberPendingAdminApproval.toLocal(): DecryptedRequestingMember { return DecryptedRequestingMember( aciBytes = this.userId, @@ -359,13 +298,6 @@ private fun Group.MemberPendingAdminApproval.toLocal(): DecryptedRequestingMembe ) } -private fun DecryptedRequestingMember.toSnapshot(): Group.MemberPendingAdminApproval { - return Group.MemberPendingAdminApproval( - userId = this.aciBytes, - timestamp = this.timestamp - ) -} - private fun Group.MemberBanned.toLocal(): DecryptedBannedMember { return DecryptedBannedMember( serviceIdBytes = this.userId, @@ -373,14 +305,7 @@ private fun Group.MemberBanned.toLocal(): DecryptedBannedMember { ) } -private fun DecryptedBannedMember.toSnapshot(): Group.MemberBanned { - return Group.MemberBanned( - userId = this.serviceIdBytes, - timestamp = this.timestamp - ) -} - -private fun Group.GroupSnapshot.toDecryptedGroup(operations: GroupsV2Operations.GroupOperations): DecryptedGroup { +private fun Group.GroupSnapshot.toLocal(operations: GroupsV2Operations.GroupOperations): DecryptedGroup { return DecryptedGroup( title = this.title?.title ?: "", avatar = this.avatarUrl, @@ -403,139 +328,6 @@ private fun Contact.toLocalExtras(): RecipientExtras { ) } -/** - * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. - * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. - */ -class BackupContactIterator(private val cursor: Cursor, private val selfId: Long) : Iterator, Closeable { - override fun hasNext(): Boolean { - return cursor.count > 0 && !cursor.isLast - } - - override fun next(): BackupRecipient? { - if (!cursor.moveToNext()) { - throw NoSuchElementException() - } - - val id = cursor.requireLong(RecipientTable.ID) - if (id == selfId) { - return BackupRecipient( - id = id, - self = Self() - ) - } - - val aci = ACI.parseOrNull(cursor.requireString(RecipientTable.ACI_COLUMN)) - val pni = PNI.parseOrNull(cursor.requireString(RecipientTable.PNI_COLUMN)) - val e164 = cursor.requireString(RecipientTable.E164)?.e164ToLong() - val registeredState = RecipientTable.RegisteredState.fromId(cursor.requireInt(RecipientTable.REGISTERED)) - val profileKey = cursor.requireString(RecipientTable.PROFILE_KEY) - val extras = RecipientTableCursorUtil.getExtras(cursor) - - if (aci == null && pni == null && e164 == null) { - return null - } - - val contactBuilder = Contact.Builder() - .aci(aci?.rawUuid?.toByteArray()?.toByteString()) - .pni(pni?.rawUuid?.toByteArray()?.toByteString()) - .username(cursor.requireString(RecipientTable.USERNAME)) - .e164(cursor.requireString(RecipientTable.E164)?.e164ToLong()) - .blocked(cursor.requireBoolean(RecipientTable.BLOCKED)) - .visibility(Recipient.HiddenState.deserialize(cursor.requireInt(RecipientTable.HIDDEN)).toRemote()) - .profileKey(if (profileKey != null) Base64.decode(profileKey).toByteString() else null) - .profileSharing(cursor.requireBoolean(RecipientTable.PROFILE_SHARING)) - .profileGivenName(cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME)) - .profileFamilyName(cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME)) - .hideStory(extras?.hideStory() ?: false) - - if (registeredState == RecipientTable.RegisteredState.REGISTERED) { - contactBuilder.registered = Contact.Registered() - } else { - contactBuilder.notRegistered = Contact.NotRegistered(unregisteredTimestamp = cursor.requireLong(RecipientTable.UNREGISTERED_TIMESTAMP)) - } - - return BackupRecipient( - id = id, - contact = contactBuilder.build() - ) - } - - override fun close() { - cursor.close() - } -} - -private fun Recipient.HiddenState.toRemote(): Contact.Visibility { - return when (this) { - Recipient.HiddenState.NOT_HIDDEN -> return Contact.Visibility.VISIBLE - Recipient.HiddenState.HIDDEN -> return Contact.Visibility.HIDDEN - Recipient.HiddenState.HIDDEN_MESSAGE_REQUEST -> return Contact.Visibility.HIDDEN_MESSAGE_REQUEST - } -} - -/** - * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. - * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. - */ -class BackupGroupIterator(private val cursor: Cursor) : Iterator, Closeable { - override fun hasNext(): Boolean { - return cursor.count > 0 && !cursor.isLast - } - - override fun next(): BackupRecipient { - if (!cursor.moveToNext()) { - throw NoSuchElementException() - } - - val extras = RecipientTableCursorUtil.getExtras(cursor) - val showAsStoryState: GroupTable.ShowAsStoryState = GroupTable.ShowAsStoryState.deserialize(cursor.requireInt(GroupTable.SHOW_AS_STORY_STATE)) - - val decryptedGroup: DecryptedGroup = DecryptedGroup.ADAPTER.decode(cursor.requireBlob(GroupTable.V2_DECRYPTED_GROUP)!!) - - return BackupRecipient( - id = cursor.requireLong(RecipientTable.ID), - group = BackupGroup( - masterKey = cursor.requireNonNullBlob(GroupTable.V2_MASTER_KEY).toByteString(), - whitelisted = cursor.requireBoolean(RecipientTable.PROFILE_SHARING), - hideStory = extras?.hideStory() ?: false, - storySendMode = showAsStoryState.toGroupStorySendMode(), - snapshot = decryptedGroup.toSnapshot() - ) - ) - } - - override fun close() { - cursor.close() - } -} - -private fun String.e164ToLong(): Long? { - val fixed = if (this.startsWith("+")) { - this.substring(1) - } else { - this - } - - return fixed.toLongOrNull() -} - -private fun GroupTable.ShowAsStoryState.toGroupStorySendMode(): Group.StorySendMode { - return when (this) { - GroupTable.ShowAsStoryState.ALWAYS -> Group.StorySendMode.ENABLED - GroupTable.ShowAsStoryState.NEVER -> Group.StorySendMode.DISABLED - GroupTable.ShowAsStoryState.IF_ACTIVE -> Group.StorySendMode.DEFAULT - } -} - -private fun Group.StorySendMode.toGroupShowAsStoryState(): GroupTable.ShowAsStoryState { - return when (this) { - Group.StorySendMode.ENABLED -> GroupTable.ShowAsStoryState.ALWAYS - Group.StorySendMode.DISABLED -> GroupTable.ShowAsStoryState.NEVER - Group.StorySendMode.DEFAULT -> GroupTable.ShowAsStoryState.IF_ACTIVE - } -} - private val Contact.formattedE164: String? get() { return e164?.let { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt index a8f17d0e88..6745697556 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt @@ -5,21 +5,15 @@ package org.thoughtcrime.securesms.backup.v2.database -import android.database.Cursor import androidx.core.content.contentValuesOf import org.signal.core.util.SqlUtil -import org.signal.core.util.decodeOrNull import org.signal.core.util.insertInto import org.signal.core.util.logging.Log -import org.signal.core.util.requireBlob -import org.signal.core.util.requireBoolean -import org.signal.core.util.requireInt -import org.signal.core.util.requireLong import org.signal.core.util.toInt import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.backup.v2.ImportState +import org.thoughtcrime.securesms.backup.v2.exporters.ChatArchiveExportIterator import org.thoughtcrime.securesms.backup.v2.proto.Chat -import org.thoughtcrime.securesms.backup.v2.util.ChatStyleConverter import org.thoughtcrime.securesms.backup.v2.util.parseChatWallpaper import org.thoughtcrime.securesms.backup.v2.util.toLocal import org.thoughtcrime.securesms.backup.v2.util.toLocalAttachment @@ -27,15 +21,12 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColors import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.ThreadTable -import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor -import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.wallpaper.UriChatWallpaper -import java.io.Closeable private val TAG = Log.tag(ThreadTable::class.java) -fun ThreadTable.getThreadsForBackup(db: SignalDatabase): ChatExportIterator { +fun ThreadTable.getThreadsForBackup(db: SignalDatabase): ChatArchiveExportIterator { //language=sql val query = """ SELECT @@ -57,7 +48,7 @@ fun ThreadTable.getThreadsForBackup(db: SignalDatabase): ChatExportIterator { """ val cursor = readableDatabase.query(query) - return ChatExportIterator(cursor, db) + return ChatArchiveExportIterator(cursor, db) } fun ThreadTable.clearAllDataForBackupRestore() { @@ -107,48 +98,3 @@ fun ThreadTable.restoreFromBackup(chat: Chat, recipientId: RecipientId, importSt return threadId } - -class ChatExportIterator(private val cursor: Cursor, private val db: SignalDatabase) : Iterator, Closeable { - override fun hasNext(): Boolean { - return cursor.count > 0 && !cursor.isLast - } - - override fun next(): Chat { - if (!cursor.moveToNext()) { - throw NoSuchElementException() - } - - val customChatColorsId = ChatColors.Id.forLongValue(cursor.requireLong(RecipientTable.CUSTOM_CHAT_COLORS_ID)) - - val chatColors: ChatColors? = cursor.requireBlob(RecipientTable.CHAT_COLORS)?.let { serializedChatColors -> - val chatColor = ChatColor.ADAPTER.decodeOrNull(serializedChatColors) - chatColor?.let { ChatColors.forChatColor(customChatColorsId, it) } - } - - val chatWallpaper: Wallpaper? = cursor.requireBlob(RecipientTable.WALLPAPER)?.let { serializedWallpaper -> - Wallpaper.ADAPTER.decodeOrNull(serializedWallpaper) - } - - return Chat( - id = cursor.requireLong(ThreadTable.ID), - recipientId = cursor.requireLong(ThreadTable.RECIPIENT_ID), - archived = cursor.requireBoolean(ThreadTable.ARCHIVED), - pinnedOrder = cursor.requireInt(ThreadTable.PINNED), - expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME), - expireTimerVersion = cursor.requireInt(RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION), - muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL), - markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.FORCED_UNREAD, - dontNotifyForMentionsIfMuted = RecipientTable.MentionSetting.DO_NOT_NOTIFY.id == cursor.requireInt(RecipientTable.MENTION_SETTING), - style = ChatStyleConverter.constructRemoteChatStyle( - db = db, - chatColors = chatColors, - chatColorId = customChatColorsId, - chatWallpaper = chatWallpaper - ) - ) - } - - override fun close() { - cursor.close() - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExportIterator.kt new file mode 100644 index 0000000000..ecd965514c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExportIterator.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.database.Cursor +import org.signal.core.util.requireLong +import org.thoughtcrime.securesms.backup.v2.proto.AdHocCall +import org.thoughtcrime.securesms.database.CallTable +import java.io.Closeable + +/** + * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. + * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. + */ +class AdHocCallArchiveExportIterator(private val cursor: Cursor) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): AdHocCall { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val callId = cursor.requireLong(CallTable.CALL_ID) + + return AdHocCall( + callId = callId, + recipientId = cursor.requireLong(CallTable.PEER), + state = AdHocCall.State.GENERIC, + callTimestamp = cursor.requireLong(CallTable.TIMESTAMP) + ) + } + + override fun close() { + cursor.close() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExportIterator.kt new file mode 100644 index 0000000000..ee948cebd5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExportIterator.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.database.Cursor +import okio.ByteString +import okio.ByteString.Companion.toByteString +import org.signal.ringrtc.CallLinkState +import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient +import org.thoughtcrime.securesms.backup.v2.proto.CallLink +import org.thoughtcrime.securesms.database.CallLinkTable +import java.io.Closeable + +/** + * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. + * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. + */ +class CallLinkArchiveExportIterator(private val cursor: Cursor) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): ArchiveRecipient { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val callLink = CallLinkTable.CallLinkDeserializer.deserialize(cursor) + return ArchiveRecipient( + id = callLink.recipientId.toLong(), + callLink = CallLink( + rootKey = callLink.credentials?.linkKeyBytes?.toByteString() ?: ByteString.EMPTY, + adminKey = callLink.credentials?.adminPassBytes?.toByteString(), + name = callLink.state.name, + expirationMs = try { + callLink.state.expiration.toEpochMilli() + } catch (e: ArithmeticException) { + Long.MAX_VALUE + }, + restrictions = callLink.state.restrictions.toRemote() + ) + ) + } + + override fun close() { + cursor.close() + } +} + +private fun CallLinkState.Restrictions.toRemote(): CallLink.Restrictions { + return when (this) { + CallLinkState.Restrictions.ADMIN_APPROVAL -> CallLink.Restrictions.ADMIN_APPROVAL + CallLinkState.Restrictions.NONE -> CallLink.Restrictions.NONE + CallLinkState.Restrictions.UNKNOWN -> CallLink.Restrictions.UNKNOWN + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExportIterator.kt new file mode 100644 index 0000000000..e4c13bb988 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExportIterator.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.exporters + +import android.database.Cursor +import org.signal.core.util.decodeOrNull +import org.signal.core.util.requireBlob +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.thoughtcrime.securesms.backup.v2.proto.Chat +import org.thoughtcrime.securesms.backup.v2.util.ChatStyleConverter +import org.thoughtcrime.securesms.conversation.colors.ChatColors +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.ThreadTable +import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor +import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper +import java.io.Closeable + +class ChatArchiveExportIterator(private val cursor: Cursor, private val db: SignalDatabase) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): Chat { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val customChatColorsId = ChatColors.Id.forLongValue(cursor.requireLong(RecipientTable.CUSTOM_CHAT_COLORS_ID)) + + val chatColors: ChatColors? = cursor.requireBlob(RecipientTable.CHAT_COLORS)?.let { serializedChatColors -> + val chatColor = ChatColor.ADAPTER.decodeOrNull(serializedChatColors) + chatColor?.let { ChatColors.forChatColor(customChatColorsId, it) } + } + + val chatWallpaper: Wallpaper? = cursor.requireBlob(RecipientTable.WALLPAPER)?.let { serializedWallpaper -> + Wallpaper.ADAPTER.decodeOrNull(serializedWallpaper) + } + + return Chat( + id = cursor.requireLong(ThreadTable.ID), + recipientId = cursor.requireLong(ThreadTable.RECIPIENT_ID), + archived = cursor.requireBoolean(ThreadTable.ARCHIVED), + pinnedOrder = cursor.requireInt(ThreadTable.PINNED), + expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME), + expireTimerVersion = cursor.requireInt(RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION), + muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL), + markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.FORCED_UNREAD, + dontNotifyForMentionsIfMuted = RecipientTable.MentionSetting.DO_NOT_NOTIFY.id == cursor.requireInt(RecipientTable.MENTION_SETTING), + style = ChatStyleConverter.constructRemoteChatStyle( + db = db, + chatColors = chatColors, + chatColorId = customChatColorsId, + chatWallpaper = chatWallpaper + ) + ) + } + + override fun close() { + cursor.close() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExportIterator.kt new file mode 100644 index 0000000000..e2271d965f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExportIterator.kt @@ -0,0 +1,1127 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.exporters + +import android.database.Cursor +import okio.ByteString.Companion.toByteString +import org.json.JSONArray +import org.json.JSONException +import org.signal.core.util.Base64 +import org.signal.core.util.Hex +import org.signal.core.util.logging.Log +import org.signal.core.util.nullIfEmpty +import org.signal.core.util.orNull +import org.signal.core.util.requireBlob +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireLongOrNull +import org.signal.core.util.requireString +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.backup.v2.proto.ChatItem +import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage +import org.thoughtcrime.securesms.backup.v2.proto.ContactAttachment +import org.thoughtcrime.securesms.backup.v2.proto.ContactMessage +import org.thoughtcrime.securesms.backup.v2.proto.ExpirationTimerChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.GroupCall +import org.thoughtcrime.securesms.backup.v2.proto.IndividualCall +import org.thoughtcrime.securesms.backup.v2.proto.LearnedProfileChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.MessageAttachment +import org.thoughtcrime.securesms.backup.v2.proto.PaymentNotification +import org.thoughtcrime.securesms.backup.v2.proto.ProfileChangeChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.Quote +import org.thoughtcrime.securesms.backup.v2.proto.Reaction +import org.thoughtcrime.securesms.backup.v2.proto.RemoteDeletedMessage +import org.thoughtcrime.securesms.backup.v2.proto.SendStatus +import org.thoughtcrime.securesms.backup.v2.proto.SessionSwitchoverChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage +import org.thoughtcrime.securesms.backup.v2.proto.Sticker +import org.thoughtcrime.securesms.backup.v2.proto.StickerMessage +import org.thoughtcrime.securesms.backup.v2.proto.Text +import org.thoughtcrime.securesms.backup.v2.proto.ThreadMergeChatUpdate +import org.thoughtcrime.securesms.backup.v2.util.toRemoteFilePointer +import org.thoughtcrime.securesms.contactshare.Contact +import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.database.CallTable +import org.thoughtcrime.securesms.database.GroupReceiptTable +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.MessageTypes +import org.thoughtcrime.securesms.database.PaymentTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet +import org.thoughtcrime.securesms.database.documents.NetworkFailureSet +import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil +import org.thoughtcrime.securesms.database.model.GroupsV2UpdateMessageConverter +import org.thoughtcrime.securesms.database.model.Mention +import org.thoughtcrime.securesms.database.model.ReactionRecord +import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList +import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context +import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge +import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras +import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails +import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent +import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.linkpreview.LinkPreview +import org.thoughtcrime.securesms.mms.QuoteModel +import org.thoughtcrime.securesms.payments.FailureReason +import org.thoughtcrime.securesms.payments.State +import org.thoughtcrime.securesms.payments.proto.PaymentMetaData +import org.thoughtcrime.securesms.util.JsonUtils +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.api.util.toByteArray +import java.io.Closeable +import java.io.IOException +import java.util.HashMap +import java.util.LinkedList +import java.util.Queue +import kotlin.jvm.optionals.getOrNull +import org.thoughtcrime.securesms.backup.v2.proto.BodyRange as BackupBodyRange +import org.thoughtcrime.securesms.backup.v2.proto.GiftBadge as BackupGiftBadge + +private val TAG = Log.tag(ChatItemArchiveExportIterator::class.java) +private const val COLUMN_BASE_TYPE = "base_type" + +/** + * An iterator for chat items with a clever performance twist: rather than do the extra queries one at a time (for reactions, + * attachments, etc), this will populate items in batches, doing bulk lookups to improve throughput. We keep these in a buffer + * and only do more queries when the buffer is empty. + * + * All of this complexity is hidden from the user -- they just get a normal iterator interface. + */ +class ChatItemArchiveExportIterator( + private val db: SignalDatabase, + private val cursor: Cursor, + private val batchSize: Int, + private val mediaArchiveEnabled: Boolean +) : Iterator, Closeable { + + /** + * A queue of already-parsed ChatItems. Processing in batches means that we read ahead in the cursor and put + * the pending items here. + */ + private val buffer: Queue = LinkedList() + + private val revisionMap: HashMap> = HashMap() + + override fun hasNext(): Boolean { + return buffer.isNotEmpty() || (cursor.count > 0 && !cursor.isLast && !cursor.isAfterLast) + } + + override fun next(): ChatItem? { + if (buffer.isNotEmpty()) { + return buffer.remove() + } + + val records: LinkedHashMap = linkedMapOf() + + for (i in 0 until batchSize) { + if (cursor.moveToNext()) { + val record = cursor.toBackupMessageRecord() + records[record.id] = record + } else { + break + } + } + + val reactionsById: Map> = db.reactionTable.getReactionsForMessages(records.keys).map { entry -> entry.key to entry.value.sortedBy { it.dateReceived } }.toMap() + val mentionsById: Map> = db.mentionTable.getMentionsForMessages(records.keys) + val attachmentsById: Map> = db.attachmentTable.getAttachmentsForMessages(records.keys) + val groupReceiptsById: Map> = db.groupReceiptTable.getGroupReceiptInfoForMessages(records.keys) + + for ((id, record) in records) { + val builder = record.toBasicChatItemBuilder(groupReceiptsById[id]) + + when { + record.remoteDeleted -> { + builder.remoteDeletedMessage = RemoteDeletedMessage() + } + + MessageTypes.isJoinedType(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.JOINED_SIGNAL) + } + + MessageTypes.isIdentityUpdate(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.IDENTITY_UPDATE) + } + + MessageTypes.isIdentityVerified(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.IDENTITY_VERIFIED) + } + + MessageTypes.isIdentityDefault(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.IDENTITY_DEFAULT) + } + + MessageTypes.isChangeNumber(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.CHANGE_NUMBER) + } + + MessageTypes.isReleaseChannelDonationRequest(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST) + } + + MessageTypes.isEndSessionType(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.END_SESSION) + } + + MessageTypes.isChatSessionRefresh(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.CHAT_SESSION_REFRESH) + } + + MessageTypes.isBadDecryptType(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.BAD_DECRYPT) + } + + MessageTypes.isPaymentsActivated(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.PAYMENTS_ACTIVATED) + } + + MessageTypes.isPaymentsRequestToActivate(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST) + } + + MessageTypes.isUnsupportedMessageType(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE) + } + + MessageTypes.isReportedSpam(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.REPORTED_SPAM) + } + + MessageTypes.isMessageRequestAccepted(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.MESSAGE_REQUEST_ACCEPTED) + } + + MessageTypes.isBlocked(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.BLOCKED) + } + + MessageTypes.isUnblocked(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.UNBLOCKED) + } + + MessageTypes.isExpirationTimerUpdate(record.type) -> { + builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn)) + builder.expiresInMs = 0 + } + + MessageTypes.isProfileChange(record.type) -> { + builder.updateMessage = record.toRemoteProfileChangeUpdate() + } + + MessageTypes.isSessionSwitchoverType(record.type) -> { + builder.updateMessage = record.toRemoteSessionSwitchoverUpdate() + } + + MessageTypes.isThreadMergeType(record.type) -> { + builder.updateMessage = record.toRemoteThreadMergeUpdate() + } + + MessageTypes.isGroupV2(record.type) && MessageTypes.isGroupUpdate(record.type) -> { + builder.updateMessage = record.toRemoteGroupUpdate() + } + + MessageTypes.isCallLog(record.type) -> { + val call = db.callTable.getCallByMessageId(record.id) + builder.updateMessage = call?.toRemoteCallUpdate(db, record) + } + + MessageTypes.isPaymentsNotification(record.type) -> { + builder.paymentNotification = record.toRemotePaymentNotificationUpdate(db) + } + + MessageTypes.isGiftBadge(record.type) -> { + builder.giftBadge = record.toRemoteGiftBadgeUpdate() + } + + !record.sharedContacts.isNullOrEmpty() -> { + builder.contactMessage = record.toRemoteContactMessage(mediaArchiveEnabled = mediaArchiveEnabled, reactionRecords = reactionsById[id], attachments = attachmentsById[id]) + } + + else -> { + if (record.body == null && !attachmentsById.containsKey(record.id)) { + Log.w(TAG, "Record with ID ${record.id} missing a body and doesn't have attachments. Skipping.") + continue + } + + val attachments = attachmentsById[record.id] + val sticker = attachments?.firstOrNull { dbAttachment -> dbAttachment.isSticker } + + if (sticker?.stickerLocator != null) { + builder.stickerMessage = sticker.toRemoteStickerMessage(mediaArchiveEnabled = mediaArchiveEnabled, reactions = reactionsById[id]) + } else { + builder.standardMessage = record.toRemoteStandardMessage( + db = db, + mediaArchiveEnabled = mediaArchiveEnabled, + reactionRecords = reactionsById[id], + mentions = mentionsById[id], + attachments = attachmentsById[record.id] + ) + } + } + } + + if (record.latestRevisionId == null) { + val previousEdits = revisionMap.remove(record.id) + if (previousEdits != null) { + builder.revisions = previousEdits + } + buffer += builder.build() + } else { + var previousEdits = revisionMap[record.latestRevisionId] + if (previousEdits == null) { + previousEdits = ArrayList() + revisionMap[record.latestRevisionId] = previousEdits + } + previousEdits += builder.build() + } + } + + return if (buffer.isNotEmpty()) { + buffer.remove() + } else { + null + } + } + + override fun close() { + cursor.close() + } +} + +private fun simpleUpdate(type: SimpleChatUpdate.Type): ChatUpdateMessage { + return ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = type)) +} + +private fun BackupMessageRecord.toBasicChatItemBuilder(groupReceipts: List?): ChatItem.Builder { + val record = this + + return ChatItem.Builder().apply { + chatId = record.threadId + authorId = record.fromRecipientId + dateSent = record.dateSent + expireStartDate = if (record.expireStarted > 0) record.expireStarted else 0 + expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0 + revisions = emptyList() + sms = record.type.isSmsType() + if (record.type.isDirectionlessType()) { + directionless = ChatItem.DirectionlessMessageDetails() + } else if (MessageTypes.isOutgoingMessageType(record.type)) { + outgoing = ChatItem.OutgoingMessageDetails( + sendStatus = record.toRemoteSendStatus(groupReceipts) + ) + } else { + incoming = ChatItem.IncomingMessageDetails( + dateServerSent = record.dateServer, + dateReceived = record.dateReceived, + read = record.read, + sealedSender = record.sealedSender + ) + } + } +} + +private fun BackupMessageRecord.toRemoteProfileChangeUpdate(): ChatUpdateMessage? { + val profileChangeDetails = if (this.messageExtras != null) { + this.messageExtras.profileChangeDetails + } else { + Base64.decodeOrNull(this.body)?.let { ProfileChangeDetails.ADAPTER.decode(it) } + } + + return if (profileChangeDetails?.profileNameChange != null) { + ChatUpdateMessage(profileChange = ProfileChangeChatUpdate(previousName = profileChangeDetails.profileNameChange.previous, newName = profileChangeDetails.profileNameChange.newValue)) + } else if (profileChangeDetails?.learnedProfileName != null) { + ChatUpdateMessage(learnedProfileChange = LearnedProfileChatUpdate(e164 = profileChangeDetails.learnedProfileName.e164?.e164ToLong(), username = profileChangeDetails.learnedProfileName.username)) + } else { + null + } +} + +private fun BackupMessageRecord.toRemoteSessionSwitchoverUpdate(): ChatUpdateMessage { + if (this.body == null) { + return ChatUpdateMessage(sessionSwitchover = SessionSwitchoverChatUpdate()) + } + + return ChatUpdateMessage( + sessionSwitchover = try { + val event = SessionSwitchoverEvent.ADAPTER.decode(Base64.decodeOrThrow(this.body)) + SessionSwitchoverChatUpdate(event.e164.e164ToLong()!!) + } catch (e: IOException) { + SessionSwitchoverChatUpdate() + } + ) +} + +private fun BackupMessageRecord.toRemoteThreadMergeUpdate(): ChatUpdateMessage { + if (this.body == null) { + return ChatUpdateMessage(threadMerge = ThreadMergeChatUpdate()) + } + + return ChatUpdateMessage( + threadMerge = try { + val event = ThreadMergeEvent.ADAPTER.decode(Base64.decodeOrThrow(this.body)) + ThreadMergeChatUpdate(event.previousE164.e164ToLong()!!) + } catch (e: IOException) { + ThreadMergeChatUpdate() + } + ) +} + +private fun BackupMessageRecord.toRemoteGroupUpdate(): ChatUpdateMessage? { + val groupChange = this.messageExtras?.gv2UpdateDescription?.groupChangeUpdate + if (groupChange != null) { + return ChatUpdateMessage( + groupChange = groupChange + ) + } + + if (this.body != null) { + return try { + val decoded: ByteArray = Base64.decode(this.body) + val context = DecryptedGroupV2Context.ADAPTER.decode(decoded) + ChatUpdateMessage( + groupChange = GroupsV2UpdateMessageConverter.translateDecryptedChange(selfIds = SignalStore.account.getServiceIds(), context) + ) + } catch (e: IOException) { + null + } + } + + return null +} + +private fun CallTable.Call.toRemoteCallUpdate(db: SignalDatabase, messageRecord: BackupMessageRecord): ChatUpdateMessage? { + return when (this.type) { + CallTable.Type.GROUP_CALL -> { + val groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(messageRecord.body) + + ChatUpdateMessage( + groupCall = GroupCall( + callId = this.callId, + state = when (this.event) { + CallTable.Event.MISSED -> GroupCall.State.MISSED + CallTable.Event.ONGOING -> GroupCall.State.GENERIC + CallTable.Event.ACCEPTED -> GroupCall.State.ACCEPTED + CallTable.Event.NOT_ACCEPTED -> GroupCall.State.GENERIC + CallTable.Event.MISSED_NOTIFICATION_PROFILE -> GroupCall.State.MISSED_NOTIFICATION_PROFILE + CallTable.Event.GENERIC_GROUP_CALL -> GroupCall.State.GENERIC + CallTable.Event.JOINED -> GroupCall.State.JOINED + CallTable.Event.RINGING -> GroupCall.State.RINGING + CallTable.Event.DECLINED -> GroupCall.State.DECLINED + CallTable.Event.OUTGOING_RING -> GroupCall.State.OUTGOING_RING + CallTable.Event.DELETE -> return null + }, + ringerRecipientId = this.ringerRecipient?.toLong(), + startedCallRecipientId = ACI.parseOrNull(groupCallUpdateDetails.startedCallUuid)?.let { db.recipientTable.getByAci(it).getOrNull()?.toLong() }, + startedCallTimestamp = this.timestamp, + endedCallTimestamp = groupCallUpdateDetails.endedCallTimestamp, + read = messageRecord.read + ) + ) + } + + CallTable.Type.AUDIO_CALL, + CallTable.Type.VIDEO_CALL -> { + ChatUpdateMessage( + individualCall = IndividualCall( + callId = this.callId, + type = if (this.type == CallTable.Type.VIDEO_CALL) IndividualCall.Type.VIDEO_CALL else IndividualCall.Type.AUDIO_CALL, + direction = if (this.direction == CallTable.Direction.INCOMING) IndividualCall.Direction.INCOMING else IndividualCall.Direction.OUTGOING, + state = when (this.event) { + CallTable.Event.MISSED -> IndividualCall.State.MISSED + CallTable.Event.MISSED_NOTIFICATION_PROFILE -> IndividualCall.State.MISSED_NOTIFICATION_PROFILE + CallTable.Event.ACCEPTED -> IndividualCall.State.ACCEPTED + CallTable.Event.NOT_ACCEPTED -> IndividualCall.State.NOT_ACCEPTED + else -> IndividualCall.State.UNKNOWN_STATE + }, + startedCallTimestamp = this.timestamp, + read = messageRecord.read + ) + ) + } + + CallTable.Type.AD_HOC_CALL -> throw IllegalArgumentException("AdHoc calls are not update messages!") + } +} + +private fun BackupMessageRecord.toRemotePaymentNotificationUpdate(db: SignalDatabase): PaymentNotification { + val paymentUuid = UuidUtil.parseOrNull(this.body) + val payment = if (paymentUuid != null) { + db.paymentTable.getPayment(paymentUuid) + } else { + null + } + + return if (payment == null) { + PaymentNotification() + } else { + PaymentNotification( + amountMob = payment.amount.serializeAmountString(), + feeMob = payment.fee.serializeAmountString(), + note = payment.note.takeUnless { it.isEmpty() }, + transactionDetails = payment.toRemoteTransactionDetails() + ) + } +} + +private fun BackupMessageRecord.toRemoteSharedContacts(attachments: List?): List { + if (this.sharedContacts.isNullOrEmpty()) { + return emptyList() + } + + val attachmentIdMap: Map = attachments?.associateBy { it.attachmentId } ?: emptyMap() + + try { + val contacts: MutableList = LinkedList() + val jsonContacts = JSONArray(sharedContacts) + + for (i in 0 until jsonContacts.length()) { + val contact: Contact = Contact.deserialize(jsonContacts.getJSONObject(i).toString()) + + if (contact.avatar != null && contact.avatar!!.attachmentId != null) { + val attachment = attachmentIdMap[contact.avatar!!.attachmentId] + + val updatedAvatar = Contact.Avatar( + contact.avatar!!.attachmentId, + attachment, + contact.avatar!!.isProfile + ) + + contacts += Contact(contact, updatedAvatar) + } else { + contacts += contact + } + } + + return contacts + } catch (e: JSONException) { + Log.w(TAG, "Failed to parse shared contacts.", e) + } catch (e: IOException) { + Log.w(TAG, "Failed to parse shared contacts.", e) + } + + return emptyList() +} + +private fun BackupMessageRecord.toRemoteLinkPreviews(attachments: List?): List { + if (linkPreview.isNullOrEmpty()) { + return emptyList() + } + val attachmentIdMap: Map = attachments?.associateBy { it.attachmentId } ?: emptyMap() + + try { + val previews: MutableList = LinkedList() + val jsonPreviews = JSONArray(linkPreview) + + for (i in 0 until jsonPreviews.length()) { + val preview = LinkPreview.deserialize(jsonPreviews.getJSONObject(i).toString()) + + if (preview.attachmentId != null) { + val attachment = attachmentIdMap[preview.attachmentId] + + if (attachment != null) { + previews += LinkPreview(preview.url, preview.title, preview.description, preview.date, attachment) + } else { + previews += preview + } + } else { + previews += preview + } + } + + return previews + } catch (e: JSONException) { + Log.w(TAG, "Failed to parse link preview", e) + } catch (e: IOException) { + Log.w(TAG, "Failed to parse shared contacts.", e) + } + + return emptyList() +} + +private fun LinkPreview.toRemoteLinkPreview(mediaArchiveEnabled: Boolean): org.thoughtcrime.securesms.backup.v2.proto.LinkPreview { + return org.thoughtcrime.securesms.backup.v2.proto.LinkPreview( + url = url, + title = title.nullIfEmpty(), + image = (thumbnail.orNull() as? DatabaseAttachment)?.toRemoteMessageAttachment(mediaArchiveEnabled)?.pointer, + description = description.nullIfEmpty(), + date = date + ) +} + +private fun BackupMessageRecord.toRemoteContactMessage(mediaArchiveEnabled: Boolean, reactionRecords: List?, attachments: List?): ContactMessage { + val sharedContacts = toRemoteSharedContacts(attachments) + + val contacts = sharedContacts.map { + ContactAttachment( + name = it.name.toRemote(), + avatar = (it.avatar?.attachment as? DatabaseAttachment)?.toRemoteMessageAttachment(mediaArchiveEnabled)?.pointer, + organization = it.organization, + number = it.phoneNumbers.map { phone -> + ContactAttachment.Phone( + value_ = phone.number, + type = phone.type.toRemote(), + label = phone.label + ) + }, + email = it.emails.map { email -> + ContactAttachment.Email( + value_ = email.email, + label = email.label, + type = email.type.toRemote() + ) + }, + address = it.postalAddresses.map { address -> + ContactAttachment.PostalAddress( + type = address.type.toRemote(), + label = address.label, + street = address.street, + pobox = address.poBox, + neighborhood = address.neighborhood, + city = address.city, + region = address.region, + postcode = address.postalCode, + country = address.country + ) + } + ) + } + return ContactMessage( + contact = contacts, + reactions = reactionRecords.toRemote() + ) +} + +private fun Contact.Name.toRemote(): ContactAttachment.Name { + return ContactAttachment.Name( + givenName = givenName, + familyName = familyName, + prefix = prefix, + suffix = suffix, + middleName = middleName, + nickname = nickname + ) +} + +private fun Contact.Phone.Type.toRemote(): ContactAttachment.Phone.Type { + return when (this) { + Contact.Phone.Type.HOME -> ContactAttachment.Phone.Type.HOME + Contact.Phone.Type.MOBILE -> ContactAttachment.Phone.Type.MOBILE + Contact.Phone.Type.WORK -> ContactAttachment.Phone.Type.WORK + Contact.Phone.Type.CUSTOM -> ContactAttachment.Phone.Type.CUSTOM + } +} + +private fun Contact.Email.Type.toRemote(): ContactAttachment.Email.Type { + return when (this) { + Contact.Email.Type.HOME -> ContactAttachment.Email.Type.HOME + Contact.Email.Type.MOBILE -> ContactAttachment.Email.Type.MOBILE + Contact.Email.Type.WORK -> ContactAttachment.Email.Type.WORK + Contact.Email.Type.CUSTOM -> ContactAttachment.Email.Type.CUSTOM + } +} + +private fun Contact.PostalAddress.Type.toRemote(): ContactAttachment.PostalAddress.Type { + return when (this) { + Contact.PostalAddress.Type.HOME -> ContactAttachment.PostalAddress.Type.HOME + Contact.PostalAddress.Type.WORK -> ContactAttachment.PostalAddress.Type.WORK + Contact.PostalAddress.Type.CUSTOM -> ContactAttachment.PostalAddress.Type.CUSTOM + } +} + +private fun BackupMessageRecord.toRemoteStandardMessage(db: SignalDatabase, mediaArchiveEnabled: Boolean, reactionRecords: List?, mentions: List?, attachments: List?): StandardMessage { + val text = if (body == null) { + null + } else { + Text( + body = this.body, + bodyRanges = (this.bodyRanges?.toRemoteBodyRanges() ?: emptyList()) + (mentions?.toRemoteBodyRanges(db) ?: emptyList()) + ) + } + val linkPreviews = this.toRemoteLinkPreviews(attachments) + val linkPreviewAttachments = linkPreviews.mapNotNull { it.thumbnail.orElse(null) }.toSet() + val quotedAttachments = attachments?.filter { it.quote } ?: emptyList() + val longTextAttachment = attachments?.firstOrNull { it.contentType == "text/x-signal-plain" } + val messageAttachments = attachments + ?.filterNot { it.quote } + ?.filterNot { linkPreviewAttachments.contains(it) } + ?.filterNot { it == longTextAttachment } + ?: emptyList() + return StandardMessage( + quote = this.toRemoteQuote(mediaArchiveEnabled, quotedAttachments), + text = text, + attachments = messageAttachments.toRemoteAttachments(mediaArchiveEnabled), + linkPreview = linkPreviews.map { it.toRemoteLinkPreview(mediaArchiveEnabled) }, + longText = longTextAttachment?.toRemoteFilePointer(mediaArchiveEnabled), + reactions = reactionRecords.toRemote() + ) +} + +private fun BackupMessageRecord.toRemoteQuote(mediaArchiveEnabled: Boolean, attachments: List? = null): Quote? { + if (this.quoteTargetSentTimestamp == MessageTable.QUOTE_NOT_PRESENT_ID || this.quoteAuthor <= 0) { + return null + } + + val type = QuoteModel.Type.fromCode(this.quoteType) + return Quote( + targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID }, + authorId = this.quoteAuthor, + text = this.quoteBody?.let { body -> + Text( + body = body, + bodyRanges = this.quoteBodyRanges?.toRemoteBodyRanges() ?: emptyList() + ) + }, + attachments = attachments?.toRemoteQuoteAttachments(mediaArchiveEnabled) ?: emptyList(), + type = when (type) { + QuoteModel.Type.NORMAL -> Quote.Type.NORMAL + QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFTBADGE + } + ) +} + +private fun BackupMessageRecord.toRemoteGiftBadgeUpdate(): BackupGiftBadge { + val giftBadge = try { + GiftBadge.ADAPTER.decode(Base64.decode(this.body ?: "")) + } catch (e: IOException) { + Log.w(TAG, "Failed to decode GiftBadge!") + return BackupGiftBadge() + } + + return BackupGiftBadge( + receiptCredentialPresentation = giftBadge.redemptionToken, + state = when (giftBadge.redemptionState) { + GiftBadge.RedemptionState.REDEEMED -> BackupGiftBadge.State.REDEEMED + GiftBadge.RedemptionState.FAILED -> BackupGiftBadge.State.FAILED + GiftBadge.RedemptionState.PENDING -> BackupGiftBadge.State.UNOPENED + GiftBadge.RedemptionState.STARTED -> BackupGiftBadge.State.OPENED + } + ) +} + +private fun DatabaseAttachment.toRemoteStickerMessage(mediaArchiveEnabled: Boolean, reactions: List?): StickerMessage { + val stickerLocator = this.stickerLocator!! + return StickerMessage( + sticker = Sticker( + packId = Hex.fromStringCondensed(stickerLocator.packId).toByteString(), + packKey = Hex.fromStringCondensed(stickerLocator.packKey).toByteString(), + stickerId = stickerLocator.stickerId, + emoji = stickerLocator.emoji, + data_ = this.toRemoteMessageAttachment(mediaArchiveEnabled).pointer + ), + reactions = reactions.toRemote() + ) +} + +private fun List.toRemoteQuoteAttachments(mediaArchiveEnabled: Boolean): List { + return this.map { attachment -> + Quote.QuotedAttachment( + contentType = attachment.contentType, + fileName = attachment.fileName, + thumbnail = attachment.toRemoteMessageAttachment(mediaArchiveEnabled, contentTypeOverride = "image/jpeg").takeUnless { it.pointer?.invalidAttachmentLocator != null } + ) + } +} + +private fun DatabaseAttachment.toRemoteMessageAttachment(mediaArchiveEnabled: Boolean, contentTypeOverride: String? = null): MessageAttachment { + return MessageAttachment( + pointer = this.toRemoteFilePointer(mediaArchiveEnabled, contentTypeOverride), + wasDownloaded = this.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE || this.transferState == AttachmentTable.TRANSFER_NEEDS_RESTORE, + flag = if (this.voiceNote) { + MessageAttachment.Flag.VOICE_MESSAGE + } else if (this.videoGif) { + MessageAttachment.Flag.GIF + } else if (this.borderless) { + MessageAttachment.Flag.BORDERLESS + } else { + MessageAttachment.Flag.NONE + }, + clientUuid = this.uuid?.let { UuidUtil.toByteString(uuid) } + ) +} + +private fun List.toRemoteAttachments(mediaArchiveEnabled: Boolean): List { + return this.map { attachment -> + attachment.toRemoteMessageAttachment(mediaArchiveEnabled) + } +} + +private fun PaymentTable.PaymentTransaction.toRemoteTransactionDetails(): PaymentNotification.TransactionDetails { + if (this.failureReason != null || this.state == State.FAILED) { + return PaymentNotification.TransactionDetails(failedTransaction = PaymentNotification.TransactionDetails.FailedTransaction(reason = this.failureReason.toRemote())) + } + + return PaymentNotification.TransactionDetails( + transaction = PaymentNotification.TransactionDetails.Transaction( + status = this.state.toRemote(), + timestamp = this.timestamp, + blockIndex = this.blockIndex, + blockTimestamp = this.blockTimestamp, + mobileCoinIdentification = this.paymentMetaData.mobileCoinTxoIdentification?.toRemote(), + transaction = this.transaction?.toByteString(), + receipt = this.receipt?.toByteString() + ) + ) +} + +private fun PaymentMetaData.MobileCoinTxoIdentification.toRemote(): PaymentNotification.TransactionDetails.MobileCoinTxoIdentification { + return PaymentNotification.TransactionDetails.MobileCoinTxoIdentification( + publicKey = this.publicKey, + keyImages = this.keyImages + ) +} + +private fun State.toRemote(): PaymentNotification.TransactionDetails.Transaction.Status { + return when (this) { + State.INITIAL -> PaymentNotification.TransactionDetails.Transaction.Status.INITIAL + State.SUBMITTED -> PaymentNotification.TransactionDetails.Transaction.Status.SUBMITTED + State.SUCCESSFUL -> PaymentNotification.TransactionDetails.Transaction.Status.SUCCESSFUL + State.FAILED -> throw IllegalArgumentException("state cannot be failed") + } +} + +private fun FailureReason?.toRemote(): PaymentNotification.TransactionDetails.FailedTransaction.FailureReason { + return when (this) { + FailureReason.UNKNOWN -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.GENERIC + FailureReason.INSUFFICIENT_FUNDS -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.INSUFFICIENT_FUNDS + FailureReason.NETWORK -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.NETWORK + else -> PaymentNotification.TransactionDetails.FailedTransaction.FailureReason.GENERIC + } +} + +private fun List.toRemoteBodyRanges(db: SignalDatabase): List { + return this.map { + BackupBodyRange( + start = it.start, + length = it.length, + mentionAci = db.recipientTable.getRecord(it.recipientId).aci?.toByteString() + ) + } +} + +private fun ByteArray.toRemoteBodyRanges(): List { + val decoded: BodyRangeList = try { + BodyRangeList.ADAPTER.decode(this) + } catch (e: IOException) { + Log.w(TAG, "Failed to decode BodyRangeList!") + return emptyList() + } + + return decoded.ranges.map { + val mention = it.mentionUuid?.let { uuid -> UuidUtil.parseOrThrow(uuid) }?.toByteArray()?.toByteString() + val style = if (mention == null) { + it.style?.toRemote() ?: BackupBodyRange.Style.NONE + } else { + null + } + + BackupBodyRange( + start = it.start, + length = it.length, + mentionAci = mention, + style = style + ) + } +} + +private fun BodyRangeList.BodyRange.Style.toRemote(): BackupBodyRange.Style { + return when (this) { + BodyRangeList.BodyRange.Style.BOLD -> BackupBodyRange.Style.BOLD + BodyRangeList.BodyRange.Style.ITALIC -> BackupBodyRange.Style.ITALIC + BodyRangeList.BodyRange.Style.STRIKETHROUGH -> BackupBodyRange.Style.STRIKETHROUGH + BodyRangeList.BodyRange.Style.MONOSPACE -> BackupBodyRange.Style.MONOSPACE + BodyRangeList.BodyRange.Style.SPOILER -> BackupBodyRange.Style.SPOILER + } +} + +private fun List?.toRemote(): List { + return this + ?.map { + Reaction( + emoji = it.emoji, + authorId = it.author.toLong(), + sentTimestamp = it.dateSent, + sortOrder = it.dateReceived + ) + } ?: emptyList() +} + +private fun BackupMessageRecord.toRemoteSendStatus(groupReceipts: List?): List { + if (!MessageTypes.isOutgoingMessageType(this.type)) { + return emptyList() + } + + if (!groupReceipts.isNullOrEmpty()) { + return groupReceipts.toRemoteSendStatus(this, this.networkFailureRecipientIds, this.identityMismatchRecipientIds) + } + + val statusBuilder = SendStatus.Builder() + .recipientId(this.toRecipientId) + .timestamp(this.receiptTimestamp) + + when { + this.identityMismatchRecipientIds.contains(this.toRecipientId) -> { + statusBuilder.failed = SendStatus.Failed( + reason = SendStatus.Failed.FailureReason.IDENTITY_KEY_MISMATCH + ) + } + this.networkFailureRecipientIds.contains(this.toRecipientId) -> { + statusBuilder.failed = SendStatus.Failed( + reason = SendStatus.Failed.FailureReason.NETWORK + ) + } + this.viewed -> { + statusBuilder.viewed = SendStatus.Viewed( + sealedSender = this.sealedSender + ) + } + this.hasReadReceipt -> { + statusBuilder.read = SendStatus.Read( + sealedSender = this.sealedSender + ) + } + this.hasDeliveryReceipt -> { + statusBuilder.delivered = SendStatus.Delivered( + sealedSender = this.sealedSender + ) + } + this.baseType == MessageTypes.BASE_SENT_FAILED_TYPE -> { + statusBuilder.failed = SendStatus.Failed( + reason = SendStatus.Failed.FailureReason.UNKNOWN + ) + } + this.baseType == MessageTypes.BASE_SENDING_SKIPPED_TYPE -> { + statusBuilder.skipped = SendStatus.Skipped() + } + this.baseType == MessageTypes.BASE_SENT_TYPE -> { + statusBuilder.sent = SendStatus.Sent( + sealedSender = this.sealedSender + ) + } + else -> { + statusBuilder.pending = SendStatus.Pending() + } + } + + return listOf(statusBuilder.build()) +} + +private fun List.toRemoteSendStatus(messageRecord: BackupMessageRecord, networkFailureRecipientIds: Set, identityMismatchRecipientIds: Set): List { + return this.map { + val statusBuilder = SendStatus.Builder() + .recipientId(it.recipientId.toLong()) + .timestamp(it.timestamp) + + when { + identityMismatchRecipientIds.contains(it.recipientId.toLong()) -> { + statusBuilder.failed = SendStatus.Failed( + reason = SendStatus.Failed.FailureReason.IDENTITY_KEY_MISMATCH + ) + } + networkFailureRecipientIds.contains(it.recipientId.toLong()) -> { + statusBuilder.failed = SendStatus.Failed( + reason = SendStatus.Failed.FailureReason.NETWORK + ) + } + messageRecord.baseType == MessageTypes.BASE_SENT_FAILED_TYPE -> { + statusBuilder.failed = SendStatus.Failed( + reason = SendStatus.Failed.FailureReason.UNKNOWN + ) + } + it.status == GroupReceiptTable.STATUS_UNKNOWN -> { + statusBuilder.pending = SendStatus.Pending() + } + it.status == GroupReceiptTable.STATUS_UNDELIVERED -> { + statusBuilder.sent = SendStatus.Sent( + sealedSender = it.isUnidentified + ) + } + it.status == GroupReceiptTable.STATUS_DELIVERED -> { + statusBuilder.delivered = SendStatus.Delivered( + sealedSender = it.isUnidentified + ) + } + it.status == GroupReceiptTable.STATUS_READ -> { + statusBuilder.read = SendStatus.Read( + sealedSender = it.isUnidentified + ) + } + it.status == GroupReceiptTable.STATUS_VIEWED -> { + statusBuilder.viewed = SendStatus.Viewed( + sealedSender = it.isUnidentified + ) + } + it.status == GroupReceiptTable.STATUS_SKIPPED -> { + statusBuilder.skipped = SendStatus.Skipped() + } + else -> { + statusBuilder.pending = SendStatus.Pending() + } + } + + statusBuilder.build() + } +} + +private fun String?.parseNetworkFailures(): Set { + if (this.isNullOrBlank()) { + return emptySet() + } + + return try { + JsonUtils.fromJson(this, NetworkFailureSet::class.java).items.map { it.recipientId.toLong() }.toSet() + } catch (e: IOException) { + emptySet() + } +} + +private fun String?.parseIdentityMismatches(): Set { + if (this.isNullOrBlank()) { + return emptySet() + } + + return try { + JsonUtils.fromJson(this, IdentityKeyMismatchSet::class.java).items.map { it.recipientId.toLong() }.toSet() + } catch (e: IOException) { + emptySet() + } +} + +private fun ByteArray?.parseMessageExtras(): MessageExtras? { + if (this == null) { + return null + } + return try { + MessageExtras.ADAPTER.decode(this) + } catch (e: java.lang.Exception) { + null + } +} + +private fun Long.isSmsType(): Boolean { + if (MessageTypes.isSecureType(this)) { + return false + } + + if (MessageTypes.isCallLog(this)) { + return false + } + + return MessageTypes.isOutgoingMessageType(this) || MessageTypes.isInboxType(this) +} + +private fun Long.isDirectionlessType(): Boolean { + return MessageTypes.isCallLog(this) || + MessageTypes.isExpirationTimerUpdate(this) || + MessageTypes.isThreadMergeType(this) || + MessageTypes.isSessionSwitchoverType(this) || + MessageTypes.isProfileChange(this) || + MessageTypes.isJoinedType(this) || + MessageTypes.isIdentityUpdate(this) || + MessageTypes.isIdentityVerified(this) || + MessageTypes.isIdentityDefault(this) || + MessageTypes.isReleaseChannelDonationRequest(this) || + MessageTypes.isChangeNumber(this) || + MessageTypes.isEndSessionType(this) || + MessageTypes.isChatSessionRefresh(this) || + MessageTypes.isBadDecryptType(this) || + MessageTypes.isPaymentsActivated(this) || + MessageTypes.isPaymentsRequestToActivate(this) || + MessageTypes.isUnsupportedMessageType(this) || + MessageTypes.isReportedSpam(this) || + MessageTypes.isMessageRequestAccepted(this) || + MessageTypes.isBlocked(this) || + MessageTypes.isUnblocked(this) || + MessageTypes.isGroupCall(this) +} + +private fun String.e164ToLong(): Long? { + val fixed = if (this.startsWith("+")) { + this.substring(1) + } else { + this + } + + return fixed.toLongOrNull() +} + +private fun Cursor.toBackupMessageRecord(): BackupMessageRecord { + return BackupMessageRecord( + id = this.requireLong(MessageTable.ID), + dateSent = this.requireLong(MessageTable.DATE_SENT), + dateReceived = this.requireLong(MessageTable.DATE_RECEIVED), + dateServer = this.requireLong(MessageTable.DATE_SERVER), + type = this.requireLong(MessageTable.TYPE), + threadId = this.requireLong(MessageTable.THREAD_ID), + body = this.requireString(MessageTable.BODY), + bodyRanges = this.requireBlob(MessageTable.MESSAGE_RANGES), + fromRecipientId = this.requireLong(MessageTable.FROM_RECIPIENT_ID), + toRecipientId = this.requireLong(MessageTable.TO_RECIPIENT_ID), + expiresIn = this.requireLong(MessageTable.EXPIRES_IN), + expireStarted = this.requireLong(MessageTable.EXPIRE_STARTED), + remoteDeleted = this.requireBoolean(MessageTable.REMOTE_DELETED), + sealedSender = this.requireBoolean(MessageTable.UNIDENTIFIED), + linkPreview = this.requireString(MessageTable.LINK_PREVIEWS), + sharedContacts = this.requireString(MessageTable.SHARED_CONTACTS), + quoteTargetSentTimestamp = this.requireLong(MessageTable.QUOTE_ID), + quoteAuthor = this.requireLong(MessageTable.QUOTE_AUTHOR), + quoteBody = this.requireString(MessageTable.QUOTE_BODY), + quoteMissing = this.requireBoolean(MessageTable.QUOTE_MISSING), + quoteBodyRanges = this.requireBlob(MessageTable.QUOTE_BODY_RANGES), + quoteType = this.requireInt(MessageTable.QUOTE_TYPE), + originalMessageId = this.requireLongOrNull(MessageTable.ORIGINAL_MESSAGE_ID), + latestRevisionId = this.requireLongOrNull(MessageTable.LATEST_REVISION_ID), + hasDeliveryReceipt = this.requireBoolean(MessageTable.HAS_DELIVERY_RECEIPT), + viewed = this.requireBoolean(MessageTable.VIEWED_COLUMN), + hasReadReceipt = this.requireBoolean(MessageTable.HAS_READ_RECEIPT), + read = this.requireBoolean(MessageTable.READ), + receiptTimestamp = this.requireLong(MessageTable.RECEIPT_TIMESTAMP), + networkFailureRecipientIds = this.requireString(MessageTable.NETWORK_FAILURES).parseNetworkFailures(), + identityMismatchRecipientIds = this.requireString(MessageTable.MISMATCHED_IDENTITIES).parseIdentityMismatches(), + baseType = this.requireLong(COLUMN_BASE_TYPE), + messageExtras = this.requireBlob(MessageTable.MESSAGE_EXTRAS).parseMessageExtras() + ) +} + +private class BackupMessageRecord( + val id: Long, + val dateSent: Long, + val dateReceived: Long, + val dateServer: Long, + val type: Long, + val threadId: Long, + val body: String?, + val bodyRanges: ByteArray?, + val fromRecipientId: Long, + val toRecipientId: Long, + val expiresIn: Long, + val expireStarted: Long, + val remoteDeleted: Boolean, + val sealedSender: Boolean, + val linkPreview: String?, + val sharedContacts: String?, + val quoteTargetSentTimestamp: Long, + val quoteAuthor: Long, + val quoteBody: String?, + val quoteMissing: Boolean, + val quoteBodyRanges: ByteArray?, + val quoteType: Int, + val originalMessageId: Long?, + val latestRevisionId: Long?, + val hasDeliveryReceipt: Boolean, + val hasReadReceipt: Boolean, + val viewed: Boolean, + val receiptTimestamp: Long, + val read: Boolean, + val networkFailureRecipientIds: Set, + val identityMismatchRecipientIds: Set, + val baseType: Long, + val messageExtras: MessageExtras? +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExportIterator.kt new file mode 100644 index 0000000000..051bb19e88 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExportIterator.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.exporters + +import android.database.Cursor +import okio.ByteString.Companion.toByteString +import org.signal.core.util.Base64 +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireString +import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient +import org.thoughtcrime.securesms.backup.v2.proto.Contact +import org.thoughtcrime.securesms.backup.v2.proto.Self +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.RecipientTableCursorUtil +import org.thoughtcrime.securesms.recipients.Recipient +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.api.util.toByteArray +import java.io.Closeable + +/** + * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [ArchiveRecipient]s. + * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. + */ +class ContactArchiveExportIterator(private val cursor: Cursor, private val selfId: Long) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): ArchiveRecipient { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val id = cursor.requireLong(RecipientTable.ID) + if (id == selfId) { + return ArchiveRecipient( + id = id, + self = Self() + ) + } + + val aci = ServiceId.ACI.Companion.parseOrNull(cursor.requireString(RecipientTable.ACI_COLUMN)) + val pni = ServiceId.PNI.Companion.parseOrNull(cursor.requireString(RecipientTable.PNI_COLUMN)) + val e164 = cursor.requireString(RecipientTable.E164)?.e164ToLong() + + if (aci == null && pni == null && e164 == null) { + throw IllegalStateException("Should not happen! Query guards against this.") + } + + val contactBuilder = Contact.Builder() + .aci(aci?.rawUuid?.toByteArray()?.toByteString()) + .pni(pni?.rawUuid?.toByteArray()?.toByteString()) + .username(cursor.requireString(RecipientTable.USERNAME)) + .e164(cursor.requireString(RecipientTable.E164)?.e164ToLong()) + .blocked(cursor.requireBoolean(RecipientTable.BLOCKED)) + .visibility(Recipient.HiddenState.deserialize(cursor.requireInt(RecipientTable.HIDDEN)).toRemote()) + .profileKey(cursor.requireString(RecipientTable.PROFILE_KEY)?.let { Base64.decode(it) }?.toByteString()) + .profileSharing(cursor.requireBoolean(RecipientTable.PROFILE_SHARING)) + .profileGivenName(cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME)) + .profileFamilyName(cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME)) + .hideStory(RecipientTableCursorUtil.getExtras(cursor)?.hideStory() ?: false) + + val registeredState = RecipientTable.RegisteredState.fromId(cursor.requireInt(RecipientTable.REGISTERED)) + if (registeredState == RecipientTable.RegisteredState.REGISTERED) { + contactBuilder.registered = Contact.Registered() + } else { + contactBuilder.notRegistered = Contact.NotRegistered(unregisteredTimestamp = cursor.requireLong(RecipientTable.UNREGISTERED_TIMESTAMP)) + } + + return ArchiveRecipient( + id = id, + contact = contactBuilder.build() + ) + } + + override fun close() { + cursor.close() + } +} + +private fun Recipient.HiddenState.toRemote(): Contact.Visibility { + return when (this) { + Recipient.HiddenState.NOT_HIDDEN -> return Contact.Visibility.VISIBLE + Recipient.HiddenState.HIDDEN -> return Contact.Visibility.HIDDEN + Recipient.HiddenState.HIDDEN_MESSAGE_REQUEST -> return Contact.Visibility.HIDDEN_MESSAGE_REQUEST + } +} + +private fun String.e164ToLong(): Long? { + val fixed = if (this.startsWith("+")) { + this.substring(1) + } else { + this + } + + return fixed.toLongOrNull() +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExportIterator.kt new file mode 100644 index 0000000000..c33308ccad --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExportIterator.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.exporters + +import android.database.Cursor +import okio.ByteString.Companion.toByteString +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireLong +import org.signal.core.util.requireNonNullString +import org.signal.core.util.requireObject +import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient +import org.thoughtcrime.securesms.backup.v2.database.getMembersForBackup +import org.thoughtcrime.securesms.backup.v2.proto.DistributionList +import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem +import org.thoughtcrime.securesms.database.DistributionListTables +import org.thoughtcrime.securesms.database.model.DistributionListId +import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode +import org.thoughtcrime.securesms.database.model.DistributionListRecord +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.signalservice.api.push.DistributionId +import org.whispersystems.signalservice.api.util.toByteArray +import java.io.Closeable + +class DistributionListArchiveExportIterator( + private val cursor: Cursor, + private val distributionListTables: DistributionListTables +) : Iterator, Closeable { + + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): ArchiveRecipient { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val id: DistributionListId = DistributionListId.from(cursor.requireLong(DistributionListTables.ListTable.ID)) + val privacyMode: DistributionListPrivacyMode = cursor.requireObject(DistributionListTables.ListTable.PRIVACY_MODE, DistributionListPrivacyMode.Serializer) + val recipientId: RecipientId = RecipientId.from(cursor.requireLong(DistributionListTables.ListTable.RECIPIENT_ID)) + + val record = DistributionListRecord( + id = id, + name = cursor.requireNonNullString(DistributionListTables.ListTable.NAME), + distributionId = DistributionId.from(cursor.requireNonNullString(DistributionListTables.ListTable.DISTRIBUTION_ID)), + allowsReplies = cursor.requireBoolean(DistributionListTables.ListTable.ALLOWS_REPLIES), + rawMembers = distributionListTables.getRawMembers(id, privacyMode), + members = distributionListTables.getMembersForBackup(id), + deletedAtTimestamp = cursor.requireLong(DistributionListTables.ListTable.DELETION_TIMESTAMP), + isUnknown = cursor.requireBoolean(DistributionListTables.ListTable.IS_UNKNOWN), + privacyMode = privacyMode + ) + + val distributionListItem = if (record.deletedAtTimestamp != 0L) { + DistributionListItem( + distributionId = record.distributionId.asUuid().toByteArray().toByteString(), + deletionTimestamp = record.deletedAtTimestamp + ) + } else { + DistributionListItem( + distributionId = record.distributionId.asUuid().toByteArray().toByteString(), + distributionList = DistributionList( + name = record.name, + allowReplies = record.allowsReplies, + privacyMode = record.privacyMode.toBackupPrivacyMode(), + memberRecipientIds = record.members.map { it.toLong() } + ) + ) + } + + return ArchiveRecipient( + id = recipientId.toLong(), + distributionList = distributionListItem + ) + } + + override fun close() { + cursor.close() + } +} + +private fun DistributionListPrivacyMode.toBackupPrivacyMode(): DistributionList.PrivacyMode { + return when (this) { + DistributionListPrivacyMode.ONLY_WITH -> DistributionList.PrivacyMode.ONLY_WITH + DistributionListPrivacyMode.ALL -> DistributionList.PrivacyMode.ALL + DistributionListPrivacyMode.ALL_EXCEPT -> DistributionList.PrivacyMode.ALL_EXCEPT + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExportIterator.kt new file mode 100644 index 0000000000..30d930c9f2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExportIterator.kt @@ -0,0 +1,147 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.exporters + +import android.database.Cursor +import okio.ByteString.Companion.toByteString +import org.signal.core.util.requireBlob +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireNonNullBlob +import org.signal.storageservice.protos.groups.AccessControl +import org.signal.storageservice.protos.groups.Member +import org.signal.storageservice.protos.groups.local.DecryptedBannedMember +import org.signal.storageservice.protos.groups.local.DecryptedGroup +import org.signal.storageservice.protos.groups.local.DecryptedMember +import org.signal.storageservice.protos.groups.local.DecryptedPendingMember +import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember +import org.signal.storageservice.protos.groups.local.EnabledState +import org.thoughtcrime.securesms.backup.v2.ArchiveGroup +import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient +import org.thoughtcrime.securesms.backup.v2.proto.Group +import org.thoughtcrime.securesms.database.GroupTable +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.RecipientTableCursorUtil +import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor +import java.io.Closeable + +/** + * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [ArchiveRecipient]s. + * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. + */ +class GroupArchiveExportIterator(private val cursor: Cursor) : Iterator, Closeable { + + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): ArchiveRecipient { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val extras = RecipientTableCursorUtil.getExtras(cursor) + val showAsStoryState: GroupTable.ShowAsStoryState = GroupTable.ShowAsStoryState.deserialize(cursor.requireInt(GroupTable.SHOW_AS_STORY_STATE)) + + val decryptedGroup: DecryptedGroup = DecryptedGroup.ADAPTER.decode(cursor.requireBlob(GroupTable.V2_DECRYPTED_GROUP)!!) + + return ArchiveRecipient( + id = cursor.requireLong(RecipientTable.ID), + group = ArchiveGroup( + masterKey = cursor.requireNonNullBlob(GroupTable.V2_MASTER_KEY).toByteString(), + whitelisted = cursor.requireBoolean(RecipientTable.PROFILE_SHARING), + hideStory = extras?.hideStory() ?: false, + storySendMode = showAsStoryState.toRemote(), + snapshot = decryptedGroup.toRemote() + ) + ) + } + + override fun close() { + cursor.close() + } +} + +private fun GroupTable.ShowAsStoryState.toRemote(): Group.StorySendMode { + return when (this) { + GroupTable.ShowAsStoryState.ALWAYS -> Group.StorySendMode.ENABLED + GroupTable.ShowAsStoryState.NEVER -> Group.StorySendMode.DISABLED + GroupTable.ShowAsStoryState.IF_ACTIVE -> Group.StorySendMode.DEFAULT + } +} + +private fun DecryptedGroup.toRemote(): Group.GroupSnapshot? { + if (this.revision == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION || this.revision == GroupsV2StateProcessor.PLACEHOLDER_REVISION) { + return null + } + + return Group.GroupSnapshot( + title = Group.GroupAttributeBlob(title = this.title), + avatarUrl = this.avatar, + disappearingMessagesTimer = this.disappearingMessagesTimer?.takeIf { it.duration > 0 }?.let { Group.GroupAttributeBlob(disappearingMessagesDuration = it.duration) }, + accessControl = this.accessControl?.toRemote(), + version = this.revision, + members = this.members.map { it.toRemote() }, + membersPendingProfileKey = this.pendingMembers.map { it.toRemote() }, + membersPendingAdminApproval = this.requestingMembers.map { it.toRemote() }, + inviteLinkPassword = this.inviteLinkPassword, + description = this.description.takeUnless { it.isBlank() }?.let { Group.GroupAttributeBlob(descriptionText = it) }, + announcements_only = this.isAnnouncementGroup == EnabledState.ENABLED, + members_banned = this.bannedMembers.map { it.toRemote() } + ) +} + +private fun AccessControl.AccessRequired.toRemote(): Group.AccessControl.AccessRequired { + return when (this) { + AccessControl.AccessRequired.UNKNOWN -> Group.AccessControl.AccessRequired.UNKNOWN + AccessControl.AccessRequired.ANY -> Group.AccessControl.AccessRequired.ANY + AccessControl.AccessRequired.MEMBER -> Group.AccessControl.AccessRequired.MEMBER + AccessControl.AccessRequired.ADMINISTRATOR -> Group.AccessControl.AccessRequired.ADMINISTRATOR + AccessControl.AccessRequired.UNSATISFIABLE -> Group.AccessControl.AccessRequired.UNSATISFIABLE + } +} + +private fun AccessControl.toRemote(): Group.AccessControl { + return Group.AccessControl(members = members.toRemote(), attributes = attributes.toRemote(), addFromInviteLink = addFromInviteLink.toRemote()) +} + +private fun Member.Role.toRemote(): Group.Member.Role { + return when (this) { + Member.Role.UNKNOWN -> Group.Member.Role.UNKNOWN + Member.Role.DEFAULT -> Group.Member.Role.DEFAULT + Member.Role.ADMINISTRATOR -> Group.Member.Role.ADMINISTRATOR + } +} + +private fun DecryptedMember.toRemote(): Group.Member { + return Group.Member(userId = aciBytes, role = role.toRemote(), joinedAtVersion = joinedAtRevision) +} + +private fun DecryptedPendingMember.toRemote(): Group.MemberPendingProfileKey { + return Group.MemberPendingProfileKey( + member = Group.Member( + userId = this.serviceIdBytes, + role = this.role.toRemote() + ), + addedByUserId = this.addedByAci, + timestamp = this.timestamp + ) +} + +private fun DecryptedBannedMember.toRemote(): Group.MemberBanned { + return Group.MemberBanned( + userId = this.serviceIdBytes, + timestamp = this.timestamp + ) +} + +private fun DecryptedRequestingMember.toRemote(): Group.MemberPendingAdminApproval { + return Group.MemberPendingAdminApproval( + userId = this.aciBytes, + timestamp = this.timestamp + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataBackupProcessor.kt similarity index 65% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataBackupProcessor.kt index 27c8ebb8ab..7f38528e1c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataBackupProcessor.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.backup.v2.processor +import android.content.Context import okio.ByteString.Companion.EMPTY import okio.ByteString.Companion.toByteString import org.signal.core.util.logging.Log @@ -40,9 +41,12 @@ import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.api.util.toByteArray import java.util.Currency -object AccountDataProcessor { +/** + * Handles importing/exporting [AccountData] frames for an archive. + */ +object AccountDataBackupProcessor { - private val TAG = Log.tag(AccountDataProcessor::class) + private val TAG = Log.tag(AccountDataBackupProcessor::class) fun export(db: SignalDatabase, signalStore: SignalStore, emitter: BackupFrameEmitter) { val context = AppDependencies.application @@ -68,7 +72,7 @@ object AccountDataProcessor { AccountData.UsernameLink( entropy = signalStore.accountValues.usernameLink?.entropy?.toByteString() ?: EMPTY, serverId = signalStore.accountValues.usernameLink?.serverId?.toByteArray()?.toByteString() ?: EMPTY, - color = signalStore.miscValues.usernameQrCodeColorScheme.toBackupUsernameColor() + color = signalStore.miscValues.usernameQrCodeColorScheme.toRemoteUsernameColor() ) } else { null @@ -80,7 +84,7 @@ object AccountDataProcessor { sealedSenderIndicators = TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context), linkPreviews = signalStore.settingsValues.isLinkPreviewsEnabled, notDiscoverableByPhoneNumber = signalStore.phoneNumberPrivacyValues.phoneNumberDiscoverabilityMode == PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE, - phoneNumberSharingMode = signalStore.phoneNumberPrivacyValues.phoneNumberSharingMode.toBackupPhoneNumberSharingMode(), + phoneNumberSharingMode = signalStore.phoneNumberPrivacyValues.phoneNumberSharingMode.toRemotePhoneNumberSharingMode(), preferContactAvatars = signalStore.settingsValues.isPreferSystemContactPhotos, universalExpireTimerSeconds = signalStore.settingsValues.universalExpireTimer, preferredReactionEmoji = signalStore.emojiValues.rawReactions, @@ -114,115 +118,119 @@ object AccountDataProcessor { val settings = accountData.accountSettings if (settings != null) { - TextSecurePreferences.setReadReceiptsEnabled(context, settings.readReceipts) - TextSecurePreferences.setTypingIndicatorsEnabled(context, settings.typingIndicators) - TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, settings.sealedSenderIndicators) - SignalStore.settings.isLinkPreviewsEnabled = settings.linkPreviews - SignalStore.phoneNumberPrivacy.phoneNumberDiscoverabilityMode = if (settings.notDiscoverableByPhoneNumber) PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE else PhoneNumberDiscoverabilityMode.DISCOVERABLE - SignalStore.phoneNumberPrivacy.phoneNumberSharingMode = settings.phoneNumberSharingMode.toLocalPhoneNumberMode() - SignalStore.settings.isPreferSystemContactPhotos = settings.preferContactAvatars - SignalStore.settings.universalExpireTimer = settings.universalExpireTimerSeconds - SignalStore.emoji.reactions = settings.preferredReactionEmoji - SignalStore.inAppPayments.setDisplayBadgesOnProfile(settings.displayBadgesOnProfile) - SignalStore.settings.setKeepMutedChatsArchived(settings.keepMutedChatsArchived) - SignalStore.story.userHasBeenNotifiedAboutStories = settings.hasSetMyStoriesPrivacy - SignalStore.story.userHasViewedOnboardingStory = settings.hasViewedOnboardingStory - SignalStore.story.isFeatureDisabled = settings.storiesDisabled - SignalStore.story.userHasSeenGroupStoryEducationSheet = settings.hasSeenGroupStoryEducationSheet - SignalStore.story.viewedReceiptsEnabled = settings.storyViewReceiptsEnabled ?: settings.readReceipts - - settings.customChatColors - .mapNotNull { chatColor -> - val id = ChatColors.Id.forLongValue(chatColor.id) - when { - chatColor.solid != null -> { - ChatColors.forColor(id, chatColor.solid) - } - chatColor.gradient != null -> { - ChatColors.forGradient( - id, - ChatColors.LinearGradient( - degrees = chatColor.gradient.angle.toFloat(), - colors = chatColor.gradient.colors.toIntArray(), - positions = chatColor.gradient.positions.toFloatArray() - ) - ) - } - else -> null - } - } - .forEach { chatColor -> - // We need to use the "NotSet" chatId so that this operation is treated as an insert rather than an update - val saved = SignalDatabase.chatColors.saveChatColors(chatColor.withId(ChatColors.Id.NotSet)) - importState.remoteToLocalColorId[chatColor.id.longValue] = saved.id.longValue - } + importSettings(context, settings, importState) + } - if (settings.defaultChatStyle != null) { - val chatColors = settings.defaultChatStyle.toLocal(importState) - SignalStore.chatColors.chatColors = chatColors + if (accountData.donationSubscriberData != null) { + if (accountData.donationSubscriberData.subscriberId.size > 0) { + val remoteSubscriberId = SubscriberId.fromBytes(accountData.donationSubscriberData.subscriberId.toByteArray()) + val localSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION) + + val subscriber = InAppPaymentSubscriberRecord( + remoteSubscriberId, + Currency.getInstance(accountData.donationSubscriberData.currencyCode), + InAppPaymentSubscriberRecord.Type.DONATION, + localSubscriber?.requiresCancel ?: accountData.donationSubscriberData.manuallyCancelled, + InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.DONATION) + ) - val wallpaperAttachmentId: AttachmentId? = settings.defaultChatStyle.wallpaperPhoto?.let { filePointer -> - filePointer.toLocalAttachment(importState)?.let { - SignalDatabase.attachments.restoreWallpaperAttachment(it) - } - } + InAppPaymentsRepository.setSubscriber(subscriber) + } - SignalStore.wallpaper.wallpaper = settings.defaultChatStyle.parseChatWallpaper(wallpaperAttachmentId) - } else { - SignalStore.chatColors.chatColors = null - SignalStore.wallpaper.wallpaper = null + if (accountData.donationSubscriberData.manuallyCancelled) { + SignalStore.inAppPayments.updateLocalStateForManualCancellation(InAppPaymentSubscriberRecord.Type.DONATION) } + } - if (accountData.donationSubscriberData != null) { - if (accountData.donationSubscriberData.subscriberId.size > 0) { - val remoteSubscriberId = SubscriberId.fromBytes(accountData.donationSubscriberData.subscriberId.toByteArray()) - val localSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION) - - val subscriber = InAppPaymentSubscriberRecord( - remoteSubscriberId, - Currency.getInstance(accountData.donationSubscriberData.currencyCode), - InAppPaymentSubscriberRecord.Type.DONATION, - localSubscriber?.requiresCancel ?: accountData.donationSubscriberData.manuallyCancelled, - InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.DONATION) - ) + if (accountData.avatarUrlPath.isNotEmpty()) { + AppDependencies.jobManager.add(RetrieveProfileAvatarJob(Recipient.self().fresh(), accountData.avatarUrlPath)) + } - InAppPaymentsRepository.setSubscriber(subscriber) - } + if (accountData.usernameLink != null) { + SignalStore.account.usernameLink = UsernameLinkComponents( + accountData.usernameLink.entropy.toByteArray(), + UuidUtil.parseOrThrow(accountData.usernameLink.serverId.toByteArray()) + ) + SignalStore.misc.usernameQrCodeColorScheme = accountData.usernameLink.color.toLocalUsernameColor() + } else { + SignalStore.account.usernameLink = null + } + + SignalDatabase.runPostSuccessfulTransaction { ProfileUtil.handleSelfProfileKeyChange() } - if (accountData.donationSubscriberData.manuallyCancelled) { - SignalStore.inAppPayments.updateLocalStateForManualCancellation(InAppPaymentSubscriberRecord.Type.DONATION) + Recipient.self().live().refresh() + } + + private fun importSettings(context: Context, settings: AccountData.AccountSettings, importState: ImportState) { + TextSecurePreferences.setReadReceiptsEnabled(context, settings.readReceipts) + TextSecurePreferences.setTypingIndicatorsEnabled(context, settings.typingIndicators) + TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, settings.sealedSenderIndicators) + SignalStore.settings.isLinkPreviewsEnabled = settings.linkPreviews + SignalStore.phoneNumberPrivacy.phoneNumberDiscoverabilityMode = if (settings.notDiscoverableByPhoneNumber) PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE else PhoneNumberDiscoverabilityMode.DISCOVERABLE + SignalStore.phoneNumberPrivacy.phoneNumberSharingMode = settings.phoneNumberSharingMode.toLocalPhoneNumberMode() + SignalStore.settings.isPreferSystemContactPhotos = settings.preferContactAvatars + SignalStore.settings.universalExpireTimer = settings.universalExpireTimerSeconds + SignalStore.emoji.reactions = settings.preferredReactionEmoji + SignalStore.inAppPayments.setDisplayBadgesOnProfile(settings.displayBadgesOnProfile) + SignalStore.settings.setKeepMutedChatsArchived(settings.keepMutedChatsArchived) + SignalStore.story.userHasBeenNotifiedAboutStories = settings.hasSetMyStoriesPrivacy + SignalStore.story.userHasViewedOnboardingStory = settings.hasViewedOnboardingStory + SignalStore.story.isFeatureDisabled = settings.storiesDisabled + SignalStore.story.userHasSeenGroupStoryEducationSheet = settings.hasSeenGroupStoryEducationSheet + SignalStore.story.viewedReceiptsEnabled = settings.storyViewReceiptsEnabled ?: settings.readReceipts + + settings.customChatColors + .mapNotNull { chatColor -> + val id = ChatColors.Id.forLongValue(chatColor.id) + when { + chatColor.solid != null -> { + ChatColors.forColor(id, chatColor.solid) + } + chatColor.gradient != null -> { + ChatColors.forGradient( + id, + ChatColors.LinearGradient( + degrees = chatColor.gradient.angle.toFloat(), + colors = chatColor.gradient.colors.toIntArray(), + positions = chatColor.gradient.positions.toFloatArray() + ) + ) + } + else -> null } } - - if (accountData.avatarUrlPath.isNotEmpty()) { - AppDependencies.jobManager.add(RetrieveProfileAvatarJob(Recipient.self().fresh(), accountData.avatarUrlPath)) + .forEach { chatColor -> + // We need to use the "NotSet" chatId so that this operation is treated as an insert rather than an update + val saved = SignalDatabase.chatColors.saveChatColors(chatColor.withId(ChatColors.Id.NotSet)) + importState.remoteToLocalColorId[chatColor.id.longValue] = saved.id.longValue } - if (accountData.usernameLink != null) { - SignalStore.account.usernameLink = UsernameLinkComponents( - accountData.usernameLink.entropy.toByteArray(), - UuidUtil.parseOrThrow(accountData.usernameLink.serverId.toByteArray()) - ) - SignalStore.misc.usernameQrCodeColorScheme = accountData.usernameLink.color.toLocalUsernameColor() - } else { - SignalStore.account.usernameLink = null - } + if (settings.defaultChatStyle != null) { + val chatColors = settings.defaultChatStyle.toLocal(importState) + SignalStore.chatColors.chatColors = chatColors - if (settings.preferredReactionEmoji.isNotEmpty()) { - SignalStore.emoji.reactions = settings.preferredReactionEmoji + val wallpaperAttachmentId: AttachmentId? = settings.defaultChatStyle.wallpaperPhoto?.let { filePointer -> + filePointer.toLocalAttachment(importState)?.let { + SignalDatabase.attachments.restoreWallpaperAttachment(it) + } } - if (settings.hasCompletedUsernameOnboarding) { - SignalStore.uiHints.setHasCompletedUsernameOnboarding(true) - } + SignalStore.wallpaper.wallpaper = settings.defaultChatStyle.parseChatWallpaper(wallpaperAttachmentId) + } else { + SignalStore.chatColors.chatColors = null + SignalStore.wallpaper.wallpaper = null } - SignalDatabase.runPostSuccessfulTransaction { ProfileUtil.handleSelfProfileKeyChange() } + if (settings.preferredReactionEmoji.isNotEmpty()) { + SignalStore.emoji.reactions = settings.preferredReactionEmoji + } - Recipient.self().live().refresh() + if (settings.hasCompletedUsernameOnboarding) { + SignalStore.uiHints.setHasCompletedUsernameOnboarding(true) + } } - private fun PhoneNumberPrivacyValues.PhoneNumberSharingMode.toBackupPhoneNumberSharingMode(): AccountData.PhoneNumberSharingMode { + private fun PhoneNumberPrivacyValues.PhoneNumberSharingMode.toRemotePhoneNumberSharingMode(): AccountData.PhoneNumberSharingMode { return when (this) { PhoneNumberPrivacyValues.PhoneNumberSharingMode.DEFAULT -> AccountData.PhoneNumberSharingMode.EVERYBODY PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYBODY -> AccountData.PhoneNumberSharingMode.EVERYBODY @@ -252,7 +260,7 @@ object AccountDataProcessor { } } - private fun UsernameQrCodeColorScheme.toBackupUsernameColor(): AccountData.UsernameLink.Color { + private fun UsernameQrCodeColorScheme.toRemoteUsernameColor(): AccountData.UsernameLink.Color { return when (this) { UsernameQrCodeColorScheme.Blue -> AccountData.UsernameLink.Color.BLUE UsernameQrCodeColorScheme.White -> AccountData.UsernameLink.Color.WHITE diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallBackupProcessor.kt index 4748362407..d5f341a7c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallBackupProcessor.kt @@ -14,6 +14,9 @@ import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter import org.thoughtcrime.securesms.database.SignalDatabase +/** + * Handles importing/exporting [AdHocCall] frames for an archive. + */ object AdHocCallBackupProcessor { val TAG = Log.tag(AdHocCallBackupProcessor::class.java) @@ -21,9 +24,7 @@ object AdHocCallBackupProcessor { fun export(db: SignalDatabase, emitter: BackupFrameEmitter) { db.callTable.getAdhocCallsForBackup().use { reader -> for (callLog in reader) { - if (callLog != null) { - emitter.emit(Frame(adHocCall = callLog)) - } + emitter.emit(Frame(adHocCall = callLog)) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt index b4969c3ad2..a4d77b5154 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt @@ -16,6 +16,9 @@ import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.recipients.RecipientId +/** + * Handles importing/exporting [Chat] frames for an archive. + */ object ChatBackupProcessor { val TAG = Log.tag(ChatBackupProcessor::class.java) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt index 36664b818a..1bec7dda35 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt @@ -11,17 +11,21 @@ import org.thoughtcrime.securesms.backup.v2.ImportState import org.thoughtcrime.securesms.backup.v2.database.ChatItemImportInserter import org.thoughtcrime.securesms.backup.v2.database.createChatItemInserter import org.thoughtcrime.securesms.backup.v2.database.getMessagesForBackup +import org.thoughtcrime.securesms.backup.v2.proto.ChatItem import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter import org.thoughtcrime.securesms.database.SignalDatabase +/** + * Handles importing/exporting [ChatItem] frames for an archive. + */ object ChatItemBackupProcessor { val TAG = Log.tag(ChatItemBackupProcessor::class.java) fun export(db: SignalDatabase, exportState: ExportState, emitter: BackupFrameEmitter) { - db.messageTable.getMessagesForBackup(exportState.backupTime, exportState.mediaBackupEnabled).use { chatItems -> + db.messageTable.getMessagesForBackup(db, exportState.backupTime, exportState.mediaBackupEnabled).use { chatItems -> while (chatItems.hasNext()) { - val chatItem = chatItems.next() + val chatItem: ChatItem? = chatItems.next() if (chatItem != null) { if (exportState.threadIds.contains(chatItem.chatId)) { emitter.emit(Frame(chatItem = chatItem)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt index e599e5b27d..16f5170077 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt @@ -6,9 +6,9 @@ package org.thoughtcrime.securesms.backup.v2.processor import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient import org.thoughtcrime.securesms.backup.v2.ExportState import org.thoughtcrime.securesms.backup.v2.ImportState -import org.thoughtcrime.securesms.backup.v2.database.BackupRecipient import org.thoughtcrime.securesms.backup.v2.database.getAllForBackup import org.thoughtcrime.securesms.backup.v2.database.getCallLinksForBackup import org.thoughtcrime.securesms.backup.v2.database.getContactsForBackup @@ -24,6 +24,9 @@ import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.Recipient +/** + * Handles importing/exporting [ArchiveRecipient] frames for an archive. + */ object RecipientBackupProcessor { val TAG = Log.tag(RecipientBackupProcessor::class.java) @@ -35,7 +38,7 @@ object RecipientBackupProcessor { exportState.recipientIds.add(releaseChannelId.toLong()) emitter.emit( Frame( - recipient = BackupRecipient( + recipient = ArchiveRecipient( id = releaseChannelId.toLong(), releaseNotes = ReleaseNotes() ) @@ -46,33 +49,35 @@ object RecipientBackupProcessor { } db.recipientTable.getContactsForBackup(selfId).use { reader -> - for (backupRecipient in reader) { - if (backupRecipient != null) { - exportState.recipientIds.add(backupRecipient.id) - emitter.emit(Frame(recipient = backupRecipient)) - } + for (recipient in reader) { + exportState.recipientIds.add(recipient.id) + emitter.emit(Frame(recipient = recipient)) } } db.recipientTable.getGroupsForBackup().use { reader -> - for (backupRecipient in reader) { - exportState.recipientIds.add(backupRecipient.id) - emitter.emit(Frame(recipient = backupRecipient)) + for (recipient in reader) { + exportState.recipientIds.add(recipient.id) + emitter.emit(Frame(recipient = recipient)) } } - db.distributionListTables.getAllForBackup().forEach { - exportState.recipientIds.add(it.id) - emitter.emit(Frame(recipient = it)) + db.distributionListTables.getAllForBackup().use { reader -> + for (recipient in reader) { + exportState.recipientIds.add(recipient.id) + emitter.emit(Frame(recipient = recipient)) + } } - db.callLinkTable.getCallLinksForBackup().forEach { - exportState.recipientIds.add(it.id) - emitter.emit(Frame(recipient = it)) + db.callLinkTable.getCallLinksForBackup().use { reader -> + for (recipient in reader) { + exportState.recipientIds.add(recipient.id) + emitter.emit(Frame(recipient = recipient)) + } } } - fun import(recipient: BackupRecipient, importState: ImportState) { + fun import(recipient: ArchiveRecipient, importState: ImportState) { val newId = when { recipient.contact != null -> SignalDatabase.recipients.restoreContactFromBackup(recipient.contact) recipient.group != null -> SignalDatabase.recipients.restoreGroupFromBackup(recipient.group) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerBackupProcessor.kt index 8c82aad470..ff96b5d2cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerBackupProcessor.kt @@ -19,6 +19,9 @@ import org.thoughtcrime.securesms.database.model.StickerPackRecord import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob +/** + * Handles importing/exporting [StickerPack] frames for an archive. + */ object StickerBackupProcessor { fun export(db: SignalDatabase, emitter: BackupFrameEmitter) { StickerPackRecordReader(db.stickerTable.allStickerPacks).use { reader -> From 77fd87c3e6729f7910abaa3059fd68fd89213429 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 1 Oct 2024 12:20:40 -0300 Subject: [PATCH 25/53] Remove admin toggle from call info sheet. --- .../webrtc/controls/CallInfoView.kt | 10 ------ .../controls/ControlsAndInfoController.kt | 2 +- .../controls/ControlsAndInfoViewModel.kt | 6 ---- .../components/webrtc/v2/CallActivity.kt | 5 +-- .../components/webrtc/v2/CallInfoCallbacks.kt | 32 ------------------- 5 files changed, 2 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/CallInfoView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/CallInfoView.kt index 71eb2026a3..b2e61a704d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/CallInfoView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/CallInfoView.kt @@ -57,7 +57,6 @@ import org.signal.core.ui.Previews import org.signal.core.ui.Rows import org.signal.core.ui.theme.LocalExtendedColors import org.signal.core.ui.theme.SignalTheme -import org.signal.ringrtc.CallLinkState import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarImage @@ -118,7 +117,6 @@ object CallInfoView { controlAndInfoState = controlAndInfoState, onShareLinkClicked = callbacks::onShareLinkClicked, onEditNameClicked = onEditNameClicked, - onToggleAdminApprovalClicked = callbacks::onToggleAdminApprovalClicked, onBlock = callbacks::onBlock, modifier = modifier ) @@ -129,7 +127,6 @@ object CallInfoView { interface Callbacks { fun onShareLinkClicked() fun onEditNameClicked(name: String) - fun onToggleAdminApprovalClicked(checked: Boolean) fun onBlock(callParticipant: CallParticipant) } } @@ -145,7 +142,6 @@ private fun CallInfoPreview() { controlAndInfoState = ControlAndInfoState(), onShareLinkClicked = { }, onEditNameClicked = { }, - onToggleAdminApprovalClicked = { }, onBlock = { } ) } @@ -158,7 +154,6 @@ private fun CallInfo( controlAndInfoState: ControlAndInfoState, onShareLinkClicked: () -> Unit, onEditNameClicked: () -> Unit, - onToggleAdminApprovalClicked: (Boolean) -> Unit, onBlock: (CallParticipant) -> Unit, modifier: Modifier = Modifier ) { @@ -320,11 +315,6 @@ private fun CallInfo( }, onClick = onEditNameClicked ) - Rows.ToggleRow( - checked = controlAndInfoState.callLink.state.restrictions == CallLinkState.Restrictions.ADMIN_APPROVAL, - text = stringResource(id = R.string.CallLinkDetailsFragment__require_admin_approval), - onCheckChanged = onToggleAdminApprovalClicked - ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt index c499cf281f..c0b24effae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt @@ -133,7 +133,7 @@ class ControlsAndInfoController private constructor( private var previousCallControlHeightData = HeightData() private var controlState: WebRtcControls = WebRtcControls.NONE - private val callInfoCallbacks = CallInfoCallbacks(webRtcCallActivity, controlsAndInfoViewModel, disposables) + private val callInfoCallbacks = CallInfoCallbacks(webRtcCallActivity, controlsAndInfoViewModel) init { raiseHandComposeView.apply { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoViewModel.kt index 635e08a823..f997668f94 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoViewModel.kt @@ -13,7 +13,6 @@ import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.subscribeBy -import org.signal.ringrtc.CallLinkState import org.thoughtcrime.securesms.calls.links.CallLinks import org.thoughtcrime.securesms.calls.links.UpdateCallLinkRepository import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsRepository @@ -55,11 +54,6 @@ class ControlsAndInfoViewModel( _state.value = _state.value.copy(resetScrollState = System.currentTimeMillis()) } - fun setApproveAllMembers(approveAllMembers: Boolean): Single { - val credentials = _state.value.callLink?.credentials ?: error("User cannot change the name of this call.") - return mutationRepository.setCallRestrictions(credentials, if (approveAllMembers) CallLinkState.Restrictions.ADMIN_APPROVAL else CallLinkState.Restrictions.NONE) - } - fun setName(name: String): Single { val credentials = _state.value.callLink?.credentials ?: error("User cannot change the name of this call.") return mutationRepository.setCallName(credentials, name) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt index c8e463b122..a14ec22add 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallActivity.kt @@ -29,7 +29,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import io.reactivex.rxjava3.disposables.CompositeDisposable import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus @@ -89,10 +88,8 @@ class CallActivity : BaseActivity(), CallControlsCallback { val fullscreenHelper = FullscreenHelper(this) lifecycleDisposable.bindTo(this) - val compositeDisposable = CompositeDisposable() - lifecycleDisposable.add(compositeDisposable) - val callInfoCallbacks = CallInfoCallbacks(this, controlsAndInfoViewModel, compositeDisposable) + val callInfoCallbacks = CallInfoCallbacks(this, controlsAndInfoViewModel) observeCallEvents() viewModel.processCallIntent(CallIntent(intent)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt index 87d5a7ab7f..d91703ea57 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt @@ -10,10 +10,7 @@ import android.content.Intent import android.widget.Toast import androidx.core.app.ShareCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.disposables.CompositeDisposable -import io.reactivex.rxjava3.kotlin.addTo -import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.BaseActivity import org.thoughtcrime.securesms.R @@ -24,7 +21,6 @@ import org.thoughtcrime.securesms.components.webrtc.controls.CallInfoView import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.events.CallParticipant -import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult /** * Callbacks for the CallInfoView, shared between CallActivity and ControlsAndInfoController. @@ -32,13 +28,8 @@ import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult class CallInfoCallbacks( private val activity: BaseActivity, private val controlsAndInfoViewModel: ControlsAndInfoViewModel, - private val disposables: CompositeDisposable ) : CallInfoView.Callbacks { - companion object { - private val TAG = Log.tag(CallInfoCallbacks::class) - } - override fun onShareLinkClicked() { val mimeType = Intent.normalizeMimeType("text/plain") val shareIntent = ShareCompat.IntentBuilder(activity) @@ -59,18 +50,6 @@ class CallInfoCallbacks( }.show(activity.supportFragmentManager, null) } - override fun onToggleAdminApprovalClicked(checked: Boolean) { - controlsAndInfoViewModel.setApproveAllMembers(checked) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Update) { - Log.w(TAG, "Failed to change restrictions. $it") - toastFailure() - } - }, onError = handleError("onApproveAllMembersChanged")) - .addTo(disposables) - } - override fun onBlock(callParticipant: CallParticipant) { MaterialAlertDialogBuilder(activity) .setNegativeButton(android.R.string.cancel, null) @@ -83,15 +62,4 @@ class CallInfoCallbacks( } .show() } - - private fun handleError(method: String): (throwable: Throwable) -> Unit { - return { - Log.w(TAG, "Failure during $method", it) - toastFailure() - } - } - - private fun toastFailure() { - Toast.makeText(activity, R.string.CallLinkDetailsFragment__couldnt_save_changes, Toast.LENGTH_LONG).show() - } } From 3a821af0a8a68cc1f9d98aaafbc6502a0886b83b Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 1 Oct 2024 13:29:18 -0400 Subject: [PATCH 26/53] Use auth header for websocket connection instead of param. --- .../websocket/OkHttpWebSocketConnection.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java index 305e69624d..f72b759d25 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java @@ -11,6 +11,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalProxy; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; +import org.whispersystems.signalservice.internal.push.AuthCredentials; import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager; import org.whispersystems.signalservice.internal.util.Util; @@ -40,6 +41,7 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject; import io.reactivex.rxjava3.subjects.SingleSubject; import okhttp3.ConnectionSpec; +import okhttp3.Credentials; import okhttp3.Dns; import okhttp3.Interceptor; import okhttp3.OkHttpClient; @@ -115,11 +117,7 @@ private Pair getConnectionInfo() { SignalServiceUrl serviceUrl = serviceUrls[random.nextInt(serviceUrls.length)]; String uri = serviceUrl.getUrl().replace("https://", "wss://").replace("http://", "ws://"); - if (credentialsProvider.isPresent()) { - return new Pair<>(serviceUrl, uri + "/v1/websocket/" + extraPathUri + "?login=%s&password=%s"); - } else { - return new Pair<>(serviceUrl, uri + "/v1/websocket/" + extraPathUri); - } + return new Pair<>(serviceUrl, uri + "/v1/websocket/" + extraPathUri); } @Override @@ -130,13 +128,6 @@ public synchronized Observable connect() { Pair connectionInfo = getConnectionInfo(); SignalServiceUrl serviceUrl = connectionInfo.first(); String wsUri = connectionInfo.second(); - String filledUri; - - if (credentialsProvider.isPresent()) { - filledUri = String.format(wsUri, credentialsProvider.get().getUsername(), credentialsProvider.get().getPassword()); - } else { - filledUri = wsUri; - } Pair socketFactory = createTlsSocketFactory(trustStore); @@ -157,12 +148,20 @@ public synchronized Observable connect() { OkHttpClient okHttpClient = clientBuilder.build(); - Request.Builder requestBuilder = new Request.Builder().url(filledUri); + Request.Builder requestBuilder = new Request.Builder().url(wsUri); if (signalAgent != null) { requestBuilder.addHeader("X-Signal-Agent", signalAgent); } + if (credentialsProvider.isPresent()) { + if (credentialsProvider.get().getUsername() != null && credentialsProvider.get().getPassword() != null) { + requestBuilder.addHeader("Authorization", Credentials.basic(credentialsProvider.get().getUsername(), credentialsProvider.get().getPassword())); + } else { + Log.w(TAG, "CredentialsProvider was present, but username or password was missing!"); + } + } + requestBuilder.addHeader("X-Signal-Receive-Stories", allowStories ? "true" : "false"); if (serviceUrl.getHostHeader().isPresent()) { From 4dba5849130c22164475a514173990ff4c4bf3b6 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 1 Oct 2024 13:41:14 -0400 Subject: [PATCH 27/53] Added some translation comments. --- app/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a6cfa8627c..2941e28af1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1007,6 +1007,7 @@ Unlink \'%s\'? By unlinking this device, it will no longer be able to send or receive messages. Network connection failed + Try again Unlinking device… Unlinking device @@ -3071,6 +3072,7 @@ Attachment Thumbnail Toggle quick camera attachment drawer Record and send audio attachment + Lock recording of audio attachment Message could not be sent. Check your connection and try again. @@ -5382,6 +5384,7 @@ Add to story Add a message + Add a reply Send to View once media From 9d0aef8dbc7453c9c2461b29edcceedea14ef2fb Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Tue, 1 Oct 2024 13:50:59 -0400 Subject: [PATCH 28/53] Fix link device crash. --- .../securesms/BiometricDeviceAuthentication.kt | 8 +++++++- .../securesms/linkdevice/LinkDeviceFragment.kt | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/BiometricDeviceAuthentication.kt b/app/src/main/java/org/thoughtcrime/securesms/BiometricDeviceAuthentication.kt index 25250791ef..676bcd024f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BiometricDeviceAuthentication.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/BiometricDeviceAuthentication.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms import android.app.Activity +import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.os.Build @@ -51,7 +52,12 @@ class BiometricDeviceAuthentication( return if (!DISALLOWED_BIOMETRIC_VERSIONS.contains(Build.VERSION.SDK_INT) && biometricManager.canAuthenticate(ALLOWED_AUTHENTICATORS) == BiometricManager.BIOMETRIC_SUCCESS) { if (force) { Log.i(TAG, "Listening for biometric authentication...") - biometricPrompt.authenticate(biometricPromptInfo) + try { + biometricPrompt.authenticate(biometricPromptInfo) + } catch (e: ActivityNotFoundException) { + Log.w(TAG, "Failed to launch confirm device credential settings", e) + return false + } } else { Log.i(TAG, "Skipping show system biometric or device lock dialog unless forced") } 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 8e7a996708..0013c2fc74 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt @@ -135,7 +135,9 @@ class LinkDeviceFragment : ComposeFragment() { LaunchedEffect(state.seenEducationSheet) { if (state.seenEducationSheet) { - biometricAuth.authenticate(requireContext(), true) { biometricDeviceLockLauncher.launch(getString(R.string.LinkDeviceFragment__unlock_to_link)) } + if (!biometricAuth.authenticate(requireContext(), true) { biometricDeviceLockLauncher.launch(getString(R.string.LinkDeviceFragment__unlock_to_link)) }) { + navController.safeNavigate(R.id.action_linkDeviceFragment_to_addLinkDeviceFragment) + } viewModel.markEducationSheetSeen(false) } } From ac0e80ca05b40db422cc0eaf7f2df7a4f1fb235b Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 1 Oct 2024 14:07:08 -0400 Subject: [PATCH 29/53] Refactor archive importing. --- .../securesms/backup/v2/ArchiveTypeAliases.kt | 1 + .../securesms/backup/v2/BackupRepository.kt | 40 +-- ...kt => AttachmentTableArchiveExtensions.kt} | 0 .../CallLinkTableArchiveExtensions.kt | 18 + .../database/CallLinkTableBackupExtensions.kt | 57 --- .../v2/database/CallTableArchiveExtensions.kt | 19 + .../v2/database/CallTableBackupExtensions.kt | 41 --- ...kt => ChatColorsTableArchiveExtensions.kt} | 0 ...=> DatabaseAttachmentArchiveExtensions.kt} | 0 ...DistributionListTablesArchiveExtensions.kt | 45 +++ .../DistributionListTablesBackupExtensions.kt | 102 ------ ... => InAppPaymentTableArchiveExtensions.kt} | 0 ...ns.kt => MessageTableArchiveExtensions.kt} | 11 +- ...s.kt => ReactionTableArchiveExtensions.kt} | 0 .../RecipientTableArchiveExtensions.kt | 142 ++++++++ .../RecipientTableBackupExtensions.kt | 336 ------------------ ...ns.kt => StickerTableArchiveExtensions.kt} | 0 .../database/ThreadTableArchiveExtensions.kt | 43 +++ .../database/ThreadTableBackupExtensions.kt | 100 ------ ...terator.kt => AdHocCallArchiveExporter.kt} | 2 +- ...Iterator.kt => CallLinkArchiveExporter.kt} | 2 +- ...portIterator.kt => ChatArchiveExporter.kt} | 2 +- ...Iterator.kt => ChatItemArchiveExporter.kt} | 4 +- ...tIterator.kt => ContactArchiveExporter.kt} | 2 +- ....kt => DistributionListArchiveExporter.kt} | 2 +- ...ortIterator.kt => GroupArchiveExporter.kt} | 2 +- .../v2/importer/AdHodCallArchiveImporter.kt | 36 ++ .../v2/importer/CallLinkArchiveImporter.kt | 53 +++ .../backup/v2/importer/ChatArchiveImporter.kt | 71 ++++ .../ChatItemArchiveImporter.kt} | 10 +- .../v2/importer/ContactArchiveImporter.kt | 86 +++++ .../DistributionListArchiveImporter.kt | 72 ++++ .../v2/importer/GroupArchiveImporter.kt | 146 ++++++++ ...ssor.kt => AccountDataArchiveProcessor.kt} | 4 +- ...cessor.kt => AdHocCallArchiveProcessor.kt} | 8 +- ...upProcessor.kt => ChatArchiveProcessor.kt} | 15 +- ...ocessor.kt => ChatItemArchiveProcessor.kt} | 8 +- ...cessor.kt => RecipientArchiveProcessor.kt} | 19 +- ...rocessor.kt => StickerArchiveProcessor.kt} | 2 +- .../components/webrtc/v2/CallInfoCallbacks.kt | 4 +- .../securesms/database/SignalDatabase.kt | 8 + 41 files changed, 808 insertions(+), 705 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/{AttachmentTableBackupExtensions.kt => AttachmentTableArchiveExtensions.kt} (100%) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/{ChatColorsTableBackupExtensions.kt => ChatColorsTableArchiveExtensions.kt} (100%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/{DatabaseAttachmentBackupExtensions.kt => DatabaseAttachmentArchiveExtensions.kt} (100%) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/{InAppPaymentTableBackupExtensions.kt => InAppPaymentTableArchiveExtensions.kt} (100%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/{MessageTableBackupExtensions.kt => MessageTableArchiveExtensions.kt} (88%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/{ReactionTableBackupExtensions.kt => ReactionTableArchiveExtensions.kt} (100%) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/{StickerTableBackupExtensions.kt => StickerTableArchiveExtensions.kt} (100%) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableArchiveExtensions.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/{AdHocCallArchiveExportIterator.kt => AdHocCallArchiveExporter.kt} (91%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/{CallLinkArchiveExportIterator.kt => CallLinkArchiveExporter.kt} (94%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/{ChatArchiveExportIterator.kt => ChatArchiveExporter.kt} (95%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/{ChatItemArchiveExportIterator.kt => ChatItemArchiveExporter.kt} (99%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/{ContactArchiveExportIterator.kt => ContactArchiveExporter.kt} (96%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/{DistributionListArchiveExportIterator.kt => DistributionListArchiveExporter.kt} (98%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/{GroupArchiveExportIterator.kt => GroupArchiveExporter.kt} (98%) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/AdHodCallArchiveImporter.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/CallLinkArchiveImporter.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/{database/ChatItemImportInserter.kt => importer/ChatItemArchiveImporter.kt} (99%) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/DistributionListArchiveImporter.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/GroupArchiveImporter.kt rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/{AccountDataBackupProcessor.kt => AccountDataArchiveProcessor.kt} (99%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/{AdHocCallBackupProcessor.kt => AdHocCallArchiveProcessor.kt} (79%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/{ChatBackupProcessor.kt => ChatArchiveProcessor.kt} (76%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/{ChatItemBackupProcessor.kt => ChatItemArchiveProcessor.kt} (84%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/{RecipientBackupProcessor.kt => RecipientArchiveProcessor.kt} (79%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/{StickerBackupProcessor.kt => StickerArchiveProcessor.kt} (98%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt index 9655d3339e..58c8f03724 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveTypeAliases.kt @@ -7,3 +7,4 @@ package org.thoughtcrime.securesms.backup.v2 typealias ArchiveRecipient = org.thoughtcrime.securesms.backup.v2.proto.Recipient typealias ArchiveGroup = org.thoughtcrime.securesms.backup.v2.proto.Group +typealias ArchiveCallLink = org.thoughtcrime.securesms.backup.v2.proto.CallLink diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index edb5b3fb85..08735439d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -27,14 +27,14 @@ import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment -import org.thoughtcrime.securesms.backup.v2.database.ChatItemImportInserter import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackupRestore -import org.thoughtcrime.securesms.backup.v2.processor.AccountDataBackupProcessor -import org.thoughtcrime.securesms.backup.v2.processor.AdHocCallBackupProcessor -import org.thoughtcrime.securesms.backup.v2.processor.ChatBackupProcessor -import org.thoughtcrime.securesms.backup.v2.processor.ChatItemBackupProcessor -import org.thoughtcrime.securesms.backup.v2.processor.RecipientBackupProcessor -import org.thoughtcrime.securesms.backup.v2.processor.StickerBackupProcessor +import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter +import org.thoughtcrime.securesms.backup.v2.processor.AccountDataArchiveProcessor +import org.thoughtcrime.securesms.backup.v2.processor.AdHocCallArchiveProcessor +import org.thoughtcrime.securesms.backup.v2.processor.ChatArchiveProcessor +import org.thoughtcrime.securesms.backup.v2.processor.ChatItemArchiveProcessor +import org.thoughtcrime.securesms.backup.v2.processor.RecipientArchiveProcessor +import org.thoughtcrime.securesms.backup.v2.processor.StickerArchiveProcessor import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo import org.thoughtcrime.securesms.backup.v2.stream.BackupExportWriter import org.thoughtcrime.securesms.backup.v2.stream.BackupImportReader @@ -285,37 +285,37 @@ object BackupRepository { // We're using a snapshot, so the transaction is more for perf than correctness dbSnapshot.rawWritableDatabase.withinTransaction { progressEmitter?.onAccount() - AccountDataBackupProcessor.export(dbSnapshot, signalStoreSnapshot) { + AccountDataArchiveProcessor.export(dbSnapshot, signalStoreSnapshot) { writer.write(it) eventTimer.emit("account") } progressEmitter?.onRecipient() - RecipientBackupProcessor.export(dbSnapshot, signalStoreSnapshot, exportState) { + RecipientArchiveProcessor.export(dbSnapshot, signalStoreSnapshot, exportState) { writer.write(it) eventTimer.emit("recipient") } progressEmitter?.onThread() - ChatBackupProcessor.export(dbSnapshot, exportState) { frame -> + ChatArchiveProcessor.export(dbSnapshot, exportState) { frame -> writer.write(frame) eventTimer.emit("thread") } progressEmitter?.onCall() - AdHocCallBackupProcessor.export(dbSnapshot) { frame -> + AdHocCallArchiveProcessor.export(dbSnapshot) { frame -> writer.write(frame) eventTimer.emit("call") } progressEmitter?.onSticker() - StickerBackupProcessor.export(dbSnapshot) { frame -> + StickerArchiveProcessor.export(dbSnapshot) { frame -> writer.write(frame) eventTimer.emit("sticker-pack") } progressEmitter?.onMessage() - ChatItemBackupProcessor.export(dbSnapshot, exportState) { frame -> + ChatItemArchiveProcessor.export(dbSnapshot, exportState) { frame -> writer.write(frame) eventTimer.emit("message") } @@ -406,38 +406,38 @@ object BackupRepository { eventTimer.emit("setup") val importState = ImportState(backupKey) - val chatItemInserter: ChatItemImportInserter = ChatItemBackupProcessor.beginImport(importState) + val chatItemInserter: ChatItemArchiveImporter = ChatItemArchiveProcessor.beginImport(importState) val totalLength = frameReader.getStreamLength() for (frame in frameReader) { when { frame.account != null -> { - AccountDataBackupProcessor.import(frame.account, selfId, importState) + AccountDataArchiveProcessor.import(frame.account, selfId, importState) eventTimer.emit("account") } frame.recipient != null -> { - RecipientBackupProcessor.import(frame.recipient, importState) + RecipientArchiveProcessor.import(frame.recipient, importState) eventTimer.emit("recipient") } frame.chat != null -> { - ChatBackupProcessor.import(frame.chat, importState) + ChatArchiveProcessor.import(frame.chat, importState) eventTimer.emit("chat") } frame.adHocCall != null -> { - AdHocCallBackupProcessor.import(frame.adHocCall, importState) + AdHocCallArchiveProcessor.import(frame.adHocCall, importState) eventTimer.emit("call") } frame.stickerPack != null -> { - StickerBackupProcessor.import(frame.stickerPack) + StickerArchiveProcessor.import(frame.stickerPack) eventTimer.emit("sticker-pack") } frame.chatItem != null -> { - chatItemInserter.insert(frame.chatItem) + chatItemInserter.import(frame.chatItem) eventTimer.emit("chatItem") // TODO if there's stuff in the stream after chatItems, we need to flush the inserter before going to the next phase } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableArchiveExtensions.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableBackupExtensions.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableArchiveExtensions.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt new file mode 100644 index 0000000000..719f798996 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import org.signal.core.util.select +import org.thoughtcrime.securesms.database.CallLinkTable + +fun CallLinkTable.getCallLinksForBackup(): CallLinkArchiveExporter { + val cursor = readableDatabase + .select() + .from(CallLinkTable.TABLE_NAME) + .run() + + return CallLinkArchiveExporter(cursor) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt deleted file mode 100644 index e063e9bb82..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableBackupExtensions.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.database - -import org.signal.core.util.select -import org.signal.ringrtc.CallLinkRootKey -import org.signal.ringrtc.CallLinkState -import org.thoughtcrime.securesms.backup.v2.proto.CallLink -import org.thoughtcrime.securesms.database.CallLinkTable -import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.recipients.RecipientId -import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials -import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId -import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkState -import java.time.Instant - -fun CallLinkTable.getCallLinksForBackup(): CallLinkArchiveExportIterator { - val cursor = readableDatabase - .select() - .from(CallLinkTable.TABLE_NAME) - .run() - - return CallLinkArchiveExportIterator(cursor) -} - -fun CallLinkTable.restoreFromBackup(callLink: CallLink): RecipientId? { - val rootKey: CallLinkRootKey - try { - rootKey = CallLinkRootKey(callLink.rootKey.toByteArray()) - } catch (e: Exception) { - return null - } - return SignalDatabase.callLinks.insertCallLink( - CallLinkTable.CallLink( - recipientId = RecipientId.UNKNOWN, - roomId = CallLinkRoomId.fromCallLinkRootKey(rootKey), - credentials = CallLinkCredentials(callLink.rootKey.toByteArray(), callLink.adminKey?.toByteArray()), - state = SignalCallLinkState( - name = callLink.name, - restrictions = callLink.restrictions.toLocal(), - expiration = Instant.ofEpochMilli(callLink.expirationMs) - ), - deletionTimestamp = 0L - ) - ) -} - -private fun CallLink.Restrictions.toLocal(): CallLinkState.Restrictions { - return when (this) { - CallLink.Restrictions.ADMIN_APPROVAL -> CallLinkState.Restrictions.ADMIN_APPROVAL - CallLink.Restrictions.NONE -> CallLinkState.Restrictions.NONE - CallLink.Restrictions.UNKNOWN -> CallLinkState.Restrictions.UNKNOWN - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt new file mode 100644 index 0000000000..1c780545d6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import org.signal.core.util.select +import org.thoughtcrime.securesms.database.CallTable + +fun CallTable.getAdhocCallsForBackup(): AdHocCallArchiveExporter { + return AdHocCallArchiveExporter( + readableDatabase + .select() + .from(CallTable.TABLE_NAME) + .where("${CallTable.TYPE} = ?", CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL)) + .run() + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt deleted file mode 100644 index 737f8b2fd1..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.database - -import org.signal.core.util.insertInto -import org.signal.core.util.select -import org.thoughtcrime.securesms.backup.v2.ImportState -import org.thoughtcrime.securesms.backup.v2.proto.AdHocCall -import org.thoughtcrime.securesms.database.CallTable - -fun CallTable.getAdhocCallsForBackup(): AdHocCallArchiveExportIterator { - return AdHocCallArchiveExportIterator( - readableDatabase - .select() - .from(CallTable.TABLE_NAME) - .where("${CallTable.TYPE} = ?", CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL)) - .run() - ) -} - -fun CallTable.restoreCallLogFromBackup(call: AdHocCall, importState: ImportState) { - val event = when (call.state) { - AdHocCall.State.GENERIC -> CallTable.Event.GENERIC_GROUP_CALL - AdHocCall.State.UNKNOWN_STATE -> CallTable.Event.GENERIC_GROUP_CALL - } - - writableDatabase - .insertInto(CallTable.TABLE_NAME) - .values( - CallTable.CALL_ID to call.callId, - CallTable.PEER to importState.remoteToLocalRecipientId[call.recipientId]!!.serialize(), - CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL), - CallTable.DIRECTION to CallTable.Direction.serialize(CallTable.Direction.OUTGOING), - CallTable.EVENT to CallTable.Event.serialize(event), - CallTable.TIMESTAMP to call.callTimestamp - ) - .run() -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableArchiveExtensions.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableBackupExtensions.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableArchiveExtensions.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DatabaseAttachmentBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DatabaseAttachmentArchiveExtensions.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DatabaseAttachmentBackupExtensions.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DatabaseAttachmentArchiveExtensions.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt new file mode 100644 index 0000000000..db5232ec1e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import org.signal.core.util.deleteAll +import org.signal.core.util.select +import org.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.backup.v2.exporters.DistributionListArchiveExporter +import org.thoughtcrime.securesms.database.DistributionListTables +import org.thoughtcrime.securesms.database.model.DistributionListId +import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode +import org.thoughtcrime.securesms.recipients.RecipientId + +fun DistributionListTables.getAllForBackup(): DistributionListArchiveExporter { + val cursor = readableDatabase + .select() + .from(DistributionListTables.ListTable.TABLE_NAME) + .run() + + return DistributionListArchiveExporter(cursor, this) +} + +fun DistributionListTables.getMembersForBackup(id: DistributionListId): List { + lateinit var privacyMode: DistributionListPrivacyMode + lateinit var rawMembers: List + + readableDatabase.withinTransaction { + privacyMode = getPrivacyMode(id) + rawMembers = getRawMembers(id, privacyMode) + } + + return when (privacyMode) { + DistributionListPrivacyMode.ALL -> emptyList() + DistributionListPrivacyMode.ONLY_WITH -> rawMembers + DistributionListPrivacyMode.ALL_EXCEPT -> rawMembers + } +} + +fun DistributionListTables.clearAllDataForBackupRestore() { + writableDatabase.deleteAll(DistributionListTables.ListTable.TABLE_NAME) + writableDatabase.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt deleted file mode 100644 index 24045a63ef..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.database - -import org.signal.core.util.deleteAll -import org.signal.core.util.logging.Log -import org.signal.core.util.select -import org.signal.core.util.withinTransaction -import org.thoughtcrime.securesms.backup.v2.ImportState -import org.thoughtcrime.securesms.backup.v2.exporters.DistributionListArchiveExportIterator -import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem -import org.thoughtcrime.securesms.database.DistributionListTables -import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.database.model.DistributionListId -import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode -import org.thoughtcrime.securesms.recipients.RecipientId -import org.whispersystems.signalservice.api.push.DistributionId -import org.whispersystems.signalservice.api.util.UuidUtil -import org.thoughtcrime.securesms.backup.v2.proto.DistributionList as BackupDistributionList - -private val TAG = Log.tag(DistributionListTables::class.java) - -fun DistributionListTables.getAllForBackup(): DistributionListArchiveExportIterator { - val cursor = readableDatabase - .select() - .from(DistributionListTables.ListTable.TABLE_NAME) - .run() - - return DistributionListArchiveExportIterator(cursor, this) -} - -fun DistributionListTables.getMembersForBackup(id: DistributionListId): List { - lateinit var privacyMode: DistributionListPrivacyMode - lateinit var rawMembers: List - - readableDatabase.withinTransaction { - privacyMode = getPrivacyMode(id) - rawMembers = getRawMembers(id, privacyMode) - } - - return when (privacyMode) { - DistributionListPrivacyMode.ALL -> emptyList() - DistributionListPrivacyMode.ONLY_WITH -> rawMembers - DistributionListPrivacyMode.ALL_EXCEPT -> rawMembers - } -} - -fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, importState: ImportState): RecipientId? { - if (dlistItem.deletionTimestamp != null && dlistItem.deletionTimestamp > 0) { - val dlistId = createList( - name = "", - members = emptyList(), - distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)), - allowsReplies = false, - deletionTimestamp = dlistItem.deletionTimestamp, - storageId = null, - privacyMode = DistributionListPrivacyMode.ONLY_WITH - )!! - - return SignalDatabase.distributionLists.getRecipientId(dlistId)!! - } - - val dlist = dlistItem.distributionList ?: return null - val members: List = dlist.memberRecipientIds - .mapNotNull { importState.remoteToLocalRecipientId[it] } - - if (members.size != dlist.memberRecipientIds.size) { - Log.w(TAG, "Couldn't find some member recipients! Missing backup recipientIds: ${dlist.memberRecipientIds.toSet() - members.toSet()}") - } - - val distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)) - val privacyMode = dlist.privacyMode.toLocalPrivacyMode() - - val dlistId = createList( - name = dlist.name, - members = members, - distributionId = distributionId, - allowsReplies = dlist.allowReplies, - deletionTimestamp = dlistItem.deletionTimestamp ?: 0, - storageId = null, - privacyMode = privacyMode - )!! - - return SignalDatabase.distributionLists.getRecipientId(dlistId)!! -} - -fun DistributionListTables.clearAllDataForBackupRestore() { - writableDatabase.deleteAll(DistributionListTables.ListTable.TABLE_NAME) - writableDatabase.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME) -} - -private fun BackupDistributionList.PrivacyMode.toLocalPrivacyMode(): DistributionListPrivacyMode { - return when (this) { - BackupDistributionList.PrivacyMode.UNKNOWN -> DistributionListPrivacyMode.ALL - BackupDistributionList.PrivacyMode.ONLY_WITH -> DistributionListPrivacyMode.ONLY_WITH - BackupDistributionList.PrivacyMode.ALL -> DistributionListPrivacyMode.ALL - BackupDistributionList.PrivacyMode.ALL_EXCEPT -> DistributionListPrivacyMode.ALL_EXCEPT - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/InAppPaymentTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/InAppPaymentTableArchiveExtensions.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/InAppPaymentTableBackupExtensions.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/InAppPaymentTableArchiveExtensions.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt similarity index 88% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt index f5e4656ab6..7aa2e6fe0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt @@ -8,7 +8,8 @@ package org.thoughtcrime.securesms.backup.v2.database import org.signal.core.util.SqlUtil import org.signal.core.util.select import org.thoughtcrime.securesms.backup.v2.ImportState -import org.thoughtcrime.securesms.backup.v2.exporters.ChatItemArchiveExportIterator +import org.thoughtcrime.securesms.backup.v2.exporters.ChatItemArchiveExporter +import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter import org.thoughtcrime.securesms.database.MessageTable import org.thoughtcrime.securesms.database.MessageTypes import org.thoughtcrime.securesms.database.SignalDatabase @@ -16,7 +17,7 @@ import java.util.concurrent.TimeUnit private const val COLUMN_BASE_TYPE = "base_type" -fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, mediaBackupEnabled: Boolean): ChatItemArchiveExportIterator { +fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, mediaBackupEnabled: Boolean): ChatItemArchiveExporter { val cursor = readableDatabase .select( MessageTable.ID, @@ -66,11 +67,11 @@ fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, medi .orderBy("${MessageTable.DATE_RECEIVED} ASC") .run() - return ChatItemArchiveExportIterator(db, cursor, 100, mediaBackupEnabled) + return ChatItemArchiveExporter(db, cursor, 100, mediaBackupEnabled) } -fun MessageTable.createChatItemInserter(importState: ImportState): ChatItemImportInserter { - return ChatItemImportInserter(writableDatabase, importState, 100) +fun MessageTable.createChatItemInserter(importState: ImportState): ChatItemArchiveImporter { + return ChatItemArchiveImporter(writableDatabase, importState, 100) } fun MessageTable.clearAllDataForBackupRestore() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ReactionTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ReactionTableArchiveExtensions.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ReactionTableBackupExtensions.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ReactionTableArchiveExtensions.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt new file mode 100644 index 0000000000..4928136022 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.content.ContentValues +import org.signal.core.util.Base64 +import org.signal.core.util.SqlUtil +import org.signal.core.util.deleteAll +import org.signal.core.util.logging.Log +import org.signal.core.util.nullIfBlank +import org.signal.core.util.select +import org.signal.core.util.update +import org.signal.libsignal.zkgroup.InvalidInputException +import org.thoughtcrime.securesms.backup.v2.exporters.ContactArchiveExporter +import org.thoughtcrime.securesms.backup.v2.exporters.GroupArchiveExporter +import org.thoughtcrime.securesms.backup.v2.proto.AccountData +import org.thoughtcrime.securesms.database.GroupTable +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.RecipientId + +/** + * Fetches all individual contacts for backups and returns the result as an iterator. + * It's important to note that the iterator still needs to be closed after it's used. + * It's recommended to use `.use` or a try-with-resources pattern. + */ +fun RecipientTable.getContactsForBackup(selfId: Long): ContactArchiveExporter { + val cursor = readableDatabase + .select( + RecipientTable.ID, + RecipientTable.ACI_COLUMN, + RecipientTable.PNI_COLUMN, + RecipientTable.USERNAME, + RecipientTable.E164, + RecipientTable.BLOCKED, + RecipientTable.HIDDEN, + RecipientTable.REGISTERED, + RecipientTable.UNREGISTERED_TIMESTAMP, + RecipientTable.PROFILE_KEY, + RecipientTable.PROFILE_SHARING, + RecipientTable.PROFILE_GIVEN_NAME, + RecipientTable.PROFILE_FAMILY_NAME, + RecipientTable.PROFILE_JOINED_NAME, + RecipientTable.MUTE_UNTIL, + RecipientTable.CHAT_COLORS, + RecipientTable.CUSTOM_CHAT_COLORS_ID, + RecipientTable.EXTRAS + ) + .from(RecipientTable.TABLE_NAME) + .where( + """ + ${RecipientTable.TYPE} = ? AND ( + ${RecipientTable.ACI_COLUMN} NOT NULL OR + ${RecipientTable.PNI_COLUMN} NOT NULL OR + ${RecipientTable.E164} NOT NULL + ) + """, + RecipientTable.RecipientType.INDIVIDUAL.id + ) + .run() + + return ContactArchiveExporter(cursor, selfId) +} + +fun RecipientTable.getGroupsForBackup(): GroupArchiveExporter { + val cursor = readableDatabase + .select( + "${RecipientTable.TABLE_NAME}.${RecipientTable.ID}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_SHARING}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.EXTRAS}", + "${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY}", + "${GroupTable.TABLE_NAME}.${GroupTable.SHOW_AS_STORY_STATE}", + "${GroupTable.TABLE_NAME}.${GroupTable.TITLE}", + "${GroupTable.TABLE_NAME}.${GroupTable.V2_DECRYPTED_GROUP}" + ) + .from( + """ + ${RecipientTable.TABLE_NAME} + INNER JOIN ${GroupTable.TABLE_NAME} ON ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} = ${GroupTable.TABLE_NAME}.${GroupTable.RECIPIENT_ID} + """ + ) + .where("${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY} IS NOT NULL") + .run() + + return GroupArchiveExporter(cursor) +} + +/** + * Given [AccountData], this will insert the necessary data for the local user into the [RecipientTable]. + */ +fun RecipientTable.restoreSelfFromBackup(accountData: AccountData, selfId: RecipientId) { + val values = ContentValues().apply { + put(RecipientTable.PROFILE_GIVEN_NAME, accountData.givenName.nullIfBlank()) + put(RecipientTable.PROFILE_FAMILY_NAME, accountData.familyName.nullIfBlank()) + put(RecipientTable.PROFILE_JOINED_NAME, ProfileName.fromParts(accountData.givenName, accountData.familyName).toString().nullIfBlank()) + put(RecipientTable.PROFILE_AVATAR, accountData.avatarUrlPath.nullIfBlank()) + put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.REGISTERED.id) + put(RecipientTable.PROFILE_SHARING, true) + put(RecipientTable.UNREGISTERED_TIMESTAMP, 0) + put(RecipientTable.EXTRAS, RecipientExtras().encode()) + + try { + put(RecipientTable.PROFILE_KEY, Base64.encodeWithPadding(accountData.profileKey.toByteArray()).nullIfBlank()) + } catch (e: InvalidInputException) { + Log.w(TAG, "Missing profile key during restore") + } + + put(RecipientTable.USERNAME, accountData.username) + } + + writableDatabase + .update(RecipientTable.TABLE_NAME) + .values(values) + .where("${RecipientTable.ID} = ?", selfId) + .run() +} + +fun RecipientTable.clearAllDataForBackupRestore() { + writableDatabase.deleteAll(RecipientTable.TABLE_NAME) + SqlUtil.resetAutoIncrementValue(writableDatabase, RecipientTable.TABLE_NAME) + + RecipientId.clearCache() + AppDependencies.recipientCache.clear() + AppDependencies.recipientCache.clearSelf() +} + +fun RecipientTable.restoreReleaseNotes(): RecipientId { + val releaseChannelId: RecipientId = insertReleaseChannelRecipient() + SignalStore.releaseChannel.setReleaseChannelRecipientId(releaseChannelId) + + setProfileName(releaseChannelId, ProfileName.asGiven("Signal")) + setMuted(releaseChannelId, Long.MAX_VALUE) + return releaseChannelId +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt deleted file mode 100644 index b4a6bd7002..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.database - -import android.content.ContentValues -import androidx.core.content.contentValuesOf -import org.signal.core.util.Base64 -import org.signal.core.util.SqlUtil -import org.signal.core.util.deleteAll -import org.signal.core.util.logging.Log -import org.signal.core.util.nullIfBlank -import org.signal.core.util.select -import org.signal.core.util.toInt -import org.signal.core.util.update -import org.signal.libsignal.zkgroup.InvalidInputException -import org.signal.libsignal.zkgroup.groups.GroupMasterKey -import org.signal.libsignal.zkgroup.groups.GroupSecretParams -import org.signal.storageservice.protos.groups.AccessControl -import org.signal.storageservice.protos.groups.Member -import org.signal.storageservice.protos.groups.local.DecryptedBannedMember -import org.signal.storageservice.protos.groups.local.DecryptedGroup -import org.signal.storageservice.protos.groups.local.DecryptedMember -import org.signal.storageservice.protos.groups.local.DecryptedPendingMember -import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember -import org.signal.storageservice.protos.groups.local.DecryptedTimer -import org.signal.storageservice.protos.groups.local.EnabledState -import org.thoughtcrime.securesms.backup.v2.exporters.ContactArchiveExportIterator -import org.thoughtcrime.securesms.backup.v2.exporters.GroupArchiveExportIterator -import org.thoughtcrime.securesms.backup.v2.proto.AccountData -import org.thoughtcrime.securesms.backup.v2.proto.Contact -import org.thoughtcrime.securesms.backup.v2.proto.Group -import org.thoughtcrime.securesms.conversation.colors.AvatarColorHash -import org.thoughtcrime.securesms.database.GroupTable -import org.thoughtcrime.securesms.database.RecipientTable -import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.groups.GroupId -import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor -import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter -import org.thoughtcrime.securesms.profiles.ProfileName -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.recipients.RecipientId -import org.thoughtcrime.securesms.storage.StorageSyncHelper -import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations -import org.whispersystems.signalservice.api.push.ServiceId -import org.whispersystems.signalservice.api.push.ServiceId.ACI -import org.whispersystems.signalservice.api.push.ServiceId.PNI - -private typealias BackupRecipient = org.thoughtcrime.securesms.backup.v2.proto.Recipient - -/** - * Fetches all individual contacts for backups and returns the result as an iterator. - * It's important to note that the iterator still needs to be closed after it's used. - * It's recommended to use `.use` or a try-with-resources pattern. - */ -fun RecipientTable.getContactsForBackup(selfId: Long): ContactArchiveExportIterator { - val cursor = readableDatabase - .select( - RecipientTable.ID, - RecipientTable.ACI_COLUMN, - RecipientTable.PNI_COLUMN, - RecipientTable.USERNAME, - RecipientTable.E164, - RecipientTable.BLOCKED, - RecipientTable.HIDDEN, - RecipientTable.REGISTERED, - RecipientTable.UNREGISTERED_TIMESTAMP, - RecipientTable.PROFILE_KEY, - RecipientTable.PROFILE_SHARING, - RecipientTable.PROFILE_GIVEN_NAME, - RecipientTable.PROFILE_FAMILY_NAME, - RecipientTable.PROFILE_JOINED_NAME, - RecipientTable.MUTE_UNTIL, - RecipientTable.CHAT_COLORS, - RecipientTable.CUSTOM_CHAT_COLORS_ID, - RecipientTable.EXTRAS - ) - .from(RecipientTable.TABLE_NAME) - .where( - """ - ${RecipientTable.TYPE} = ? AND ( - ${RecipientTable.ACI_COLUMN} NOT NULL OR - ${RecipientTable.PNI_COLUMN} NOT NULL OR - ${RecipientTable.E164} NOT NULL - ) - """, - RecipientTable.RecipientType.INDIVIDUAL.id - ) - .run() - - return ContactArchiveExportIterator(cursor, selfId) -} - -fun RecipientTable.getGroupsForBackup(): GroupArchiveExportIterator { - val cursor = readableDatabase - .select( - "${RecipientTable.TABLE_NAME}.${RecipientTable.ID}", - "${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED}", - "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_SHARING}", - "${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}", - "${RecipientTable.TABLE_NAME}.${RecipientTable.EXTRAS}", - "${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY}", - "${GroupTable.TABLE_NAME}.${GroupTable.SHOW_AS_STORY_STATE}", - "${GroupTable.TABLE_NAME}.${GroupTable.TITLE}", - "${GroupTable.TABLE_NAME}.${GroupTable.V2_DECRYPTED_GROUP}" - ) - .from( - """ - ${RecipientTable.TABLE_NAME} - INNER JOIN ${GroupTable.TABLE_NAME} ON ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} = ${GroupTable.TABLE_NAME}.${GroupTable.RECIPIENT_ID} - """ - ) - .where("${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY} IS NOT NULL") - .run() - - return GroupArchiveExportIterator(cursor) -} - -/** - * Given [AccountData], this will insert the necessary data for the local user into the [RecipientTable]. - */ -fun RecipientTable.restoreSelfFromBackup(accountData: AccountData, selfId: RecipientId) { - val values = ContentValues().apply { - put(RecipientTable.PROFILE_GIVEN_NAME, accountData.givenName.nullIfBlank()) - put(RecipientTable.PROFILE_FAMILY_NAME, accountData.familyName.nullIfBlank()) - put(RecipientTable.PROFILE_JOINED_NAME, ProfileName.fromParts(accountData.givenName, accountData.familyName).toString().nullIfBlank()) - put(RecipientTable.PROFILE_AVATAR, accountData.avatarUrlPath.nullIfBlank()) - put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.REGISTERED.id) - put(RecipientTable.PROFILE_SHARING, true) - put(RecipientTable.UNREGISTERED_TIMESTAMP, 0) - put(RecipientTable.EXTRAS, RecipientExtras().encode()) - - try { - put(RecipientTable.PROFILE_KEY, Base64.encodeWithPadding(accountData.profileKey.toByteArray()).nullIfBlank()) - } catch (e: InvalidInputException) { - Log.w(TAG, "Missing profile key during restore") - } - - put(RecipientTable.USERNAME, accountData.username) - } - - writableDatabase - .update(RecipientTable.TABLE_NAME) - .values(values) - .where("${RecipientTable.ID} = ?", selfId) - .run() -} - -fun RecipientTable.clearAllDataForBackupRestore() { - writableDatabase.deleteAll(RecipientTable.TABLE_NAME) - SqlUtil.resetAutoIncrementValue(writableDatabase, RecipientTable.TABLE_NAME) - - RecipientId.clearCache() - AppDependencies.recipientCache.clear() - AppDependencies.recipientCache.clearSelf() -} - -fun RecipientTable.restoreContactFromBackup(contact: Contact): RecipientId { - val id = getAndPossiblyMergePnpVerified( - aci = ACI.parseOrNull(contact.aci?.toByteArray()), - pni = PNI.parseOrNull(contact.pni?.toByteArray()), - e164 = contact.formattedE164 - ) - - val profileKey = contact.profileKey?.toByteArray() - val values = contentValuesOf( - RecipientTable.BLOCKED to contact.blocked, - RecipientTable.HIDDEN to contact.visibility.toLocal().serialize(), - RecipientTable.TYPE to RecipientTable.RecipientType.INDIVIDUAL.id, - RecipientTable.PROFILE_FAMILY_NAME to contact.profileFamilyName, - RecipientTable.PROFILE_GIVEN_NAME to contact.profileGivenName, - RecipientTable.PROFILE_JOINED_NAME to ProfileName.fromParts(contact.profileGivenName, contact.profileFamilyName).toString(), - RecipientTable.PROFILE_KEY to if (profileKey == null) null else Base64.encodeWithPadding(profileKey), - RecipientTable.PROFILE_SHARING to contact.profileSharing.toInt(), - RecipientTable.USERNAME to contact.username, - RecipientTable.EXTRAS to contact.toLocalExtras().encode() - ) - - if (contact.registered != null) { - values.put(RecipientTable.UNREGISTERED_TIMESTAMP, 0L) - values.put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.REGISTERED.id) - } else if (contact.notRegistered != null) { - values.put(RecipientTable.UNREGISTERED_TIMESTAMP, contact.notRegistered.unregisteredTimestamp) - values.put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.NOT_REGISTERED.id) - } - - writableDatabase - .update(RecipientTable.TABLE_NAME) - .values(values) - .where("${RecipientTable.ID} = ?", id) - .run() - - return id -} - -fun RecipientTable.restoreReleaseNotes(): RecipientId { - val releaseChannelId: RecipientId = insertReleaseChannelRecipient() - SignalStore.releaseChannel.setReleaseChannelRecipientId(releaseChannelId) - - setProfileName(releaseChannelId, ProfileName.asGiven("Signal")) - setMuted(releaseChannelId, Long.MAX_VALUE) - return releaseChannelId -} - -fun RecipientTable.restoreGroupFromBackup(group: Group): RecipientId { - val masterKey = GroupMasterKey(group.masterKey.toByteArray()) - val groupId = GroupId.v2(masterKey) - - val operations = AppDependencies.groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(masterKey)) - val decryptedState = if (group.snapshot == null) { - DecryptedGroup(revision = GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) - } else { - group.snapshot.toLocal(operations) - } - - val values = ContentValues().apply { - put(RecipientTable.GROUP_ID, groupId.toString()) - put(RecipientTable.AVATAR_COLOR, AvatarColorHash.forGroupId(groupId).serialize()) - put(RecipientTable.PROFILE_SHARING, group.whitelisted) - put(RecipientTable.TYPE, RecipientTable.RecipientType.GV2.id) - put(RecipientTable.STORAGE_SERVICE_ID, Base64.encodeWithPadding(StorageSyncHelper.generateKey())) - if (group.hideStory) { - val extras = RecipientExtras.Builder().hideStory(true).build() - put(RecipientTable.EXTRAS, extras.encode()) - } - } - - val recipientId = writableDatabase.insert(RecipientTable.TABLE_NAME, null, values) - val restoredId = SignalDatabase.groups.create(masterKey, decryptedState, groupSendEndorsements = null) - if (restoredId != null) { - SignalDatabase.groups.setShowAsStoryState(restoredId, group.storySendMode.toLocal()) - } - - return RecipientId.from(recipientId) -} - -private fun Group.StorySendMode.toLocal(): GroupTable.ShowAsStoryState { - return when (this) { - Group.StorySendMode.ENABLED -> GroupTable.ShowAsStoryState.ALWAYS - Group.StorySendMode.DISABLED -> GroupTable.ShowAsStoryState.NEVER - Group.StorySendMode.DEFAULT -> GroupTable.ShowAsStoryState.IF_ACTIVE - } -} - -private fun Group.MemberPendingProfileKey.toLocal(operations: GroupsV2Operations.GroupOperations): DecryptedPendingMember { - return DecryptedPendingMember( - serviceIdBytes = member!!.userId, - role = member.role.toLocal(), - addedByAci = addedByUserId, - timestamp = timestamp, - serviceIdCipherText = operations.encryptServiceId(ServiceId.Companion.parseOrNull(member.userId)) - ) -} - -private fun Contact.Visibility.toLocal(): Recipient.HiddenState { - return when (this) { - Contact.Visibility.VISIBLE -> Recipient.HiddenState.NOT_HIDDEN - Contact.Visibility.HIDDEN -> Recipient.HiddenState.HIDDEN - Contact.Visibility.HIDDEN_MESSAGE_REQUEST -> Recipient.HiddenState.HIDDEN_MESSAGE_REQUEST - } -} - -private fun Group.AccessControl.AccessRequired.toLocal(): AccessControl.AccessRequired { - return when (this) { - Group.AccessControl.AccessRequired.UNKNOWN -> AccessControl.AccessRequired.UNKNOWN - Group.AccessControl.AccessRequired.ANY -> AccessControl.AccessRequired.ANY - Group.AccessControl.AccessRequired.MEMBER -> AccessControl.AccessRequired.MEMBER - Group.AccessControl.AccessRequired.ADMINISTRATOR -> AccessControl.AccessRequired.ADMINISTRATOR - Group.AccessControl.AccessRequired.UNSATISFIABLE -> AccessControl.AccessRequired.UNSATISFIABLE - } -} - -private fun Group.AccessControl.toLocal(): AccessControl { - return AccessControl(members = this.members.toLocal(), attributes = this.attributes.toLocal(), addFromInviteLink = this.addFromInviteLink.toLocal()) -} - -private fun Group.Member.Role.toLocal(): Member.Role { - return when (this) { - Group.Member.Role.UNKNOWN -> Member.Role.UNKNOWN - Group.Member.Role.DEFAULT -> Member.Role.DEFAULT - Group.Member.Role.ADMINISTRATOR -> Member.Role.ADMINISTRATOR - } -} - -private fun Group.Member.toLocal(): DecryptedMember { - return DecryptedMember(aciBytes = userId, role = role.toLocal(), joinedAtRevision = joinedAtVersion) -} - -private fun Group.MemberPendingAdminApproval.toLocal(): DecryptedRequestingMember { - return DecryptedRequestingMember( - aciBytes = this.userId, - timestamp = this.timestamp - ) -} - -private fun Group.MemberBanned.toLocal(): DecryptedBannedMember { - return DecryptedBannedMember( - serviceIdBytes = this.userId, - timestamp = this.timestamp - ) -} - -private fun Group.GroupSnapshot.toLocal(operations: GroupsV2Operations.GroupOperations): DecryptedGroup { - return DecryptedGroup( - title = this.title?.title ?: "", - avatar = this.avatarUrl, - disappearingMessagesTimer = DecryptedTimer(duration = this.disappearingMessagesTimer?.disappearingMessagesDuration ?: 0), - accessControl = this.accessControl?.toLocal(), - revision = this.version, - members = this.members.map { member -> member.toLocal() }, - pendingMembers = this.membersPendingProfileKey.map { pending -> pending.toLocal(operations) }, - requestingMembers = this.membersPendingAdminApproval.map { requesting -> requesting.toLocal() }, - inviteLinkPassword = this.inviteLinkPassword, - description = this.description?.descriptionText ?: "", - isAnnouncementGroup = if (this.announcements_only) EnabledState.ENABLED else EnabledState.DISABLED, - bannedMembers = this.members_banned.map { it.toLocal() } - ) -} - -private fun Contact.toLocalExtras(): RecipientExtras { - return RecipientExtras( - hideStory = this.hideStory - ) -} - -private val Contact.formattedE164: String? - get() { - return e164?.let { - PhoneNumberFormatter.get(AppDependencies.application).format(e164.toString()) - } - } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/StickerTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/StickerTableArchiveExtensions.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/StickerTableBackupExtensions.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/StickerTableArchiveExtensions.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableArchiveExtensions.kt new file mode 100644 index 0000000000..6874572bb1 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableArchiveExtensions.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import org.signal.core.util.SqlUtil +import org.thoughtcrime.securesms.backup.v2.exporters.ChatArchiveExporter +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.ThreadTable + +fun ThreadTable.getThreadsForBackup(db: SignalDatabase): ChatArchiveExporter { + //language=sql + val query = """ + SELECT + ${ThreadTable.TABLE_NAME}.${ThreadTable.ID}, + ${ThreadTable.RECIPIENT_ID}, + ${ThreadTable.PINNED}, + ${ThreadTable.READ}, + ${ThreadTable.ARCHIVED}, + ${RecipientTable.TABLE_NAME}.${RecipientTable.MESSAGE_EXPIRATION_TIME}, + ${RecipientTable.TABLE_NAME}.${RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION}, + ${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}, + ${RecipientTable.TABLE_NAME}.${RecipientTable.MENTION_SETTING}, + ${RecipientTable.TABLE_NAME}.${RecipientTable.CHAT_COLORS}, + ${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}, + ${RecipientTable.TABLE_NAME}.${RecipientTable.WALLPAPER} + FROM ${ThreadTable.TABLE_NAME} + LEFT OUTER JOIN ${RecipientTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} + WHERE ${ThreadTable.ACTIVE} = 1 + """ + val cursor = readableDatabase.query(query) + + return ChatArchiveExporter(cursor, db) +} + +fun ThreadTable.clearAllDataForBackupRestore() { + writableDatabase.delete(ThreadTable.TABLE_NAME, null, null) + SqlUtil.resetAutoIncrementValue(writableDatabase, ThreadTable.TABLE_NAME) + clearCache() +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt deleted file mode 100644 index 6745697556..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.database - -import androidx.core.content.contentValuesOf -import org.signal.core.util.SqlUtil -import org.signal.core.util.insertInto -import org.signal.core.util.logging.Log -import org.signal.core.util.toInt -import org.thoughtcrime.securesms.attachments.AttachmentId -import org.thoughtcrime.securesms.backup.v2.ImportState -import org.thoughtcrime.securesms.backup.v2.exporters.ChatArchiveExportIterator -import org.thoughtcrime.securesms.backup.v2.proto.Chat -import org.thoughtcrime.securesms.backup.v2.util.parseChatWallpaper -import org.thoughtcrime.securesms.backup.v2.util.toLocal -import org.thoughtcrime.securesms.backup.v2.util.toLocalAttachment -import org.thoughtcrime.securesms.conversation.colors.ChatColors -import org.thoughtcrime.securesms.database.RecipientTable -import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.database.ThreadTable -import org.thoughtcrime.securesms.recipients.RecipientId -import org.thoughtcrime.securesms.wallpaper.UriChatWallpaper - -private val TAG = Log.tag(ThreadTable::class.java) - -fun ThreadTable.getThreadsForBackup(db: SignalDatabase): ChatArchiveExportIterator { - //language=sql - val query = """ - SELECT - ${ThreadTable.TABLE_NAME}.${ThreadTable.ID}, - ${ThreadTable.RECIPIENT_ID}, - ${ThreadTable.PINNED}, - ${ThreadTable.READ}, - ${ThreadTable.ARCHIVED}, - ${RecipientTable.TABLE_NAME}.${RecipientTable.MESSAGE_EXPIRATION_TIME}, - ${RecipientTable.TABLE_NAME}.${RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION}, - ${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}, - ${RecipientTable.TABLE_NAME}.${RecipientTable.MENTION_SETTING}, - ${RecipientTable.TABLE_NAME}.${RecipientTable.CHAT_COLORS}, - ${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}, - ${RecipientTable.TABLE_NAME}.${RecipientTable.WALLPAPER} - FROM ${ThreadTable.TABLE_NAME} - LEFT OUTER JOIN ${RecipientTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} - WHERE ${ThreadTable.ACTIVE} = 1 - """ - val cursor = readableDatabase.query(query) - - return ChatArchiveExportIterator(cursor, db) -} - -fun ThreadTable.clearAllDataForBackupRestore() { - writableDatabase.delete(ThreadTable.TABLE_NAME, null, null) - SqlUtil.resetAutoIncrementValue(writableDatabase, ThreadTable.TABLE_NAME) - clearCache() -} - -fun ThreadTable.restoreFromBackup(chat: Chat, recipientId: RecipientId, importState: ImportState): Long { - val chatColor = chat.style?.toLocal(importState) - - val wallpaperAttachmentId: AttachmentId? = chat.style?.wallpaperPhoto?.let { filePointer -> - filePointer.toLocalAttachment(importState)?.let { - SignalDatabase.attachments.restoreWallpaperAttachment(it) - } - } - - val chatWallpaper = chat.style?.parseChatWallpaper(wallpaperAttachmentId) - - val threadId = writableDatabase - .insertInto(ThreadTable.TABLE_NAME) - .values( - ThreadTable.RECIPIENT_ID to recipientId.serialize(), - ThreadTable.PINNED to chat.pinnedOrder, - ThreadTable.ARCHIVED to chat.archived.toInt(), - ThreadTable.READ to if (chat.markedUnread) ThreadTable.ReadStatus.FORCED_UNREAD.serialize() else ThreadTable.ReadStatus.READ.serialize(), - ThreadTable.ACTIVE to 1 - ) - .run() - - writableDatabase - .update( - RecipientTable.TABLE_NAME, - contentValuesOf( - RecipientTable.MENTION_SETTING to (if (chat.dontNotifyForMentionsIfMuted) RecipientTable.MentionSetting.DO_NOT_NOTIFY.id else RecipientTable.MentionSetting.ALWAYS_NOTIFY.id), - RecipientTable.MUTE_UNTIL to chat.muteUntilMs, - RecipientTable.MESSAGE_EXPIRATION_TIME to chat.expirationTimerMs, - RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION to chat.expireTimerVersion, - RecipientTable.CHAT_COLORS to chatColor?.serialize()?.encode(), - RecipientTable.CUSTOM_CHAT_COLORS_ID to (chatColor?.id ?: ChatColors.Id.NotSet).longValue, - RecipientTable.WALLPAPER_URI to if (chatWallpaper is UriChatWallpaper) chatWallpaper.uri.toString() else null, - RecipientTable.WALLPAPER to chatWallpaper?.serialize()?.encode() - ), - "${RecipientTable.ID} = ?", - SqlUtil.buildArgs(recipientId.toLong()) - ) - - return threadId -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExporter.kt similarity index 91% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExportIterator.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExporter.kt index ecd965514c..a946a3423d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/AdHocCallArchiveExporter.kt @@ -15,7 +15,7 @@ import java.io.Closeable * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. */ -class AdHocCallArchiveExportIterator(private val cursor: Cursor) : Iterator, Closeable { +class AdHocCallArchiveExporter(private val cursor: Cursor) : Iterator, Closeable { override fun hasNext(): Boolean { return cursor.count > 0 && !cursor.isLast } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExporter.kt similarity index 94% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExportIterator.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExporter.kt index ee948cebd5..90c0fb4da3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/CallLinkArchiveExporter.kt @@ -18,7 +18,7 @@ import java.io.Closeable * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. */ -class CallLinkArchiveExportIterator(private val cursor: Cursor) : Iterator, Closeable { +class CallLinkArchiveExporter(private val cursor: Cursor) : Iterator, Closeable { override fun hasNext(): Boolean { return cursor.count > 0 && !cursor.isLast } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExporter.kt similarity index 95% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExportIterator.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExporter.kt index e4c13bb988..4c4bbc82da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExporter.kt @@ -21,7 +21,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper import java.io.Closeable -class ChatArchiveExportIterator(private val cursor: Cursor, private val db: SignalDatabase) : Iterator, Closeable { +class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalDatabase) : Iterator, Closeable { override fun hasNext(): Boolean { return cursor.count > 0 && !cursor.isLast } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExportIterator.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index e2271d965f..1763385b76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -85,7 +85,7 @@ import kotlin.jvm.optionals.getOrNull import org.thoughtcrime.securesms.backup.v2.proto.BodyRange as BackupBodyRange import org.thoughtcrime.securesms.backup.v2.proto.GiftBadge as BackupGiftBadge -private val TAG = Log.tag(ChatItemArchiveExportIterator::class.java) +private val TAG = Log.tag(ChatItemArchiveExporter::class.java) private const val COLUMN_BASE_TYPE = "base_type" /** @@ -95,7 +95,7 @@ private const val COLUMN_BASE_TYPE = "base_type" * * All of this complexity is hidden from the user -- they just get a normal iterator interface. */ -class ChatItemArchiveExportIterator( +class ChatItemArchiveExporter( private val db: SignalDatabase, private val cursor: Cursor, private val batchSize: Int, diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExporter.kt similarity index 96% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExportIterator.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExporter.kt index 051bb19e88..bec86d97e0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExporter.kt @@ -26,7 +26,7 @@ import java.io.Closeable * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [ArchiveRecipient]s. * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. */ -class ContactArchiveExportIterator(private val cursor: Cursor, private val selfId: Long) : Iterator, Closeable { +class ContactArchiveExporter(private val cursor: Cursor, private val selfId: Long) : Iterator, Closeable { override fun hasNext(): Boolean { return cursor.count > 0 && !cursor.isLast } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExporter.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExportIterator.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExporter.kt index c33308ccad..4e29525946 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/DistributionListArchiveExporter.kt @@ -24,7 +24,7 @@ import org.whispersystems.signalservice.api.push.DistributionId import org.whispersystems.signalservice.api.util.toByteArray import java.io.Closeable -class DistributionListArchiveExportIterator( +class DistributionListArchiveExporter( private val cursor: Cursor, private val distributionListTables: DistributionListTables ) : Iterator, Closeable { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExporter.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExportIterator.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExporter.kt index 30d930c9f2..889c555f69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/GroupArchiveExporter.kt @@ -33,7 +33,7 @@ import java.io.Closeable * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [ArchiveRecipient]s. * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. */ -class GroupArchiveExportIterator(private val cursor: Cursor) : Iterator, Closeable { +class GroupArchiveExporter(private val cursor: Cursor) : Iterator, Closeable { override fun hasNext(): Boolean { return cursor.count > 0 && !cursor.isLast diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/AdHodCallArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/AdHodCallArchiveImporter.kt new file mode 100644 index 0000000000..22b7dc1c40 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/AdHodCallArchiveImporter.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.importer + +import org.signal.core.util.insertInto +import org.thoughtcrime.securesms.backup.v2.ImportState +import org.thoughtcrime.securesms.backup.v2.proto.AdHocCall +import org.thoughtcrime.securesms.database.CallTable +import org.thoughtcrime.securesms.database.SignalDatabase + +/** + * Handles the importing of [AdHocCall] models into the local database. + */ +object AdHodCallArchiveImporter { + fun import(call: AdHocCall, importState: ImportState) { + val event = when (call.state) { + AdHocCall.State.GENERIC -> CallTable.Event.GENERIC_GROUP_CALL + AdHocCall.State.UNKNOWN_STATE -> CallTable.Event.GENERIC_GROUP_CALL + } + + SignalDatabase.writableDatabase + .insertInto(CallTable.TABLE_NAME) + .values( + CallTable.CALL_ID to call.callId, + CallTable.PEER to importState.remoteToLocalRecipientId[call.recipientId]!!.serialize(), + CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL), + CallTable.DIRECTION to CallTable.Direction.serialize(CallTable.Direction.OUTGOING), + CallTable.EVENT to CallTable.Event.serialize(event), + CallTable.TIMESTAMP to call.callTimestamp + ) + .run() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/CallLinkArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/CallLinkArchiveImporter.kt new file mode 100644 index 0000000000..c1d7d1bad5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/CallLinkArchiveImporter.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.importer + +import org.signal.ringrtc.CallLinkRootKey +import org.signal.ringrtc.CallLinkState +import org.thoughtcrime.securesms.backup.v2.ArchiveCallLink +import org.thoughtcrime.securesms.backup.v2.proto.CallLink +import org.thoughtcrime.securesms.database.CallLinkTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials +import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId +import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkState +import java.time.Instant + +/** + * Handles the importing of [ArchiveCallLink] models into the local database. + */ +object CallLinkArchiveImporter { + fun import(callLink: ArchiveCallLink): RecipientId? { + val rootKey: CallLinkRootKey + try { + rootKey = CallLinkRootKey(callLink.rootKey.toByteArray()) + } catch (e: Exception) { + return null + } + return SignalDatabase.callLinks.insertCallLink( + CallLinkTable.CallLink( + recipientId = RecipientId.UNKNOWN, + roomId = CallLinkRoomId.fromCallLinkRootKey(rootKey), + credentials = CallLinkCredentials(callLink.rootKey.toByteArray(), callLink.adminKey?.toByteArray()), + state = SignalCallLinkState( + name = callLink.name, + restrictions = callLink.restrictions.toLocal(), + expiration = Instant.ofEpochMilli(callLink.expirationMs) + ), + deletionTimestamp = 0L + ) + ) + } +} + +private fun CallLink.Restrictions.toLocal(): CallLinkState.Restrictions { + return when (this) { + CallLink.Restrictions.ADMIN_APPROVAL -> CallLinkState.Restrictions.ADMIN_APPROVAL + CallLink.Restrictions.NONE -> CallLinkState.Restrictions.NONE + CallLink.Restrictions.UNKNOWN -> CallLinkState.Restrictions.UNKNOWN + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt new file mode 100644 index 0000000000..ae59e47d37 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.importer + +import androidx.core.content.contentValuesOf +import org.signal.core.util.SqlUtil +import org.signal.core.util.insertInto +import org.signal.core.util.toInt +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.backup.v2.ImportState +import org.thoughtcrime.securesms.backup.v2.database.restoreWallpaperAttachment +import org.thoughtcrime.securesms.backup.v2.proto.Chat +import org.thoughtcrime.securesms.backup.v2.util.parseChatWallpaper +import org.thoughtcrime.securesms.backup.v2.util.toLocal +import org.thoughtcrime.securesms.backup.v2.util.toLocalAttachment +import org.thoughtcrime.securesms.conversation.colors.ChatColors +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.ThreadTable +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.wallpaper.UriChatWallpaper + +/** + * Handles the importing of [Chat] models into the local database. + */ +object ChatArchiveImporter { + fun import(chat: Chat, recipientId: RecipientId, importState: ImportState): Long { + val chatColor = chat.style?.toLocal(importState) + + val wallpaperAttachmentId: AttachmentId? = chat.style?.wallpaperPhoto?.let { filePointer -> + filePointer.toLocalAttachment(importState)?.let { + SignalDatabase.attachments.restoreWallpaperAttachment(it) + } + } + + val chatWallpaper = chat.style?.parseChatWallpaper(wallpaperAttachmentId) + + val threadId = SignalDatabase.writableDatabase + .insertInto(ThreadTable.TABLE_NAME) + .values( + ThreadTable.RECIPIENT_ID to recipientId.serialize(), + ThreadTable.PINNED to chat.pinnedOrder, + ThreadTable.ARCHIVED to chat.archived.toInt(), + ThreadTable.READ to if (chat.markedUnread) ThreadTable.ReadStatus.FORCED_UNREAD.serialize() else ThreadTable.ReadStatus.READ.serialize(), + ThreadTable.ACTIVE to 1 + ) + .run() + + SignalDatabase.writableDatabase + .update( + RecipientTable.TABLE_NAME, + contentValuesOf( + RecipientTable.MENTION_SETTING to (if (chat.dontNotifyForMentionsIfMuted) RecipientTable.MentionSetting.DO_NOT_NOTIFY.id else RecipientTable.MentionSetting.ALWAYS_NOTIFY.id), + RecipientTable.MUTE_UNTIL to chat.muteUntilMs, + RecipientTable.MESSAGE_EXPIRATION_TIME to chat.expirationTimerMs, + RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION to chat.expireTimerVersion, + RecipientTable.CHAT_COLORS to chatColor?.serialize()?.encode(), + RecipientTable.CUSTOM_CHAT_COLORS_ID to (chatColor?.id ?: ChatColors.Id.NotSet).longValue, + RecipientTable.WALLPAPER_URI to if (chatWallpaper is UriChatWallpaper) chatWallpaper.uri.toString() else null, + RecipientTable.WALLPAPER to chatWallpaper?.serialize()?.encode() + ), + "${RecipientTable.ID} = ?", + SqlUtil.buildArgs(recipientId.toLong()) + ) + + return threadId + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt index 119f96510c..b32a19ecfa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt @@ -1,9 +1,9 @@ /* - * Copyright 2023 Signal Messenger, LLC + * Copyright 2024 Signal Messenger, LLC * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.database +package org.thoughtcrime.securesms.backup.v2.importer import android.content.ContentValues import androidx.core.content.contentValuesOf @@ -82,13 +82,13 @@ import org.thoughtcrime.securesms.backup.v2.proto.GiftBadge as BackupGiftBadge * An object that will ingest all fo the [ChatItem]s you want to write, buffer them until hitting a specified batch size, and then batch insert them * for fast throughput. */ -class ChatItemImportInserter( +class ChatItemArchiveImporter( private val db: SQLiteDatabase, private val importState: ImportState, private val batchSize: Int ) { companion object { - private val TAG = Log.tag(ChatItemImportInserter::class.java) + private val TAG = Log.tag(ChatItemArchiveImporter::class.java) private val MESSAGE_COLUMNS = arrayOf( MessageTable.DATE_SENT, @@ -150,7 +150,7 @@ class ChatItemImportInserter( * Indicate that you want to insert the [ChatItem] into the database. * If this item causes the buffer to hit the batch size, then a batch of items will actually be inserted. */ - fun insert(chatItem: ChatItem) { + fun import(chatItem: ChatItem) { val fromLocalRecipientId: RecipientId? = importState.remoteToLocalRecipientId[chatItem.authorId] if (fromLocalRecipientId == null) { Log.w(TAG, "[insert] Could not find a local recipient for backup recipient ID ${chatItem.authorId}! Skipping.") diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt new file mode 100644 index 0000000000..dd13ebcb95 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.importer + +import androidx.core.content.contentValuesOf +import org.signal.core.util.Base64 +import org.signal.core.util.toInt +import org.signal.core.util.update +import org.thoughtcrime.securesms.backup.v2.proto.Contact +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.push.ServiceId.PNI + +/** + * Handles the importing of [Contact] models into the local database. + */ +object ContactArchiveImporter { + fun import(contact: Contact): RecipientId { + val id = SignalDatabase.recipients.getAndPossiblyMergePnpVerified( + aci = ACI.parseOrNull(contact.aci?.toByteArray()), + pni = PNI.parseOrNull(contact.pni?.toByteArray()), + e164 = contact.formattedE164 + ) + + val profileKey = contact.profileKey?.toByteArray() + val values = contentValuesOf( + RecipientTable.BLOCKED to contact.blocked, + RecipientTable.HIDDEN to contact.visibility.toLocal().serialize(), + RecipientTable.TYPE to RecipientTable.RecipientType.INDIVIDUAL.id, + RecipientTable.PROFILE_FAMILY_NAME to contact.profileFamilyName, + RecipientTable.PROFILE_GIVEN_NAME to contact.profileGivenName, + RecipientTable.PROFILE_JOINED_NAME to ProfileName.fromParts(contact.profileGivenName, contact.profileFamilyName).toString(), + RecipientTable.PROFILE_KEY to if (profileKey == null) null else Base64.encodeWithPadding(profileKey), + RecipientTable.PROFILE_SHARING to contact.profileSharing.toInt(), + RecipientTable.USERNAME to contact.username, + RecipientTable.EXTRAS to contact.toLocalExtras().encode() + ) + + if (contact.registered != null) { + values.put(RecipientTable.UNREGISTERED_TIMESTAMP, 0L) + values.put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.REGISTERED.id) + } else if (contact.notRegistered != null) { + values.put(RecipientTable.UNREGISTERED_TIMESTAMP, contact.notRegistered.unregisteredTimestamp) + values.put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.NOT_REGISTERED.id) + } + + SignalDatabase.writableDatabase + .update(RecipientTable.TABLE_NAME) + .values(values) + .where("${RecipientTable.ID} = ?", id) + .run() + + return id + } +} + +private fun Contact.Visibility.toLocal(): Recipient.HiddenState { + return when (this) { + Contact.Visibility.VISIBLE -> Recipient.HiddenState.NOT_HIDDEN + Contact.Visibility.HIDDEN -> Recipient.HiddenState.HIDDEN + Contact.Visibility.HIDDEN_MESSAGE_REQUEST -> Recipient.HiddenState.HIDDEN_MESSAGE_REQUEST + } +} + +private fun Contact.toLocalExtras(): RecipientExtras { + return RecipientExtras( + hideStory = this.hideStory + ) +} + +private val Contact.formattedE164: String? + get() { + return e164?.let { + PhoneNumberFormatter.get(AppDependencies.application).format(e164.toString()) + } + } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/DistributionListArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/DistributionListArchiveImporter.kt new file mode 100644 index 0000000000..8efcb8fb90 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/DistributionListArchiveImporter.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.importer + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.v2.ImportState +import org.thoughtcrime.securesms.backup.v2.proto.DistributionList +import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.signalservice.api.push.DistributionId +import org.whispersystems.signalservice.api.util.UuidUtil + +/** + * Handles the importing of [DistributionListItem] models into the local database. + */ +object DistributionListArchiveImporter { + + private val TAG = Log.tag(DistributionListArchiveImporter.javaClass) + + fun import(dlistItem: DistributionListItem, importState: ImportState): RecipientId? { + if (dlistItem.deletionTimestamp != null && dlistItem.deletionTimestamp > 0) { + val dlistId = SignalDatabase.distributionLists.createList( + name = "", + members = emptyList(), + distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)), + allowsReplies = false, + deletionTimestamp = dlistItem.deletionTimestamp, + storageId = null, + privacyMode = DistributionListPrivacyMode.ONLY_WITH + )!! + + return SignalDatabase.distributionLists.getRecipientId(dlistId)!! + } + + val dlist = dlistItem.distributionList ?: return null + val members: List = dlist.memberRecipientIds + .mapNotNull { importState.remoteToLocalRecipientId[it] } + + if (members.size != dlist.memberRecipientIds.size) { + Log.w(TAG, "Couldn't find some member recipients! Missing backup recipientIds: ${dlist.memberRecipientIds.toSet() - members.toSet()}") + } + + val distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)) + val privacyMode = dlist.privacyMode.toLocalPrivacyMode() + + val dlistId = SignalDatabase.distributionLists.createList( + name = dlist.name, + members = members, + distributionId = distributionId, + allowsReplies = dlist.allowReplies, + deletionTimestamp = dlistItem.deletionTimestamp ?: 0, + storageId = null, + privacyMode = privacyMode + )!! + + return SignalDatabase.distributionLists.getRecipientId(dlistId)!! + } +} + +private fun DistributionList.PrivacyMode.toLocalPrivacyMode(): DistributionListPrivacyMode { + return when (this) { + DistributionList.PrivacyMode.UNKNOWN -> DistributionListPrivacyMode.ALL + DistributionList.PrivacyMode.ONLY_WITH -> DistributionListPrivacyMode.ONLY_WITH + DistributionList.PrivacyMode.ALL -> DistributionListPrivacyMode.ALL + DistributionList.PrivacyMode.ALL_EXCEPT -> DistributionListPrivacyMode.ALL_EXCEPT + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/GroupArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/GroupArchiveImporter.kt new file mode 100644 index 0000000000..ea3597045b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/GroupArchiveImporter.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.importer + +import android.content.ContentValues +import org.signal.core.util.Base64 +import org.signal.libsignal.zkgroup.groups.GroupMasterKey +import org.signal.libsignal.zkgroup.groups.GroupSecretParams +import org.signal.storageservice.protos.groups.AccessControl +import org.signal.storageservice.protos.groups.Member +import org.signal.storageservice.protos.groups.local.DecryptedBannedMember +import org.signal.storageservice.protos.groups.local.DecryptedGroup +import org.signal.storageservice.protos.groups.local.DecryptedMember +import org.signal.storageservice.protos.groups.local.DecryptedPendingMember +import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember +import org.signal.storageservice.protos.groups.local.DecryptedTimer +import org.signal.storageservice.protos.groups.local.EnabledState +import org.thoughtcrime.securesms.backup.v2.ArchiveGroup +import org.thoughtcrime.securesms.backup.v2.proto.Group +import org.thoughtcrime.securesms.conversation.colors.AvatarColorHash +import org.thoughtcrime.securesms.database.GroupTable +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.groups.GroupId +import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.storage.StorageSyncHelper +import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations +import org.whispersystems.signalservice.api.push.ServiceId + +/** + * Handles the importing of [ArchiveGroup] models into the local database. + */ +object GroupArchiveImporter { + fun import(group: ArchiveGroup): RecipientId { + val masterKey = GroupMasterKey(group.masterKey.toByteArray()) + val groupId = GroupId.v2(masterKey) + + val operations = AppDependencies.groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(masterKey)) + val decryptedState = if (group.snapshot == null) { + DecryptedGroup(revision = GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) + } else { + group.snapshot.toLocal(operations) + } + + val values = ContentValues().apply { + put(RecipientTable.GROUP_ID, groupId.toString()) + put(RecipientTable.AVATAR_COLOR, AvatarColorHash.forGroupId(groupId).serialize()) + put(RecipientTable.PROFILE_SHARING, group.whitelisted) + put(RecipientTable.TYPE, RecipientTable.RecipientType.GV2.id) + put(RecipientTable.STORAGE_SERVICE_ID, Base64.encodeWithPadding(StorageSyncHelper.generateKey())) + if (group.hideStory) { + val extras = RecipientExtras.Builder().hideStory(true).build() + put(RecipientTable.EXTRAS, extras.encode()) + } + } + + val recipientId = SignalDatabase.writableDatabase.insert(RecipientTable.TABLE_NAME, null, values) + val restoredId = SignalDatabase.groups.create(masterKey, decryptedState, groupSendEndorsements = null) + if (restoredId != null) { + SignalDatabase.groups.setShowAsStoryState(restoredId, group.storySendMode.toLocal()) + } + + return RecipientId.from(recipientId) + } +} + +private fun Group.StorySendMode.toLocal(): GroupTable.ShowAsStoryState { + return when (this) { + Group.StorySendMode.ENABLED -> GroupTable.ShowAsStoryState.ALWAYS + Group.StorySendMode.DISABLED -> GroupTable.ShowAsStoryState.NEVER + Group.StorySendMode.DEFAULT -> GroupTable.ShowAsStoryState.IF_ACTIVE + } +} + +private fun Group.MemberPendingProfileKey.toLocal(operations: GroupsV2Operations.GroupOperations): DecryptedPendingMember { + return DecryptedPendingMember( + serviceIdBytes = member!!.userId, + role = member.role.toLocal(), + addedByAci = addedByUserId, + timestamp = timestamp, + serviceIdCipherText = operations.encryptServiceId(ServiceId.Companion.parseOrNull(member.userId)) + ) +} + +private fun Group.AccessControl.AccessRequired.toLocal(): AccessControl.AccessRequired { + return when (this) { + Group.AccessControl.AccessRequired.UNKNOWN -> AccessControl.AccessRequired.UNKNOWN + Group.AccessControl.AccessRequired.ANY -> AccessControl.AccessRequired.ANY + Group.AccessControl.AccessRequired.MEMBER -> AccessControl.AccessRequired.MEMBER + Group.AccessControl.AccessRequired.ADMINISTRATOR -> AccessControl.AccessRequired.ADMINISTRATOR + Group.AccessControl.AccessRequired.UNSATISFIABLE -> AccessControl.AccessRequired.UNSATISFIABLE + } +} + +private fun Group.AccessControl.toLocal(): AccessControl { + return AccessControl(members = this.members.toLocal(), attributes = this.attributes.toLocal(), addFromInviteLink = this.addFromInviteLink.toLocal()) +} + +private fun Group.Member.Role.toLocal(): Member.Role { + return when (this) { + Group.Member.Role.UNKNOWN -> Member.Role.UNKNOWN + Group.Member.Role.DEFAULT -> Member.Role.DEFAULT + Group.Member.Role.ADMINISTRATOR -> Member.Role.ADMINISTRATOR + } +} + +private fun Group.Member.toLocal(): DecryptedMember { + return DecryptedMember(aciBytes = userId, role = role.toLocal(), joinedAtRevision = joinedAtVersion) +} + +private fun Group.MemberPendingAdminApproval.toLocal(): DecryptedRequestingMember { + return DecryptedRequestingMember( + aciBytes = this.userId, + timestamp = this.timestamp + ) +} + +private fun Group.MemberBanned.toLocal(): DecryptedBannedMember { + return DecryptedBannedMember( + serviceIdBytes = this.userId, + timestamp = this.timestamp + ) +} + +private fun Group.GroupSnapshot.toLocal(operations: GroupsV2Operations.GroupOperations): DecryptedGroup { + return DecryptedGroup( + title = this.title?.title ?: "", + avatar = this.avatarUrl, + disappearingMessagesTimer = DecryptedTimer(duration = this.disappearingMessagesTimer?.disappearingMessagesDuration ?: 0), + accessControl = this.accessControl?.toLocal(), + revision = this.version, + members = this.members.map { member -> member.toLocal() }, + pendingMembers = this.membersPendingProfileKey.map { pending -> pending.toLocal(operations) }, + requestingMembers = this.membersPendingAdminApproval.map { requesting -> requesting.toLocal() }, + inviteLinkPassword = this.inviteLinkPassword, + description = this.description?.descriptionText ?: "", + isAnnouncementGroup = if (this.announcements_only) EnabledState.ENABLED else EnabledState.DISABLED, + bannedMembers = this.members_banned.map { it.toLocal() } + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataArchiveProcessor.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataBackupProcessor.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataArchiveProcessor.kt index 7f38528e1c..bf61d7b9dc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataArchiveProcessor.kt @@ -44,9 +44,9 @@ import java.util.Currency /** * Handles importing/exporting [AccountData] frames for an archive. */ -object AccountDataBackupProcessor { +object AccountDataArchiveProcessor { - private val TAG = Log.tag(AccountDataBackupProcessor::class) + private val TAG = Log.tag(AccountDataArchiveProcessor::class) fun export(db: SignalDatabase, signalStore: SignalStore, emitter: BackupFrameEmitter) { val context = AppDependencies.application diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallArchiveProcessor.kt similarity index 79% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallBackupProcessor.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallArchiveProcessor.kt index d5f341a7c3..2e55bbf9f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AdHocCallArchiveProcessor.kt @@ -8,7 +8,7 @@ package org.thoughtcrime.securesms.backup.v2.processor import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.backup.v2.ImportState import org.thoughtcrime.securesms.backup.v2.database.getAdhocCallsForBackup -import org.thoughtcrime.securesms.backup.v2.database.restoreCallLogFromBackup +import org.thoughtcrime.securesms.backup.v2.importer.AdHodCallArchiveImporter import org.thoughtcrime.securesms.backup.v2.proto.AdHocCall import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter @@ -17,9 +17,9 @@ import org.thoughtcrime.securesms.database.SignalDatabase /** * Handles importing/exporting [AdHocCall] frames for an archive. */ -object AdHocCallBackupProcessor { +object AdHocCallArchiveProcessor { - val TAG = Log.tag(AdHocCallBackupProcessor::class.java) + val TAG = Log.tag(AdHocCallArchiveProcessor::class.java) fun export(db: SignalDatabase, emitter: BackupFrameEmitter) { db.callTable.getAdhocCallsForBackup().use { reader -> @@ -30,6 +30,6 @@ object AdHocCallBackupProcessor { } fun import(call: AdHocCall, importState: ImportState) { - SignalDatabase.calls.restoreCallLogFromBackup(call, importState) + AdHodCallArchiveImporter.import(call, importState) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatArchiveProcessor.kt similarity index 76% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatArchiveProcessor.kt index a4d77b5154..274dc47e4f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatArchiveProcessor.kt @@ -9,7 +9,7 @@ import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.backup.v2.ExportState import org.thoughtcrime.securesms.backup.v2.ImportState import org.thoughtcrime.securesms.backup.v2.database.getThreadsForBackup -import org.thoughtcrime.securesms.backup.v2.database.restoreFromBackup +import org.thoughtcrime.securesms.backup.v2.importer.ChatArchiveImporter import org.thoughtcrime.securesms.backup.v2.proto.Chat import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter @@ -19,8 +19,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId /** * Handles importing/exporting [Chat] frames for an archive. */ -object ChatBackupProcessor { - val TAG = Log.tag(ChatBackupProcessor::class.java) +object ChatArchiveProcessor { + val TAG = Log.tag(ChatArchiveProcessor::class.java) fun export(db: SignalDatabase, exportState: ExportState, emitter: BackupFrameEmitter) { db.threadTable.getThreadsForBackup(db).use { reader -> @@ -42,10 +42,9 @@ object ChatBackupProcessor { return } - SignalDatabase.threads.restoreFromBackup(chat, recipientId, importState).let { threadId -> - importState.chatIdToLocalRecipientId[chat.id] = recipientId - importState.chatIdToLocalThreadId[chat.id] = threadId - importState.chatIdToBackupRecipientId[chat.id] = chat.recipientId - } + val threadId = ChatArchiveImporter.import(chat, recipientId, importState) + importState.chatIdToLocalRecipientId[chat.id] = recipientId + importState.chatIdToLocalThreadId[chat.id] = threadId + importState.chatIdToBackupRecipientId[chat.id] = chat.recipientId } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemArchiveProcessor.kt similarity index 84% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemArchiveProcessor.kt index 1bec7dda35..157a2b9bf2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemArchiveProcessor.kt @@ -8,9 +8,9 @@ package org.thoughtcrime.securesms.backup.v2.processor import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.backup.v2.ExportState import org.thoughtcrime.securesms.backup.v2.ImportState -import org.thoughtcrime.securesms.backup.v2.database.ChatItemImportInserter import org.thoughtcrime.securesms.backup.v2.database.createChatItemInserter import org.thoughtcrime.securesms.backup.v2.database.getMessagesForBackup +import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter import org.thoughtcrime.securesms.backup.v2.proto.ChatItem import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter @@ -19,8 +19,8 @@ import org.thoughtcrime.securesms.database.SignalDatabase /** * Handles importing/exporting [ChatItem] frames for an archive. */ -object ChatItemBackupProcessor { - val TAG = Log.tag(ChatItemBackupProcessor::class.java) +object ChatItemArchiveProcessor { + val TAG = Log.tag(ChatItemArchiveProcessor::class.java) fun export(db: SignalDatabase, exportState: ExportState, emitter: BackupFrameEmitter) { db.messageTable.getMessagesForBackup(db, exportState.backupTime, exportState.mediaBackupEnabled).use { chatItems -> @@ -35,7 +35,7 @@ object ChatItemBackupProcessor { } } - fun beginImport(importState: ImportState): ChatItemImportInserter { + fun beginImport(importState: ImportState): ChatItemArchiveImporter { return SignalDatabase.messages.createChatItemInserter(importState) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientArchiveProcessor.kt similarity index 79% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientArchiveProcessor.kt index 16f5170077..40ef804b6d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientArchiveProcessor.kt @@ -13,10 +13,11 @@ import org.thoughtcrime.securesms.backup.v2.database.getAllForBackup import org.thoughtcrime.securesms.backup.v2.database.getCallLinksForBackup import org.thoughtcrime.securesms.backup.v2.database.getContactsForBackup import org.thoughtcrime.securesms.backup.v2.database.getGroupsForBackup -import org.thoughtcrime.securesms.backup.v2.database.restoreContactFromBackup -import org.thoughtcrime.securesms.backup.v2.database.restoreFromBackup -import org.thoughtcrime.securesms.backup.v2.database.restoreGroupFromBackup import org.thoughtcrime.securesms.backup.v2.database.restoreReleaseNotes +import org.thoughtcrime.securesms.backup.v2.importer.CallLinkArchiveImporter +import org.thoughtcrime.securesms.backup.v2.importer.ContactArchiveImporter +import org.thoughtcrime.securesms.backup.v2.importer.DistributionListArchiveImporter +import org.thoughtcrime.securesms.backup.v2.importer.GroupArchiveImporter import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.proto.ReleaseNotes import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter @@ -27,9 +28,9 @@ import org.thoughtcrime.securesms.recipients.Recipient /** * Handles importing/exporting [ArchiveRecipient] frames for an archive. */ -object RecipientBackupProcessor { +object RecipientArchiveProcessor { - val TAG = Log.tag(RecipientBackupProcessor::class.java) + val TAG = Log.tag(RecipientArchiveProcessor::class.java) fun export(db: SignalDatabase, signalStore: SignalStore, exportState: ExportState, emitter: BackupFrameEmitter) { val selfId = db.recipientTable.getByAci(signalStore.accountValues.aci!!).get().toLong() @@ -79,12 +80,12 @@ object RecipientBackupProcessor { fun import(recipient: ArchiveRecipient, importState: ImportState) { val newId = when { - recipient.contact != null -> SignalDatabase.recipients.restoreContactFromBackup(recipient.contact) - recipient.group != null -> SignalDatabase.recipients.restoreGroupFromBackup(recipient.group) - recipient.distributionList != null -> SignalDatabase.distributionLists.restoreFromBackup(recipient.distributionList, importState) + recipient.contact != null -> ContactArchiveImporter.import(recipient.contact) + recipient.group != null -> GroupArchiveImporter.import(recipient.group) + recipient.distributionList != null -> DistributionListArchiveImporter.import(recipient.distributionList, importState) recipient.self != null -> Recipient.self().id recipient.releaseNotes != null -> SignalDatabase.recipients.restoreReleaseNotes() - recipient.callLink != null -> SignalDatabase.callLinks.restoreFromBackup(recipient.callLink) + recipient.callLink != null -> CallLinkArchiveImporter.import(recipient.callLink) else -> { Log.w(TAG, "Unrecognized recipient type!") null diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerArchiveProcessor.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerBackupProcessor.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerArchiveProcessor.kt index ff96b5d2cd..cf835f234c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerArchiveProcessor.kt @@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob /** * Handles importing/exporting [StickerPack] frames for an archive. */ -object StickerBackupProcessor { +object StickerArchiveProcessor { fun export(db: SignalDatabase, emitter: BackupFrameEmitter) { StickerPackRecordReader(db.stickerTable.allStickerPacks).use { reader -> var record: StickerPackRecord? = reader.next diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt index d91703ea57..3349df9a75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallInfoCallbacks.kt @@ -10,8 +10,6 @@ import android.content.Intent import android.widget.Toast import androidx.core.app.ShareCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder -import io.reactivex.rxjava3.disposables.CompositeDisposable -import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.BaseActivity import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.calls.links.CallLinks @@ -27,7 +25,7 @@ import org.thoughtcrime.securesms.events.CallParticipant */ class CallInfoCallbacks( private val activity: BaseActivity, - private val controlsAndInfoViewModel: ControlsAndInfoViewModel, + private val controlsAndInfoViewModel: ControlsAndInfoViewModel ) : CallInfoView.Callbacks { override fun onShareLinkClicked() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt index dea60f8cbb..d8b6e717cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt @@ -247,6 +247,14 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data val rawDatabase: net.zetetic.database.sqlcipher.SQLiteDatabase get() = instance!!.rawWritableDatabase + @JvmStatic + val readableDatabase: SQLiteDatabase + get() = instance!!.signalReadableDatabase + + @JvmStatic + val writableDatabase: SQLiteDatabase + get() = instance!!.signalWritableDatabase + @JvmStatic val backupDatabase: net.zetetic.database.sqlcipher.SQLiteDatabase get() = instance!!.rawReadableDatabase From 7427619bfd0eff5bdbed212fc92cac25d74e9aad Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 1 Oct 2024 15:43:13 -0400 Subject: [PATCH 30/53] Add registered check to connectivity warning. --- .../org/thoughtcrime/securesms/notifications/VitalsViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/VitalsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/VitalsViewModel.kt index ded919bce7..baebccd488 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/VitalsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/VitalsViewModel.kt @@ -73,7 +73,7 @@ class VitalsViewModel(private val context: Application) : AndroidViewModel(conte val timeSinceLastConnection = System.currentTimeMillis() - SignalStore.misc.lastWebSocketConnectTime val timeSinceLastConnectionWarning = System.currentTimeMillis() - SignalStore.misc.lastConnectivityWarningTime - if (ConnectivityWarning.isEnabled && timeSinceLastConnection > ConnectivityWarning.threshold && timeSinceLastConnectionWarning > 14.days.inWholeMilliseconds && NetworkUtil.isConnected(context)) { + if (ConnectivityWarning.isEnabled && timeSinceLastConnection > ConnectivityWarning.threshold && timeSinceLastConnectionWarning > 14.days.inWholeMilliseconds && NetworkUtil.isConnected(context) && SignalStore.account.isRegistered) { return@fromCallable if (ConnectivityWarning.isDebugPromptEnabled) { State.PROMPT_DEBUGLOGS_FOR_CONNECTIVITY_WARNING } else { From d2ba42074f65913b44f3bd227ec5674e4c6ed477 Mon Sep 17 00:00:00 2001 From: NetSysFire <59517351+NetSysFire@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:30:57 +0200 Subject: [PATCH 31/53] Switch to using an issue form for bug reports. Resolves #13721 --- .github/ISSUE_TEMPLATE/bug_report.md | 54 ------------------- .github/ISSUE_TEMPLATE/bug_report.yaml | 75 ++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 4 files changed, 77 insertions(+), 56 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 83a616771b..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: 🛠️ Bug report -about: Let us know that something isn't working as intended -title: '' -labels: '' -assignees: '' - ---- - - - -- [ ] I have searched open and closed issues for duplicates -- [ ] I am submitting a bug report for existing functionality that does not work as intended -- [ ] I have read https://github.com/signalapp/Signal-Android/wiki/Submitting-useful-bug-reports -- [ ] This isn't a feature request or a discussion topic - ----------------------------------------- - -### Bug description -Describe here the issue that you are experiencing. - -### Steps to reproduce -- using hyphens as bullet points -- list the steps -- that reproduce the bug - -**Actual result:** Describe here what happens after you run the steps above (i.e. the buggy behaviour) -**Expected result:** Describe here what should happen after you run the steps above (i.e. what would be the correct behaviour) - -### Screenshots - - - -### Device info - -**Device:** Manufacturer Model XVI -**Android version:** 0.0.0 -**Signal version:** 0.0.0 - -### Link to debug log - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000000..98176172df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,75 @@ +name: 🛠️ Bug report +description: Let us know that something isn't working as intended + +body: + - type: markdown + attributes: + value: | + Before we begin, please note that this tracker is only for issues. It is not for questions, comments, or feature requests. + + If you would like to discuss a new feature or submit suggestions, please visit the [community forum](https://community.signalusers.org). + + If you are looking for support, please visit our [support center](https://support.signal.org/) or email support@signal.org. + + - type: checkboxes + id: guidelines + attributes: + label: "Guidelines" + description: "Search issues here: https://github.com/signalapp/Signal-Android/issues" + options: + - label: I have searched searched open and closed issues for duplicates + required: true + - label: I am submitting a bug report for existing functionality that does not work as intended + required: true + - label: This isn't a feature request or a discussion topic + required: true + - type: textarea + id: description-of-bug + attributes: + label: Bug description + description: A clear and concise description of what the problem is that made you submit this report. + placeholder: When trying to do this, then... + validations: + required: true + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: "How to take screenshots on Android: https://support.google.com/android/answer/9075928" + placeholder: You can drag and drop images into this text box. + validations: + required: false + - type: input + id: device-model + attributes: + label: Device + description: Usually located in system settings -> About Phone + placeholder: Manufacturer and model, e.g. Samsung S24 + validations: + required: false + - type: input + id: android-version + attributes: + label: Android version + description: Usually located in system settings -> About Phone + placeholder: Android version, e.g. 14 + validations: + required: false + - type: input + id: signal-version + attributes: + label: Signal version + description: You can see Signal's version number at Settings -> Help + placeholder: App version, e.g. 7.17.6 + validations: + required: false + - type: textarea + id: debug-log + attributes: + label: Link to debug log + description: | + Submit a debug log via Settings -> Help -> Debug Log, then copy that URL here. Logs can only reliably cover ~24 hours, so please try to capture it as soon as you experience the bug! + See https://support.signal.org/hc/en-us/articles/360007318591#android_debug + placeholder: Debug log link, e.g. https://debuglogs.org/... + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 62f847d133..f4f1ad2233 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,6 +1,6 @@ blank_issues_enabled: false contact_links: - - name: 📃Support Center + - name: 📃 Support Center url: https://support.signal.org/ about: Find answers to many common questions. - name: ✨ Feature request diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 63d416fa08..82aa18646c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ ### First time contributor checklist -- [ ] I have read [how to contribute](https://github.com/signalapp/Signal-Android/blob/master/CONTRIBUTING.md) to this project +- [ ] I have read [how to contribute](https://github.com/signalapp/Signal-Android/blob/main/CONTRIBUTING.md) to this project - [ ] I have signed the [Contributor License Agreement](https://signal.org/cla/) ### Contributor checklist From bfe9fb66797191be60107f4f820c29f7b2d5cdcf Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 1 Oct 2024 16:22:17 -0400 Subject: [PATCH 32/53] Remove very old database migration. The migration was done over 4.5 years ago and has been causing random problems ever since. --- .../securesms/database/JobDatabase.kt | 74 ------------------- .../securesms/database/KeyValueDatabase.java | 48 ------------ .../securesms/database/MegaphoneDatabase.java | 30 -------- 3 files changed, 152 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt index feab88373b..42d35c8530 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt @@ -7,9 +7,7 @@ import android.database.Cursor import androidx.core.content.contentValuesOf import net.zetetic.database.sqlcipher.SQLiteDatabase import net.zetetic.database.sqlcipher.SQLiteOpenHelper -import org.signal.core.util.CursorUtil import org.signal.core.util.SqlUtil -import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.delete import org.signal.core.util.forEach import org.signal.core.util.insertInto @@ -128,21 +126,6 @@ class JobDatabase( db.execSQL(Jobs.CREATE_TABLE) db.execSQL(Constraints.CREATE_TABLE) db.execSQL(Dependencies.CREATE_TABLE) - - if (SignalDatabase.hasTable("job_spec")) { - Log.i(TAG, "Found old job_spec table. Migrating data.") - migrateJobSpecsFromPreviousDatabase(SignalDatabase.rawDatabase, db) - } - - if (SignalDatabase.hasTable("constraint_spec")) { - Log.i(TAG, "Found old constraint_spec table. Migrating data.") - migrateConstraintSpecsFromPreviousDatabase(SignalDatabase.rawDatabase, db) - } - - if (SignalDatabase.hasTable("dependency_spec")) { - Log.i(TAG, "Found old dependency_spec table. Migrating data.") - migrateDependencySpecsFromPreviousDatabase(SignalDatabase.rawDatabase, db) - } } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -161,14 +144,7 @@ class JobDatabase( override fun onOpen(db: SQLiteDatabase) { Log.i(TAG, "onOpen()") - db.setForeignKeyConstraintsEnabled(true) - - SignalExecutors.BOUNDED.execute { - dropTableIfPresent("job_spec") - dropTableIfPresent("constraint_spec") - dropTableIfPresent("dependency_spec") - } } @Synchronized @@ -488,13 +464,6 @@ class JobDatabase( return writableDatabase } - private fun dropTableIfPresent(table: String) { - if (SignalDatabase.hasTable(table)) { - Log.i(TAG, "Dropping original $table table from the main database.") - SignalDatabase.rawDatabase.execSQL("DROP TABLE $table") - } - } - /** Should only be used for debugging! */ fun debugResetBackoffInterval() { writableDatabase.update(Jobs.TABLE_NAME, contentValuesOf(Jobs.NEXT_BACKOFF_INTERVAL to 0), null, null) @@ -539,48 +508,5 @@ class JobDatabase( } return instance!! } - - private fun migrateJobSpecsFromPreviousDatabase(oldDb: SQLiteDatabase, newDb: SQLiteDatabase) { - oldDb.rawQuery("SELECT * FROM job_spec", null).use { cursor -> - while (cursor.moveToNext()) { - val values = ContentValues() - values.put(Jobs.JOB_SPEC_ID, CursorUtil.requireString(cursor, "job_spec_id")) - values.put(Jobs.FACTORY_KEY, CursorUtil.requireString(cursor, "factory_key")) - values.put(Jobs.QUEUE_KEY, CursorUtil.requireString(cursor, "queue_key")) - values.put(Jobs.CREATE_TIME, CursorUtil.requireLong(cursor, "create_time")) - values.put(Jobs.LAST_RUN_ATTEMPT_TIME, 0) - values.put(Jobs.NEXT_BACKOFF_INTERVAL, 0) - values.put(Jobs.RUN_ATTEMPT, CursorUtil.requireInt(cursor, "run_attempt")) - values.put(Jobs.MAX_ATTEMPTS, CursorUtil.requireInt(cursor, "max_attempts")) - values.put(Jobs.LIFESPAN, CursorUtil.requireLong(cursor, "lifespan")) - values.put(Jobs.SERIALIZED_DATA, CursorUtil.requireString(cursor, "serialized_data")) - values.put(Jobs.SERIALIZED_INPUT_DATA, CursorUtil.requireString(cursor, "serialized_input_data")) - values.put(Jobs.IS_RUNNING, CursorUtil.requireInt(cursor, "is_running")) - newDb.insert(Jobs.TABLE_NAME, null, values) - } - } - } - - private fun migrateConstraintSpecsFromPreviousDatabase(oldDb: SQLiteDatabase, newDb: SQLiteDatabase) { - oldDb.rawQuery("SELECT * FROM constraint_spec", null).use { cursor -> - while (cursor.moveToNext()) { - val values = ContentValues() - values.put(Constraints.JOB_SPEC_ID, CursorUtil.requireString(cursor, "job_spec_id")) - values.put(Constraints.FACTORY_KEY, CursorUtil.requireString(cursor, "factory_key")) - newDb.insert(Constraints.TABLE_NAME, null, values) - } - } - } - - private fun migrateDependencySpecsFromPreviousDatabase(oldDb: SQLiteDatabase, newDb: SQLiteDatabase) { - oldDb.rawQuery("SELECT * FROM dependency_spec", null).use { cursor -> - while (cursor.moveToNext()) { - val values = ContentValues() - values.put(Dependencies.JOB_SPEC_ID, CursorUtil.requireString(cursor, "job_spec_id")) - values.put(Dependencies.DEPENDS_ON_JOB_SPEC_ID, CursorUtil.requireString(cursor, "depends_on_job_spec_id")) - newDb.insert(Dependencies.TABLE_NAME, null, values) - } - } - } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/KeyValueDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/KeyValueDatabase.java index f39e2e8a5d..4f7c0380ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/KeyValueDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/KeyValueDatabase.java @@ -82,13 +82,7 @@ private KeyValueDatabase(@NonNull Application application, @NonNull DatabaseSecr @Override public void onCreate(SQLiteDatabase db) { Log.i(TAG, "onCreate()"); - db.execSQL(CREATE_TABLE); - - if (SignalDatabase.hasTable("key_value")) { - Log.i(TAG, "Found old key_value table. Migrating data."); - migrateDataFromPreviousDatabase(SignalDatabase.getRawDatabase(), db); - } } @Override @@ -99,15 +93,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @Override public void onOpen(SQLiteDatabase db) { Log.i(TAG, "onOpen()"); - db.setForeignKeyConstraintsEnabled(true); - - SignalExecutors.BOUNDED.execute(() -> { - if (SignalDatabase.hasTable("key_value")) { - Log.i(TAG, "Dropping original key_value table from the main database."); - SignalDatabase.getRawDatabase().execSQL("DROP TABLE key_value"); - } - }); } @Override @@ -200,40 +186,6 @@ public void writeDataSet(@NonNull KeyValueDataSet dataSet, @NonNull Collection { - if (SignalDatabase.hasTable("megaphone")) { - Log.i(TAG, "Dropping original megaphone table from the main database."); - SignalDatabase.getRawDatabase().execSQL("DROP TABLE megaphone"); - } - }); } public void insert(@NonNull Collection events) { @@ -201,20 +187,4 @@ public void delete(@NonNull Event event) { public @NonNull SQLiteDatabase getSqlCipherDatabase() { return getWritableDatabase(); } - - private static void migrateDataFromPreviousDatabase(@NonNull SQLiteDatabase oldDb, @NonNull SQLiteDatabase newDb) { - try (Cursor cursor = oldDb.rawQuery("SELECT * FROM megaphone", null)) { - while (cursor.moveToNext()) { - ContentValues values = new ContentValues(); - - values.put(EVENT, CursorUtil.requireString(cursor, "event")); - values.put(SEEN_COUNT, CursorUtil.requireInt(cursor, "seen_count")); - values.put(LAST_SEEN, CursorUtil.requireLong(cursor, "last_seen")); - values.put(FIRST_VISIBLE, CursorUtil.requireLong(cursor, "first_visible")); - values.put(FINISHED, CursorUtil.requireInt(cursor, "finished")); - - newDb.insert(TABLE_NAME, null, values); - } - } - } } From 8460c226204faf5c4e5f544f71622d21956084f2 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 1 Oct 2024 16:26:34 -0400 Subject: [PATCH 33/53] Add backup integration test cases. --- .../assets/backupTests/account_data_00.binproto | Bin 0 -> 180 bytes .../assets/backupTests/account_data_01.binproto | Bin 0 -> 410 bytes .../assets/backupTests/account_data_02.binproto | Bin 0 -> 507 bytes .../assets/backupTests/account_data_03.binproto | Bin 0 -> 431 bytes .../assets/backupTests/account_data_04.binproto | Bin 0 -> 429 bytes .../assets/backupTests/account_data_05.binproto | Bin 0 -> 368 bytes .../assets/backupTests/account_data_06.binproto | Bin 0 -> 458 bytes .../assets/backupTests/account_data_07.binproto | Bin 0 -> 318 bytes .../assets/backupTests/account_data_08.binproto | Bin 0 -> 291 bytes .../assets/backupTests/account_data_09.binproto | Bin 0 -> 222 bytes .../assets/backupTests/account_data_10.binproto | Bin 0 -> 342 bytes .../assets/backupTests/account_data_11.binproto | Bin 0 -> 325 bytes .../assets/backupTests/account_data_12.binproto | Bin 0 -> 255 bytes .../assets/backupTests/account_data_13.binproto | Bin 0 -> 317 bytes .../assets/backupTests/account_data_14.binproto | Bin 0 -> 349 bytes .../assets/backupTests/account_data_15.binproto | Bin 0 -> 289 bytes .../assets/backupTests/account_data_16.binproto | Bin 0 -> 284 bytes .../assets/backupTests/account_data_17.binproto | Bin 0 -> 324 bytes .../assets/backupTests/account_data_18.binproto | Bin 0 -> 247 bytes .../assets/backupTests/account_data_19.binproto | Bin 0 -> 318 bytes .../assets/backupTests/account_data_20.binproto | Bin 0 -> 291 bytes .../assets/backupTests/account_data_21.binproto | Bin 0 -> 288 bytes .../assets/backupTests/account_data_22.binproto | Bin 0 -> 342 bytes .../assets/backupTests/account_data_23.binproto | Bin 0 -> 325 bytes .../assets/backupTests/account_data_24.binproto | Bin 0 -> 255 bytes .../assets/backupTests/account_data_25.binproto | Bin 0 -> 317 bytes .../assets/backupTests/account_data_26.binproto | Bin 0 -> 349 bytes .../assets/backupTests/account_data_27.binproto | Bin 0 -> 223 bytes .../assets/backupTests/ad_hoc_call_00.binproto | Bin 0 -> 383 bytes .../assets/backupTests/ad_hoc_call_01.binproto | Bin 0 -> 390 bytes .../assets/backupTests/ad_hoc_call_02.binproto | Bin 0 -> 390 bytes .../assets/backupTests/chat_00.binproto | Bin 0 -> 511 bytes .../assets/backupTests/chat_01.binproto | Bin 0 -> 586 bytes .../assets/backupTests/chat_02.binproto | Bin 0 -> 576 bytes .../assets/backupTests/chat_03.binproto | Bin 0 -> 559 bytes .../assets/backupTests/chat_04.binproto | Bin 0 -> 477 bytes .../assets/backupTests/chat_05.binproto | Bin 0 -> 573 bytes .../assets/backupTests/chat_06.binproto | Bin 0 -> 419 bytes .../assets/backupTests/chat_07.binproto | Bin 0 -> 427 bytes .../assets/backupTests/chat_08.binproto | Bin 0 -> 433 bytes .../assets/backupTests/chat_09.binproto | Bin 0 -> 413 bytes .../assets/backupTests/chat_10.binproto | Bin 0 -> 433 bytes .../assets/backupTests/chat_11.binproto | Bin 0 -> 427 bytes .../assets/backupTests/chat_12.binproto | Bin 0 -> 419 bytes .../assets/backupTests/chat_13.binproto | Bin 0 -> 427 bytes .../assets/backupTests/chat_14.binproto | Bin 0 -> 433 bytes .../assets/backupTests/chat_15.binproto | Bin 0 -> 413 bytes .../assets/backupTests/chat_16.binproto | Bin 0 -> 433 bytes .../assets/backupTests/chat_17.binproto | Bin 0 -> 427 bytes .../assets/backupTests/chat_18.binproto | Bin 0 -> 419 bytes .../assets/backupTests/chat_19.binproto | Bin 0 -> 427 bytes .../assets/backupTests/chat_20.binproto | Bin 0 -> 433 bytes .../assets/backupTests/chat_21.binproto | Bin 0 -> 413 bytes .../assets/backupTests/chat_22.binproto | Bin 0 -> 433 bytes .../assets/backupTests/chat_23.binproto | Bin 0 -> 427 bytes .../assets/backupTests/chat_24.binproto | Bin 0 -> 419 bytes .../assets/backupTests/chat_25.binproto | Bin 0 -> 427 bytes .../assets/backupTests/chat_26.binproto | Bin 0 -> 433 bytes .../chat_item_contact_message_00.binproto | Bin 0 -> 451 bytes .../chat_item_contact_message_01.binproto | Bin 0 -> 635 bytes .../chat_item_contact_message_02.binproto | Bin 0 -> 869 bytes .../chat_item_contact_message_03.binproto | Bin 0 -> 649 bytes .../chat_item_contact_message_04.binproto | Bin 0 -> 716 bytes .../chat_item_contact_message_05.binproto | Bin 0 -> 723 bytes .../chat_item_contact_message_06.binproto | Bin 0 -> 624 bytes .../chat_item_contact_message_07.binproto | Bin 0 -> 735 bytes .../chat_item_contact_message_08.binproto | Bin 0 -> 697 bytes .../chat_item_contact_message_09.binproto | Bin 0 -> 448 bytes .../chat_item_contact_message_10.binproto | Bin 0 -> 634 bytes .../chat_item_contact_message_11.binproto | Bin 0 -> 869 bytes .../chat_item_contact_message_12.binproto | Bin 0 -> 647 bytes .../chat_item_contact_message_13.binproto | Bin 0 -> 718 bytes .../chat_item_contact_message_14.binproto | Bin 0 -> 723 bytes ...chat_item_expiration_timer_update_00.binproto | Bin 0 -> 421 bytes ...chat_item_expiration_timer_update_01.binproto | Bin 0 -> 428 bytes ...chat_item_expiration_timer_update_02.binproto | Bin 0 -> 428 bytes .../backupTests/chat_item_gift_badge_00.binproto | Bin 0 -> 773 bytes .../backupTests/chat_item_gift_badge_01.binproto | Bin 0 -> 771 bytes .../backupTests/chat_item_gift_badge_02.binproto | Bin 0 -> 763 bytes .../backupTests/chat_item_gift_badge_03.binproto | Bin 0 -> 437 bytes .../backupTests/chat_item_gift_badge_04.binproto | Bin 0 -> 768 bytes .../backupTests/chat_item_gift_badge_05.binproto | Bin 0 -> 765 bytes .../backupTests/chat_item_gift_badge_06.binproto | Bin 0 -> 770 bytes .../backupTests/chat_item_gift_badge_07.binproto | Bin 0 -> 437 bytes .../backupTests/chat_item_gift_badge_08.binproto | Bin 0 -> 761 bytes .../backupTests/chat_item_gift_badge_09.binproto | Bin 0 -> 772 bytes .../backupTests/chat_item_gift_badge_10.binproto | Bin 0 -> 770 bytes .../backupTests/chat_item_gift_badge_11.binproto | Bin 0 -> 428 bytes .../backupTests/chat_item_gift_badge_12.binproto | Bin 0 -> 768 bytes .../backupTests/chat_item_gift_badge_13.binproto | Bin 0 -> 772 bytes .../backupTests/chat_item_gift_badge_14.binproto | Bin 0 -> 765 bytes .../chat_item_group_call_update_00.binproto | Bin 0 -> 707 bytes .../chat_item_group_call_update_01.binproto | Bin 0 -> 715 bytes .../chat_item_group_call_update_02.binproto | Bin 0 -> 717 bytes .../chat_item_group_call_update_03.binproto | Bin 0 -> 709 bytes .../chat_item_group_call_update_04.binproto | Bin 0 -> 719 bytes .../chat_item_group_call_update_05.binproto | Bin 0 -> 717 bytes .../chat_item_group_call_update_06.binproto | Bin 0 -> 709 bytes .../chat_item_group_call_update_07.binproto | Bin 0 -> 715 bytes .../chat_item_individual_call_update_00.binproto | Bin 0 -> 438 bytes .../chat_item_individual_call_update_01.binproto | Bin 0 -> 446 bytes .../chat_item_individual_call_update_02.binproto | Bin 0 -> 444 bytes .../chat_item_individual_call_update_03.binproto | Bin 0 -> 438 bytes .../chat_item_learned_profile_update_00.binproto | Bin 0 -> 427 bytes .../chat_item_learned_profile_update_01.binproto | Bin 0 -> 427 bytes .../chat_item_learned_profile_update_02.binproto | Bin 0 -> 434 bytes .../chat_item_learned_profile_update_03.binproto | Bin 0 -> 432 bytes .../chat_item_learned_profile_update_04.binproto | Bin 0 -> 429 bytes .../chat_item_payment_notification_00.binproto | Bin 0 -> 657 bytes .../chat_item_payment_notification_01.binproto | Bin 0 -> 730 bytes .../chat_item_payment_notification_02.binproto | Bin 0 -> 730 bytes .../chat_item_payment_notification_03.binproto | Bin 0 -> 536 bytes .../chat_item_payment_notification_04.binproto | Bin 0 -> 483 bytes .../chat_item_payment_notification_05.binproto | Bin 0 -> 542 bytes .../chat_item_payment_notification_06.binproto | Bin 0 -> 722 bytes .../chat_item_payment_notification_07.binproto | Bin 0 -> 720 bytes .../chat_item_payment_notification_08.binproto | Bin 0 -> 660 bytes .../chat_item_payment_notification_09.binproto | Bin 0 -> 547 bytes .../chat_item_payment_notification_10.binproto | Bin 0 -> 555 bytes .../chat_item_payment_notification_11.binproto | Bin 0 -> 527 bytes .../chat_item_payment_notification_12.binproto | Bin 0 -> 652 bytes .../chat_item_payment_notification_13.binproto | Bin 0 -> 731 bytes .../chat_item_payment_notification_14.binproto | Bin 0 -> 732 bytes .../chat_item_profile_change_update_00.binproto | Bin 0 -> 447 bytes .../chat_item_profile_change_update_01.binproto | Bin 0 -> 448 bytes .../chat_item_profile_change_update_02.binproto | Bin 0 -> 445 bytes .../chat_item_remote_delete_00.binproto | Bin 0 -> 437 bytes .../chat_item_remote_delete_01.binproto | Bin 0 -> 433 bytes .../chat_item_remote_delete_02.binproto | Bin 0 -> 425 bytes .../chat_item_remote_delete_03.binproto | Bin 0 -> 434 bytes .../chat_item_remote_delete_04.binproto | Bin 0 -> 432 bytes .../chat_item_remote_delete_05.binproto | Bin 0 -> 427 bytes .../chat_item_remote_delete_06.binproto | Bin 0 -> 432 bytes .../chat_item_remote_delete_07.binproto | Bin 0 -> 434 bytes .../chat_item_remote_delete_08.binproto | Bin 0 -> 425 bytes .../chat_item_remote_delete_09.binproto | Bin 0 -> 434 bytes .../chat_item_remote_delete_10.binproto | Bin 0 -> 432 bytes .../chat_item_remote_delete_11.binproto | Bin 0 -> 425 bytes .../chat_item_remote_delete_12.binproto | Bin 0 -> 432 bytes .../chat_item_remote_delete_13.binproto | Bin 0 -> 434 bytes .../chat_item_remote_delete_14.binproto | Bin 0 -> 427 bytes ...at_item_session_switchover_update_00.binproto | Bin 0 -> 427 bytes ...at_item_session_switchover_update_01.binproto | Bin 0 -> 428 bytes .../chat_item_simple_updates_00.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_01.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_02.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_03.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_04.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_05.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_06.binproto | Bin 0 -> 445 bytes .../chat_item_simple_updates_07.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_08.binproto | Bin 0 -> 445 bytes .../chat_item_simple_updates_09.binproto | Bin 0 -> 445 bytes .../chat_item_simple_updates_10.binproto | Bin 0 -> 445 bytes .../chat_item_simple_updates_11.binproto | Bin 0 -> 445 bytes .../chat_item_simple_updates_12.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_13.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_14.binproto | Bin 0 -> 423 bytes .../chat_item_simple_updates_15.binproto | Bin 0 -> 423 bytes ...m_standard_message_formatted_text_00.binproto | Bin 0 -> 804 bytes ...m_standard_message_formatted_text_01.binproto | Bin 0 -> 820 bytes ...m_standard_message_formatted_text_02.binproto | Bin 0 -> 824 bytes ...m_standard_message_formatted_text_03.binproto | Bin 0 -> 852 bytes ...m_standard_message_formatted_text_04.binproto | Bin 0 -> 884 bytes ...m_standard_message_formatted_text_05.binproto | Bin 0 -> 856 bytes ...m_standard_message_formatted_text_06.binproto | Bin 0 -> 820 bytes ...m_standard_message_formatted_text_07.binproto | Bin 0 -> 852 bytes ...m_standard_message_formatted_text_08.binproto | Bin 0 -> 808 bytes ...m_standard_message_formatted_text_09.binproto | Bin 0 -> 852 bytes ...m_standard_message_formatted_text_10.binproto | Bin 0 -> 884 bytes ...m_standard_message_formatted_text_11.binproto | Bin 0 -> 864 bytes ...m_standard_message_formatted_text_12.binproto | Bin 0 -> 820 bytes ...m_standard_message_formatted_text_13.binproto | Bin 0 -> 836 bytes ...m_standard_message_formatted_text_14.binproto | Bin 0 -> 816 bytes ...t_item_standard_message_long_text_00.binproto | Bin 0 -> 576 bytes ...t_item_standard_message_long_text_01.binproto | Bin 0 -> 624 bytes ...t_item_standard_message_long_text_02.binproto | Bin 0 -> 636 bytes ...t_item_standard_message_long_text_03.binproto | Bin 0 -> 573 bytes ...t_item_standard_message_long_text_04.binproto | Bin 0 -> 582 bytes ...t_item_standard_message_long_text_05.binproto | Bin 0 -> 611 bytes ...t_item_standard_message_long_text_06.binproto | Bin 0 -> 554 bytes ...t_item_standard_message_long_text_07.binproto | Bin 0 -> 528 bytes ...t_item_standard_message_long_text_08.binproto | Bin 0 -> 615 bytes ...t_item_standard_message_long_text_09.binproto | Bin 0 -> 602 bytes ...t_item_standard_message_long_text_10.binproto | Bin 0 -> 615 bytes ...t_item_standard_message_long_text_11.binproto | Bin 0 -> 615 bytes ...t_item_standard_message_long_text_12.binproto | Bin 0 -> 558 bytes ...t_item_standard_message_long_text_13.binproto | Bin 0 -> 590 bytes ...t_item_standard_message_long_text_14.binproto | Bin 0 -> 601 bytes .../chat_item_standard_message_sms_00.binproto | Bin 0 -> 448 bytes .../chat_item_standard_message_sms_01.binproto | Bin 0 -> 548 bytes .../chat_item_standard_message_sms_02.binproto | Bin 0 -> 1138 bytes .../chat_item_standard_message_sms_03.binproto | Bin 0 -> 449 bytes .../chat_item_standard_message_sms_04.binproto | Bin 0 -> 543 bytes .../chat_item_standard_message_sms_05.binproto | Bin 0 -> 1144 bytes .../chat_item_standard_message_sms_06.binproto | Bin 0 -> 443 bytes .../chat_item_standard_message_sms_07.binproto | Bin 0 -> 549 bytes .../chat_item_standard_message_sms_08.binproto | Bin 0 -> 1138 bytes .../chat_item_standard_message_sms_09.binproto | Bin 0 -> 449 bytes .../chat_item_standard_message_sms_10.binproto | Bin 0 -> 543 bytes .../chat_item_standard_message_sms_11.binproto | Bin 0 -> 1142 bytes .../chat_item_standard_message_sms_12.binproto | Bin 0 -> 443 bytes .../chat_item_standard_message_sms_13.binproto | Bin 0 -> 549 bytes .../chat_item_standard_message_sms_14.binproto | Bin 0 -> 1140 bytes ...ndard_message_special_attachments_00.binproto | Bin 0 -> 538 bytes ...ndard_message_special_attachments_01.binproto | Bin 0 -> 551 bytes ...ndard_message_special_attachments_02.binproto | Bin 0 -> 565 bytes ...ndard_message_special_attachments_03.binproto | Bin 0 -> 535 bytes ...ndard_message_special_attachments_04.binproto | Bin 0 -> 550 bytes ...ndard_message_special_attachments_05.binproto | Bin 0 -> 567 bytes ...ndard_message_special_attachments_06.binproto | Bin 0 -> 532 bytes ...ndard_message_special_attachments_07.binproto | Bin 0 -> 552 bytes ...ndard_message_special_attachments_08.binproto | Bin 0 -> 565 bytes ...ndard_message_special_attachments_09.binproto | Bin 0 -> 535 bytes ...ndard_message_special_attachments_10.binproto | Bin 0 -> 550 bytes ...ndard_message_special_attachments_11.binproto | Bin 0 -> 565 bytes ...ndard_message_special_attachments_12.binproto | Bin 0 -> 532 bytes ...ndard_message_special_attachments_13.binproto | Bin 0 -> 552 bytes ...ndard_message_special_attachments_14.binproto | Bin 0 -> 567 bytes ...dard_message_standard_attachments_00.binproto | Bin 0 -> 537 bytes ...dard_message_standard_attachments_01.binproto | Bin 0 -> 1015 bytes ...dard_message_standard_attachments_02.binproto | Bin 0 -> 1071 bytes ...dard_message_standard_attachments_03.binproto | Bin 0 -> 687 bytes ...dard_message_standard_attachments_04.binproto | Bin 0 -> 890 bytes ...dard_message_standard_attachments_05.binproto | Bin 0 -> 1160 bytes ...dard_message_standard_attachments_06.binproto | Bin 0 -> 647 bytes ...dard_message_standard_attachments_07.binproto | Bin 0 -> 929 bytes ...dard_message_standard_attachments_08.binproto | Bin 0 -> 1134 bytes ...dard_message_standard_attachments_09.binproto | Bin 0 -> 618 bytes ...dard_message_standard_attachments_10.binproto | Bin 0 -> 901 bytes ...dard_message_standard_attachments_11.binproto | Bin 0 -> 1197 bytes ...dard_message_standard_attachments_12.binproto | Bin 0 -> 547 bytes ...dard_message_standard_attachments_13.binproto | Bin 0 -> 846 bytes ...dard_message_standard_attachments_14.binproto | Bin 0 -> 1243 bytes ...t_item_standard_message_text_only_00.binproto | Bin 0 -> 470 bytes ...t_item_standard_message_text_only_01.binproto | Bin 0 -> 489 bytes ...t_item_standard_message_text_only_02.binproto | Bin 0 -> 509 bytes ...t_item_standard_message_text_only_03.binproto | Bin 0 -> 467 bytes ...t_item_standard_message_text_only_04.binproto | Bin 0 -> 488 bytes ...t_item_standard_message_text_only_05.binproto | Bin 0 -> 511 bytes ...t_item_standard_message_text_only_06.binproto | Bin 0 -> 465 bytes ...t_item_standard_message_text_only_07.binproto | Bin 0 -> 490 bytes ...t_item_standard_message_text_only_08.binproto | Bin 0 -> 509 bytes ...t_item_standard_message_text_only_09.binproto | Bin 0 -> 467 bytes ...t_item_standard_message_text_only_10.binproto | Bin 0 -> 488 bytes ...t_item_standard_message_text_only_11.binproto | Bin 0 -> 509 bytes ...t_item_standard_message_text_only_12.binproto | Bin 0 -> 465 bytes ...t_item_standard_message_text_only_13.binproto | Bin 0 -> 490 bytes ...t_item_standard_message_text_only_14.binproto | Bin 0 -> 511 bytes ..._item_standard_message_with_edits_00.binproto | Bin 0 -> 566 bytes ..._item_standard_message_with_edits_01.binproto | Bin 0 -> 705 bytes ..._item_standard_message_with_edits_02.binproto | Bin 0 -> 554 bytes ...tandard_message_with_link_preview_00.binproto | Bin 0 -> 527 bytes ...tandard_message_with_link_preview_01.binproto | Bin 0 -> 714 bytes ...tandard_message_with_link_preview_02.binproto | Bin 0 -> 805 bytes ...tandard_message_with_link_preview_03.binproto | Bin 0 -> 702 bytes ...tandard_message_with_link_preview_04.binproto | Bin 0 -> 698 bytes ...tandard_message_with_link_preview_05.binproto | Bin 0 -> 744 bytes ...tandard_message_with_link_preview_06.binproto | Bin 0 -> 698 bytes ...tandard_message_with_link_preview_07.binproto | Bin 0 -> 677 bytes ...tandard_message_with_link_preview_08.binproto | Bin 0 -> 646 bytes ...tandard_message_with_link_preview_09.binproto | Bin 0 -> 524 bytes ...tandard_message_with_link_preview_10.binproto | Bin 0 -> 713 bytes ...tandard_message_with_link_preview_11.binproto | Bin 0 -> 805 bytes ...tandard_message_with_link_preview_12.binproto | Bin 0 -> 700 bytes ...tandard_message_with_link_preview_13.binproto | Bin 0 -> 700 bytes ...tandard_message_with_link_preview_14.binproto | Bin 0 -> 744 bytes ..._item_standard_message_with_quote_00.binproto | Bin 0 -> 547 bytes ..._item_standard_message_with_quote_01.binproto | Bin 0 -> 601 bytes ..._item_standard_message_with_quote_02.binproto | Bin 0 -> 1117 bytes ..._item_standard_message_with_quote_03.binproto | Bin 0 -> 626 bytes ..._item_standard_message_with_quote_04.binproto | Bin 0 -> 521 bytes ..._item_standard_message_with_quote_05.binproto | Bin 0 -> 727 bytes ..._item_standard_message_with_quote_06.binproto | Bin 0 -> 714 bytes ..._item_standard_message_with_quote_07.binproto | Bin 0 -> 674 bytes ..._item_standard_message_with_quote_08.binproto | Bin 0 -> 734 bytes ..._item_standard_message_with_quote_09.binproto | Bin 0 -> 678 bytes ..._item_standard_message_with_quote_10.binproto | Bin 0 -> 640 bytes ..._item_standard_message_with_quote_11.binproto | Bin 0 -> 712 bytes ..._item_standard_message_with_quote_12.binproto | Bin 0 -> 621 bytes ..._item_standard_message_with_quote_13.binproto | Bin 0 -> 826 bytes ..._item_standard_message_with_quote_14.binproto | Bin 0 -> 588 bytes .../chat_item_sticker_message_00.binproto | Bin 0 -> 596 bytes .../chat_item_sticker_message_01.binproto | Bin 0 -> 635 bytes .../chat_item_sticker_message_02.binproto | Bin 0 -> 664 bytes .../chat_item_sticker_message_03.binproto | Bin 0 -> 603 bytes .../chat_item_sticker_message_04.binproto | Bin 0 -> 601 bytes .../chat_item_sticker_message_05.binproto | Bin 0 -> 635 bytes .../chat_item_sticker_message_06.binproto | Bin 0 -> 581 bytes .../chat_item_sticker_message_07.binproto | Bin 0 -> 546 bytes .../chat_item_sticker_message_08.binproto | Bin 0 -> 626 bytes .../chat_item_sticker_message_09.binproto | Bin 0 -> 624 bytes .../chat_item_sticker_message_10.binproto | Bin 0 -> 652 bytes .../chat_item_sticker_message_11.binproto | Bin 0 -> 628 bytes .../chat_item_sticker_message_12.binproto | Bin 0 -> 575 bytes .../chat_item_sticker_message_13.binproto | Bin 0 -> 627 bytes .../chat_item_sticker_message_14.binproto | Bin 0 -> 621 bytes .../chat_item_thread_merge_update_00.binproto | Bin 0 -> 427 bytes .../chat_item_thread_merge_update_01.binproto | Bin 0 -> 428 bytes .../backupTests/recipient_call_link_00.binproto | Bin 0 -> 318 bytes .../backupTests/recipient_call_link_01.binproto | Bin 0 -> 369 bytes .../backupTests/recipient_call_link_02.binproto | Bin 0 -> 336 bytes .../backupTests/recipient_call_link_03.binproto | Bin 0 -> 352 bytes .../backupTests/recipient_call_link_04.binproto | Bin 0 -> 335 bytes .../backupTests/recipient_call_link_05.binproto | Bin 0 -> 370 bytes .../backupTests/recipient_call_link_06.binproto | Bin 0 -> 318 bytes .../backupTests/recipient_call_link_07.binproto | Bin 0 -> 369 bytes .../backupTests/recipient_call_link_08.binproto | Bin 0 -> 336 bytes .../backupTests/recipient_call_link_09.binproto | Bin 0 -> 352 bytes .../backupTests/recipient_call_link_10.binproto | Bin 0 -> 335 bytes .../backupTests/recipient_call_link_11.binproto | Bin 0 -> 370 bytes .../backupTests/recipient_call_link_12.binproto | Bin 0 -> 318 bytes .../backupTests/recipient_call_link_13.binproto | Bin 0 -> 369 bytes .../backupTests/recipient_call_link_14.binproto | Bin 0 -> 336 bytes .../backupTests/recipient_call_link_15.binproto | Bin 0 -> 352 bytes .../backupTests/recipient_contacts_00.binproto | Bin 0 -> 350 bytes .../backupTests/recipient_contacts_01.binproto | Bin 0 -> 408 bytes .../backupTests/recipient_contacts_02.binproto | Bin 0 -> 395 bytes .../backupTests/recipient_contacts_03.binproto | Bin 0 -> 411 bytes .../recipient_distribution_list_00.binproto | Bin 0 -> 627 bytes .../recipient_distribution_list_01.binproto | Bin 0 -> 642 bytes .../recipient_distribution_list_02.binproto | Bin 0 -> 636 bytes .../recipient_distribution_list_03.binproto | Bin 0 -> 646 bytes .../backupTests/recipient_groups_00.binproto | Bin 0 -> 1052 bytes .../backupTests/recipient_groups_01.binproto | Bin 0 -> 1236 bytes .../backupTests/recipient_groups_02.binproto | Bin 0 -> 1194 bytes .../backupTests/recipient_groups_03.binproto | Bin 0 -> 1183 bytes .../assets/backupTests/standard_frames.binproto | Bin 0 -> 293 bytes .../assets/backupTests/sticker_pack_00.binproto | Bin 0 -> 348 bytes .../assets/backupTests/sticker_pack_01.binproto | Bin 0 -> 348 bytes .../assets/backupTests/sticker_pack_02.binproto | Bin 0 -> 348 bytes 331 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/androidTest/assets/backupTests/account_data_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_15.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_16.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_17.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_18.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_19.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_20.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_21.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_22.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_23.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_24.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_25.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_26.binproto create mode 100644 app/src/androidTest/assets/backupTests/account_data_27.binproto create mode 100644 app/src/androidTest/assets/backupTests/ad_hoc_call_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/ad_hoc_call_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/ad_hoc_call_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_15.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_16.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_17.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_18.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_19.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_20.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_21.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_22.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_23.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_24.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_25.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_26.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_contact_message_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_gift_badge_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_call_update_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_individual_call_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_individual_call_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_individual_call_update_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_individual_call_update_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_payment_notification_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_profile_change_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_profile_change_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_profile_change_update_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_remote_delete_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_simple_updates_15.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_sticker_message_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_call_link_15.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_contacts_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_contacts_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_contacts_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_contacts_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_distribution_list_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_distribution_list_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_distribution_list_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_distribution_list_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_groups_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_groups_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_groups_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/recipient_groups_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/standard_frames.binproto create mode 100644 app/src/androidTest/assets/backupTests/sticker_pack_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/sticker_pack_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/sticker_pack_02.binproto diff --git a/app/src/androidTest/assets/backupTests/account_data_00.binproto b/app/src/androidTest/assets/backupTests/account_data_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5d75cf9f56a6e25341d1642a0fdb0f5977730af8 GIT binary patch literal 180 zcmd<&U=+CVYr*{QhIL#uTna~jfB1jDYD(8_qx8;od$ul2TlnPu-dBuLKK?#;#WYnj zy&{0>86_AM7&RCT7%kSV*u0C;gE4?Hf-!+HgRy|Ig0X>-v4fFu79%?cqo^%c*?$m= zN%Z`S>p+$?2eXtoM<=5YN90b4|Bf0z|4VT&fI)*jn-B{JqZR{*VlrZo5t8CyRubb9 MfJh@qDJBVK0NUd&-v9sr literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_01.binproto b/app/src/androidTest/assets/backupTests/account_data_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5e63d535c27d0073e7921a2d429736c06e0360bf GIT binary patch literal 410 zcmd<&U=+CVYr*{QhL4%J9x!n!9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP%*s!#)H5?MG>|gmQb@ZMeCivI-${l|_ntPgw|Z$V-t=+mhHutaPDpsRwQ-6I z2}GZ4`em1L{oRV`_7gPzwa7{^DzOG6mK0ZNu?1z8r4|(%NoSOl6ck(O>!(&E<`(3n z>Lusr>gT2UgjE$sSZQ)8sQGPmto^NU^PaViiJp4l?PK%p!_Lo;zvgu&z~P$T3Ns<* z&|nt{Mz4j87RNVzt}S5bU}T!am}J2y6r;qInVXoNs-IPmn$G3NrC^$DkZhV}o@8lm zYGiI{VVY!aXqud2l4fCPZj@$XWFf#HC18DBFED`pW0WGBx4BPEy`+*r3{QOZ^q|Vt z?V@M>JBz+(Xng2vyUQ%aFpH6$gHiM``@a7m7L(|@Rfm8qX%1#7agLpgLL5_jz5ny= qnDbwXg8>X0?Ae4^I2g4UKopY^gN%?A2eXnGmjFZ>K}s=6FarSP8Hk4f literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_02.binproto b/app/src/androidTest/assets/backupTests/account_data_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3e6bef8abe1b09d0ced4488124ebcf0cd5f45bb6 GIT binary patch literal 507 zcmd<&U=+CVYr*{QhDVsW4l;8o9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCPEY3*H$*I&cFqAUmQiz!S=WjS4m%uiKi&1m;#%OKtvM!qyvd}U!YlBqjw+Y=s z06^Xe8 zIjMTd`MLTzd0sBg9>I>@8I?JXR_a^|JWdL$Epxp4_P)O|+hxm%i*wvhH&nQ&&)>4- zrTWp&*$zU?uAxC*e;9#YVw7N1VANnVV6<5LY3Dl@55@q-2*w1)48{V+3dROT#tuft zNsJ2^g=RA>MuvtamT9JzmMMuzCTW(* z7Ks8(QUY84|5lBtoNA|9wbR{wYt~aGfye3m<*&a!R}`5aId`@HoGy*N?&r_gjW}IP zvJxXo(!!H07!(+1F|umx-f0wllyyLF-rOP``i`N zRLvCP$jVQt)H60UkTT;^(3|0#s&}1p+H|k4%a=r^a;Z*uf3Zhp-t8m5xFVZRzGM~> z_%VOutL1HBQ$=>H+PQV<{>u`~O3Xf~iCUZ$ zlafqQk}QoaQw@wwj1qyQX-Y~`s(D(fi2$>dz)h3*J5d|Y7j0m-c&PfmxIjsut7zYD z-<_5ZOtc=D-rZOKM5A%ux#es|yy=ntRe`}}Udc&S?vWNu3QV&Y**O?RZMn++gIG+W z=U-e0vZOhfrNlWp8HG3^cS`(s)cE;dih}_R8tmDGSU4E97(f)05rd476bG}C7?%J< L8bL}iNiYKd_gaz0 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_04.binproto b/app/src/androidTest/assets/backupTests/account_data_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..bfda3a761648f0dfaea86c5bc283e43c67f3adf3 GIT binary patch literal 429 zcmd<&U=+CVYr*{QhToXDJ~MGC9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP%*s!#)H5?MG>|gmQYbmq`(~=kjA{#Bg|2^Rj~p$Ky8g!d$MUbrEvJ^dU3E_u z67UzE-!PT8Z{^3{eF7662USV17)fW8loS+O>FcLfB<2?6r0ON-=j!LB`h-;#M|izs z1UiXPf>D7{gVBJ|;`pY|wH}NCj1i0pj2Vmtj1`OxjEo(OjFT8AGYa)FDsg7!CZ?zA z7v!aD2}J%*m6~d*dN+3Es!IJ&_J#%x0#PzPDW3ij_I|FGktQK^1wjEpS+0f=mT9J0 zMI~ljeq0J>DanSZ2FWRg7D)yvX~w20Mky(Y7DkCCX=$m6#zsa03{nDI%pJ+EH`u>z zY5O9XotK)UB#?XX!C8q7_f0R|U7_XwZ?TfbhrYJE%nHo27}+@(MIW>8`wwCz@WjNO^AhqQHudYF&Qz)2uX1;D~WLlK%^0* I6q5uq0OMJX-2eap literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_05.binproto b/app/src/androidTest/assets/backupTests/account_data_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f96b4258dd4841127397d177fe051577042b56dc GIT binary patch literal 368 zcmd<&U=+CVYr*{QhC7+KwlQ%j9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCPEY3*H$*I&cFqAUmQsAzeJlk#Slti0q{a@$Pyc#0ow3e%GeBQ7uLry@L-}|_b z0Kc!`1qHeCpSxt%o#;59{Y!#Xi8Uaxq_|RxEhw`rwW!!gF{7lUpx8=ZKeZw;w;(4~ zFF8L~KPS)2#n~g+(L19u$I(iYOF_+Vt7GkNg`4-Rbxics3vVBrZy$DkhWs_JGXW0Q z{8pF=F^2}bNHBU8SuFmv^Bqe8LkA<%Bt|0(Mj;&~uFTxT^i=(mx-f0wllyyLF-rOP``i`N zRLvCP$jVQt)H60UkTT;^P+ZX0?Ae4^I2g4UKopY^gN%?A2eXnGmjFZ>K}s=6FarQ5#*&5r literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_07.binproto b/app/src/androidTest/assets/backupTests/account_data_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9d2312ccb1e775c2618e13fb84b67f5bdcc41a84 GIT binary patch literal 318 zcmd<&U=+CVYr*{QhV4vTElgYrM}L3#f4^!<*KMQp&UJgXE=*haPx# literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_08.binproto b/app/src/androidTest/assets/backupTests/account_data_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..93d4ca921f3116fa62cbe277f2a25ce8458dda78 GIT binary patch literal 291 zcmd<&U=+CVYr*{QhHn_TUNUki9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCPEY3*H$*I&cFqAUmQn%VyKNwaoZeNb_g;iXYFpFBfNzU`Owa z${a_pc%Zu&B^VVLH5d&TEf#;;`HsbdF@Q0GF@Z6Iv4F9Hv4N4XgOPC(BMS$U0{bjR zb`D0-DK{kkgIG+WPd@1YS<)QLQsNvtn1nc%#4h~*Wp3GjDGmlOXs~A!V&P!aVgON0 XMhr4SQXI@mVq5|cX#^?7B*6>-rOR3Q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_09.binproto b/app/src/androidTest/assets/backupTests/account_data_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a4558f6fb6f70c72f49cbd27fce683d0169f719c GIT binary patch literal 222 zcmd<&U=+CVYr*{QhRYebmN0TD9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLxXk4M;30uGC@+$}CGQDz?((Qc&~T>R9_*;pRPS9TPqE!rRB@+lQT>A%D&5On}2R zzZGUe%%Q<95{zDM7VB1Q-o;qJ(80(wiIL5Mk%L)*V-_Pj2cxJhSJ{6Mi%InSi|as^ zGzYVkI7cU=5J%)riT{ooKmSW{Fn~dWJ(~~<2cs4Ph+;BgkP(vNU{(_25`ah}NGT=> FW&pifLOcKf literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_10.binproto b/app/src/androidTest/assets/backupTests/account_data_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..787f20d52f5a3efa8f6ec39f5c4d802da022c631 GIT binary patch literal 342 zcmd<&U=+CVYr*{QhVz-Y<}h(79R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP%*s!#)H5?MG>|gmQb@ZMeCivI-${l|_ntPgw|Z$V-t=+mhHutaPDpsRwQ-6I z2}GZ4`em1L{oRV`_7gPzwa7{^DzQ7~Bo<{Rr)sfzq?Tuvlo&~8l#~<{Tj}ekRwU*Y zp5ZEhjF{aX;Nq;i5i&%aWJsM?Ysf z2r;{c26@E+{lX~0sKBVfXuxQ3eADMz55@q-2*w1)48{V+3dROT#tuftNsKHUEDD^n z7}+@(MIW>8`wwCz@WjNO^AhqQHudY YF&Qz)2uX1;D~WLlK%^0*6q5uq0G9A?(f|Me literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_11.binproto b/app/src/androidTest/assets/backupTests/account_data_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..fa48dc62646b080281a69410d4fd1b83cc3da424 GIT binary patch literal 325 zcmd<&U=+CVYr*{QhJ8$2-Ar5xM}L3#f4^!<*KMQp&UJgXE=*haZ9=z@ zfNM|I>Dq0nPt*UNmO9qDVx0t&60=Wgq86t|YEfQdUP@|}kzz(kNkOrdzJ6*&Vs1fB zs$Oz_u6|CQmy5GUu%mZIWsakjCYQq3o>QW+-)G1PILha}$}(xuYME`uY#aJcX>CL2 zjm>i0Ld@{lz|g_SG>MVTf{}w&fom2cI|rlalp7NNK`bWGC!chH zENKpADRGV+OhOz>Vi*4ZGPmr%6bAzsG}yBVv2ZYIF@Pv0BL*2EDGp{OF)jg!G=h|3 Hl3)e^@nvhH literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_12.binproto b/app/src/androidTest/assets/backupTests/account_data_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b3b16d9f423513a2aecf5eb5baa1ebcb995780fd GIT binary patch literal 255 zcmd<&U=+CVYr*{Qh9?-gjxus79R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP$jVQt)H60UkTT;^(3|0#s&}1p+H|k4%a=r^a;Z*uf3Zhp-t8m5xFVZRzGM~> z_%VOutL1HBQ$=>H+PQV<{>u`~UhzOX86_AM7&RCT7%kSV*u0C;gE4?Hf-!+HgRy|I zg0X>-v4fFu5+e%-n*#SNMs^NHQCqIE{~#8V==m4dfh=hbW+`!wPDUY)$ej}Z9W{Rb nm*QXmg9dvxAr=lsEd~(9WW*pNB*nq3B*rBGkw%bGOcKlhQxi@3 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_13.binproto b/app/src/androidTest/assets/backupTests/account_data_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ca8130ae6c71a9d50fb9446dfed01203bf849ffb GIT binary patch literal 317 zcmd<&U=+CVYr*{QhHXq-%}iVhM}L3#f4^!<*KMQp&UJgXE=*haCmY(bf2sYS&`(itTs1;tkS`l%I(xdl0? zddc~@`gy57VO7NuR+?N2YJOWCYkw=;yl1UrqNiSX``CQ@u=6wIuX&vbaJc5T!c2%c zG}uLg(aY81_@>Xb1q>aGOp_SdEEqZ16?kSbvU4zsK4#zdAH-r3UAO8GkR{E*EG5pd zlTnCcYOnWyz8!P^OK~uOL4!S;5DN#R76XW4GGdSslHy=i65|qpNFzuoCJAN$xMFI* literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_14.binproto b/app/src/androidTest/assets/backupTests/account_data_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e7e056445c8934a11cfc53205ffc1b9a50bdeb3e GIT binary patch literal 349 zcmd<&U=+CVYr*{QhRc|^7Bg`v9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCPEY3*H$*I&cFqAUmQsAzeJlk#Slti0q{a@$Pyc#0ow3e%GeBQ7uLry@L-}|_b z0Kc!`1qHeCpSxt%o#;59{Y!#XiQPFTu_!Y+Rg29dwLG(=#7Hrtq@-uG8#yKFgeagO`xh6)$;`CFE} zR6qJT+d+ugH8jX89_S-R2}T7*4Mqb-i^ZRIzGLxV3}B34Okm7lEMTl)Y+z*UU}T)c z$il&)z&nePor6(y$_ih}_R8tmDG fSU4E97(f)05rd476bG}C7?%J<8bL}iNiYKd`E_rE literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_15.binproto b/app/src/androidTest/assets/backupTests/account_data_15.binproto new file mode 100644 index 0000000000000000000000000000000000000000..343e27523f7e7f85d6d76b2243a35edffe4a167a GIT binary patch literal 289 zcmd<&U=+CVYr*{QhOZd8o-=YO9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP$jVQt)H60UkTT;^P+ZsV)HJ>0)`Gorb&!!7K|L63VgE| z**O?RZMn++gIG+W=U-e0vZOhfrNlWp8HG3^cS`(s)cE;dih}_R8tmDGSU4E97(f)0 X5rd476bG}C7?%J<8bL}iNiYKd^DJEc literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_16.binproto b/app/src/androidTest/assets/backupTests/account_data_16.binproto new file mode 100644 index 0000000000000000000000000000000000000000..fc98294a55ddab4437069e2d703930991e8c99dd GIT binary patch literal 284 zcmd<&U=+CVYr*{QhEEx}9x-w$9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP%*s!#)H5?MG>|gmQn+GVWpm_1d-x~q^qJEH1YKs7%(uUk@%o?7_GG`6tBt=2 z39t)oUAsEw(J~pqFH5aYR0~M38%bxBloS+O>FcLfB<2?6r0ON-=j!LB`h-;#M|i~n zUBoECsKBVfXuxQ3eADMz55@q-2*w1)48{V+3dROT#tuftNsKHUTnhZN7}+@(MIW>8 z`wwCz@WjNO^AhqQHudYF&Qz)2uX1; QD~WLlK%^0*6q5uq0J`{BdH?_b literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_17.binproto b/app/src/androidTest/assets/backupTests/account_data_17.binproto new file mode 100644 index 0000000000000000000000000000000000000000..7a59a2e944013d371fb2390a9dd52e7c00cd167a GIT binary patch literal 324 zcmd<&U=+CVYr*{QhP_N&T})gGM}L3#f4^!<*KMQp&UJgXE=*ha`b2_5i8Uaxq_|RxEhw`rwW!!gF{7lUpx8=ZKeZw;w;(4~ zFF8L~KPS)2#n~g+(L19u$I(iYOF_+Vt7GkNg`4-Rbxics3vVBrZy$DkhWs_JGXW0Q z{8pF=F^2}bNHBW2SuFmv^Bqe8LkA<%Bt|w1Mhmx-f0wllyyLF-rOP``i`N zRLxXkcg{&H%1lnxV)IBX&nzjiQs+|OaZ*@qnd9BJ_x+XGE?Z7qoa27Fp~6Ld{+1;# z)sKG8b`WBA4Gr>&2inCb!KlEf!Dzr}v2MlYU5p-#0gMrh35*$x1&kGp4UCK(jEs{Q zSvYtU1ZOd_b1;h9a+UoDv6w{9zqk%$NpmnuiF0%^3UNg4l=$zc@$2yfG8#-1{onK4rV1WE&+%%f|O#CUmx-f0wllyyLF-rOP``i`N zRLvCPEY3*H$*I&cFqAUmQiz!S=WjS4m%uiKi&1m;#%OKtvM!qyvd}U!YlBqjw+Y=s z0sqY z>>P}uQ*KE72eFt$pM25*vZOhfrNlXQFbQ!iiCy^r%iOa6QXC9m&|uFd#KOU-#Q>t1 Xj2L8uq&S$B#JB_?(g;$DNrD*wY6V*~ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_21.binproto b/app/src/androidTest/assets/backupTests/account_data_21.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5f8a7af4577ab3a505cba367983fdc44e6cb53ea GIT binary patch literal 288 zcmd<&U=+CVYr*{QhA$bpo-uMM9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP$jVQt)H60UkTT;^(3|0#s&}1p+H|k4%a=r^a;Z*uf3Zhp-t8m5xFVZRzGM~> z_%VOutL1HBQ$=>H+PQV<{>u`~N~{5iCB>CmY(bf2sYS(Bnp_HMep?-De=FR)XRTwR zr(Ssb*nIo2^E2eHd7TMxxaPOQOo%x&*hPZT%gtilip{$i3m7^WnI-?NVTd literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_22.binproto b/app/src/androidTest/assets/backupTests/account_data_22.binproto new file mode 100644 index 0000000000000000000000000000000000000000..782546a80bf9b4f75b0b7190f06ce0260592337e GIT binary patch literal 342 zcmd<&U=+CVYr*{QhVz-Y<}h(79R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP%*s!#)H5?MG>|gmQYbmq`(~=kjA{#Bg|2^Rj~p$Ky8g!d$MUbrEvJ^dU3E_u z67UzE-!PT8Z{^3{eF7662USV1D6u=|Bo<{Rr)sfzq?Tuvlo&~8l#~<{Tj}ekRwU*Y zp5ZEhjF{aX;Nq;i5i&%aWJsM?Ysf z2r;{c26@E+{lX~0sKBVfXuxQ3eADMz55@q-2*w1)48{V+3dROT#tuftNsKHU0t%wD z7}+@(MIW>8`wwCz@WjNO^AhqQHudY YF&Qz)2uX1;D~WLlK%^0*6q5uq08Sckl>h($ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_23.binproto b/app/src/androidTest/assets/backupTests/account_data_23.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3cb8d26b902b514293b6ec0e57977edcbbdc675c GIT binary patch literal 325 zcmd<&U=+CVYr*{QhJ8$2-Ar5xM}L3#f4^!<*KMQp&UJgXE=*havm4pEoSakP{H*_dYHp z!0#(~K|!wk=PsFbCpyk&|B_%;V)jW*)Z+9=Ey_#GOG&LVQp_kRDJZtm*H5iT%q_@C z)l1IL)z8WEa&h(ucJ$7u%yG2RuA;h literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_24.binproto b/app/src/androidTest/assets/backupTests/account_data_24.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ba1331c6c1110d6d2a076152488d7be5749f205c GIT binary patch literal 255 zcmd<&U=+CVYr*{Qh9?-gjxus79R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCP$jVQt)H60UkTT;^P+ZNDN-!!gYA_lwTC7{Kc^9JxV*q0WV*+CaV*z6Y zV*?{&2P5MoMives4aQlF>>P}uwp?ZZK`bWG^DnLgS<)QLQsNw)j6xidJ0<=*YW(~! m#lZju4fbq8EF6ql3?Pchh(Sh3ii256j7tC_jUc6%B$xs6?n{FJ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_25.binproto b/app/src/androidTest/assets/backupTests/account_data_25.binproto new file mode 100644 index 0000000000000000000000000000000000000000..cc65a08903520b3c80dc0674bb48e9c4e581737b GIT binary patch literal 317 zcmd<&U=+CVYr*{QhHXq-%}iVhM}L3#f4^!<*KMQp&UJgXE=*haCmY(bf2sYS&`(itTs1;tkS`l%I(xdl0? zddc~@`gy57VO7NuR+?N2YJOWCYkw=;yl1UrqNiSX``CQ@u=6wIuX&vbaJc5T!c2%c zG}uLg(aY81_@>Xb1q>aGOp_SdEEqY2HJD~GvU4zsK4#zdAH-r3UAO8GkR{E*EG5pd zlTnCcYOnWyz8!P^OK~uOL4!S;5DN#R76XW4GGdSslHy=i65|qpNFzuoCJAN$8bfIN literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_26.binproto b/app/src/androidTest/assets/backupTests/account_data_26.binproto new file mode 100644 index 0000000000000000000000000000000000000000..7f8b37263f5d46612d2a475d6d6d45cb6e783445 GIT binary patch literal 349 zcmd<&U=+CVYr*{QhRc|^7Bg`v9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLvCPEY3*H$*I&cFqAUmQn%VyKNwaoZeNb_g;iXmO0*id*5G~?Xu;>#X0V$8!BAX=Wki^ zQvK-XYzHA`*U%uZc%Y9MB^VVLH5d&TEf#;;`HsbdF@Q0GF@Z6Iv4F9Hv4N4XgOPC( zBMXO!2J-;_7iw literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/account_data_27.binproto b/app/src/androidTest/assets/backupTests/account_data_27.binproto new file mode 100644 index 0000000000000000000000000000000000000000..6b547a0bf18c049d0c0dba3f2cfae0df2643e1dc GIT binary patch literal 223 zcmd<&U=+CVYr*{QhASAkmNIfF9R2;_|NW{dUAK+WJJ;>mx-f0wllyyLF-rOP``i`N zRLxXk_DN0D;`B%@%1g{kNv*QdP? zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6JggmU=fKrT1%q*;I>>Qk2+&sK|{6Y$h1b`G@NNRD3 gf^%X{j)G5SUbX@wpAiqo%5{IwKUy?nt(ycR0C|CCbN~PV literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/ad_hoc_call_01.binproto b/app/src/androidTest/assets/backupTests/ad_hoc_call_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..6042301f50e52e3a70aba0a6217065a23c36e48e GIT binary patch literal 390 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6JggmU=fKrT1%q*;I>>Qk2+&sK|{6Y$h1b`G@NNRD3 nf^%X{j)G5SUbX_GsF5(ogx7PHPW^r9at_cV^Vcs~alr@xR!M4U literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/ad_hoc_call_02.binproto b/app/src/androidTest/assets/backupTests/ad_hoc_call_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..81ad3568eeb2b58a0d876550e782303e1e58f78e GIT binary patch literal 390 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6JggmU=fKrT1%q*;I>>Qk2+&sK|{6Y$h1b`G@NNRD3 nf^%X{j)G5SUbX_GsF5(om)|q?FPwCLT?o)4k9!UrTVVhIT5xKY literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_00.binproto b/app/src/androidTest/assets/backupTests/chat_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..77105521e90ac2da68da0d1472a271bf67a2d7fd GIT binary patch literal 511 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJP$pHv!7RWc!Dzwg!044^!6+1?#Fd$wn4YSiRgjv_ z<;SI9nrx74nr5D4X>Mv{ZfRkfWNv7hoMMt@VQFrZW@2O^z#t`HeO)gwfc;~XBAd6l pPffj~l0XbkeD(C8%GT|oXZ<^izG!HC=xe*nEX5G;`s$`H>;UMZo74aR literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_01.binproto b/app/src/androidTest/assets/backupTests/chat_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d8aa949dc67fc5c382591988f707aeddc9df5fd6 GIT binary patch literal 586 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJaDY*2FCz!D01MD-7oPr{|3l2+*Xe&hHko@ZU=*6o zsKl9>o0y)eUyzrsB@p>LRcflK>fP9tt19(B*&7-(2t>*Fr1@l+h2L6pVOt$*Zuq%yAh{rNmgP+Nm_W4 O1%m=(z`B#Iuh;?hl)ttB literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_02.binproto b/app/src/androidTest/assets/backupTests/chat_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0e5e9a8c7264d0b05c1f2638d09fb7a1e81d0c0a GIT binary patch literal 576 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJu#Hh_Gb0DH0E+~p#)aR{&u(Wm*nFdLR*$I#qXVPY zBt{EHp>9ScuFTxT^i=(`#Q^0~J>vppP E00&378UO$Q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_03.binproto b/app/src/androidTest/assets/backupTests/chat_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..06e7fb1855b700731f62dd1253aeea91923d6ad8 GIT binary patch literal 559 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJu#{115hDk)0E+^n*JMVaK1LFhlO;zv4u3S~A|HP6)iTl~q^=++ASlb#Fv2p;G^?n@jLVNp z!7L@&Fx4PA#n2+jASKP%G{q<-CDFnt(IhP`HPP6}NPs~~fQz{!`Sk|-w=Hd7B(w8U nbCd*f4?Z|6vEjbyrMoM%{QoUh()iHVc9&U!IpFoxO<&jnm#wo9 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_04.binproto b/app/src/androidTest/assets/backupTests/chat_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..bbc9a5a93311f12d50468cec3bde43f664290654 GIT binary patch literal 477 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJ5F!=C!7RWc!KiWJ>CgE;#0-9&{`X^(xdo#GqnD8d zqmYggS7vTvda8a_L27!GjE`54j+t4iUY&DAsj5%Dk8e$|k*aZcxMzNGt`vg;OTfC5 Ht*_Vt-~E)! literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_05.binproto b/app/src/androidTest/assets/backupTests/chat_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..dfac1078689be6757d217a1885eec28ffe68feb4 GIT binary patch literal 573 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJu$fV610x5s01MD-7k)oKyPefw^Nq$?J*Hlh8HM^7 zl{hnV6Vp@m3-Z#n1R{T@N=-FYy&Jo7Ri*wXdqaZ;fhZXtPhYFrASVlZ-8dh;6q8tg zy}WGi_@Yuz`}`CxKQ0B+WP@bWH1i}&b5kR8OAFH^b3@bQ6q7UyOLL<%6C(=&1}OpS z>w19!>>r~P*}TntYU(AG1Y&sNtEUH5wr&?a>)%=QMML95U)x<~1=fHCPuA^b2LO2f BwA=sy literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_06.binproto b/app/src/androidTest/assets/backupTests/chat_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..bf24d36e222471d01833df1672f9f3ce3400870e GIT binary patch literal 419 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJASETi!7RWc!Dzwg!05$h!N|d=z!vcO>ZUL30P~f7 Av;Y7A literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_07.binproto b/app/src/androidTest/assets/backupTests/chat_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a214806d0121ea5761a45d73601bfab991e678ed GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpd_Wh!7RW6^w@=`Kj;4tGx&A--;Yh^UO>kwum`L= I+4_nd0A(tL@Bjb+ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_08.binproto b/app/src/androidTest/assets/backupTests/chat_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..77498fcec305cc1f04c293154e9888b320309c01 GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpdqEk!7RWc!KiWJ_w%#cSq(PdXq?q!YQgBh=*4Ei P$ib|@5wPINy4~ymVG)H@ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_09.binproto b/app/src/androidTest/assets/backupTests/chat_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..1f0cc7a88cc2ebb9603a3a4dcd38c88e638c376d GIT binary patch literal 413 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z udTc3P6vgV8lbM{F#2TENS(0JJAR;Bi!7RX{!05%o!J@z!@cQbeFYEvl7<~Z% literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_10.binproto b/app/src/androidTest/assets/backupTests/chat_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..be00d610928e8966aa16a4af5b58707224a70735 GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpdqEk!7RWc!KiWJ>CgE;#0-9&{`X^(xdo#GqZgY6 PBL}MjSHQZHt*_Vte*lKB literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_11.binproto b/app/src/androidTest/assets/backupTests/chat_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..aad2d79f0432813758784dfd5ffc59b679661fa7 GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpd_Wh!7RW6^w@>p&(CgWHQ0QkaaND17YhfQ0(ZcI JC+l{z0{~CSgrWcd literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_12.binproto b/app/src/androidTest/assets/backupTests/chat_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a96a84eca7cb29447b89afbac63e5e235ad5e818 GIT binary patch literal 419 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJASETi!7RWc!Dzwg!05$h!N|d`z!UKL>ZUL30Q2>I AzyJUM literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_13.binproto b/app/src/androidTest/assets/backupTests/chat_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..cb836a7078805d8a18749f952276a761b2880118 GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpd_Wh!7RW6^w@=`Kj;4tGx&A--;Yh^UMw6O3cLa9 JPPV>c2LNS?h5P^j literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_14.binproto b/app/src/androidTest/assets/backupTests/chat_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..8c50c2b5d4d384abe8fecced06a5dc481d493e23 GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpdqEk!7RWc!KiWJ_w%#cSq(PdXq?q!YQgBh=*4Ei P$ib<=7qH;Ty4~ymVR40F literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_15.binproto b/app/src/androidTest/assets/backupTests/chat_15.binproto new file mode 100644 index 0000000000000000000000000000000000000000..7d63bddc065c0c03e303ad30708088fbc8164264 GIT binary patch literal 413 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z udTc3P6vgV8lbM{F#2TENS(0JJAR;Bi!7RX{!05%o!KJ_-@cQbeFYEvlczq23 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_16.binproto b/app/src/androidTest/assets/backupTests/chat_16.binproto new file mode 100644 index 0000000000000000000000000000000000000000..182a6f1a3924f4e5ff0444b325de2104d50f386a GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpdqEk!7RWc!KiWJ>CgE;#0-9&{`X^(xdo#GqZgY6 PBL}yFK)||_t*_Vte_)2Y literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_17.binproto b/app/src/androidTest/assets/backupTests/chat_17.binproto new file mode 100644 index 0000000000000000000000000000000000000000..afbbcc53e4bb49947f5deeaaa1dd608b23db91df GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpd_Wh!7RW6^w@>p&(CgWHQ0QkaaND17YhfEf?&Xc JC+l{z0{~DdgsuPp literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_18.binproto b/app/src/androidTest/assets/backupTests/chat_18.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3daa1d50693ddbb680532e207efe69910f708b58 GIT binary patch literal 419 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJASETi!7RWc!Dzwg!05$h!N|d@AQbTW>ZUL30Q6OT A%m4rY literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_19.binproto b/app/src/androidTest/assets/backupTests/chat_19.binproto new file mode 100644 index 0000000000000000000000000000000000000000..91dd622cf3bc5ba317d2ffcdede02db33c52b662 GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpd_Wh!7RW6^w@=`Kj;4tGx&A--;Yh^UMw7Z3c>;F JPPV>c2LNU2h6n%v literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_20.binproto b/app/src/androidTest/assets/backupTests/chat_20.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5c9a21b24cf54b44aa11144ba4f06f5a7f9c9624 GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpdqEk!7RWc!KiWJ_w%#cSq(PdXq?q!YQgBh=*4Ei P$ic5560qROy4~ymVbO(c literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_21.binproto b/app/src/androidTest/assets/backupTests/chat_21.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f9dc758d70353f0664c1a5f38537c58ac2407510 GIT binary patch literal 413 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z udTc3P6vgV8lbM{F#2TENS(0JJAR;Bi!7RX{!05%oA)p`{@cQbeFYEvl*nJrQ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_22.binproto b/app/src/androidTest/assets/backupTests/chat_22.binproto new file mode 100644 index 0000000000000000000000000000000000000000..21fe5712676e128725a0ea6c95637ec1a614a3bb GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpdqEk!7RWc!KiWJ>CgE;#0-9&{`X^(xdo#GqZgY6 PBZr`ZSiriIt*_Vtf53*v literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_23.binproto b/app/src/androidTest/assets/backupTests/chat_23.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b9d6bf880d6370f06e19203bb9045c71950ee4dc GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpd_Wh!7RW6^w@>p&(CgWHQ0QkaaND17Ym1w24ld2 JC+l{z0{~DjgscDn literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_24.binproto b/app/src/androidTest/assets/backupTests/chat_24.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f5df8c2a403c4b0243f4482edd729270bbb4b355 GIT binary patch literal 419 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJASETi!7RWc!Dzwg!05$h!N?)3!4&ZN>ZUL30Q6gZ A$^ZZW literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_25.binproto b/app/src/androidTest/assets/backupTests/chat_25.binproto new file mode 100644 index 0000000000000000000000000000000000000000..8bc8d3b228f82d8f26b73c7c13c7c0d5d5676223 GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpd_Wh!7RW6^w@=`Kj;4tGx&A--;Yh^UMw6U8q5Lf JPPV>c2LNU8h6Vrt literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_26.binproto b/app/src/androidTest/assets/backupTests/chat_26.binproto new file mode 100644 index 0000000000000000000000000000000000000000..750fcfc2537208dc5c08848c72d0eb44fe6fedfe GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJpdqEk!7RWc!KiWJ_w%#cSq(PdXq?q!YQgBh=*4Ei P$RR4l5U}9My4~ymVM~Qs literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..77991a7f10644682b00e401905e8d6867ae56945 GIT binary patch literal 451 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez-XwX4`N8HyEy0VX9Fi8j=N|6A3ka* eu<%dI#n~nxuP5 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#hu=_w(pb4e`7 z$V)Zi@XbulNCnDE32^b4n3$Uz7+4w@7+V+$Fe-^~33#XGRVF%kl;)MB7U?DD=L#@t zadI#!v8H&3g=ZVZC~;-xCZ?zAXBDKTbNO*8m|K`xniyLeSsJCJnk5;Vm|Lb8Ct0Kz zS{fxMnoA0IX<ihM)Wcqm0%+AXXS-RQ6$P%5)52n`D>m)AnHx4?I7f z=lA>ZJn*#AkX`%z`n7jmuOagjWW0zt`}wN}a)0vb(&et@n=6-Ao?5@15Z8^D`PX3f z#TwV9SsMCO$F}X4Bwk{?xI8xVc+(fG>*d|gQi-=0zQO9dQ%AR-Z2z%+ZO426IgUm5 z;0^Fh{_xa&muIcN{)>kaDA{rGQE|s6x|8w#eAoCo6y*I&!IfKy<LBZ<6J!#&z7r&q+K+|`Hf~`Ad4!8%&8%5R25u~w{7^`@r;t2jES_3l;nJD z&Wg6+i9+5;49T<|OWVpwVI;M|SS;>WYjIROB#RfJ&{~;BWxS_qmZNRyY)zd{PV`ns zGQB;2ZcQF8c4YL7A&pollI_obG@p&{wyezVefd}+Y%MaQ&g+UI(z^mdNk@1uE@4R* z^-`!iEa~BpghJT4p8Z2P>ee-7>GAooAEM6#d4XN`y& zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#?(=`$mc$tbb#z|5XWhF<(!yc{e7 zHx|F%y3SOIiGwle3M1D=MlM}04!4}tlH`okA|ZC?oW#nK{5&ZZ-^}7-C1y`gPc06g z{Gy`7%)C@1X3w%jqp6HaT$#Cv>8biz1*z#)TrQ>AL7pi&=@F$49V{N3nWAKT+}*X( zbaXTIa&+_U-Bs%Bqe{#RJp2r8OI(e)!nqVIQVop~lZ*`#4ULVG4N@%=Q_L;RjT24G zj7h0A)ZA+7ED$g;hFA1A$ci905Lws;s5{u literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..aa2bd5e3fde4f523ea180d8525063d7d49f8a07b GIT binary patch literal 716 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^o1X&V!e$tbbz;+(gi4ZL`{xH(t^ z*3Uh)bhV)pL()tpu4znM99(Qc`FVMnsZvs0+{VUc#+H^A7M3PP0xVLTrLN9_;iaXe zu1d;W0->SV-tHj|1tFoXA$s{m=|Ba%RW5!3c}dQdiOx>$j#?fZj6!Uc>7^yZHcCdx_@sFHN7(zhT1J|L)D;8;1ZBAzMp&kq zW)+o~artp6n5G#d85kH^n3*M|q^2b$TbddfTbh_!q#7if8YiWu2rx(qd@{4UIi-;O nQAY4IDYuhM_DTZr+zb+Fd*APwsyrp)!BOkC8e7gEn#m3TbJp5% literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..634fa201e338133e66357b7dd3a2a30d7d5576e1 GIT binary patch literal 723 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7X3X)hCy$tbb#z|5XWhF%<8Y#c0F zOdO0!OPRP9F>x7iu?Hn4XC$WP2ywb+=H#Rn7i6YNG5Z$jDKUE$>1naKBo<`kr5bVg zW+rE(0_CLyxOhxV%*_o9EDa2dEer)1r3AUSjg8HWEiEl9EKQ6An4}n#M7RXJQ}Zel z9Xv|&N>YpTlJj!~7(oFN8k+6x9^z0C66zYFmtT|)Qmw_w!KlQV;vE*AttH99B*Y-a z=^Evq#Ty)0;GL8bXo4+aN4eSUP-f1 pu&45@@+T$FmTL(t7m54$>LuTczbz8q<7YaqHF*8$To zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8HY=@ui9$tbbz;+(gi4ZL`{xH(t^ zZY+Mib)BgeL(*PGu3d~=I$RuXIjJSd8L34=Y~hI|#rb(sEWSm>dP>Z}MS5DSUim4R zsYYzUrNxPPiAGgQoSC_a>8bh!dFfVME~VK)o+&x$5v2})q&@1RWPCh*t!jguEbMjT zeDqRGV*T~En|e)7juNnomziPOQqdaRt8 zzxS=ZC~2!DQ0v&vjPoIGRut$l*)EDY|qzh1>;#RoL2$}u%BIV~h3J)i^tzxKv^ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..728843a7166eceac2008e8ccbd86d8006c18f56b GIT binary patch literal 735 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7p9=_C`7$tbb#z|5XWhF<(!yc{e7 z>*t2anRc zlGGx-Wo&LKr^ ziBW}4MxjbvnYoGSsrp$3sp$?KEFPPggdDkeg98h^lTxBQgB(M>G6a|<)_z^Q;JCSx zfOz&v_lKoZ*8UYaB(bP|$rCMsW1%Z8|E%$mi!Hj7*vG4GWpMq;4O1(Q@J#ohki3*4 E0IC|}7XSbN literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..19e67ee4fa60cbdd2810b3368ba263a00e638b15 GIT binary patch literal 697 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^S_X)zO!$tbbz;+(gi4ZPU6SU6aW z7?OIKxVo6QIJww^5|c9$Q*)%GxVVjt&5SKAEi5cej09MuI7?lf1H(&8OI@V|xOhxV z%*_o9EDa2dEer)1m6W*zLPN8?-9sD-LPA|b^zw_+fm(R0T>Jv^lAJ3Oot@krl|;A% zyi@Zk6CFHC^GZ^S^pf*)1sJtFI2eW4D$`3#(uQ@q2%v$fcr)520b!o7@G3nDxc z!>xF7OVWZ|vXVo?f}?VrxU;g7N>jorT`JQnytqO`Gc!wavpsyuv^Y5!p(YsFD{*G# zCZ?zA7v!Z|@p$_cg_L__xfF!?xHue^_Sh&HCF7IklVKK?pJ?gl8E#+h=cb#V@1tuS M?igW_Un#`^0K1^d=Kufz literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..26791d0edc8af80a0748c33a8120de67c0f280cd GIT binary patch literal 448 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JFB17t8tEIcr?XOf{8KNl|ti@=S= dueYu;o{=raLY+8NzO zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$a|=?No{$tbbz;+(gi4ZL`{xH(t^ z*3Uh)bhV)sL(*|Zt|N?G`dn;5`FVMnsY0CYnK?PB#RZwEQp~Y*MS5CnE{O#h zd8tMmzM07xsX%!t0WKaB6LWI|14{z~V+%t8MkNt00q@kj%0vf`(!7$?BE97NTmeQc zP7X#T))eos@NAa|<&|6JtvwOQV!jvm|2^bITOtB#RV7 zOQYmuvm}#L0R}07iI%16tMhK$vRV7A$Vay6l#)P2rt_pp_p(hYFYmZG=}fkt#+LJk JX0lr`003hT!BzkO literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..2326bea64cbaf04287cb923b0a783b6284701ea0 GIT binary patch literal 869 zcmaJ}yANwh(_|8-N%p~JwoTF`O{dMYNki5BC;car zUS_iC^cDnL6l`H1ywF{`uy`q=Ac#+b^u-%Uk=C*xTR}v+_#kRgK?+`o6MfPL4+qZ2 zIp6nj4xDxxvPZwY`pRkW1Tx=4#*2s--~RDqc_?*v=T30%js5NYd)BYdi^s-;!UwotKfL*o%X2i;_{l>Fl9n&h^gu7X0s}DeLYEAY3^f^+Q&53LcpVzB23O!3gd32!1KViW z2>@ue5aFQa--zr4P0)066b*f#RR|GuaXd{54>7Dm|5xyYht#<1G{sFhL+1Pe0R3R? z9}49D+abJL$6qzCroH!{Sp45Cx#DxxGS*!A`KoQi0L{B;f~6FAj}$&99SA+IeXxD= zrC{34P&6e2?R}DYk7T-;wlP#T5T4`Q1!ON+CQl957yJU5uls$jv^8DDqC3C1fK0@n zXU>E|QYadYM51AlC3wJJWIDp(zP>;p8VH0UL6+p(XS1Wp!7+O|-#UMMd^VF*`)lg> zpz62km6k|&gkdw-Tt)ql3I>0 zS+O=eQ!E(CQJJ>m8C#hsPNdHmtEFpdJ%LI$Wbrl>+N<-Zf_rObCDxJ2)z#(H%s_1- zJJA2f#ndyUuB@IlqzMZ}b3=v4mvf08%gXj#EyRlvYn7RFURM;69t?ye9pT4t8B4mT zm&1J#Nsojj6voc=?C;9DTi2AG!>y@rWA_6Efj##9Oj_?ed0_Q^vSK|qle=>K?4Bvo btJA~PrDCQK0kL!DY^D*%W?5H_iemo-VqXDl literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5d749a5188ded39a8e2f28e8334c4f53764c13d1 GIT binary patch literal 647 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#qx=_4bM$tbbz;+(gi4ZL`{xH(t^ zZY+Mib)BgfL(&ySu8WLZx?CJ?IjJSd8L34=?9Mrfl_mLkQY^li#l=d@o}Qjs96tF) zMTwbtsYcA6Wr;>p8I`y)a}(23^|K06)2+B%O0$DJQ*zQHN*y{_JT@~$$@sXtYo+Pv zX6ohW=G(ig)Y(Usm=}2X8QPY(8gqqnDOjW$8YLzf8zdSU8zmd0S|+BLTbdgunwS}z z7+4yZrU)=g3GAv0w04hN>VMF^baTxCPbnpVKB3hH3yluETw7VZB-vp?tj6a>3xkc= W!h%9Pi-IhetT@6m-Gf5%Qi=cx^~c!& literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..04380d9e539e11b2e73b6603e010a4baf09b9042 GIT binary patch literal 718 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^=9X$KRK$tbb#z|5XWhF<(!yc{e7 z>*t4q*OA8B26C(i@Db7+?=fLpN z(o$C?WiElx&}?t_5Ql=0P}dN>{GxQA0^TYYzks|X=gLH9CwE6J4-Q5lw#xL*2)m#pN_u;8d1C+@7Qq|%hIN|(y?3NNnE z(9Fz|+-wh@GNXDW&dl7z^i=(VymW`d(jFTnqhx$iJpCi={ah_0O+xAlf&zlFTn!^E z(@e99O3b+YxD-s&43Z2C3@yyel2TIBl9DY=jf^c#Of6Ckl1+`1Qd0yNqy#>h+1;E{ p$o?oJc$$>kNhW(Gfp~5PiL|}%_e@ou67k@u^;?ZC=MT+f2LP~!*~$O_ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..8bce91dae499514a3b0e140d5f7b9f5cf5eb1d3f GIT binary patch literal 723 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7X3X)hCy$tbbz;+(gi4ZJwG*f?0c zfI>-2nYb1)aT##22PGzFB&OyFak^*b-pPn(gf#;!qG0>KdY#Uz83~t;NZ~sKlD$9TuLgCCR}g#304# z8s(qlmRA)LrNv;xV8!6X;Kg86ti+X>o0y)epH+~WZpGv6R}@n2k>yel=Hn72i zV`i4BSLa+&s_K*P<69GKq-q=U>I7qb-u05ne3IsgCw literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3249c59223236cad29e4cdf023354b99e6071fad GIT binary patch literal 421 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mOF2xKry+*tg2>pIgQhAJj01_1j! Bem4LB literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e3bcec810603746b2bfcef3fd54add07e7b2b35d GIT binary patch literal 428 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m380b)q3pL=TQYQrFgDo!bOjsvg% ItUWFd0DB06F#rGn literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..6a0551bb3cc4284955cffa06d1c6972a5b095e31 GIT binary patch literal 428 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m380c0>r+*tg2>pIgQhAK`ec8&?N J4qklD0|0)`f(ift literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..2f4793110f69880c67f945316b9036272f74446b GIT binary patch literal 773 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#$t={*yODY5S2oVTA1oP;>;p80?H zsG-2ZKP?w$n}9su#dwB^>m(CH-O7uOPbIF^{g6~N-@4jx(UODd9TVLdC!Mm{qxXDg z8`HEPh8`J-Re`4$PdT#ZtB7W+URmY?qp+>p>L$rdeHQ&ti&i>y!|mysSxjx83L2Ns+Bn5zjbQeJ!bPvmn-+QqKKAC? zv9j>-@i|H1i?%4Nb5_oHrWuht-|XERm(nR`M1cGH9n>d*Fr~0nR{2b5jw4|-H{mdNB30x(Po6WOKf7edR-1>xN zPuEJmP> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#el=`9n8DY5?IoVTA1ocK8Io%w(G zsG-23KP?w$n{+XrW8yl=#89{LqT^GEYjr;)70tJBUoy?D;C9*{WBT`M@Y_>$bYdGx}>ngyRYt&RAtW`I$fCl|kh$(<`el z>E64rKd(sm)pDOjKh&a?PTg>Ox@Hzr+oyuY<+C zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#hm=_wPC$tbbm;+(gi4ZPU6SU6au z7`hnGF>#$_VyIhr(ebIowYndYisoBa8!lRMFuh}6O)&bnji*pI0RO zYPrv%A8OG`r*61CT{DZR?NdSH@>v_Fn5+@Zeo(mRwRzJ*55dRYTsu}4K0ZDtDSXiu zg>}x#8P7B$a_5`9d*f0%<&20B+mRw@&@j|*42#~&9x319{()(klg*8Eqeo9HR{Zg) zh+Q2R_-GRc^Zr!dHJP8|*`1cOmA0Rm!#RPg#BsBEmg(=>Nts)puv&@Xl*p*u&nphc7i?eexu$M0T}1KNB)%N~rJlV{HGr(`=#Z hnnTZ6V3EiOWxW3STv23x zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JUG2xKryY`i$H;>*u}^szzhHwB!cz; literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..01f2a949da0eb58f72bfab1c3b9b4b7772dbdfa1 GIT binary patch literal 768 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$I)=@k=@$tbbu;+(gi4ZL`{xH(t^ z*3Uh)bhV)pLl@&2Ca#lA40S6nIzE-SR`)|v(R}M_!$nIDrguzqXPk7(W{=+Uoo!6h zf*5*aBvu8UUOeT3meQ0dJzkPiH5_cJ4EV&Clz-g&JHd)WK-@TCT zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#(u={XaS$tbb;;+(gi4ZJwG*f?0U zm^c`_7|$_ron&IDTY1s(sl>IqACijZTUQ${T5>SGW1>6bq*FF~^q%i*W11Gk&?6(U zD)98;DM$8v713ZZ*&!Qh{(MqRoxIJAni>d8XLF4jS8>g785zKy2xahTc(?Soy$KG5!Ru(=!J|`)B z(H4bu&dM3jG$V57o4tGEQaa^~h!ES6B52?+)Nc%n-pd{--{Jm&X_=GFjdY_&Pb^ma z@u`Si9T@m%69@DDRNpn3pX1q`mb8_&pP9osfvd!Evw4>3@7hV3Tc5D(=~~Gb>QTL| zA!33`Z?1uK;7_}s8Br7aZg23;YhBpG-nWM@HDG=6B&$SrwL3o(GG|Jt@AhMC|GLv` jq3fDM&sbp5$OvV;{`y=|WPaq_)&6t3xcCz%3orrzX zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$g?=?xQ*$tbbq;+(gi4ZL`{xH(t^ zZY+Mib)BgeLl@&YCa#lA40S6nIzE-SR`)|v(R}M_!$nIDrguzqXPk7(W{=+Uoo!6h zf*5*aBvu8UUOeT3meQ0dJzkPiH5_cJ4EV&Clz-g&JHd)WK-@TCT zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JUG2xKryY`r+=?PmioelA`P7J>D1 SPc2<-XvD<9*u}^szzhHrvx1`l literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..24a2177e4ba5c5ae43974cce712f339a23c1ca84 GIT binary patch literal 761 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#6O)&bnji*pI0RO zYPrv%A8OG`r*61CT{DZR?NdSH@>v_Fn5+@Zeo(mRwRzJ*55dRYTsu}4K0ZDtDSXiu zg>}x#8P7B$a_5`9d*f0%<&20B+mRw@&@j|*42#~&9x319{()(klg*8Eqeo9HR{Zg) zh+Q2R_-GRc^Zr!dHJP8|*`1cOmA0Rm!#RPg#BsBEmg(=>Nts)puv&@Xl*p*u&nphc7i?eexu$M0T}1KNB)%N~rJlV{HGr(`=#Z fnnTZ6V3EiOWxW3STv23x zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$3#=^Yc0$tbb?;+(gi4ZQfdcsW=E zZY+Mib)Bgd69;1#<2fd-lS~YCD=#`emAF>-LsHRv>uSSAOAe-YOmt_QbjoIr-t(Pp zOw)oGdSoP41)g3!<;b3|BATswWtk6*!nSU!n>?exCPX-{py7;F=98cKGhP`~?lQfy z`jYOw3;XkmgkLT9S@c6KTItjcx2J1nF|~avXk0#P;}nxMg4quW7ri!bTIeD8*qdv| z%EHIT=Ol$M+M=+|SvljGW<>6Mvv+S?N~fF=5n?-11PvpG`i)`Hd)Xu9JKR4oEpxKD zk#6+piN%UPJ{7U60|Osz;$YsN>boZMb3D7#lD5+JGjlj6aFsZ2HqSEsT{|gr>l2ne zT`T!QJ*u}gL`+cW%{7n?{Au?yBWhyb?G4^}tqXhD`}Xjq2CPq>WR=LScIRh8=1d9o q-F}SiUw4` zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$g?=?xQ*$tbbo;+(gi4ZL`{xH(t^ z*3Uh)bhV)sLl@&YCa#lA40S6nIzE-SR`)|v(R}M_!$nIDrguzqXPk7(W{=+Uoo!6h zf*5*aBvu8UUOeT3meQ0dJzkPiH5_cJ4EV&Clz-g&JHd)WK-@TCT zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m380c0>r?7TSV?PmioHZB$p7AJ-- IMkWDf0CK5*;Q#;t literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f7832df85a8f07bc3b55b2a088de9ee6d3f3367e GIT binary patch literal 768 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$I)=@k=@$tbbw;+(gi4ZL`{xH(t^ zZY+Mib)BgfLl@&2Ca#lA40S6nIzE-SR`)|v(R}M_!$nIDrguzqXPk7(W{=+Uoo!6h zf*5*aBvu8UUOeT3meQ0dJzkPiH5_cJ4EV&Clz-g&JHd)WK-@TCT zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$3#=^Yc0$tbb=;+(gi4ZQfdcsW=E z*3Uh)bhV)u69;1#<2fd-lS~YCD=#`emAF>-LsHRv>uSSAOAe-YOmt_QbjoIr-t(Pp zOw)oGdSoP41)g3!<;b3|BATswWtk6*!nSU!n>?exCPX-{py7;F=98cKGhP`~?lQfy z`jYOw3;XkmgkLT9S@c6KTItjcx2J1nF|~avXk0#P;}nxMg4quW7ri!bTIeD8*qdv| z%EHIT=Ol$M+M=+|SvljGW<>6Mvv+S?N~fF=5n?-11PvpG`i)`Hd)Xu9JKR4oEpxKD zk#6+piN%UPJ{7U60|Osz;$YsN>boZMb3D7#lD5+JGjlj6aFsZ2HqSEsT{|gr>l2ne zT`T!QJ*u}gL`+cW%{7n?{Au?yBWhyb?G4^}tqXhD`}Xjq2CPq>WR=LScIRh8=1d9o q-F}SiUw4` zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#(u={XaS$tbbs;+(gi4ZJwG*f?0c zfI?l2=a{%oGBMPxyy*B;;#%DgNk#Lms|^<|Ihfut(VcP9DVsfd&v&*lO$%b^k&##x zczW@aBYVDzXtwH=Wj-(p+q$i8@{InP5aGCjhBH=~Pk!dlcx6zz%k;|XOS<Marvx`Q%u$fWp@-mOZ>}9H3m+e!lN7#a zi^4i*<&0;V5xMit-o0@topMG*i0w!bG;kQ|H-<&;Wsj8aaR0!x%*p0Py3wO27AyYv zRK%_h41BbSgL!|d@0!fd@$61Z+DhBc%;B8CRpPkWJj?WV?WD}DPgwSJt>g>!sNU8P zF+rs_*FZY(r`^wtsEK{IH+bi@F6?3N+ryU{us(T`RU*6Eou3JrGbPk_`!TkE-D$Sa hb zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a va!9dpFbi-6Fe)i202z!DHx|F%y3RC+p-Rw+pMyaFlmh0jU$WwYkp&|F*wLx( literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..df3844cdcc173765817d971a593ada409e57c437 GIT binary patch literal 715 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fls4j02z!DHy6L&y3RC+p-R$8oTLBsoTXELU%H$lz@)&U@wn%}u@wdu Fi~xV_u%G|{ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..fe7bbf6bcb337fa07d4f75b1e28d3751506bceaa GIT binary patch literal 717 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6FzP620U3-Ew-&$Ny3RC+p-S3GlH>F58T%Jby1y<&fLVe?fmLJv`Xwta I7+Ej^0NhHjlK=n! literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..82bc99417fd0015c65901e5545516590752a6cb1 GIT binary patch literal 709 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a ya!9dpFbi-6Fsdji0U3-Ew->+Oy3RC+p-R|Ekb^;hMS=yG1|IhuIJUyTf)M~I7ph4B literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3be234be44005ba0be5371a2678e0620cf649bad GIT binary patch literal 719 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6FzPAk02z!DcNV|iy3RC+p-R?Cnxp^qoTXELU%H$lz$(F_z@jmK{gM?I Jj0{pN7y&)#u{8hy literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..2829afb4a6315daf3886f80537b2fe3685099f25 GIT binary patch literal 717 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6FzP620U3-EcNf3jy3RC+p-S3GlH>F58T%Jby1y<&fK7r$fmP#i&w*nr H3=C2L-uba5 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..68d81179582166e80fc52ea175647d851b7583b3 GIT binary patch literal 709 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a ya!9dpFbi-6Fsdji0U3-E_ZGk2y3RC+p-R|Ekb^;h9hd^ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fls4j02z!D_ZPq3y3RC+p-R$8oTLBsoTXELU%H$lz@fmR@wn%}u@wdu Fi~xZLu&e+8 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..2732d155ece8e8f3dbf699af6510bf9dfe0ee9d6 GIT binary patch literal 438 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^J061Y|Hu+*tg2>pIgQhALqzK@J9} Or{}L zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^J371!OQv++6&6>pIgQhAL?*Nsj*4 cbCyp1ed%(J0Fwlh0+Yt$o&(2L7#J`D0Q}yGlmGw# literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..143e0efcedaa4acdbe6d3fa17efd765f39a0e130 GIT binary patch literal 444 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^J990c0>r+*pIgQhAK%bagNWw XXY5}%>HfM9u;-aI=C5C};(`$X>I#V` literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e4740c00b67919907e9a6cc548160dcde2f72d2a GIT binary patch literal 438 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^J061Y|Hu++O^8>pIgQhALqzK@J81 SCZMNTG#>XHIJUyTfDr(M5`!B6 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..bc9239855cf0da7e3583b1735080531342a7c4eb GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mFC4rDM&yt~`FcaK33LluV?8^`q} I4<|YT0RFjwumAu6 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0718662d2e42a831d88f49e245fd7e2d38a58dd8 GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mFC4rDM&yuaJJcaK33LluV?8^`No I$KQGa0020GF#rGn literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..baf3a7edf5ddc563d42ff9bcdef3c84dac65ca14 GIT binary patch literal 434 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^I@k2V^ixe7M`XcaK33LlwUluMl@? OZe~tqzMhGtnK=N3tAacL literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f96a632e3421e87995410968b1f58cd472b22bcd GIT binary patch literal 432 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m9A17t8te7xJbcaK33Llv(Vw-9GW MX?lj9g@vgl0DlC5WB>pF literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..879aa81db6ce2d73a1357a998cb765fd5605f687 GIT binary patch literal 429 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mRG31l!ze7f7ZcaK33Llu`7hY(v) Jev+PnF#!49fN}r; literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..67afc34bef09fca188e42c7a629234f9e431f9c9 GIT binary patch literal 657 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#|*={F;YDY5wE+uobTPC^`a&-_1p z)KFmIpO%ZWO+cP+V!Xk~C2DA9VPt4(WN2w*Y-w(2VPR-&Vq_^KY+`I-W@2e*Y-(g~ zVrF7!X<=!mw33l)8KaOAmx7r<-W%R!?E7DYu{?R4&R_oe>vKhs`H^#1`_Jj(;!m6` zvF+f#Ii1D|3>pkZ3KzCriaO&P)fCyTS@fsXW?95S1@$MIj*lLGiJG>@r%2r@mXz3E8N^m(7L@xl54!KYO literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..684e7ff013cbb255bd658664a8a316456b9a9d17 GIT binary patch literal 730 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8WV=?D{uDY4|_+uobTPJA5q&ip@o z)KFm2pO%ZWO_~^&GI0r8n3-Cdn;4r}T9})d8e5uMSXc;&nVFfG0fkJ>jE#)U%*>2U zOwBEUQDmKwpPO2guTYR$l$l(blUb}#2I3Z$CZ-mp=B1S8D&*$pq!yQCrYfYR78PYG z6lE5d7AqvC7M7+e?O^2E%E-Yeq{O8#VeRT&T}AtL`|h-SV50TF^zOd;C#iooyWTX3 zzZ13be38WVgZt)m8Y`Sw)bslJ>$i96HIDpWaeSMZkwVB#<3qesEaz^j%>3%Ubc)_| nVeYe)GH-wV;nSR&d+@QI!E5N literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..472abecbdc43647fd09fd9c9ea6bf47fbadea6b6 GIT binary patch literal 730 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8WV=?D{$$tbb(g}lJoP*5_58Z z^2I>KIhlo}sS3HNnMyktxwbNLFbOGfDg4!1uFb{Vk^Fjt{o9tdFOu1LsX3hGV)>VU zeU+1V&VF8E$H9GbI*k?9JpQ%h_p`6>xHMLN+A@8snUTWpg4^?jx;9AWW zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U*D)Wis6GD<8v`L_3_u@^rVF9(aj zjm59Gt}|6);$UoIOy&}{Ff+9@H!(J|v@ka@HMTUju&@vkGcz+W0}7d%85 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez!<3H4`eV(EI;|S_olHIFBdlli@^H1 zr MG*e;`V&Y%~0EWPee*gdg literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..7f790cdddb0172005b94ab1e468587c8d62b5a34 GIT binary patch literal 542 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U^G)WHa3GD@sC`L_3_u@?sy8wZOP z69;1xV*!`2g_)_PxrwovrG>ePsj;QGg@uKXn30X<}+oYF zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez__1DX%7>S$tbb%Km_W?p7)W^!qO%L za(-S}VonZFz8I)DC$q3LRUtPuQ)wk5*D^*SB`yUsfxI`o%h>n72xEEjIGw-z_1EW$ zBJ(5XuJ)hP#l@dESz_D4eRDdE6&N%aj1(?xyA*ZCH>xSJU9;#7k57@hRWKLONR;5l!qL(tMwQ@lCWu}D?9|U= literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..192e256b7c32201178f2b1006422508f0ca148f6 GIT binary patch literal 720 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^!5X%`ca$tbbv*tl z*u>P_5*SAY#resZnZ*i)rI`x3`8lb@C7G!}MqWv3u|i5_a!F}!afw1%Vo_#ku|j4_ zVQH$;4o0r6j2w(YN?ZyP)~?>wRkUxn?@r4HCRz_n@9wLAlKOYE>rIpRJ5d|Y7fEbC zxNlCUvBHT(J+Gg?etV~0 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U#7^p6q9WRzHa@@?-;V=p!?77i98 zh9<`6j9j9IW)?<nz)_^Y*Cn~S+4`Sk|-w=Hd7B(w8Ub2!Vz@-P4TDkt%r{k+7EgZt)m8Y`@M z{A53A^HBl}3r9 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&L?Xsh1JRWRzHQ@@?-;V=sO#UJe$4 z8;f6WU1w^=#KG9an8zh-VP zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&L|ZX(}U-$tbb**tbv%*4{r*wo0}#LUFd z(!$bA$|W zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez*wtP4P-D%tULL(_olHI8y5=)ixWc= zV=9-hg_)_PxrwovrG>ePsj;QGg@uKXn3 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$9<=^G=E$tbb@#xrhMdnA&UF|=oi;F*Tvc$H7 z`{r~SD==s<7%5!Xb}8zNZ&XucyJpd!R-0uJ3l-F#XgWT6_$6xE9-kt0t6(mmkthL$ Pg`=fQj4Hw9Oc1>QVyd-Q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5c1dee4ae2ffce4c747e957785d8544fa6749d9e GIT binary patch literal 731 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7d5=_nJB$tbbm*tl z*u>P_5*SC;8Tq-XMfnN^sYRK|r8$|!3S}T}VQFG&QEFaFX|6(Ueoks}NoJ}-T53^I zrb1C>acQwaVrpS&s?rWduC0t5j6zCW3KQ0@-qlsKZ@2GG%LgV}4@~dwtACREceCqF zllVJP8_yR>Y(KbfPN%WLiA6oHpTB;4r(Wa8{}so#nHedB+%!JKE5&l|rpnB(?n|fW pO&8`qTPgGQ*B?I3sksLqoV5z(0-BE!?pQcly2PjwoW}&w3jplG+N}Tp literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e47d835bfb936c9e66b7567355b88d735a3e8f86 GIT binary patch literal 732 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<82L=@=7`$tbb$w=7ts)hQ=mFmO{cN#ujEKmWIZrM&>4FCWe+4mS$2e znR#WYd6`A23YmGCxtYnO#R`R`nZ*iesYSV|c_pQ}3W-I@r3!iZd8N6P3d#9-Wr;aC zK>1>z;+)LF(o}`q)J&xvj9gn8Ihcf$xD@_sE!XB^?nr*U!TxPa+ZV~~ywn`daIvE$&rIi1D|Yaahv^84A>cU&4PKW&-5)yzoYcfsxXLR}jqbF&{FTmCkv pQ%To@WACavKVL1G(0| zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JRF4Pr>#Sp0hHI@2JADj6jyF7A-b aTm`SxyzI=pVj=FJ{3He6#Js%BymSCX7l#7? literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4dd5d18a14032ef1fb1a25b62ce64f8df104c735 GIT binary patch literal 448 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JFB17b+5pL=TQYQrFgDp@6IE*{t1 b%$&@8h475bl2jq?ko=Ssg@F8$lGGvqz|n^z literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..c9ad0d272b087568f20096c6771a624e0311a06c GIT binary patch literal 445 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JXH31UdxSp0hHI@2JADk&uiE-sJK Y^bCdI{G7~UAujjSyhH_;{PMhF05yz;E&u=k literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..64174c651d2bd2f79436f291d9859ca52247300c GIT binary patch literal 437 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JUG2x3UAyEy0VX9Fi8j=N|6A3ka* Qu<%dI#n~nxuNN@@08ZwH*#H0l literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..756e33d12ffb48fccf8340d20b2eaefab72d822e GIT binary patch literal 433 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mXI3t~vDzc}aZX9Fibj(cbRA3ka* Nu;@?A#n~oB3;;9lhL!*T literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4210317c504cc4238a9106940de062f7320a9641 GIT binary patch literal 425 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mUH3S=-!Y`8e*?PmioHZB$p7Ab}z F1_1OEetZA` literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4a906cf0df3095131f48093fbf91bac382566fc1 GIT binary patch literal 434 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^I@k2V^ixY`i$H;>Si}GTj!A;T literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..96d168d2f45eff8dc1386958515d9fd8c840c91e GIT binary patch literal 432 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m9A17t8tY`Qq-?PmioUM_A97J>D1 NPc2<-sKijj004z}f=K`X literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b71677dd4c022e1cb6e7428d56e0a0f2186b26b6 GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mFC4rDM&Y`!?>?Pmio4lXth7A+ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m9A17t8tY`Hk+?PmioUM_A97J(a! NUvFJ!s>M*m004 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^I@k2V^ixY`r+=?PmioelA`P7J>D1 PPc2<-XvD<9Si}GTiqV2^ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..298ac13d173834f0186ffa61734ff7f3c1fd492a GIT binary patch literal 425 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mUH3S=-!Y`Zw;?PmioHZB$p79)lt F1_1QaewqLP literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..313296e48814e0d1b9be5a625081a6bab6feeda0 GIT binary patch literal 434 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^I@k2V^ixY`-|??PmioelA`P7J(a! PUvFJ!YQ@CCSi}GTk8gtD literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..17ad297df4c6d210e8d93fab3469fac2f7411853 GIT binary patch literal 432 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m9A17t8t?6^4R?PmioUM_A97J>D1 NPc2<-XvI*(004$!f@c5# literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f4f7d2222df5fe05ef0163e2bda8a68f6ecb52bc GIT binary patch literal 425 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mUH3S=-!?7TSV?PmioHZB$p7AJ-x F1_1R#eysoi literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..870ac8d8cb22b7ee6fd1f3f5f2f5bb210c8277b3 GIT binary patch literal 432 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m9A17t8t?7BGT?PmioUM_A97J(a! NUvFJ!>cvpR004?Ug5UrE literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a4bd82ca4a18a95fdb198729ebdfffcdf9a3989c GIT binary patch literal 434 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^I@k2V^ix?7leX?PmioelA`P7J>D1 PPc2<-=*7grSi}GTi}!+! literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..7d14f5b67d17bce661bd24617aca59c17cc8f3cf GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mFC4rDM&?72AS?Pmio4lXth7B40a HrXmIa^6Gxf literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..44cecda13b80aad52e653181b0d15264625c6bd7 GIT binary patch literal 427 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mFC4q`~$Sp0hHI@2JADh?wyj*HDb GtDOP-$$>xs literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d678c1f4846ee669db1e9e3a97f1944e0e5fbf44 GIT binary patch literal 428 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m380b)q3pL=TQYQrFgDo!JIjzwFy Iy=dkI0C^pOlmGw# literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5f0f8a04684f4a9a8cc6e4d1f91d636238d981be GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}Q&!Sp0hHI@2JADi$s#4n_d@ C9)3Fj literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d4598661b315637e69c6cb4c3a5b76031dbab48b GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}Q&!T>N_LI@2JADi$s#4kiHj CEq*-! literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..8c67432144757febf9ac8e97edd91cb088e6f819 GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}Q&!TKszJI@2JADi$s#4rT!O CJbpg_ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b2dd35289d82ff181663340746684bc4cd332e7b GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}Q&!Ui^CNI@2JADi$s#4i*6T COMXEB literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4c68339a9e2233f537fb06a672d93eeec1ff88fb GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}Q&!S^RqII@2JADi$s#4psp8 CT7E+S literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..439cd6d00cb1209da372bd011ce699e7af9126a4 GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4#++F;7>pIgQhAI{=CJr_L D_(^_4 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..cdc8542634f332dbdc0fa3b7cc45403c94716a70 GIT binary patch literal 445 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$+*|y5>pIgQhAI{=CJuIp LD2v3g)ySd%5g3D` literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..620a6217dc1f67ea595b0580e1867a77b6021ff9 GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$++X~9>pIgQhAI{=CJqh& D_*Z^L literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0cf5d7e5c4030dfe4f0adf6d592a08afc2a0241e GIT binary patch literal 445 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JXrjC>pIgQhAI{=CJs)B LD2v32)ySd%5v+r& literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..6625c7e75307e86d899202308b5c1af6743535aa GIT binary patch literal 445 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JY4*G>pIgQhAI{=CJru$ LD2v3&)ySd%5%z zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JX-vE>pIgQhAI{=CJt_h LD2v3Y)ySd%5 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JYM{I>pIgQhAI{=CJr8m LD2v4D)ySd%5{iSj literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ea315edc29c3483999d5b1b9e940533d1860e3e6 GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JX!pD>pIgQhAI{=CJtTz D_@aJL literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..78b7e127c3d2f2eec17ff1076d2db65350da83bf GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JYD>H>pIgQhAI{=CJsIT D__2Oc literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ed94fb58262a2ccd0a0bca9a8ea1f2e24c28ac55 GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JX`#F>pIgQhAI{=CJuf8 D_`rTt literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_15.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_15.binproto new file mode 100644 index 0000000000000000000000000000000000000000..bee6dd31ab3b77ea1e0abc602f61488a57a8b52b GIT binary patch literal 423 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mID3}i4$JYW2J>pIgQhAI{=CJq4r D_|JY; literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..8a260952113b1c631f273ce0e3dc97be5c46fbcf GIT binary patch literal 804 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fd8c90~st5$G^SZJkQigh@<7s%q_j<0vlGnp0v&YloF!&gm}2P3=EBo uO-#+qEi40-{J}cjyuW?wtT|R4dO|wLI)vCb7zDT!7=^?@v=n-rG6Dc>1i| zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6FuE!^0~st5C%(PiJkQjLkE8X@%q_j<0vlJop0v&&N=- zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6FnTL_0vU`FC%?VjJkQjNhl_=SMT!APvPv;T=?Up@aTypI8Jn1znOj&2 riE(fUFiBwsAsYvS0E+_LtatBkpE_$!z$}m+E(K zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6For4x0~w4Gr@p=2JkQiij*FLrMc~Hb*IU<_Dlu^|0=cXLchCGkeAE!c zjZza*;o>qdG%_|ZH8Z!c6k-FW7ghx(APsby0yB_i1JW$9O3`3b-@m_o>a00IQ}u*& V&`f0z;8I`_65{~UQs`-b5de(C#|{7h literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..74a94953fb529eeeba0c99ff5c547d4453fc8ead GIT binary patch literal 884 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fjgv+0~w4Gr@y`3JkQiiii?|rMPU8hQ%hGHDlq^#tO5)Fv|OBRqQns8 zC*;G$WngGzY+`C=Zec0J#=$1Qq`)d92BM|VV~P zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fs3V|0vU`FXTH7NJkQiifQyZTMT?1p5lFLw=qNWK7cMRXLnC7oQ!{f5 zOCdI33Sm`X0@6UoC@=$QHXzLcrCEi zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fj^{^0~w4GXTQDOJkQiiii?|rMc~Hb*IU<_YB2ygtO9q>{6BouP>Ugo zPl$($%fQgc*u>P#+`=+aDIBcx)BD?}&YI)ZsVAg^tW$`MgF%2xfk{XVL`$K^F(Uww CH^qAZ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ef229c2044dc18d1943f28f6c643b7d8a5fc7b7d GIT binary patch literal 852 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6For4x0~w4G=f1t&JkQiij*FLrMPU8hQ%hGH8ZmJ&0=cXL3;(oSoNWT) zMyUy@aB&$J8X23InweWz3W;%W2rx-u1Zu2OG}zS7?{A+vYfjKqJs}+wQ`tBe1h^EK Nfu;j#py_Cy0s!sw#fJa@ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..057f782ce42c1b986f5a25380ad135f5832f0199 GIT binary patch literal 808 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fj^~F0vU`F=fAz(JkQjNhl_=S#fSk&vKlc&$qC7DaTypI8Jn1znOj&2 yv2id6uqZGBX+{B71txDLPq5xE?{A+vYmTN@Pe=ztFP8#~kQmSmE-CbQWds1os zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6For4x0~w4G7rwpSJkQiij*FLrMc~Hb*IU<_S}}1j0=cXLchCGkeAE!c zjZza*;o>qdG%_|ZH8Z!c6aqU*ff-1%2{0+JKxx)krD(9JU*F$8b=I7qsd_>>Xr?j< Ra4E0~iE#jFDfBeJ2mrfT$AAC; literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..77c780b77d0bc73307c8e7e068460df746533a21 GIT binary patch literal 884 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fjgv+0~w4G7r(vTJkQiiii?|rMPU8hQ%hGHS}_1QtO5)Fv|OBRV#N^U zC*;G$WngGzY+`C=Zeb}T#=$1QB!wPBj6iV?usCY4G77PAFbJ?HfM`YmRt2U=rEsv# d-`?Lob=DlO&3ZyQC^myk;sTl^2BI-t007A_$jATy literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b37a6c5e2c7a7c6834bb373ab9dc797c44588673 GIT binary patch literal 864 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6FcvEn0vU`Fm%hE-JkQjNhl_=S#fbq(vN|zDg$adlaTypI8Jn1znOj&2 zfgPa045Zlvm=su`G^>yph%bd6SByY$4zM_C05f_kd4lcy{{HrIvzf*~uWl NrN9I<3rJ(y0RSS}x()yU literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5b2351bcea483d836e21b947706a5e92b2ba4241 GIT binary patch literal 820 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fj^{^0~w4Gm%qK;JkQiiii?|rMc~Hb*IU<_dNBYwtO9q>{6Bou(2F68 zPl$($%fQgc*u>P#+`=+aDIBcx$NSr-&YI)ZsVAg^tW$`MgF%2xfmuilL`$K^F(UxL CM8*gJ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..bc69c63469c34d2670dbe6beae20d50eaee36c11 GIT binary patch literal 836 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6FuE!^0~w4GSH8X7JkQiij*FLrMPU8hQ%hGHdNFY@0=cXL3;(oSoNWT) zMu`cDaB&$J8X23InweWz3bAo82(TzH#wtaFP5Jr$_NlYx1WnNs(!nr=OMyj53}_mc J6ndO90sw!}#J&Ik literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..264a19e3eebbf437663b51e2923712adce9f4897 GIT binary patch literal 816 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i_VH8@)$iXGG1Q^FC9>l^CVAQG;65!(VP1R8VJ5xsiXq1#(MoCFQv6a4l zYDHphK~Ab(a(=FUVp(EIVv$}}LAn+j2a^Dk1d9Qql?W^x5kV!u1QtaLKmn*)R6h$a za!9dpFbi-6Fxo5G0vU`FSHHd8JkQiifQyZT#fyo92}rYo=qNcM87?jZLnC7oQ!{f5 zOCdI33Sm`X0@6UoC@}jg`GWQTdVl-WS#vD<^@McL^fL%>DX zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^xCX%!=gDY5S2oVTA1oP;>;p80?H zsG-2ZKP?w$n}9qY)xjtv&m~)&npd1!lv$#XlA4@fl&X-KSCU#(P?TDdSd>|;m8v9M zl3G!sU!hx^nVy%Jqg#-Zn3>1r$E9GAXl|60nv`Ucl4NOYnQCBcVw4CZO;b{mQq9v+ zO#~RE1a6wd--+6IzGwrx#Y5Hi#RW zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8HY=@ui1DY5?IoVTA1ocK8Io%w(G zsG-23KP?w$n?&tl6jJ6=%*<0rNlnYl%Ph&v&r2; zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$6;=@}!C$tbbm;+(gi4ZPU6SU6au z7@{sP3h8iZm6l}YCFUxW<*!`_~)r7QL!Kc3Q_?=V|P}23_*t_b^&sR$(^dC1q z-`pp#>Awm;-1z04GK!lh8 Dc-GBR literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4ee50a7adfd97bac91e11cafa32b6fcc37509495 GIT binary patch literal 573 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^N0X*na1$tbb$;+(gi4ZQfdcsW=E zZY+Mib)Bga69;2d2cwWYmuzurUU6zsW{E;dYI1&2szPR7Nor9+QEEwIQD(7Ls*-R? zYDI~Dg>G?XdR}6VZb43BW*(Oxmx6gpl9`!ls;QZIT1s+~VPdkGg=wNiVroiKijie< zl8FF=lz`rK&S}%VzAj%9naZU);r&G=f!b}UPt*UNmO9qDV%_aabGB$)?pnT*82|z+ BxJ&>5 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9c6bb90f0eaeef3b1a5f0d16578107f829641479 GIT binary patch literal 582 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^)FX#*pW$tbbu;+(gi4ZL`{xH(t^ z*3Uh)bhV)pL)2tOA!RPb%shpZ)U?dJ%#zIfywqZavizLVl7hq%h2q5Y%#xDKVy#Fe z;gZyf68#F@;>`5C#2np%oW#sLEAHHEfA>uHLf?vt0wGf_&Z_Wmx5C8as&XX(?};}u z`F#a1D9Dxn+$FQ_gqA?Z<4<0S%UcBln zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7aC=`16V$tbb;;+(gi4ZJwG*f?0U zm^c`twlWIoaA}p6WacI2DwO1xBq|i87L=xBCg!CirYa;Am6oI`l%*DBmXsDNl;)M@ zYQ-xFm!wvd=vU|#XQt;R=I9pWBxdGWu_b$Fx>QvOIdJi$RC)OL=V$vy1>|N4Fi9-B zf3N?xv68^yLuz-F zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M>zX$m8d$tbbq;+(gi4ZL`{xH(t^ zZY+Mib)BgeLsW&3JeO>7YF=?_QD%ukN@{X`QK~{_UP)?EK~ZW+Vo_$XR;ZG2Noqxj zeuZvvW_n&?j&4CtVrHI@9T#U+c(_|(Vscfv0JFr&o!92PHC7V%C^@I%%SuHvtMBtP id;Wa#)Dk$r6nR|DZ>wYNZ-txptaVHbnwP$~!Uh2DShL#z literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..2d83e9f8133240edbc8fbbd287b0ee712e9ab58e GIT binary patch literal 528 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez*whL17t8tY`r+=?PmioelA`P7J>D1 zPc2<-XvD<97!@a^%%zx_r;w7GmYJ7Xl9`{ETC7l(pHo^=kXWKnoS2?jQj%G$rJ^KU zl3G!sU!hx^nVy%Jqg#-Zn3-qAoty3Np6OobTTxLU#b6}J#qwePqz0gMhd1s!({HZ8 F2mtYhq-FpB literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..535fe73ac1a2a1c34b5df02e3dd35a215cf519ca GIT binary patch literal 615 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7mG=^`VL$tbby;+(gi4ZPU6SU6aW z7@~GF3h8iZm6l}YCFUxW zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8Wd=?Ei`$tbb?;+(gi4ZQfdcsW=E zZY+Mib)Bgd69;3|Qbr+pF4^MLyyDcN%o2r^)a3l4RE5mElGLJtqSTVaqRe8gMn)y! zlGKV4{R-XU%=Em(9NmJP#LPS^?%ZsD_e}Rf--?O?u2?Pwvy^1RRDAQ4+{K_~5L>hWn94 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7mG=^`VL$tbbo;+(gi4ZL`{xH(t^ z*3Uh)bhV)sL(~>VA!RPb%shpZ)U?dJ%#zIfywqZavizLVl7hq%h2q5Y%#xDKVy!AA z;gZyf68#F@;>`5C#2np%oW#sLE4F0sOqZ%Eu4pa=BO{{}6AJ^2q?E+Ol;kwa)Wk#+ zO9P9vWOHNVWFr$30cI%yuc!sPzg4fAkajEh)Hfc#lS%?gx*i;RSKayfYRQED zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7mG=^`VL$tbb&;+(gi4ZPU6SU6am z7@~GF3h8iZm6l}YCFUxW+&U$sa&cP-d|J_sNI(OH2v>ssbj4x*4@4|XN$(=uH`G4 jjRd(^KFpug0Ce!-jr-2@n=3FvgeLY1ut*$%2r&TwT7bu~ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e3a18d87b6d2df87abaeefb4fded234b13b2cbb8 GIT binary patch literal 558 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M*xX$B*Z$tbbw;+(gi4ZL`{xH(t^ zZY+Mib)BgfLsX5BJeO>7YF=?_QD%ukN@{X`QK~{_UP)?EK~ZW+Vo_$XR-}?}Noqxj zeuZvvW_n&?j&4CtVrHHdcW$=7d!~D#Z$(9ckSP~uRd~2tVPbMsxsrhQ#2cCXzJeDN l zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^=HX$K>a$tbb=;+(gi4ZQfdcsW=E z*3Uh)bhV)u69;3|OhzGPF2&3|g_P8^%)HE!%>2C6VuiB&oYInl#1e(##PrOPlFVYQ zcqQSI)QS@Q3fv0mD9lFOfJRq51Ymks{jI9g{U$i?zu S{-g$=Qx9+4cc$N5fe`@zVZSy2 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..1dd8e1928efdf189e199cfbc73884483813fa05e GIT binary patch literal 601 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7*N=`bUZ$tbbs;+(gi4ZJwG*f?0c zm^hfCRx=9eaA}p6WacI2DwO1xBq|i87L=xBCg!CirYa;Am6oI`l%*DBmXsDNl;)M@ zYK1BZm!wvd=vU|#XQt;R=I9pWBxdFb*>Q1Jg@?NpCMH*v3ouKZ+<9%zTVo}GkCJmL zzN}O zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JFB17b+5yEy0VX9EjHCn1izXZ{~P bYACSqPs_#GCLqs8aSE|>u@ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&MdnsgDuFlvsap&fCuh7K~1O9QV%r zKYY|sV9}qJi?dCl@`bp$I3vT;Ttd$ zlafqQk}QoaQw@wwj1qyQX-Y~`s(D(fi2#F?z)h3*J5d|Y7j0m-c&PfmxIjsut7zYD Z-<_5ZOtc=D-rZOKMB{ST@|DaIi~yNluJZr@ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..c6727cc931919c08f41b60a1b8711ba870b2939a GIT binary patch literal 1138 zcmcIiZERCj7{2GW-P+^SYvZ)s>_>BjWK`F+_ujU*BW9!0wQII+tY}Atz`6IF+jd{~ zI$9?aVN3iN#ORC>1KGmjFq@c|YK+D#l7L1s=Z4G}h7OsaKXeWZ!iXj~?^sNfAO7>? z&3T`k^X7ftkH=;O%215dysN-iE{*SrMXny4BwKFm?|Y!X^rzCMg2mRAsMpygZ0R7x#V%SCpx6}YGsNApyfowb^{`ygY4|Ld@{!z9~xTFv%t z#wWaInxbl`-qjFbzx{=FzIJhLc|**3c6ho!Npxg$nJUW47FlV=+ypLI zxH*dffunF{aPc>f1Y$+dY&Ek~>Qjro7+Q3kjiuN@01pAqmhC`kQmJ>Qt86{VWbG43 z>FKVydT;caBUM(f)@S^WX($-4OICNsYb!LZzG6diV~eEihTEajj}9;^yJ z%wvos9*L?-kihc{%`(u`uPhX*7oUCp+M2db(#|m*C+4Rdg$ur!^JA{YKS#LoFBVSE znfW_*C)(;aN5ajy?u10Ky#VgvY$%VjT4jeWRF&YKXnmvB){tuIXsvmPi4NTfABRq! z=dmbxBpD-w6*Wi@5`!K|RRV&b@S@0=SSFSl9$$L;*ns*?;`61F_yosr6;bQQ$L4a>>lw^5I7|C-eDySfU+(?E`QhXL~Y>$w~=gO&=ZYV~wdqA};lz zimbc6LaUCf-CLc?1@vg#`*1ESz&LaUuzF+>;fzh{2tPb0`yOFLNt46u3 z)>)}z^&gvB;ge8+mx(NkSPlw7;t@n18MhB(nFLgFi|?GcV=ndX>8?Vt^!>UkyL65@ z&~<#g;iKTZ=$iMPKfdpWRX(HZVE~5!2Um5`s+06>qyJkQxUR9KIvx&g6}Rlwo12TB)F`Tu8a`~|D5dw2i< literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..c38bcf0b6e5fd6f2318890e7d7130d156b96dcdc GIT binary patch literal 449 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JdJ3uG`#Y`i$-5x3x-wz literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9c3363da7440e675446961f7d90cee648caf1693 GIT binary patch literal 543 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}Um6)X4~BGD>W^IOpwW0}DnkUM_A9 z7J>D1Pc2<-sKgMJEyT{nT9A_I6(yCx6{EzJnVXoNs-IPmn$G3NrC^b0Zj_Xolw^{U zWNB=fYG7<)ln5kEQ&N&r&C^m%1Q?_QZkoj3iQ0I+Xal>&L)G`i1xf;4Mf-O9?zDVh VqV>S^?!Njb8kf74uVj{B1OReFsapU5 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..fb62ed6db346670e6926b25fa49b5f5a5f7a3789 GIT binary patch literal 1144 zcmcIiZA@EL7{2GWWw&Rj*G9Cv;YV}b5>_p=_ujU*ECyC7Ee#Y#(L&v_oqNx@rF`|u zT5&ixNc>=nQH*hB3uHuQz?m5Qh{h0?Y$i@bn8TPcWs1!GP-HW6W6@;dWyD1H!+)N< zIq#Em-n`HI@mLwa&i*lU^oHj$fQtY+5dHF7OP>U*B7?(6JQHtB9iE!WTz_6zXug=Z zOy$nc+VYi*f_7>on=SVFI3IM7)b`z9-Z{ovE{^YwhOZu)Bn>wX^xofB@KeF;#+${j z*jRKDyiR=`yzjs%i+#4L`&YY}HwTR@PIIM^rgSrO{4(BrS`xY9VR33CJn=#(PI%P# zK<#vL{=3ndX|lm)dL&+92hpfeF0ca!C<1OE_ZJ2M3IZhv)Ci~pjbImO20C~W^Z?KY z=p$eq0}3dLT3JQ3fmUuIQvtO?t>oFv3~*Abjuxmw8^@UVdm(Ft|LbtH-K5xdF=pF# z;}hOJO;P1k&sqqu-Tp#5UR{`5*%Eb}+mZWEOIo(JrB$L>;_c~db1B7ec?`{(>%b)o zzhE&Su)lY9VCgrv45AFoW6VyP0m;uSw)N23l+Y>~QhHUC8vyVy;H~L)lp-a1N2=7? zok&zZj@0h1@~ihmuGy1?)mnA4@6ikj#HtcyU9n16CR6R&lGxfPXZFAy(BZ?XhqzVj zkrg-AL@yRFMzVk;O(RGY1eWGlXzEiJ$I6zTdG6YV)(+Cfv+XD6r|e_jduJ|;I&1$N z=8L{qJTqq&*6)e8R&NW3>a$&Onc{i?+{;^04yQ9E?YdZ+kGmt)wVBqMWL9qAwQu5|FXzW5c$RlX zn2(Ro{r>Z@{+CXRrz(m*e0Or$ELd})hqonixm-x5?SShAa6fN-B8|yv2~v449qHwa zsf5E$?Lk+%xXqO*3vb+4mdyI~NX+w4HYCCrbojC2^LdHa=hv{Rc#!Ioy+}p`D_97q zio{YJTYS!X=(Xtekz-8+ZtA~smS>;-fLu!5oa8=zcJ%FUX9nIc`s(1wn@??7h{F^HFAsZytH#J`OcZnv32qf zo38BEdG=uEiSe3`0`rn{-h1K1{vX%)jIM_O90WXE(?yF;Qn!u%Z*AbJ+Qzb2D9|J| X?9=P(>RUpd#(?H+>CF26XKnliuMvFd literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..88f58767390afc54c13cbbb748c1a57de1eec192 GIT binary patch literal 443 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JLD4rDM&Y`Hk+?PmiEMlW72ZVncK Y8;f6WU1zGr5XC9P&c#}glIax%04;)r0{{R3 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..03ae657477a9f362ceab4b59ebbe8092f0fa48c6 GIT binary patch literal 549 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&MFfsh<(ZWR%!?an9S%1{RE7{9L>o zECTE2o?5!v(1?kHF)Ck(n~O6tJk2E}y};8;DuF9Ti7PWVF+Ej3s~|O<%a2RJBGKF^ zDK#m{Bqhnx*fQ0?*u*FiNSdakB&C|CrJ4vZND16DiN6!I@qEz+c8iCq?~4nR1iFg$ b?e^Vi`M^Z$f$80S^-nY|cP(GZEWro>By+0F literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a76900cadb17b6f2c42a7a2a83d14e56fc36fde5 GIT binary patch literal 1138 zcmcIiZ%i9y7{B*=h4vNoY(%>YADVMZunM$y*Y?U{V0ERXfx_sttZv!fyZ1dPQ0`={ zIGh_K{$Y!87~{+qh(u<7ll-+c4r zeSUf0C(rNs^RwFkJNL)%8#la{0bBskh3Hq_S^Oka8yy-s?45jT`q1>Oe*Hz^V(X>k zWh!@W&QYZ33fiubY_{C*=lsw`TDI=|@~&~#dTC;JEOPbW6xn=ZU*7}$#Xl9#t-V?P zs)I!*z?;<9q5Jopv^wW%dwz8?JQFgrxS1L=P342o^~*%-DM{qYN5tvT$mC1m1mRKh zLk%;j`R~T+XUH0d<*~#@Cy2$&a)A@LKq>G5d7vZ&PzWd?phiI>Xa+k#D=@&*pcjCC zKpzH$Hc(7a)ba|VO|*OqS&FG;YPrC{*npc_akM~{I5?YyzYnrz_`eQEJ1vT1hmCP; zH9z4!GZa-#^{$2h$L%k)>$Qvd<@GVw*=@Q1w3Ky2M_MI{HIYeYTPrCWS74)ArV(7Q z@_DNXf%Djz!NuP^GKdvG#>NO#>Qk$u5LykKj-|Lk01pA)p3a~aQekwqRN8xz$(kpT z+S6Tq_1@?;XR4%5t84W?uA^YQHd)mjuPM{@y0Z1j4b8H?3vPohKUTfOqhhbDc(5k= zuz)d=1te)2L82hAG{-_qzq&A9wfNi%*VeRmk`A8DoS2_>j$iQ2o*#2J{5isxez9;m z&j@$yO0?H)iiDf8-3giEdI8+c+ffdu^@@xkRu%Mu+Z%Pe7L- z2w0LmvVsx9sum;&NkNaSsR2<`1xaEp92-jwPb@uibU^zi@%d6we3EDRvZ(Fjqxs)| zJ~D9Nlz4Ju>4)!6Eir;U7k+e8GMCGRW!ee2J^=Uf_NUUAtdt;C_tDWl&YVgl;?^E6 zOP6<)=~a=nd#X~|fDw&*AIXMA7>BL^R{VY+@%aN9RuwN&{jv|qh+qW^K~<4hiet;q z+7G@SyFPkk3xVqfuAJf7XOEE|TW(HqpFKbJ?zgjpAC!KzfAnn&XTT2rZe$qds!=wr zcUBl!`^TnM_+%6i6rv~+R)S)XctlA+=Iz5+Apwot;yWj8&!^ry-8Eh)f4}a^PJ?In zcO9Ro|0p;wx#xZ7kMI3qmCx*Y7{DRG!&P0h8KmX5(f_RtT-(rG6%PluNSpT zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JdJ3uG`#Y`-|??PmiEMlXIYUJe$4 e8;f6WU1w^=#K9QFE5yyk85y4D5|Uov=>-5z!-jMK literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3595c0db3c32c676a029610e3e30556b26ab7373 GIT binary patch literal 543 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}Um6)X4~BGD_^YIOpwW0}DnkUM_A9 z7J>D1Pc2<-XvGkfEyT{nT9A_I6(yCx6{EzJnVXoNs-IPmn$G3NrC^b0Zj_Xolw^{U zWNB=fYG7<)ln5kEQ&N&r&C^m%1Q?_QZkoj3iQ0I+Xal>&L)G`i1xf;4Mf-O9?zDVh VqV>S^?!Njb8kf74uVj{B1OR`-sd)eZ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..cc6c23947fb2fa13bd7240b3d8f6d3bf2d0ada83 GIT binary patch literal 1142 zcmcIiZA@EL7`~?;tUW`$Hlp1PKbq^7FtpI#d)wZ!7+9tBBc(8k7V4Jm+Xah}JEyYqsLOT_b%~rVGj2qfWa{KNt?w+8Hi<5i9foq4SN%PGE{SOY5{8Tc(@m9sF z78;!f$EdG-4;(mSw9bcmf3+G|gHOv+Zm84JRBeW~UnWy$1)ixGB2SFof0lPp7sNiYP2fzT( z9R=%5poF5R!Ya~fsBjzUOQ-@>D6$w#pqyHDI*ux}FeW{FKcuzre;r0=)k~ILCWB?W z_6hHurKlRJZ!H8^?tIbNUc0XatK! zHgD7*u%0|WwEUYx1mPlRFd2MQ;!~q#Jv6t*mD=W{8VWN*03HFXIo*kpq*ColR+)R_ z@wz9G+}l%g?Y`i3YoauwL{jd@Gsqha#jAUwb@ogqV&4+q+9GE5z#Y)$#W>6W_V!E{&Hr{5i^& zeZF*V!N9HG6Kjub3;3I|Ju#7D`T*R^no$m?GnJhxUsa5IgOP?zdwrs@v#sW3Iyih6 zd;;1yj>CfJ5G9NdmK85SNbovDMfUK#%n1UmXXtQZWOC)16NAdvvCmeDqf;!++JmN# zPAvTX^YOt$XZbU=Wgosby<*_ZIsc>E;<;STFY2s-=?8E>Ykn$?$!ZBgO}{SK&uCK# z1j?0%?dggRd!{rsPI=ObA^52MiL!IInUA}+T_!LsB;vRiZ^5fLn5&MQj- zO)+%E1@qz8!#Bo`w-UH%@alP%e)a?MWAfHC^U3q$?|d^i^nTfw2glyhGb-$0??I}n zts7<2nXXC|EC1NkDxZivoJ1r^z>=5u5(h7ENV{DaOT?p)+k6*=oePO~&vj3%6Tjbd zb+^jW2fI&A)}Qp|h4Q@X(y9GFtnq1Gj{rCfSh%K(HkBmr82#Vcz@dhg>Zsq_Dm3p? Vn;M(i{LU7y;%e*8y8mZw`~}UteDnYS literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ab836778d9793e513e0a50e5b96d38cb9648e012 GIT binary patch literal 443 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JLD4rDM&?7BGT?PmiEMlW72ZVncK Y8;f6WU1#dW5XC9P&c#}glIax%059%^DF6Tf literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..dccc7a153399b8b62017d792c943d85fa7a64315 GIT binary patch literal 549 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&MFfsh<(ZWR%!_an9S%1{RE7{9L>o zECTE2o?5!v(2I$KF)Ck(n~O6tJk2E}y};8;DuF9Ti7PWVF+Ej3s~|O<%a2RJBGKF^ zDK#m{Bqhnx*fQ0?*u*FiNSdakB&C|CrJ4vZND16DiN6!I@qEz+c8iCq?~4nR1iFg$ b?e^Vi`M^Z$f$80S^-nY|cP(GZEWro>GN-EQ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9cdaa5c58273eaa1bc964a25dd4ed09c793d0d7f GIT binary patch literal 1140 zcmcIiZ%i9y7{B-0!u1vPY(%>YADVMZunM$y*Y?U{V0ERXDTUE#ySin2@80*IKDhlfTExhLP8J~%y_z4p9tq4i?o z5|uwQXDd;%3fiubT&~jV<-E{ATDI=|>W*>Na&cmJBy{D#6xn=zU*CQGr9YLd2_(X;xyM8X{zssj$bBPPe>wHIU-Jvh9+MK#tDy- z4>ZoC7S4?|%#bxU(^Nu|2iCPHz~FqjM=u; z_=I=OP*g3|yBY#)x4zJhS1-&L)<+ztx8?uSQkD%JX_Y9JcqW}|t)>{Rh@n|?6F6_- z&shuz>>r;RT>8x=g9rnQ7;}VX=+tKxTQRigI2}oGg8&``yfvLcEu>2CY^k>PBocLx zBekcy_R8JitM*h`RExHHAI&0vtUgiG9jmLzW}_AB6C09pb{E_R9bT-uiA%+9S#e=a z^k4yFBnwE=G=fAyU}=tprhavCyk_Z{=dP}4?<5^On>n^HZ6E*MGkbQ-+4$!OU;gFd z$$7JI+pc(fbWIu6xR#jZr+OWIGwG^=wfvV?g>X5v+WJ3rc7Jyi)?u4cK8@{ z2!enm*(EC&A*^bCf{^5Q$(rgDMOBa_*2J-q)bPaeQ%450Z{uGqm&7J{mahmipB$P0 z{pZ62FP#vNZ!G`ty{TohV9f^~-jvAa^Ff)m1FjFiy}b2_G$t!0NVR=*xQ{cY5(+uB z2P@K*9TnM{(AqsUshm#_$J`I)f+CDThYu@WuZMWNJ`Jmi8>wE|gJeXof(5^-NG!#% zm8Y!-UW;5CJ-mg$bpw}A@$AzdkRMxaOmUw-JNC|ZvxD!Kf4zV7Efc514*pK0>&CKC zE}iYH(y{iBO|9_B$R{X7Q6#MRML%(gl7NicgRw$<8o9}LM%q4~diP}4c(Htb-Q}G+ z&+hL!I??cve?f9Cc+MW(`@<@q(e*HZLx6{?x@gl$%Pph-TN}8(F zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U{H)WQg2O3dv1x_Fg|lMu(Yi1x%{{kERxdFl9J3U%q>zZlT%DhOpHyE zk`v91j7`iEP1DjW1sJ3R_U0A;pP+Q?SN^))qLInAnMwlP|959p+&mlN?0u@pBrE=> N#-EQL`dL6>3;@O8s@VVl literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f1ae7bd5875944ff429f3fabf48583ed2932f994 GIT binary patch literal 551 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M3bX(A(tDKV?_>*7@=PJA5q&ip@o z)KFm2pO%ZWO`=Mq;<=)gI5Tq-(^K^e^3u8dxD zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^G}X+9&6$tW?q^XuYOCSGh@EF3IS z3{jnoQi)vAN}OexDXIDTxdkR%eq0I$Nk&PjDHfI%i56x_X{JWTsph7srl~2Z$z~=% zIa2`!DFLqune8v4(u_izEw!U}`7$dBaBIKq>}uN|WLNt1?VA*CRgJT6mi}N7V3uGs h669j}Fn>}5(E9Zk4n3c2ssI(5*ek#yu>m5)1OPbbt{4CS literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..2cf3aaf019e828838f1d70a6bd5b9dd0b048add9 GIT binary patch literal 535 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}Uj5)W`^AGD^(p{JMCRi5EW?F9(aj zjm59Gt}|6);$VzQmP+J`R^m)7P07sH&n+jfN;0!Bw@9%}PBAqxF*Zp` zPBb?%HZe;yO-r*BV2~2nn^*jQg3_^H`RjIzMkd>4DhYJ|- zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M#vX#yjV$tW?m^XuYOCSJT;+#D*t?T)p)q& c(h4>KCL=*EmJjnMH2|Hk{=%W>lT8&E0b|On@c;k- literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..cde976e242e90bfb6afb9428ab868ecb978976f8 GIT binary patch literal 567 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^4_X(1z!$tW?e^XuYOCSDv|Y#c0F zOdO0+os3e6T+vFLWtl0d`TDs9CR~183I<6=NvSComKKQ?W=UzLM#ib;rm3c>DXGb3 zCO|n;0R|}nuL+s$FQU?nLYpnMqj&i-D+zFGzwPX5+aF|C`tn|L7KG{?ODm1ZIfJI^hM2HChOXRLT literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..12d3c2c6916add79fc4ce199325c4c755a56acf2 GIT binary patch literal 532 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U#BR1ah^O3d&4x_Fg|7cUn#2aCXs z#jm%nGu2{ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&MprX%Zul$tba)^XuYOCSLqpyc{e7 z>*t5+$b^8BrV0v z%rX%uoR(ykl9D38ASIwxcXxF~rt_pp_p(hYFYmahBw*Y!O+M&((JKFInTsuc=ib$L exaHCcHUTChK`xdL^CvX`9kKqxq34rL6&L|~5v>;h literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0238324a5be872dbcd7e649dcd594afe99cf18ec GIT binary patch literal 565 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^G}X+9&6$tba~^XuYOCSGh@EF3IG z3{jnoQi)vAN}OexDXIDTxdkR%eq0I$Nk&PjDHfI%i56x_X{JWTsph7srl~2Z$z~=% zIa2`!DFLqune8v4(u_izEw!U}`7$dBaBIKq>}uN|WLNt1?VA*CRgJT6mi}N7V3uGs h669j}Fn>}5(E9Zk4n3c2ssI(5*ek#yu>m5)1OQ3Yt~LMw literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a43b5426efc5da8db11807f354d45149e9c77e81 GIT binary patch literal 535 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}Uj5)W`^AGDjfN;0!Bw@9%}PBAqxF*Zp` zPBb?%HZe;yO-r*BV2~2nn^*jQg3_^H`RjIzMkd>4DhYJ|- zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M#vX#yjV$tbb7^XuYOCSJT;+#D*t?T)p)q& c(h4>KCL=*EmJjnMH2|Hk{=%W>lT8&E0dh>O4*&oF literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..49c82842471c44718f5c31dad355e172ca2442e1 GIT binary patch literal 565 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^G}X+9&6$tba;^XuYOCSGh@EF3IO z3{jnoQi)vAN}OexDXIDTxdkR%eq0I$Nk&PjDHfI%i56x_X{JWTsph7srl~2Z$z~=% zIa2`!DFLqune8v4(u_izEw!U}`7$dBaBIKq>}uN|WLNt1?VA*CRgJT6mi}N7V3uGs h669j}Fn>}5(E9Zk4n3c2ssI(5*ek#yu>m5)1OQZmu1Np@ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..97c14a303256886b32734079b6067d1523433d71 GIT binary patch literal 532 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U#BR1ah^N-XXCx_Fg|7cUn#2aCXs z#jm%nGxcJKN|s9GidN!GEKSMG*Uv35=JMlGut-WvOG+}cFt zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&MprX%Zul$tba`^XuYOCSLqpyc{e7 z>*t5+$b^8BrV0v z%rX%uoR(ykl9D38ASIwxcXxF~rt_pp_p(hYFYmahBw*Y!O+M&((JKFInTsuc=ib$L exaHCcHUTChK`xdL^CvX`9kKqxq34rL6&L}D&#g58 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..6cc583112d4b52a41415f0c4ddde733e635a81d4 GIT binary patch literal 567 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^4_X(1z!$tbbB^XuYOCSDv|Y#c0J zOdL#6os3e6T+vFLWtl0d`TDs9CR~183I<6=NvSComKKQ?W=UzLM#ib;rm3c>DXGb3 zCO|n;0R|}nuL+s$FQU?nLYpnMqj&i-D+zFGzwPX5+aF|C`tn|L7KG{?ODm1ZIfJI^hM2HChW9_bJ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d273f786f3cca19df3ac48b82a31fe31d786ddf5 GIT binary patch literal 537 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}Uv9)XWHCO02s$=j~?$Cn1izXZ{~P zYACSqPs_#GCLqs8B}pZ4#VB!Q<|d}6>SqH+ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#nk=^@ZX0xS~iFV1=U*}#d97+rf&FVedUCNXImSm_|;I!ze)mWw}MZ78+1ORs6SgUuGHHH=)# z;eJWaOta$1s4S}rcJoVj=w2t)F;^Uyf?-lpnn9|CnYl@Fq6IMYEKN;JEG^8Fj4cdOj0`Li1(>A- z3Z$;T@&2*=t8&Y!L@ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M9hX(lU>$tbbm;+(gi4ZPU6SU6au z7^0e4g_OAzGxHQuQqwZ?GD|Y^^HPfy%JOqcO9~Q86p9nmGfPS`i>30pvXwZ?GE-9X z^>YhMthjtryu-q?i@j6xqGWthJpCi={ah_0O+xAlf&zlFTn!^E(@e99O3Z{zxHz&) z!X2aXa@>^!gq~hEIDAO$j*?ve8&jpFPqYNSrZu?>BSI*m%FInnPu0&VNKMxgP+#2Ux>`5fQe&alZM_K-3Jn?rtT-|%%c_Fi{L&qk zaeDj^4`Kj1$16z3%q&%}&bgvg)hFM_wA2w|(zw+SS zdgDSSErG%%nh(l$T(&=xI@Q4;dy}}q`(Gb?gic1aVg7nNZD~Q!G z-#INziUEuB6S!iKgP6;YOCc@AC^6C8D9Iqvz}UjV%s4eE$-p=*HO0cj!Ys|i&{BXw zN`SLmEdTPauW}O4+0WN(aY zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M9VX(khp$tbb$;+(gi4ZQfdcsW=E zZY+Mib)Bga69;3|FGe99F0Im%%)G>0g_8V|M1`W%g3^@C#JrTmRE4CX(vnn#vecr? zlG0*@(!A1KsgsOcTNsr%GjkKuQ}qk-(zOKC7q_{t)(yARSm1Vh6VuELl1);REe#Bkj8allO)U%!%q>h!5(Su~1g38IW_{&^glAhDr}))S$-hbh zX}5w;edF;v$*}3((?<4IFO798uRdco;&ZGj@`%iKi}3Y#D$I4ZU{Dh1nJ*jhcBiL7 M8%wWgw}Z_V0EJcC zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$b5=?OEC$tbbu;+(gi4ZL`{xH(t^ z*3Uh)bhV)pL)39*sWpsT%Ndn8GjkKuQ}xp`)2ui$D$A;Z-TcxWdYL^g2?z1`RHm1f zq!&fF`2++<$@sXtYo+PvX6ohW=G(ig)Y(Usm=}2X8QPY(8gs>QDHtXtr5U7Jn3`Cm{?kvCmCB9q!<}kBnmJ~2^2_Of8+gQ`B&wZQ_J11x+g0MNbl!sD>>Es zW~$4KY71V4E{*A*{vTm9;)x8ej10)jP7L>S@~~i%V3eB3$W^PvnOK^VnXjK)V5}ve zzPQbGwQjhj#zL>#dJ`rT8Z-zvoEP+X%N4}xnD3kx#^uMQU}|ibY-yZql$MlYk!WO? zW?*h)YHX5Xnr3cho|KrFD!?EmP`y0KJi^mXdV^X|?27akTa*NXWhNi`ePfY4yQ<06 z@9DeNX`I@6>I<8aK+k;Hh_^dE4cb_GO}iazwn*i3Wh-%(Wu~NpoMd9f<(uLi7M@+~ zotg&>t`txI2zx(Q%Se-ux`Lp9pe$Fz2+K6ntfCS#Armf+?2>TDsJt9^B>|zQ*9{II zQoExh*Z;;;Y3UO!fv;&z?gGg0>)h4@ KOXis>FaiL-NFp%+ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..09cc7486e694f10c06c19c5524afc5120c90c03a GIT binary patch literal 1160 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$F_=@ToE$tbb;;+(gi4ZJwG*f?0U zm^c`tZm|lZXkBu0!VVK90jX)v*$*&9 z9#`|*>R9_*;ii^=o_gW!WAp99&d-p)=5;2(!Qj#FhyU4?1bXJnM!enWY0$>fYufE# zvqh?bk*h|D6XM8%ymW_NW{*q4K|DT{>7^y&E%$rI^I} z>*Zy8#}}1)+UKVT*>Z7YmxMb;<>j~wFiYHTp1p35p^|`5!Gb$>A2w|(zw+SSdgDSS zErG%%nh(l$T(&=xI@Q4;dy}}q`(G3;vE*AUF@Bj=Wt%o z<1JSZt7E=%T9_0A7Uw5$#UKYUmmilxT8dF(qPbC$L85`Ng@u`MYEqJcaawAMg^7h( znu(#M0E3hOXSrDZTTb4T*)4fbza+P+9?oZ5Qo3medN zCmFf6Fv8pi3?b~Xi0o)YJf`_%n1$shTKaj0+n4*f>E`GA=vs$6Mp)!mawTvnn3<&{ zni?l3rkNQeo1`XN8W zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#qx=_4bM$tbbq;+(gi4ZL`{xH(t^ zZY+Mib)BgeL(~;UA!RPb%shpZ)U?dJ%#zIfywqZavizLVl7hq%h2q5Y%#xDKVyQKZ zT+11iI5Tq-(^K`+Gt;a%GAhfeg5CVm9eSBPE(r(m_*ABsmZTR&xcLMGN6GlOyKANC z=w|BW=;qtItJK*?m6#WJ_!-)kxEgcCaVZ!kC8ZgpT9}!eBqv&=q$XRMnwVHxm?s%q z7^D~(SR@KCO9>Q6U4P^KWBFI*mQ%~!uDT~H2}tkfYb!a``(~=kjA{#Bg)WWhpZ*_V XG~$U2uZ#@H%T5gUbn>uZl3)Y?qx;O8 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..8deca89936e6cb77b65e755c1cc22269b47ff6a4 GIT binary patch literal 929 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}Uy4)D3hI(3e{;&UyRUz>A-YmxD!M z{oGSaR~s5JaWF=`VHVQi(kd;<%uCEwD9JBLR47UX`4G7RKerrC@4om~3gBY?PLiVv%TMm}X#ZWNK`ZVwz@dW}cLom@2>^B~ZOQ$vnc- zPI`k{Pwa~H7h9ABf@LNj`h8=OJiDsN)$i%M)@hvDdg=?Cl0eUV*@(A0Jq_AedQH0> zY_>?{b7d=WmSv`-f}CVx#pRpg9TuKl?46nyCF7Ig=^tV5=V}>g5>i(X6cCit&X+76>e$?=&2XpJ~rPz?EDP*YhGsp91I@)e)yjqGgyoSxmZ5TpVR=1 O{kg3NmdrC%U<3eT0yQ82 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..c140003bdbb643d56d07cc399f3ba8a43a0516d1 GIT binary patch literal 1134 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8Tg=>{v1$tbby;+(gi4ZPU6SU6aW z7@`ibN;NQY)hKah<|d}6>KEjtJM=PpToMlA@u^HNElDqmaPtWWj*{{5^tGxDaJUL33rUj%W)TAmbl+Md)*#GB>|y=1$XQ|Y}!_S z<-xo4#)V8;0)T8nm(Wnsz(bY>`Ufic#W%xIU{OHJ!_k zOCc@AC^6C8D9Iqvz}UjV%s4eE$-p=*HO0cj!Ys|i&{BXwN`SLmEdTPauW}O4+0WN( zaY}PTw54n?gNI9mVo-=HrLg<;g%W; zy>9DGm{4fYAb{fNWt<*A#Df@s!ItKeVHTF3XzAw}ZeQ-_rkkJdqiY@R7-5lL$(6vR zU}lz*Xlk6Cm}X{>Y?7L6X<(3Kl#-fiYGG(#ZeePYD8M8oFm=N>>nkTDJlonh#jl1+ z{#6o4yA^!u8;{>fhE4aLHnO*RX{=j$^%=7fpJP>#M`X5Jgs;C-VXm_Uw$NI`$h92q z7f@JpWK@<_1-tp-ig$N+tu!6oOuZc4e0z75I{T;+^8yb)L)#KpW3D(Z1;eDIG=o$N zGjo&VM2i%l4@^x=EG^8Fj4cdOj0`Li1(>A-3Z$;T@&2*=t8&Y! zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8Ze=?Wu|$tbb?;+(gi4ZQfdcsW=E zZY+Mib)Bgd69;3|Rz@LtF4^MLyyDcN%o2r^)a3l4RE5mElGLJtqSTVaqRe8ciHuye zN}P$MDVh2Dxdp~r0_uy~TvzLcTWT!yx~(^1LZLx}fWvt~kGEVwtd9B4X<=M`TneVf zhRK%3$wp~ODHe%FhG_=oMyAFlDW+-WX68wWiKzk%QUcY>lguML?W8xT^~A17f3ZbL vAXsMdq2D(a$+N4PT>YNDYn{fat*5@QDGBt zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#$-={+-$$tbbo;+(gi4ZL`{xH(t^ z*3Uh)bhV)sL)0Z^A!RPb%shpZ)U?dJ%#zIfywqZavizLVl7hq%h2q5Y%#xDKVyS$t zY$eXJ%#_r8{oDc*D=yy@@38RfV(-+vC>fs=PyYyeKUd30laRWCpn#w(SHlR)G}El2 z5;GwaE{^PyaL1^;9CsxFp{Lgk4j)pxqa@e=##CwP6D@(SX-)0|$vH39OM6ao`BSZG z@aNwCNz4+AQi~b6`WThCGIJBtQ}we7Qq#2r)EBq8uGS5=)L7_sTW`XILW2eYD~^oH zvZ`P=zjTLXoE|^KgBXC$@e0y0GfUO0bFL^=^~v|~tqC?#H4YE=%rDLrvgTs*aLMw{ za}r>ZXkBu0!VVK90jX)v*$*&99#`|*>R9_*;ii^=o_gW!WAp99&d-p)=5;2(!Qj#F zhyU4?1bXJnM!enWY0$>fYufE#vqh?bk*h|D6XM8%ymW_NW{*q4K|DT{>7^y&E%$rI^I}>*Zy8#}}1)+UKVT*&@P6fLY>x^Xzqd41obvu;7l} zhfUkcuRM6S-nfuSOQ3Lx=7X{wm+jA_PIYj|-Xw1D{@03CEE0@Hf?O;g=1*z>#_-(M L154(aDlh^7ytFRh literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..bb3491070e60ba9469af10ca932aafa57a6aed27 GIT binary patch literal 1197 zcmaJ037n~9|bf}$~S%|?Ob3fE+qF`V;CBu&_W{Y1LEXy2YnIW~6Xo4=Doa8)t z&-=c4-sgSPDuB=5eSK_Me;vSU0Fp>He0uQ{tJ86Od{jU6*37Fj7h_8=FjqoXz1OkC zxAU4jKE|V`MM09vO(wzwNg>$Y{bl|Ho^$owewY2`%U=jjE+0Oyp}+9A!ugFWsw6$ntw-Rdq($2*H-nN{YU17h#=XfGvDO7=}2&GDfE1JNlk~)@! zKzsJ9*A{=LT}r4>DXduA0i|XO)Fq^F6h%lWCLyv*h$jMJ6b&GOv**>RY^>-StAW`a*$Y5jN-4#I}ho1V-TlV-NTi&pPR( zV$k?v^<$Gn2W+R{YV9*|Aqd@&6ax%GW`?H`&2pTH6?mRzMIpc$IFo45TX2lP)qB^) zzP`Kkp!S#C3;QQ)5rwblQk$W?-+yhQYVlz5R&nX{`_s3ST0l$zI0^pd7mo&Y_#@-V zy3=J|OX(Y~@5_gVcGG?M8q#m89e0YFv9^*1u_0t?i6M*I>8;x9ww1+V4P}+y8Xp(y zfgO-EnF7ej`jKd&{RY8rp=q882!fH*(`L?SK)8&+M<#Ape)GB3nM|sS=5`dXP`LP= zXZm)YdkTB+@=phH54lRuyfgQ+oXM~0?Q*ns+I)@Mt-B-JxO=JfE`X_jX31);3ia&i z_1N6O>Or~V;{DBe4L!kx6zuZZ8k#(r0ql02xTP#yzN;)&W#9OGRhwjvJKXxmr8*Wq z1xY>6i?qO*%mzQ=0+|Vn27`q&^DIXP7@9-4oWSG6rG@%Csih4Er&G2$yPv|>f0&(N5%bS{))5+%|7Q7>nAo1>^v3xb&|q8B}dQy yF@N%*+=mTw%fS!MtgipRhGPn#;>OrZeHq&yKlIU?CyXRm4Gj zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&L?Xsh1JRWR%!-an9S%241{e+#Diw ayFhZzi}li;lU)8(s~Y^dw|^3|1S0?^FRZ!% literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..dfe30e623a106a1e055d3353784a759b0b042024 GIT binary patch literal 846 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^=PX$Lcq$tbb=;+(gi4ZQfdcsW=E z*3Uh)bhV)u69;3|OlBc@F4^MLyyDcN%o2r^)a3l4RE5mElGLJtqSTVaqRe8c#f)5i zj7nUYxryni`dJ03=~@Eni`!gR>xNruEcCjqH(^4dL4$x5M@D5?Rj`|1y2CO~k00Ve z3{f&ZUO_r$W~q90&K0GqKKVYrHNi%z#^K?f`Ng?H)?92JE?M4rP6A94txHZ$*kPh1 zAT{kd`vIoN<7$3e9czCp+|&}#Q!l)IY`%Ti`5E%pyv_tT7(Dv@@ISkfK+k;Hh_^dE z4cb_GO}iazwn#NFa@8ntLL6C;m+sKZ>~Tprh{vZgy|g5~D8kJrAQSa8Sg!=`QJ zS021uZ(PWvB~Z9T^Fi5;%l2ndr#d)fZxT0n|7*o6770cvD=syd3(_;wthjtryu-q? ti@j6x9L@`RyyXgFbmLAp#5jSlrOzWM(>v!9R2+Q>JlXU^*qBF2rn3X9mkM$5`e_ZN(Thi!U#EpM2go z-}n7|K6xr7K+8gv8D17wFl zoDw7<2ojDWSpbGDo7>U$zq=-;ph@s?OpLo(km_>~r8ZV6LlqU^Y%KnH z6avl1U%$5W8{t&KSf#=#Q_5=Ija8?^hlBvea=Z`_BtZ;ttysI*)FuUK32UYOf+Pv8 z+B#T^L!p`Wb6XmMoFBR%#MItz+uQPr+>;x~7e z*K2->JGXnhkjBx~%+bwI-s8JEmbbJobTcJm>Vv79u^OPA1aKVSsd?@JOiW zM3%>r@rLt2rDv`Fh9G<*guHr0F zEX~UG6nH7I1J*&zWMXNf!AJ8Z!bfsG3qde?mg9_+jxbY3l161(bZG2W?zd+(j!;OI zJi9$*6-W8+-BY*XU6aWB3zzrC?RRFJe0%n1xjvzwv&CNBSnMq;x9)7-N+CGfJN$rm z=|cw*QuG;L$bW2H`woDke{1mze2%idqrKBz?DFUL$?fOwuZ%D4@V80+7H@HBg*zg^ zX3OLs%@VSkvcx>whCO*Tk~wI1=^mGg4Dd;a=@^D5ILc%ueKf^J6c|a;LYWx@MX-8; zqEWdP4Ql5WOKy)Yr|+8@EuOXca5VLFLP(nGTO2BUrHi@`dp_gk8{yA@KEB%1QC$&e zq&*I+osvbY9{}(fTt&CBg1DtQXt<~I`R5YtORzGgK-|h#MdLRbbUus0r{@`#qV+nS zFzby5k~Q*XlbNAuE*gK=C}Z|Io=6?r)Vt%j|H?Ryy3-C${62s5!MKg&?27-RlWXzc z!$J`@;;MFm<)R~z|I9sN7wahZ=DCV2)#SE4!ODuth9aHU!W$b}B$Jwi%0|gkP!njf z;i&rZ9C2o1(*=C}i;G75*iV`0GXJa%^)+32FV!(vc(-dKad+YLpj-p=iBNHUc(6P2 YNe=J-@RAd4^?Q6tWmN*0G$(>sQ>@~ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..c897976023c2754af2c76db69a6e616693c20d5a GIT binary patch literal 470 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!04jn1Y$_6yEy0VX9Fi8j=N|6A3ka* xu<%dI#n~nxuSY2g$#cmTr{)!>7G;(wq@*V27o{p>=9Q!t6%?hGBo<{B0|0!7lP3TG literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ac2ab391cdd85f79f5304b2abf37de578c2ab4d0 GIT binary patch literal 489 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez!6s-ZnZ-teTr3~v SPig>$)wYBC=5!h>FaiMmgqv;v literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9f46cbd01d2f6dcb86847e10ef15027dd5bc9ebe GIT binary patch literal 509 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez?iL+31l!zY`8e*?PmioHZB$p7Ac0P z5Fs5dt zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!04!C4`eV(Y`i$H;>7^NsA&m~)&npd1!lv$#XlA4@fl&X-KSCU#(P?TDdSd>`|05mO+5C8xG literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..54ecd6c869b94e391980eac8c160f8b2aded23eb GIT binary patch literal 488 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez!;_!0%R~sY`Qq-?PmioUM_A97J>D1 zPc2<-sKgLuA*9Tun3<=LlA4y8msygTpO;#!P?n!lT2hc$qEMWeo>@|oS!^W8#qweP Rqz0fd+YauV(`l^02moEhmmvTE literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4d3bf60b65c6c3f73d285410e4e6136fd5ef7457 GIT binary patch literal 511 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez?iF)4P-D%Y`!?>?Pmio4lXth7A+9WJfXlFYosT!oVSl0=1~)PmBK%*4Eu#8ic(qSBI7g|gJ5%#zY#h0?szTq8j) fmJjnMH2{s;c5vUEPGbc|h|t7d0Tzkv5FsW2(wLq6 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3ba18626957fb197b8a9204ba6be1dcc25556c80 GIT binary patch literal 465 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez-X^z3uG`#Y`Hk+?PmioUM_A97J(a! uUvFJ!s>KkcC?wA%Tb!C#oLZDwqL7lBoL`ixkeOGKT2xS!T9R0lSquOzjE@Ka literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..13c24552102d6d47a925d5a0499b6451642ad4d2 GIT binary patch literal 490 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d)}^n_9vkijUi_2QhjpAEeDxp+BP1lG?z zwRE+i5fcYvl!cHomttm~LP}~{W?p7VW`163u|ipXPH9O&Vu?a=VtQstNoKK;AQ#Jr S`I8!e25mdIZ%(JN0wVxyPM1dj literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a6bd4ca5d3345ccf8e28febfc68d70d1a459a4d3 GIT binary patch literal 509 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez?iL+31l!zY`Zw;?PmioHZB$p79)nJ z5Fs5dt zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!04!C4`eV(Y`-|??PmioelA`P7J(a! wUvFJ!YQ@CC7^NsA&m~)&npd1!lv$#XlA4@fl&X-KSCU#(P?TDdSd>`|068&_E&u=k literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..087fab31b8a8b3d4daca566e4a894dabca260134 GIT binary patch literal 488 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez!;_!0%R~s?6^4R?PmioUM_A97J>D1 zPc2<-XvGj^A*9Tun3<=LlA4y8msygTpO;#!P?n!lT2hc$qEMWeo>@|oS!^W8#qweP Rqz0fd+YauV(`l^02mob2mp=di literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9ffa624a96a6d9d998cdedd0df87e16e807794b0 GIT binary patch literal 509 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez?iL+31l!z?7TSV?PmioHZB$p7AJ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez-X^z3uG`#?7BGT?PmioUM_A97J(a! uUvFJ!>ctSHC?wA%Tb!C#oLZDwqL7lBoL`ixkeOGKT2xS!T9R0lSquO(e2*>w literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d95c3c4b9701ff2cccfcb8fd9d8faaf4055c7f9d GIT binary patch literal 490 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d)}^n_9vkijUi`{JCppAEeDxp+BP1lG?z zwRE+i7ZV3#l!cHomttm~LP}~{W?p7VW`163u|ipXPH9O&Vu?a=VtQstNoKK;AQ#Jr S`I8!e25mdIZ%(JN0wVx)1eaw1 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0f2e5ac20b837a4c385ccc1b6646e1910205b6cb GIT binary patch literal 511 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez?iF)4P-D%?72AS?Pmio4lXth7B40a zrl=4h9WJfXlFYosT!oVSl0=1~)PmBK%*4Eu#8ic(qSBI7g|gJ5%#zY#h0?szTq8j) fmJjnMH2{s;c5vUEPGbc|h|t7d0Tzkv5FsW2;l-XN literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..90f54656b0a94b5af4db89da491f8ed16437f14a GIT binary patch literal 566 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^%EX#pdMDRE=*>#gfdjUvFD-}j#b zIZi?xAHfWPUtkIp5>e(trd-Ck`9(>Y3MrY%i6sh&c_o?o1&O%|g{Ao^3Yn!K(WFeE zcxiG;evv{!PGU)ZUTQATuybH51g?WAuwkk~%3O+>c?v11X_6wre~IvWEKMey9T%q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..58c19d484bf508a9e25800b32b0f3fd70f5d02aa GIT binary patch literal 705 zcmaKoL1+^}6o%(*lDaM>tOL;)dKjVytrBfoqtuHoYTJNVh++s8JDW_+Kz1i?W{Zf2 zda?x%iYQnpilrzNs`s8$P+Muys+fX^v?o!E2vQIvZgx+4a2R<1d-M4I_hzFCdi~ek zJL?^5pnU<2Q04mcx2Hy5@5JPI$K2Gy?S&=r^GbT9yn1>KxyyCjY6_E{9b!_ZGMPk1 zBkbHj?N#)V9$B5elm@t6K6ZKAXzX*WzWsOV2G;3gxQE^uTgIM5;`P3Z-{awA z*zhRr;bR`Ad#6Ue&X%8dbtY1ioeTGS=B^&iu_Vp!IrhDyworDq(nRrkQ;{oqc+q8(#Av6-S0RFe4kee$X8V9rF{TcVa zw-7m8;YDUfau%26Zloq!R7DT>!;F%gR+=*YaAJOHx+9(UlcnWnUSeSNg)j8ierWzg zVL#rdby~tPIVtfXm4w=oiz>Cqc~_JOcbip{dv&)cg+rcJpI2JEayGDdVJved7!Y;Zs!mTQEN}NeDEGn+$*3x@?=-U8_)%gG# znE2yHajT|Pm>`lCx#UJvo$?peXZ%n|;oVxhE4l40Acdr!ebrlm+9k?}>9~@SVdik@ Jxx2P&{Q(Ng>@@%Y literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a058f62b51d1b9b10a1582a1954e391b8c3c65c7 GIT binary patch literal 554 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&M>zX$m8VDRE=*>#gfdjUvFD-}j#b zIZi?xAHfWPUtkIp5>e(trd-Ck`9(>Y3MrY%i6sh&c_o?o1&O%|g{Ao^3Yn!K(WFeE zcxiG;evv{!PGU)ZUTQATuybH51g?WAuwjxy;#{J|i3ORdc?u;(nZ+fUg{7$q#fdqo E04CkD0{{R3 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..97e9c6189a78c77c8e2ed6622b60f16dc8d2b67b GIT binary patch literal 527 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez*wtP4Pr=a`FQBzLqjJaj=N|6A3ka* zu<%dI#n~nxuSZ1*nQ@tvmSpB7<|-5?7G$R8DU=jt7MElemZmCXl#~<{Tj}ekRwU*Y c zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^`BX$upGDY5nAp@$C*o%lHJo%w(G zsG-23KP?w$n?y}x67uA7FDy+=ElSNxDa}<#$t*4?ElDiN%+JeI0EritrYa=nDHIf? zmSh6uGfGMdimmkZQ!5g43vyERlJj%*{e25kGQFbwm9{c+$>38U#21vAlA2en5R#Y+ zG&@%+Mu{skH!(d`KdT@$oy(6)AuZV;)yUG))GQ_0Fx4PA#n2+jASKP%G{q<-CDFnt zQGh{8;IGzlZ7$}H2hl_uzxG5*zNDUb?$NOQYrcnmx=)3d#9- z#i_|9sU@YUMG9p=FD4c#B=1%L literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..770507fc919a169c5611cc41609254ad7b379347 GIT binary patch literal 805 zcmaKqT}TvB6vyYzjy4V&K3t|A%y8Aww%zq1T~gP@uiA2LvGVTj-QA(xopE-C zOnWd0QYie0f`n4Cuu^>46ED!2@`2F__#)z;u`cOlRi!m#YEet1#tf>B)m2| zohWj_iIduv=}jt$h<@x#az{lqY$s?_GEK5nE$A`uodz9&@yU;onVEc7GDFdniSVwG zTs>2#U|GRo91CbM6*g&Nyh+0_F8M_<4g*p|QlN|#Rg59Oh@dRVT10|DtSBN3tEwEg z=jP&O6qO^`?3bdsHC1vzba`v|6~JU}W#G~(k)&X(!ZIx+s-{lBr-)G$!ntxRRQz;@ z^M0x~vocs2w$w?;R$bvabhf%IN0KUYiaaItR_PpbN`I|`IB-C)+5|xe6k7AFz5qf2 zf6#^zDzxQUgDh!cQ|({PE^q(vO6yQp@T2=R&kimg?cj5q^s}MqwQqO2IWayV-aV-MHn2IA29Kt@N!Wa&1U`*8%sHrHbDOoSS kj6WVJ5YicHxv!^9Klg{JM;C7i5Nt}_S6Iq8u_=*%07*Uz0RR91 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5d710323883ac6bce18bf248d3f2f1d395993fda GIT binary patch literal 702 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^-8X$2FI$tbb?09P1U>3IZdPIdH)_ZBR*e$r@~z4l*sVP$bh_T3nnEth_5vm0N8!r{{R30 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..afcf4be4b4e697a08ffee979a502b0774443c1bc GIT binary patch literal 698 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^@AX$cdM$tbbomNW8E#RUt7?p`a+W zBoio~QBqP+Y^ATCT9KGrkdvyHoS&=j?^}?P=@sR#w2YBU2A=|{dL_=x+{E-${erx7 zhd2BlXGEi9d{R99BkcWLEh9}r>I#Aag0fr^hjZBja zQ_U?563xsk3{wpa42=!aOp=n#1sJ3R_Ek@`|ElVAcK5GiC5ju6xd5&7pODzT`9BGxsZ>cPW-^Wl5G9L_nLXkc?c z@AR#fEd%or7{rr3gP+HIHPyHJd&?#UCT~toiQg}}7ZQt&%Y-(U<2FadD1WjAtE$uM zwR#zZTbqg$cyxM8*X+>S2u2Xk)cZ|60R#j)tp(e zPxo0!22NTgIG1xK^U4L0TN`O6QB#oUMQf!Ob|qVjYysHf5~7b@Mil_z4vVan*Dk-TuQ z*x7L~qZJ3lKy7=-UF>OWtPzzI=VnZqOl3G2v9p2U*pUZ=o*(B+hUyErCl4L0t?1i8 z#jCTd*WHnsdA{XW>CD*DxXF%UVp_p56{{MS(P=DWUPeh>&=mdLq?Rgqxs-i8>Twq_ d)JosgPW@vub3+$yyAiBQJ(pR^GP^F3e*kCx^$h?3 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b42529a97189bbe3279e16ecb61078b0153ac1eb GIT binary patch literal 698 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^@AX$cdM$tbbwSFGfSkhl{hnV6Vp@m3-Zz( zb_#o}Oz;G3s@=gzj0&z?$V=D}ACHc$IFheb&*F$d^_ t%mRhv{Ji4Q9EHrhlGL1>)b!M(#1e(P)N~LtucWj{p|m71x3oxu0RRq^;dKB2 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4de1f306f17067b9fafda98d1f67d67bd8994361 GIT binary patch literal 677 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez&MFXsh*t zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$R_=>sE>$tbbsFcLfB<2?6r0ON-=j!|W7Nlf)Mfod5aLM44moil1 z%*;(pPt`BTOLus~?{P*nO2#M6C&Mf(Khe_9Gu*!1&rLT!-$&Ov+%du;zfy`pKVgdj^GtY_u literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..325dceb704228a985045cd10e43718e098e5848d GIT binary patch literal 524 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez*wbJ0c0>r?EQG?;X^|&elA`P7J(a! zUvFJ!YQ@CC7!@UC#${4kl9`v7t5BR+keQmNP*RjxT#{K>nyQddQc_TCrLUh_k(gVM bld6}TpR4chTac3J73Hs_z$JrEUV{Mura_{y literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b81127c3b43083a39e6662f2ba638746739dbb17 GIT binary patch literal 713 zcmaJ-T}TvR6#ee(tW8tNIIy)YK733)=nDHY=Azp}wN&bYDQP5&k+UuixFrKVX-1YwL+OcPhpBhAw{x{^;8dDF5JU8hr_*hHv?Su z=ly|A?>exnz+#EIHM;g9P*dGM*yo)d%G}M&#lK${mm@3Z){!xv<@bi;Vd+9E(RG*4 z=kT$Zv^A$@^T#;r%EXmm)z{wFq;WHS^ypD&E*X{ zEzkio#YvwsWBes7{yY(RdAQ8s8Z65US54nI*+2xTskCk;w74)*J3|V2%h`q#b_fQ| zb-@l8iopXFH;V!w1t1&%xf<%B30fcm3Fv@MfNr4rU>5^6givl9Q6|d$K^7azp)|OSJfTbo_UB)wmX^H1ee52F<{tIG8XEQbQK*;V zYuKuGLn28MQw$9&YARvqlCH*MD&FSX4T7Z@P9%x0ntDXn6DhyjO*$kk5hX4;uDR=K z6N(xP)d}Ok7VTu<^UtUXiKTF(q?y^8qgh~8O==_ViwV-ko@Q~Y>>*-hWw}q0y~HCc z-iokCX)P;PL`9J*M2SNVZp%4b%38YR)v=>%UDv)93f!;!k{)exF=HS5SU&e*#97?6 z`S6)Vz;Zm6BC<|&gCy}K^F%3$qb6aba4H@(wBp(HkMx2l7O?cj!0k@+OK;~Ncij^) F{06|i>n8vJ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f4d0d5602d648af5d5b8733e6858a3d471070aa1 GIT binary patch literal 805 zcmaKqT}TvB6vyYz&e}L=j1ybi;=^U?!3=kGG+Xr{T~gP@ud-ZQth~E>cXw!aXPliO z(;f_h6be70Afc2jEPJWmLLxFKEL0$?B_dmb#la8x*N4)*1Y-5tL%{!aRJNtg}Ixt9oSYLZLwN?vq2fK}C+ zpKr-$APzRh-fn-$8s|pNyUKrFe}j*$c3#-iVVXBh?)`1O%CYD($ zaJlq#ff+!M0kVO;JKX`019%<4=LEH&0W^USkUh^!aLYfU;u8F3&M!m_T#R;OAXy3E|?tg`Z*Jh^^ zMJ_mTQrj{;#*(n;!(L-uL{vkC1Z_#8NtUVwy#~I^pd&Cp`!PB*ljBNaD4KE*o;8wd zVCoetD>#Isel4oPW=)JWYZ%5PpD4zlUkXbKl(C|UQREX5lqFdUOE7>HMT8+$m1BiA z8*V`nIgG77DPpUslKrB~Q^T(SCVeXdms*J=1!EPKX+cr7bOAnDjG!R4$9a=ov$!9s~r^C~0-|lpGWz4THvS{RB^6^Fw(h`mUKQ5@XBsH!PYQ&B`yGG2a( jKOW5$(im#FzqegK_lK!R7jFp=Y)U;>Sjs%HDUp8wlE@1( literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..abe721a845dfa9c0830230e6678f721c3b4787b9 GIT binary patch literal 700 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^l0X&Doc$tZE) zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^l0X&Doc$tZE~&dl7z^i=(V zymW^*{2pgSqhx$iJpCi={ah_0O+xAlf&zlFTn!^E(@e99O3b+YxD-q+O;e2$6U~iG zlMGYMEesOP%q$F34Gj#94bn`KlFbDeqy+eV1urPbmH*r&v+hL4`Rrdx0+DfA%T+f% vZ&;QgCm_u4eO#mE`8W%c$cHT2G669j}Fn>}5Fkr6T+}JYDNP!Um;aT8r literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..735236acc0ca40dfe443c8ccd9ca20b8cc68ac92 GIT binary patch literal 744 zcmaKoZ%7ky7{~8-yUpteI}cWC?8T2O!7Mi2VuZe_WvRz(;w;Co@^<^_E_S=~{=_H| zqacNXED93JNTfo93_{2k(S%IG5rR4)BLBRJN-Qd&h;`7grEhM0G zKkxOgm9GHmBcLFU_YQp?57gG&8R#pY9Gtp6H7$R?>|IPOHLYOAe2&=~lVkkpR;250 zzu)PnAZlyY-V{EjtxIDaT=mymuTkS#&xKvx_7C>l?%(bk49&j)_pzD4j-HIwk*hto z>L6GmU`{C|j+$dC_E7NCSYl$o&*>iUO+BcdymBOrSU$2hl#RZB_pmODb~2XZ;VK8< zICEX#01%XbGH~FUD**Tahy{R91L{Epw15OqKpJ!b&<*fDP(T7ZhGF@2g_|n>S6S>> z9?RPpf&``5dcnPzi*b?`b~{Cz_J6{OJ1jA#g(R3}vq=?YG3+4LwP69w{{)9O7vJV9 zIXHLH*fgWoLrGCUF>6>7^+W|mI&CCQ6ZK#QXQy#f0!QXcW?`Y6BPkn6a3+dA!jr$|_Wls*5RJ5P7J`ijkCID^gVvCUjlVDm)&P z<|QSG+=485LUl@9nA<@3ADt2PHvj+t literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d6a02f821939e18f4926d4eac3244281b5c5b4c8 GIT binary patch literal 547 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m380b)onIR#>J9YoRU_i zR05V-{$$>^cVl zqL5OWS*%c0T2fS+tB{nFn3s}SqL7zaoT&hmE>2A@Ey+|UEX_|*C`--AOfJnygsKGq Df9R$9 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..1c9329854ff44b3c58c51a54dd7ed94940fe9a2a GIT binary patch literal 601 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez*wVH1!71rIJ9YoRTJ$ zz!js!m6@BEo~oZ!kebfr$E9GAl$Ms1WM*M*kz$#gVrpVyY?73mXl`U|VwPx{mS!oy zASJLjulWB2rDMPH*X_jwa0Rg30Bak)dVnU1<*%1JtsXQedIDl4fSRkN4VhZLhZ1 z$QUt9oiZ~s=Hdrrb5r)grhgzyOqBS?W(+}7G)oqlL1kl0*bstlZqc~mA;!dy_~y;~ zJn!%Q<#~V4liwNwkW;@NKX%u(2;e+`v|u{)-omH8rr^<`LD$5|!a(7o_Vdf^)x>Oc z5zAkmvaJ<0QP`m%U9a+ZC=aAje0%Pzk`dA}JJu5l{Pe~bsN?Sby$|)3-6)&daIfmH zjTFvHUA^Pj5^8dSImWHG%A<+^^sd}OoBPc6jBJr)@eVjRC zC?at?@L5;8jlG52tPmG+vO?%eb6BagF} ztSv$5rZfs~>&|sXlIc+W6)Je5yd8^`hm~-`vrQ9fTAQME&$e!HXj<5@Ioi<4YrA0# z(w=HX7CCpdTw@%HEL97lOH#b7=yZBH9=S*pMfO0dpG1 z-8-gZAAFe|DdMkG6pnm0Yh{VHmW-^1Vr@Leko?{U=ltIAr2`u5n_1{7rcMF)&R?Cx zRi(D%iD0CmIk7w05RL}@{`!4HaI~zwD4*KZ7El9*_CTOgdCZ|!B^_E_V8e5DG2NR9 zwz?kI8#x#?w9AqUIVIV}i%wbLs%2J|1)de$ih=}=Wl1wdLUW%qKT@~w;!C&J?@FU2 zL#EEp6zn74S6}>gxU%j35L0nuZuGi^wL}$vN1Qj=0W}EVK0~OGmsM?3D#QKf$m?%f zgV##ii(9(7GP2aH`n$VP18jvf%d)bYck-ev2(qNqAVF}~IC(|#a-77v-6Telq1f@U zJA2OdE8j#uzq7V=f*~14koe^6^e;D0^dB7OF8C{c`*GvO-SJ=V)1tr`23eP0>P9eI}1|Ixnt7 zIw! zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez*wSG1Y$@qI@?$}~1Cza1QdF9&kd%{{ mmy%hcke6AUsQ{ENPE9T?$y6vT%}-G%OU=kkF3m}Vss#XU1Hfni literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..017223ec4c849e1b886015c460955fe79c56f301 GIT binary patch literal 521 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez^JOE3}Q$yIga8jxC& zS|r34kXV$RS`?_{4^+h{vHHopZSTyyc)7SaSOnJ3J+*YTp%O!sF_#!nFN+i(7Y|sM k0z{JrqYxVxOI~Jij*%c2%ZK@s8h|Ez{`~acG6Mxh00;4x(*OVf literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..8ef246b191ec2119916242631f9e938557cd7c3d GIT binary patch literal 727 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^xCX%!=gDZ%K(2JvmyOh&G0j9dba zPqH1aOPq7ue`|H-;{4-_g%o^5gT%Ze4yQ~=P?uk1#(jAIw$#b@oZcNOoILN@3azJ# zN-Q7dPioMLQsT_aO-xVKFUU*h^5s%6urN(DN;XP0H%&4zH!v|UOEFKiOfxl2Oii&c zF)~jUV2~2naKACJ^wg=!%Im&-m;AG1lms}Ubo5HM6gcG8%@197!>LAN=ezlpQyG;e zft<@IvF6FVZSTyyIJnq2ShScp7^A9zEJgtq4MrhvF3*yT)ck^sqQv5o(qe^_%=Em> z;^NF)g~Xi9!qU_dh0-Jttx%kpo>@|oS*%c$np~0!63I_4Eh zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8EX=@cV~DZ%K(2JvmyPDZY6j9dbT z*=AZ>6x^DA;ADkV$~^v8LJDc|J5;^q@4dfvqHq4$wkcBm%Xqu?HvIP6A|qz<<7SP4 z63d7A6MMC)l(;f;6Vp@m6ASXvt$2#^{Q{DU1JeS7iu@f~#XXKoaz$_{BpH}m7?>xS zrJ1BzSeU0;8YG$~r6pP#S|%r2q$MUB2{1_s7)nOSyGV$K@A>KITWWOXnv%eV2=;AX zezd>jivBfm&Wc}$H2(bDp=iYJ7gXR9T<>kg1do^bDiK+9&h2y)*OT<>KaG5xBAV z_11N!S`1O%Tp%y9XfO&na@iLr7Ni!XmMEl@W)>?Hm6jBh<|-uRB<7`LmMG+97H28| brHfOOOG`2p3QO}-6v|RFGLuVl5}|4V6tCxt literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3a8c68f6a081894a459de125a6d6a99d47fb2826 GIT binary patch literal 674 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8Qb=>#K)DZ%K(2Jvmyc1Er(j9dcc zdS)4KEAv?`dN_lbwM%S-6oju?KEMApQ~aXrHtD8)_iB%w$u2VXfB)y$#L8)FGfVT-Gy_Wu)1;K7 z6eG(d6H^oOMDyeni)3?iOLGBcDSIKk{jg~X2ImF zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^W3X)_~;DZ%K(2JvmyB1W$Hj9dcE z+!;-E(%TXxzKOlu6SSLKNWoA zZkBAE24oo}nOhnQFh~jH3Phf}d47}7D)pUje{9&_q9maB{+0bCkIykN-*fC^g0@fA z*!gaLKzGjkOZb21A{Q%e*|lR&gWabkLANl9k0LQ!gRNh(MrKe@E1D76Tv1jx-y kNzFA9XlmGw# literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..faf4244b8f9d3ec12601309b5b815d1f92ef7113 GIT binary patch literal 678 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^@IX$d2UDZ%K(2JvmyWJazDj9dbn zB=_6!K6vsntK{EoE*EhdAq7jFSs!Z_K03M4F#BNF;lnoz+ZnE^xuxj@*{iP7sqM5; z;{Gsy;>t%2y&vXJ+O6fQ#Fd$wn4YSiSdf=)#Z#2;7m!>Wm=+jRY8S_seg?JX1?TmA0cbNGmNlr7=VE*lW+gP)eGJ(!yl-TfO z-nMsUUi@6V94rDi7Qf!Q&eV#DgE7jR3*>wj4MrhHF8kudg4Cka5`~n~%wmP2(vqUm rT!o~Z#JrTu5{10X;!Fjgba85OX-TF+VQGGfLRo4?W^!pxB2+B^D|g#M literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..2a5e665d64f1f47ff70db3c261c41c0b2956a125 GIT binary patch literal 640 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^-GX$2#QDZ%K(2JvmybVjZzj9dZ< z{3XFlKdiM94Ul5CoAlX6Na4h{bDb};gL`B4>Nm80-zgCKKED2)=4R$IT5R4=^D}dl zSU$|3)Swli#Fd$wn4YR%o|;r(#paw?P~{Qe&?@e6TvEt}i#^{tC)KUOS%68R`~CUL z8w`~M{=fZjPm2LW|_{`~ac IG6Mxh08Na|eE zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_@@>X)Ys(DZ%K(2JvlH4 zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez*wl14`N6#I|epCwizWv3!_6 zu}@1;i7PWVF+Ej3u^=zqil->wFCe)%FfA~s$loE=qfbtXAyX+GXd|P<<|p&Ey)*OT z<>KaG5xBAV_11N!UJOy*Tp)L_XfO&na@iLr7Ni!XmMEl@W)>?Hm6jBh<|-uRB<7`L jmMG+97H28|rHfOOOG`2p3QO}-6v|RFGLuVl5}|4VXo<#U literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..76992f7e93a76629e5bdce5a9ec676c9cfea8356 GIT binary patch literal 826 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$O+=>ZdnDZ%K(2Jvkd<2fd-lS~Xh z_&RpHn{6X3b-BW1kFrGSlSfZK|GB+ny5P;1yav4GqA4q_`^&mK-&$oFmv-lLEDQ)f z>LB#>eQ5u=j-QM8A81_Q*d)HrjOS)`{f`~jt}1ofv8gjkP0pkdcmVO!&v+b z|1!Np&R?W=eBWMrNOsbx;8ja2%7uBZT-xsuek z7Mwi(@P3ro0->!E&`@Ex`rLj+(WySB+iv<5QzL%XEYnJBU3Z&5GSEaK=!UKld$;O5 zo#S`9bY1ntJ``CTsH>PQm%#T!*QDye{n>HHKeNP4=C?Eyzr%BgX@x>a_L0I1vl#=V z+~*0(L>#XcTlSmxf7rR6f2(&)=y)XiygA=My5EQ`p!g2snw7Iz&CmX0W`o5aBa~5E z*eSx zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m380b)onIR#>J9YoRT(= zQE4_KP?Aw%>yvrg-kEuEaItZ)crkG>MKv>W@d1^yNI{iqFba8dd6s0P<`-lXB^H;I z7AvG=rsriA7iZ=wB<5rmmZp{{lqP{_h2q5Y%#xDKVuhmA zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7{RX&)nqDY5S2oVTA1oP;>;p80?H zsG-2ZKP?w$n}9r@HHVRFCL@=?i!hcakJI_fUw?hBC^A2Cu8;y3f8yjV|9`7SR8F;1 zt=j4CzBTJB0TBR zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#hu=_wYND}`QCf;oVxqZGk^qyG!0Qe6Z(G{FNM`4y z=5UsaFx?G|9^{>{%S4P)_A_ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U*9)Wig2GD>W?IOpwW11~l%77i9E zhO9S?T!$IC1YU%(Jb9eXU;g^*b48K)k#mIX)Y`6QxAI=V*rCpo+p z^H|5nmB6K7WR#d_l4O}?Xk?L?WMF71z$_)O{B2ODlCB5G z-c@&gzFIP&Ur9h|t@p2E4>=-#7u=pN)U`oUHeOIxlQD= zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7dD=_n(R$tbb$;+(gi4ZQfdcsW=E zZY+Mib)Bga69;3~GDfb&j9daQ!dRX>PUkOw{q?z`$o$B;LJC~`iIcbd|E(HPIn_?J zYNxyV)~u)X=6cf8Kiznhp!Mv81cwsGhxrqGKg{pnrB0Tns~|z literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d8b128833b0082b7cb53bcb4427a9166cd44ef71 GIT binary patch literal 601 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7*N=`bUZ$tbbu;+(gi4ZL`{xH(t^ z*3Uh)bhV)pL)J1zt_h4>0x!Z?o;*(HFMs{@xuVGY$hkraT>OcXxBUOD8c{jbPPJ;M zyZhFxr}gG~($hcPc$J{_?1Y4v68DGs6IVWJ=>0H%(rztpC9cfe#Pn4C#DctZE7nZ+ zLf;A@GcK<1aJRz5T9)I#uT;3`e*gWyKmVn!fJ6ypslMnsAu}Gd> f)#R$dqs!Aju?Y!su>cKi0J{3jhNUf6jTIOHX@twK literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..149d832d07b3e756fb14cfd547935100d887a97b GIT binary patch literal 635 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#hu=_wVdN5c5ytZ5aXNqb>#xrhMdnA&6;j~hPn^8v|8LcZ%Bgm$RXg3?w`M)9 zH`kM%{^`c61g&Q$B&3yCKFpugpp~Y?m6@BEo~mD-np9xLSCEqIo#|3lmFnmk<)7qm zO4>tR$eD}J!^b~A+czp8H>st34Bd!au-O>d9hyFbCS!SYE>

zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^J~X+0y5$tbbq;+(gi4ZL`{xH(t^ zZY+Mib)BgeL)Iilu6{-?ffr#cPadc9m%sk{Tv23xFJ+tyh_k|c0z(fiRHumiM?8ZN}QRwiRr2O1$pTXuf;sp@d?>+afOGw z6(%NEmAj<~FiV_WG5^p?GbI73Y0udYFhw3$^V{lJ`&;3rmVlmm;q7Dd?ZeK`kiX`2 MCcwd9&B;GG00+OpnE(I) literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..035c8a670eb8164150264f567e4c8c0351da3087 GIT binary patch literal 546 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez}U~I)WZm5GD>W{IOpwW1229qUJe$4 z^>a@xU2SN@#KD-A#}&yX@FI-m$>VhX^4DLVD~imIoGYZj#h*BN%m3f15tUQzRI7Hn zyKl{UT5ql=J^j;-R|#6rPDqF;v3!_6u}@1{i7PWVF+Ej3u^=zqiZ#={(6_?jovcTb Zj1+^AAQw>4qz0h%XErQtxoWJy2mmzzuU-HE literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..623a0a2fb5e69c6fc24170f856e50da84297cedb GIT binary patch literal 626 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8fg=?){1$tbby;+(gi4ZPU6SU6aW z7_yEra?NGr5_l2D^5k(kfBEaL&lN@HN6r;e;Nnl5yygFI)riWecB)l7-QBllJ*_v_ zlb-(R#;XLaXD1}2l{h}kpV<3he*ZqLXeG|f+{E-${erx7EV-usqR0Ctvl$4}Y z^R!eGGt;z`Bx4iP6iXnHmXa*MASJMY-QuC@`{IHcE~${^UBOvO0w!7yOz-Zif0Ful vv+GTh_&XZgH=X{*A|%Mg@?rj@2B6!|Y*^ZI)mVWMA~dmAfJNdgM2HChS}4z2 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..10d9fc2cd69f4dc0f9cc9ea0d14c6644a5f82f43 GIT binary patch literal 624 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<8HY=@ui9$tbb?;+(gi4ZQfdcsW=E zZY+Mib)Bgd69;3~Zbq&hj9daQ!dRX>PUkOw{q?z`$o$B;LJC~`iIcbd|E(HPIn_?J zYNxyV)~u)X=6cf8Kiznhp!Mv81cws$hxrp%K5FRwFn`i+tzspv%-qEERQ<$)ymTwp zO!q?H3WrnD9_n0?TndI3Nd_rt#-=GoDJh8-Mu{eAX{m|EMn-8VMu~~$Mo9upQUb3x z*uQOQ`y!d0mzu*_E|#w(AhF@T>7~0XwEX`qR{E>8TwCM$mP-w+M(n}aK7sD8K^6=E Dzb?-q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..39fddf975f27c3c8fbe259733f2828fdf6c82b11 GIT binary patch literal 652 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$9<=^G=E$tbbo;+(gi4ZL`{xH(t^ z*3Uh)bhV)sL)J}3uEUI60x!Z?o;*(HFMs{@xuVGY$hkraT>OcXxBUOD8c{jbPPJ;M zyZhFxr}gG~($hcPc$J{_?1Y4v63d7AlNz*I7?rp(a}(23^~+O}3at1FQj)zhU8<^5 z9bKdRlN?@)d935(O5jp3GD=J_NlHshHcK@$urReuOENb|NwQ2cG_pucGBC6hV3rbC z{x+ynN!Np8@2WdLUoDx?uOy(f*8A77ha8c=3vSOB>e?WwaeV8Crz}Q%<-viauFiqs crKP2=Zq61=LV{di=Kwu=X2a5!tHuh90B7RZ`v3p{ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..c15b8a845f0cc71cc8034f7fd37d3603334750fa GIT binary patch literal 628 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1$0+=^i7H$tbb&;+(gi4ZPU6SU6am z7_v?o0y)eUyzsX@J`mFNruahOTo-MEhRb0FfrN8 z!Zgt$F*PMA#mF)_$;2qt(!wOg%v69uN?^Lz*X2thQ@KFX)QpZ|X yth;?_&KA8HzNs49H=X{*A|%KKv}#fV(Di3FEN!`JtiT8nn%FDAB5@WX!~_5c4$& zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez_^A{X(c0&$tbbw;+(gi4ZL`{xH(t^ zZY+Mib)BgfLskzXS0^Kvz>6@JCy&$l%U^$et|&4;a;}g97k}d9E&qS3MpRCT9)I#uT;3`e*gWyKmVn!fJ6ypslMnsAu}Gd>)#R$dqs!Aj Gu>k zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*E!1#bs=`JIX$tbb=;+(gi4ZQfdcsW=E z*3Uh)bhV)u69;3~K1Qw;j9daQ!dRX>PUkOw{q?z`$o$B;LJC~`iIcbd|E(HPIn_?J zYNxyV)~u)X=6cf8Kiznhp!Mv8gqRY?hxrqGKg{pnrtR$eD}J!^b~A+czp8H>st34Bd!au-O> zd9hyFbCS!SYE>wWCu(ai> Hu>vCi=aSZr literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..abebdca8775670c7e5688906f311825f8340b93f GIT binary patch literal 621 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez<7&M={h5j$tbbs;+(gi4ZJwG*f?0c zm^hfS_A_!#W8@Ne5ytZ5aXNqb>#xrhMdnA&6;j~hPn^8v|8LcZ%Bgm$RXg3?w`M)9 zH`kM%{^`c61g&Q$B&3zNKg^%F@=-(YhxwCsYXvHCX67cQr|K8vr8~S9^H|3xWXHu7 z9`06{m|RuvmLk9`adyT0Lo3ac1f-@tXFtFcd0fqJt7GkNg_~Ledg_I@kIlCaJ3mAI rn%9{C2ZJ>y|Ktb>a zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$mFC4q`~$Sp0hHI@2JADh@3+j*HDb GtDOP-iGer( literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0b9568999ce1188a5b3ade31f846884dc23c6ef1 GIT binary patch literal 428 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJz#+xP!6d*Ez$m380b)q3pL=TQYQrFgDo!nSjzwFy Iy=dkI0C?4bi~s-t literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_00.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..22a78198d413f048c4db012b372abf905ff48678 GIT binary patch literal 318 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 xWW*pNB*nq3B*rBGRE`3qm?W4bgv6{wxCC?#ZRrVE_b+(!bibQ&*?ub(7y%BeVbK5p literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_01.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..24da3a2a1934fa5db7381be9935528664c6457ff GIT binary patch literal 369 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Jg*>g?xde0$ZRrVE_b+(!bibQ&*?ud96rNX~(GRZY zW_tf3!C;M!VX)hZuXheCpT_iasj$F_cQ-qvI0A#qypofu+!dHK8cuB4@mdK0H3xbp literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_02.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ea383556f22e700da386d3f7d755a7aba52d7741 GIT binary patch literal 336 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Bgfy(wxCC?#ZRrVE_b+(!bibQ&*?udfID=h6iX)t| P^WA|CYIw2x_-s7@Gzn=Q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_03.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..179ab70f1910ff0778b71677c7c38c8f02d7f023 GIT binary patch literal 352 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W63ge zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W4rh19K7xde0$ZRrVE_b+(!bibQ&*?udfI0A#qypofu O+=1?CII(5NYb5~9ENJ-v literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_05.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..87392103d65b98ecf62c09c9e10090c8ada8ba35 GIT binary patch literal 370 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5eguJXgxCC?#ZRrVE_b+(!bibQ&*?ud96rNX~(GRZY zW_tf3!C;M!VX)hZuXheCpT_iasj$F_cQ-qvID=h6iX)t|^W7DgG#Xy)K0aFy0Ig_x AOaK4? literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_06.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..22a78198d413f048c4db012b372abf905ff48678 GIT binary patch literal 318 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 xWW*pNB*nq3B*rBGRE`3qm?W4bgv6{wxCC?#ZRrVE_b+(!bibQ&*?ub(7y%BeVbK5p literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_07.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..24da3a2a1934fa5db7381be9935528664c6457ff GIT binary patch literal 369 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Jg*>g?xde0$ZRrVE_b+(!bibQ&*?ud96rNX~(GRZY zW_tf3!C;M!VX)hZuXheCpT_iasj$F_cQ-qvI0A#qypofu+!dHK8cuB4@mdK0H3xbp literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_08.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ea383556f22e700da386d3f7d755a7aba52d7741 GIT binary patch literal 336 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Bgfy(wxCC?#ZRrVE_b+(!bibQ&*?udfID=h6iX)t| P^WA|CYIw2x_-s7@Gzn=Q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_09.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..179ab70f1910ff0778b71677c7c38c8f02d7f023 GIT binary patch literal 352 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W63ge zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W4rh19K7xde0$ZRrVE_b+(!bibQ&*?udfI0A#qypofu O+=1?CII(5NYb5~9ENJ-v literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_11.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..87392103d65b98ecf62c09c9e10090c8ada8ba35 GIT binary patch literal 370 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5eguJXgxCC?#ZRrVE_b+(!bibQ&*?ud96rNX~(GRZY zW_tf3!C;M!VX)hZuXheCpT_iasj$F_cQ-qvID=h6iX)t|^W7DgG#Xy)K0aFy0Ig_x AOaK4? literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_12.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..22a78198d413f048c4db012b372abf905ff48678 GIT binary patch literal 318 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 xWW*pNB*nq3B*rBGRE`3qm?W4bgv6{wxCC?#ZRrVE_b+(!bibQ&*?ub(7y%BeVbK5p literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_13.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..24da3a2a1934fa5db7381be9935528664c6457ff GIT binary patch literal 369 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Jg*>g?xde0$ZRrVE_b+(!bibQ&*?ud96rNX~(GRZY zW_tf3!C;M!VX)hZuXheCpT_iasj$F_cQ-qvI0A#qypofu+!dHK8cuB4@mdK0H3xbp literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_14.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ea383556f22e700da386d3f7d755a7aba52d7741 GIT binary patch literal 336 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Bgfy(wxCC?#ZRrVE_b+(!bibQ&*?udfID=h6iX)t| P^WA|CYIw2x_-s7@Gzn=Q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_15.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_15.binproto new file mode 100644 index 0000000000000000000000000000000000000000..179ab70f1910ff0778b71677c7c38c8f02d7f023 GIT binary patch literal 352 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W63ge zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W4jgv>Ztgp9ZZI&Qu@Vg251ZlmC>^|9^$IfVp1o%oi* eljN`8kf~WXXTy;Ug*6|auBg=jx+a6siU9ylb#Jf$ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_contacts_01.binproto b/app/src/androidTest/assets/backupTests/recipient_contacts_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..abb2914329e1f27a66c32d46e92dc525bfb3b4c6 GIT binary patch literal 408 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5sgbFxVgmSn9I&Qu@Vg251ZlmC>^|9^$IfVp1o%oi* zljN`8kf~WXXTy;UDem zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W4}g_1c~gyOjbI&Qu@Vg251ZlmC>^|9^$IfVp1o%oi* zljN`8kf~WXXTy;UDc+Kd)clnEqEtO2BXbjlH6Nd@s0BKfDS$DG-8V6>G&LtNi7OyA YFE2AMy(B*`gVBkd zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W4>g^D>?gz~urI&Qu@Vg251ZlmC>^|9^$IfVp1o%oi* zljN`8kf~WXXTy;UDfXnI)Dk@dLxmSxKVR1MQcz^`HutHimwfNFlluXmbGdBy=AX=R p&rg;~srOAc_^%bk?vt69Qk+ zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWWX?(6oSMWM zoSRvaVa1Rj6vx3T6a}<`ftwr63RDp$g=r8gSOgsLq9|ryFeb5j<>#dqTQQ^vC2_C` z#epn1#fWYJiU^a!bch8U3Qc%X6svP$QGQMmyH|cjUU7b&6@#LXJO{gyG{_QEAjJ-f p86`#*c_CR24kbx0fk3_M)=?kb1KPdW6ea6xd<1SRe!X>_DFC#Xr$7Jz literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_distribution_list_01.binproto b/app/src/androidTest/assets/backupTests/recipient_distribution_list_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..63d9dfc8e4c0fdba60be67fbf0d8d860c348e2e6 GIT binary patch literal 642 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWWX?(6oSMWM zoSRvaVa1Rj6vx3T6a}<`ftwr63RDp$g=r8gSOgsLq9|ryFeb5j<>#dqTQQ^vC2_C` z#epn1#fWYJiU^a!bch8U3Qc%X6svP$QGQMmyH|cjUU7b&6@!A190$9S6vz@(AjKxZ zq{PI^rZ1$+!J(wdB@n20-8$-{dqBH4o1$cWjgOQl7mrtIaY?>{bADcWX(}ipl^9t7 DhFPeB literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_distribution_list_02.binproto b/app/src/androidTest/assets/backupTests/recipient_distribution_list_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4161d7d9f94466d0fcc2a8c1a45c8928d3062d71 GIT binary patch literal 636 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWWX?(6oSMWM zoSRvaVa1Rj6vx3T6a}<`ftwr63RDp$g=r8gSOgsLq9|ryFeb5j<>#dqTQQ^vC2_C` z#epn1#fWYJiU^a!bch8U3Qc%X6svP$QGQMmyH|cjUU7b&6@#3R3uY?Z1i3g}OLG-`^NW%)B^Z^MSlIxYB&O>C literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_distribution_list_03.binproto b/app/src/androidTest/assets/backupTests/recipient_distribution_list_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..044ffbfc7ff2f0e54c29ba2b963f8cf60094297c GIT binary patch literal 646 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWWX?(6oSMWM zoSRvaVa1Rj6vx3T6a}<`ftwr63RDp$g=r8gSOgsLq9|ryFeb5j<>#dqTQQ^vC2_C` z#epn1#fWYJiU^a!bch8U3Qc%X6svP$QGQMmyH|cjUU7b&6@#LXJO{gyG{_QEAjJ-f z86`#*BOwD04kaBffk3_M)=?kb1KPdW6ea6xe553}cpdXfGV==(a}@$h^HabYm|0la E03{}=1ONa4 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_00.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9eeb9ed7bad4e1fc74df9fd09a39d517d19b9a9a GIT binary patch literal 1052 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i-2qkf_3B`ddIK_x=0g4Ed!gPoQ912Z%Q536lVo`oh61!J^MqY7#9>f>y z5MMCdLbn7(gh^or#1bBZ!+22?vrA%L601{DemT?v4yXn1Ff2e8VN#e0v4B7*IYu$N zmI33zy(l#mXaOjgI3dBr@C(BNWDzEXSr7|E0$uTxa@Wcc;^E>7Em3eTP0Gy4 zOw?lIU=jePOM^A-mrpQTiC`v70YeY<^k eT0St*dSH5YU;UHRznfhX7&)ZafVqq-fDr(U3k!b$ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_01.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..6e8d40014ee8a6c6a4bc04dce107b730b7d7ed9e GIT binary patch literal 1236 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i-2qkf_3B`ddIK_x=0g4Ed!gPoQ912Z%Q536lVo`oh61!J^MqY7#9>f>y z5MMCdLbn7(gh^or#1bBZ!+22?vrA%L601{DemT?v4yXn1Ff2e8VN#e0v4B7*IYu$N zmI33zy(l#mXaOjgI3dBr@C(BNWDzEXSr7|E0$uTerbc%_COX?^S zmx5BCMF01cD(_OeSZV3=YcFlrm^@=r<%<)nH`;c#SDtB6VAPt&BqYei@0_2PUYe@l zmRMYpnUa|qC9Wh|Sel=rkXf3NnVguES)!0wnp~1!Bqf_sQc_TCrLUh_k(gVMld6}T zpR1o5Qj+HHTac2e#3pfKQO|33EjA8j0cHsngMY0*XRum{V5U3)CWt6X;s9nph$tur zATojgqmu>~*bJ0d2CBx$B8*-_AiEflBa1Oep>|v9)AYZmrH-|(Saz7@$W*Q@Nw6;D7+@6GaP#SscSihDyuf0COXEP(yX$jo0~i5>k2cN# literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_02.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e031662d1e456847e18456ba837a37ad2903e67e GIT binary patch literal 1194 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i-2qkf_3B`ddIK_x=0g4Ed!gPoQ912Z%Q536lVo`oh61!J^MqY7#9>f>y z5MMCdLbn7(gh^or#1bBZ!+22?vrA%L601{DemT?v4yXn1Ff2e8VN#e0v4B7*IYu$N zmI33zy(l#mXaOjgI3dBr@C(BNWDzEXSr7|E0$uT^nRyCDscHE|xtS@6dBqC(MTsSu`3glD`FY8u#ZpQc zB_##LR{Hv>6^Xe8IjMTd`MLU0{*mEnE+Od!o?eBO!Ah(W+t+;$(_-Ub5&$N7gEb2e z&SA6?!A$c45K+`b$tb`CR*ROlU}{0x1(9n67=skVG*vTs4Oi@vs(!J#iEF2sglXv8 jK8H)rvW9iK?<{x!NMPib;ss`QE{z*^4^3Il8o&quUtup2 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_03.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0d8e0e500e672004af52c5434513c6e99a438f4b GIT binary patch literal 1183 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W5!gc3Mdgkpe_T!QBq7#tWHAW9g8&_$ROrarp!-`Y#z zdTc3P6vgV8lbM{F#2TENS(0JJkRTMt!73C5vY(q9%?eZzCWUDbD_8^^@uDbZU>GN{ zdgbS(7F#i-2qkf_3B`ddIK_x=0g4Ed!gPoQ912Z%Q536lVo`oh61!J^MqY7#9>f>y z5MMCdLbn7(gh^or#1bBZ!+22?vrA%L601{DemT?v4yXn1Ff2e8VN#e0v4B7*IYu$N zmI33zy(l#mXaOjgI3dBr@C(BNWDzEXSr7|E0$uT=$~=_ IhGYOE0KyC?tpET3 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/standard_frames.binproto b/app/src/androidTest/assets/backupTests/standard_frames.binproto new file mode 100644 index 0000000000000000000000000000000000000000..117dbc6b8ce6facdee5b54601dff6aa702f23c55 GIT binary patch literal 293 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 XWW*pNB*nq3B*rBGRE`3qm?W41{b^X5 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/sticker_pack_00.binproto b/app/src/androidTest/assets/backupTests/sticker_pack_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..fa7f31b98fa49231c7f39ed4812d223fd7613b98 GIT binary patch literal 348 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Zv`n}JbPjFl30U_pc=L3>n{wHHD}@xESD(=juIFZY d|02O)jgDck+lsGu4lJL>^mD1Oz=?M^I{=?Pa(w^* literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/sticker_pack_01.binproto b/app/src/androidTest/assets/backupTests/sticker_pack_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..fa7f31b98fa49231c7f39ed4812d223fd7613b98 GIT binary patch literal 348 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Zv`n}JbPjFl30U_pc=L3>n{wHHD}@xESD(=juIFZY d|02O)jgDck+lsGu4lJL>^mD1Oz=?M^I{=?Pa(w^* literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/sticker_pack_02.binproto b/app/src/androidTest/assets/backupTests/sticker_pack_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..fa7f31b98fa49231c7f39ed4812d223fd7613b98 GIT binary patch literal 348 zcmd<&U=+CVYr*{QhVK};UNdqjBr;80{r0evr^npIvkXrzJv;O4rTnk$MvpQchreSe zy?H~3Cn-NEF+MG|q(slmOv;Q)AvHaw_yXSs0oKR6YrNb)O*@?$^1ZWBxkL1&=na*> zdXt3&5)U#iVz}xg-g$^s;)Z9{2MHD>7AK%7S}blrW2`i}6yEI4IBsGrrMKAl>~i;$ zJ+8s2N{JEL{^z0|+*{#$E>%*9IW*Wsg3-$h=v+n#Mg>LQ6Kf`}EAr=lsEd~(9 zWW*pNB*nq3B*rBGRE`3qm?W6Zv`n}JbPjFl30U_pc=L3>n{wHHD}@xESD(=juIFZY d|02O)jgDck+lsGu4lJL>^mD1Oz=?M^I{=?Pa(w^* literal 0 HcmV?d00001 From 320d51707d8d7838e2ffebbba694a1cfd1e63319 Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Tue, 1 Oct 2024 16:53:12 -0400 Subject: [PATCH 34/53] Add string translation comments. --- app/src/main/res/values/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2941e28af1..253ed4a59f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -261,6 +261,7 @@ Tap for photo, hold for video + Capture Change camera Open gallery @@ -404,6 +405,7 @@ Can\'t find an app able to open this media. Copied %s from %s + to %s   Read More   Download More @@ -437,6 +439,7 @@ Send edit Compose message Sorry, there was an error setting your attachment. + Recipient is not a valid SMS or email address! Message is empty! Group members @@ -577,6 +580,7 @@ Saving %1$d attachments to storage… Pending… + Data (Signal) MMS SMS @@ -1509,6 +1513,7 @@ Send %d SMS invites? Let\'s switch to Signal: %1$s + It looks like you don\'t have any apps to share to. @@ -1933,6 +1938,7 @@ Let %1$s message you and share your name and photo with them? You won\'t receive any messages until you unblock them. Let %1$s message you? You won\'t receive any messages until you unblock them. + Get updates and news from %1$s? You won\'t receive any updates until you unblock them. Continue your chat with this group and share your name and photo with its members? This Legacy Group can no longer be used. Create a new group to activate new features like @mentions and admins. @@ -2248,6 +2254,7 @@ No one else is here %1$s is in this call + %1$s are in this call %1$s and %2$s are in this call @@ -3091,6 +3098,7 @@ Message read + Contact photo @@ -3175,6 +3183,7 @@ Everything looks good now! To receive call notifications, tap here and turn on \"Show notifications.\" To receive call notifications, tap here and turn on notifications and make sure Sound and Pop-up are enabled. + To receive call notifications, tap here and enable background activity in \"Battery\" settings. Settings To receive call notifications, tap Settings and turn on \"Show notifications.\" @@ -3411,6 +3420,7 @@ emoji_1 https://support.signal.org/hc/articles/360007318591 https://support.signal.org + Support Info Signal Android Support Request Debug Log: @@ -3499,6 +3509,7 @@ Retrieve link previews directly from websites for messages you send. Change passphrase Change your passphrase + Enable passphrase screen lock Lock screen and notifications with a passphrase Screen security @@ -3511,6 +3522,7 @@ LED blink pattern Customize Change sound and vibration + Sound Silent Default From 93609106b08b8913badd10cdca03a034d6f67019 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 2 Oct 2024 09:49:28 -0400 Subject: [PATCH 35/53] Update restore progress banner UI/UX and job behavior. --- .../backup/v2/ui/BackupAlertBottomSheet.kt | 6 +- .../backup/v2/ui/BackupsIconColors.kt | 27 +-- .../backup/v2/ui/CreateBackupBottomSheet.kt | 6 +- .../backup/v2/ui/status/BackupStatus.kt | 178 ++++++++++++------ .../MessageBackupsTypeSelectionScreen.kt | 4 +- .../banners/MediaRestoreProgressBanner.kt | 115 ++++++++--- .../UpgradeToEnableOptimizedStorageSheet.kt | 6 +- .../ConversationListFragment.java | 12 +- .../impl/BatteryNotLowConstraint.kt | 39 ++++ ...gAndBatteryIsNotLowConstraintObserver.java | 85 +++++++++ .../jobmanager/impl/ChargingConstraint.java | 2 +- .../impl/ChargingConstraintObserver.java | 62 ------ .../jobmanager/impl/WifiConstraint.kt | 7 +- .../securesms/jobs/JobManagerFactories.java | 6 +- .../securesms/jobs/RestoreAttachmentJob.kt | 31 +-- .../securesms/keyvalue/BackupValues.kt | 7 +- .../keyvalue/SignalStoreValueDelegates.kt | 12 ++ .../res/drawable/symbol_backup_error_24.xml | 15 ++ app/src/main/res/values/strings.xml | 18 +- .../org/signal/core/util/ByteExtensions.kt | 56 ++++-- 20 files changed, 478 insertions(+), 216 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/BatteryNotLowConstraint.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingAndBatteryIsNotLowConstraintObserver.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraintObserver.java create mode 100644 app/src/main/res/drawable/symbol_backup_error_24.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt index 87f6406ad7..1c5edbb589 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -35,7 +36,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.parcelize.Parcelize import org.signal.core.ui.BottomSheets import org.signal.core.ui.Buttons -import org.signal.core.ui.Icons import org.signal.core.ui.Previews import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R @@ -144,10 +144,10 @@ private fun BackupAlertSheetContent( Spacer(modifier = Modifier.size(26.dp)) val iconColors = rememberBackupsIconColors(backupAlert = backupAlert) - Icons.BrushedForeground( + Icon( painter = painterResource(id = R.drawable.symbol_backup_light), // TODO [message-backups] final asset contentDescription = null, - foregroundBrush = iconColors.foreground, + tint = iconColors.foreground, modifier = Modifier .size(88.dp) .background(color = iconColors.background, shape = CircleShape) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupsIconColors.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupsIconColors.kt index d2f69ffd41..059a12a16c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupsIconColors.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupsIconColors.kt @@ -7,39 +7,32 @@ package org.thoughtcrime.securesms.backup.v2.ui import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.SolidColor sealed interface BackupsIconColors { @get:Composable - val foreground: Brush + val foreground: Color @get:Composable val background: Color data object Normal : BackupsIconColors { - override val foreground: Brush - @Composable get() = remember { - Brush.linearGradient( - colors = listOf(Color(0xFF316ED0), Color(0xFF558BE2)), - start = Offset(x = 0f, y = Float.POSITIVE_INFINITY), - end = Offset(x = Float.POSITIVE_INFINITY, y = 0f) - ) - } + override val foreground: Color @Composable get() = MaterialTheme.colorScheme.onSurface + override val background: Color @Composable get() = MaterialTheme.colorScheme.primaryContainer + } + data object Success : BackupsIconColors { + override val foreground: Color @Composable get() = MaterialTheme.colorScheme.primary override val background: Color @Composable get() = MaterialTheme.colorScheme.primaryContainer } - object Warning : BackupsIconColors { - override val foreground: Brush @Composable get() = SolidColor(Color(0xFFC86600)) + data object Warning : BackupsIconColors { + override val foreground: Color @Composable get() = Color(0xFFFF9500) override val background: Color @Composable get() = Color(0xFFF9E4B6) } - object Error : BackupsIconColors { - override val foreground: Brush @Composable get() = SolidColor(MaterialTheme.colorScheme.error) + data object Error : BackupsIconColors { + override val foreground: Color @Composable get() = MaterialTheme.colorScheme.error override val background: Color @Composable get() = Color(0xFFFFD9D9) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt index 6531d04a50..ac7607d35a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -29,7 +30,6 @@ import androidx.core.os.bundleOf import androidx.fragment.app.setFragmentResult import org.signal.core.ui.BottomSheets import org.signal.core.ui.Buttons -import org.signal.core.ui.Icons import org.signal.core.ui.Previews import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R @@ -90,9 +90,9 @@ private fun CreateBackupBottomSheetContent( ) { BottomSheets.Handle() - Icons.BrushedForeground( + Icon( painter = painterResource(id = R.drawable.symbol_backup_light), - foregroundBrush = BackupsIconColors.Normal.foreground, + tint = BackupsIconColors.Normal.foreground, contentDescription = null, modifier = Modifier .padding(top = 18.dp, bottom = 11.dp) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatus.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatus.kt index 8f51eaec85..43122837f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatus.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatus.kt @@ -7,31 +7,41 @@ package org.thoughtcrime.securesms.backup.v2.ui.status import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import org.signal.core.ui.Buttons -import org.signal.core.ui.Icons import org.signal.core.ui.Previews import org.signal.core.ui.SignalPreview +import org.signal.core.util.ByteSize +import org.signal.core.util.bytes +import org.signal.core.util.kibiBytes +import org.signal.core.util.mebiBytes import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.v2.ui.BackupsIconColors import kotlin.math.max @@ -43,10 +53,12 @@ private const val NONE = -1 * Displays a "heads up" widget containing information about the current * status of the user's backup. */ +@OptIn(ExperimentalLayoutApi::class) @Composable fun BackupStatus( data: BackupStatusData, - onActionClick: () -> Unit = {}, + onSkipClick: () -> Unit = {}, + onDismissClick: () -> Unit = {}, contentPadding: PaddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp) ) { Row( @@ -55,56 +67,81 @@ fun BackupStatus( .padding(contentPadding) .border(1.dp, color = MaterialTheme.colorScheme.outline.copy(alpha = 0.38f), shape = RoundedCornerShape(12.dp)) .fillMaxWidth() - .padding(14.dp) + .defaultMinSize(minHeight = 48.dp) + .padding(12.dp) ) { - val foreground: Brush = data.iconColors.foreground - Icons.BrushedForeground( + Icon( painter = painterResource(id = data.iconRes), contentDescription = null, - foregroundBrush = foreground, + tint = data.iconColors.foreground, modifier = Modifier - .background(color = data.iconColors.background, shape = CircleShape) - .padding(8.dp) + .padding(start = 4.dp) + .size(24.dp) ) - Column( + FlowRow( + horizontalArrangement = Arrangement.SpaceBetween, + verticalArrangement = Arrangement.Center, modifier = Modifier .padding(start = 12.dp) .weight(1f) ) { Text( text = data.title, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier + .padding(end = 20.dp) + .align(Alignment.CenterVertically) ) - if (data.progress >= 0f) { - LinearProgressIndicator( - progress = { data.progress }, - strokeCap = StrokeCap.Round, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 6.dp) - ) - } - - if (data.statusRes != NONE) { + data.status?.let { status -> Text( - text = stringResource(id = data.statusRes), + text = status, style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier + .padding(end = 12.dp) + .align(Alignment.CenterVertically) ) } } + if (data.progress >= 0f) { + CircularProgressIndicator( + progress = { data.progress }, + strokeWidth = 3.dp, + strokeCap = StrokeCap.Round, + modifier = Modifier + .size(24.dp, 24.dp) + ) + } + if (data.actionRes != NONE) { Buttons.Small( - onClick = onActionClick, + onClick = onSkipClick, modifier = Modifier.padding(start = 8.dp) ) { Text(text = stringResource(id = data.actionRes)) } } + + if (data.showDismissAction) { + val interactionSource = remember { MutableInteractionSource() } + + Icon( + painter = painterResource(id = R.drawable.symbol_x_24), + contentDescription = stringResource(R.string.Material3SearchToolbar__close), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier + .size(24.dp) + .clickable( + interactionSource = interactionSource, + indication = ripple(bounded = false), + onClick = onDismissClick + ) + ) + } } } @@ -114,19 +151,29 @@ fun BackupStatusPreview() { Previews.Preview { Column { BackupStatus( - data = BackupStatusData.CouldNotCompleteBackup + data = BackupStatusData.RestoringMedia(5755000.bytes, 1253.mebiBytes) + ) + + HorizontalDivider() + + BackupStatus( + data = BackupStatusData.RestoringMedia( + bytesDownloaded = 55000.bytes, + bytesTotal = 1253.mebiBytes, + restoreStatus = BackupStatusData.RestoreStatus.FINISHED + ) ) HorizontalDivider() BackupStatus( - data = BackupStatusData.NotEnoughFreeSpace("12 GB") + data = BackupStatusData.NotEnoughFreeSpace(40900.kibiBytes) ) HorizontalDivider() BackupStatus( - data = BackupStatusData.RestoringMedia(50, 100) + data = BackupStatusData.CouldNotCompleteBackup ) } } @@ -134,8 +181,6 @@ fun BackupStatusPreview() { /** * Sealed interface describing status data to display in BackupStatus widget. - * - * TODO [message-requests] - Finalize assets and text */ sealed interface BackupStatusData { @@ -150,16 +195,18 @@ sealed interface BackupStatusData { @get:StringRes val actionRes: Int get() = NONE - @get:StringRes - val statusRes: Int get() = NONE + @get:Composable + val status: String? get() = null val progress: Float get() = NONE.toFloat() + val showDismissAction: Boolean get() = false + /** * Generic failure */ data object CouldNotCompleteBackup : BackupStatusData { - override val iconRes: Int = R.drawable.symbol_backup_light + override val iconRes: Int = R.drawable.symbol_backup_error_24 override val title: String @Composable @@ -172,9 +219,11 @@ sealed interface BackupStatusData { * User does not have enough space on their device to complete backup restoration */ class NotEnoughFreeSpace( - private val requiredSpace: String + requiredSpace: ByteSize ) : BackupStatusData { - override val iconRes: Int = R.drawable.symbol_backup_light + private val requiredSpace = requiredSpace.toUnitString(maxPlaces = 2) + + override val iconRes: Int = R.drawable.symbol_backup_error_24 override val title: String @Composable @@ -188,44 +237,51 @@ sealed interface BackupStatusData { * Restoring media, finished, and paused states. */ data class RestoringMedia( - val bytesDownloaded: Long, - val bytesTotal: Long, - val status: Status = Status.NONE + val bytesDownloaded: ByteSize = 0.bytes, + val bytesTotal: ByteSize = 0.bytes, + val restoreStatus: RestoreStatus = RestoreStatus.NORMAL ) : BackupStatusData { override val iconRes: Int = R.drawable.symbol_backup_light - override val iconColors: BackupsIconColors = BackupsIconColors.Normal + override val iconColors: BackupsIconColors = if (restoreStatus == RestoreStatus.FINISHED) BackupsIconColors.Success else BackupsIconColors.Normal + override val showDismissAction: Boolean = restoreStatus == RestoreStatus.FINISHED override val title: String @Composable get() = stringResource( - when (status) { - Status.NONE -> R.string.default_error_msg - Status.LOW_BATTERY -> R.string.default_error_msg - Status.WAITING_FOR_INTERNET -> R.string.default_error_msg - Status.WAITING_FOR_WIFI -> R.string.default_error_msg - Status.FINISHED -> R.string.default_error_msg + when (restoreStatus) { + RestoreStatus.NORMAL -> R.string.BackupStatus__restoring_media + RestoreStatus.LOW_BATTERY -> R.string.BackupStatus__restore_paused + RestoreStatus.WAITING_FOR_INTERNET -> R.string.BackupStatus__restore_paused + RestoreStatus.WAITING_FOR_WIFI -> R.string.BackupStatus__restore_paused + RestoreStatus.FINISHED -> R.string.BackupStatus__restore_complete } ) - override val statusRes: Int = when (status) { - Status.NONE -> NONE - Status.LOW_BATTERY -> R.string.default_error_msg - Status.WAITING_FOR_INTERNET -> R.string.default_error_msg - Status.WAITING_FOR_WIFI -> R.string.default_error_msg - Status.FINISHED -> R.string.default_error_msg - } + override val status: String + @Composable get() = when (restoreStatus) { + RestoreStatus.NORMAL -> stringResource( + R.string.BackupStatus__status_size_of_size, + bytesDownloaded.toUnitString(maxPlaces = 2), + bytesTotal.toUnitString(maxPlaces = 2) + ) + + RestoreStatus.LOW_BATTERY -> stringResource(R.string.BackupStatus__status_device_has_low_battery) + RestoreStatus.WAITING_FOR_INTERNET -> stringResource(R.string.BackupStatus__status_no_internet) + RestoreStatus.WAITING_FOR_WIFI -> stringResource(R.string.BackupStatus__status_waiting_for_wifi) + RestoreStatus.FINISHED -> bytesTotal.toUnitString() + } - override val progress: Float = if (bytesTotal > 0) { - min(1f, max(0f, bytesDownloaded.toFloat() / bytesTotal)) + override val progress: Float = if (bytesTotal.bytes > 0 && restoreStatus == RestoreStatus.NORMAL) { + min(1f, max(0f, bytesDownloaded.bytes.toFloat() / bytesTotal.bytes.toFloat())) } else { - 0f + NONE.toFloat() } } /** * Describes the status of an in-progress media download session. */ - enum class Status { - NONE, + enum class RestoreStatus { + NORMAL, LOW_BATTERY, WAITING_FOR_INTERNET, WAITING_FOR_WIFI, diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt index 890ede5e27..fc872e7bdb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt @@ -323,7 +323,7 @@ private fun getFeatures(messageBackupsType: MessageBackupsType): List { val photoCount = messageBackupsType.storageAllowanceBytes / ByteUnit.MEGABYTES.toBytes(2) val photoCountThousands = photoCount / 1000 - val (count, size) = messageBackupsType.storageAllowanceBytes.bytes.getLargestNonZeroValue() + val sizeUnitString = messageBackupsType.storageAllowanceBytes.bytes.toUnitString(spaced = false) persistentListOf( MessageBackupsTypeFeature( @@ -338,7 +338,7 @@ private fun getFeatures(messageBackupsType: MessageBackupsType): List() { +@OptIn(ExperimentalCoroutinesApi::class) +class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerListener) : Banner() { + + private var totalRestoredSize: Long = 0 override val enabled: Boolean - get() = SignalStore.backup.isRestoreInProgress + get() = SignalStore.backup.isRestoreInProgress || totalRestoredSize > 0 - override val dataFlow: Flow - get() { - if (!SignalStore.backup.isRestoreInProgress) { - return flowOf(BackupStatusData.RestoringMedia(0, 0)) - } + override val dataFlow: Flow by lazy { + SignalStore + .backup + .totalRestorableAttachmentSizeFlow + .flatMapLatest { size -> + when { + size > 0 -> { + totalRestoredSize = size + getActiveRestoreFlow() + } + + totalRestoredSize > 0 -> { + flowOf( + BackupStatusData.RestoringMedia( + bytesTotal = totalRestoredSize.bytes.also { totalRestoredSize = 0 }, + restoreStatus = BackupStatusData.RestoreStatus.FINISHED + ) + ) + } - val dbNotificationFlow = callbackFlow { - val queryObserver = DatabaseObserver.Observer { - trySend(Unit) + else -> flowOf(BackupStatusData.RestoringMedia()) } + } + } - queryObserver.onChanged() - AppDependencies.databaseObserver.registerAttachmentUpdatedObserver(queryObserver) + @Composable + override fun DisplayBanner(model: BackupStatusData, contentPadding: PaddingValues) { + BackupStatus( + data = model, + onSkipClick = listener::onSkip, + onDismissClick = listener::onDismissComplete + ) + } + + private fun getActiveRestoreFlow(): Flow { + val flow: Flow = callbackFlow { + val onChange = { trySend(Unit) } + + val observer = DatabaseObserver.Observer { + onChange() + } - awaitClose { - AppDependencies.databaseObserver.unregisterObserver(queryObserver) + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + onChange() } } - return dbNotificationFlow - .throttleLatest(1.seconds) - .map { - val totalRestoreSize = SignalStore.backup.totalRestorableAttachmentSize - val remainingAttachmentSize = SignalDatabase.attachments.getRemainingRestorableAttachmentSize() - val completedBytes = totalRestoreSize - remainingAttachmentSize + onChange() - BackupStatusData.RestoringMedia(completedBytes, totalRestoreSize) - } - .flowOn(Dispatchers.IO) + AppDependencies.databaseObserver.registerAttachmentUpdatedObserver(observer) + AppDependencies.application.registerReceiver(receiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) + AppDependencies.application.registerReceiver(receiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) + + awaitClose { + AppDependencies.databaseObserver.unregisterObserver(observer) + AppDependencies.application.safeUnregisterReceiver(receiver) + } } - @Composable - override fun DisplayBanner(model: BackupStatusData, contentPadding: PaddingValues) { - BackupStatus(data = model) + return flow + .throttleLatest(1.seconds) + .map { + when { + !WifiConstraint.isMet(AppDependencies.application) -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_WIFI) + !NetworkConstraint.isMet(AppDependencies.application) -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET) + !BatteryNotLowConstraint.isMet() -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.LOW_BATTERY) + else -> { + val totalRestoreSize = SignalStore.backup.totalRestorableAttachmentSize + val remainingAttachmentSize = SignalDatabase.attachments.getRemainingRestorableAttachmentSize() + val completedBytes = totalRestoreSize - remainingAttachmentSize + + BackupStatusData.RestoringMedia(completedBytes.bytes, totalRestoreSize.bytes) + } + } + } + .flowOn(Dispatchers.IO) + } + + interface RestoreProgressBannerListener { + fun onSkip() + fun onDismissComplete() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt index bd2deba6d2..31b9214410 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -29,7 +30,6 @@ import androidx.compose.ui.unit.dp import androidx.fragment.app.viewModels import org.signal.core.ui.BottomSheets import org.signal.core.ui.Buttons -import org.signal.core.ui.Icons import org.signal.core.ui.Previews import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R @@ -89,10 +89,10 @@ private fun UpgradeToEnableOptimizedStorageSheetContent( ) { BottomSheets.Handle() - Icons.BrushedForeground( + Icon( painter = painterResource(id = R.drawable.symbol_backup_light), contentDescription = null, - foregroundBrush = BackupsIconColors.Normal.foreground, + tint = BackupsIconColors.Normal.foreground, modifier = Modifier .padding(top = 8.dp, bottom = 12.dp) .size(88.dp) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index e43fc2b238..52e47437d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -868,7 +868,17 @@ private void initializeBanners() { } return Unit.INSTANCE; }), - new MediaRestoreProgressBanner() + new MediaRestoreProgressBanner(new MediaRestoreProgressBanner.RestoreProgressBannerListener() { + @Override + public void onSkip() { + // TODO [backups] add skip restore ability + } + + @Override + public void onDismissComplete() { + bannerManager.updateContent(bannerView.get()); + } + }) ); this.bannerManager = new BannerManager(bannerRepositories); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/BatteryNotLowConstraint.kt b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/BatteryNotLowConstraint.kt new file mode 100644 index 0000000000..45418aef49 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/BatteryNotLowConstraint.kt @@ -0,0 +1,39 @@ +package org.thoughtcrime.securesms.jobmanager.impl + +import android.app.job.JobInfo +import androidx.annotation.RequiresApi +import org.thoughtcrime.securesms.jobmanager.Constraint + +/** + * Job constraint for determining whether or not the device battery is not low. + */ +class BatteryNotLowConstraint private constructor() : Constraint { + companion object { + const val KEY: String = "BatteryNotLowConstraint" + + fun isMet(): Boolean { + return ChargingAndBatteryIsNotLowConstraintObserver.isCharging() || ChargingAndBatteryIsNotLowConstraintObserver.isBatteryNotLow() + } + } + + override fun getFactoryKey(): String = KEY + + override fun isMet(): Boolean { + return Companion.isMet() + } + + @RequiresApi(26) + override fun applyToJobInfo(jobInfoBuilder: JobInfo.Builder) { + jobInfoBuilder.setRequiresBatteryNotLow(true) + } + + override fun getJobSchedulerKeyPart(): String? { + return "BATTERY_NOT_LOW" + } + + class Factory : Constraint.Factory { + override fun create(): BatteryNotLowConstraint { + return BatteryNotLowConstraint() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingAndBatteryIsNotLowConstraintObserver.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingAndBatteryIsNotLowConstraintObserver.java new file mode 100644 index 0000000000..921d06fbc9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingAndBatteryIsNotLowConstraintObserver.java @@ -0,0 +1,85 @@ +package org.thoughtcrime.securesms.jobmanager.impl; + +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.jobmanager.ConstraintObserver; + +/** + * Observes the charging state and low battery state of the device and notifies the JobManager system when appropriate. + */ +public class ChargingAndBatteryIsNotLowConstraintObserver implements ConstraintObserver { + + private static final String REASON = Log.tag(ChargingAndBatteryIsNotLowConstraintObserver.class); + private static final int STATUS_BATTERY = 0; + private static final int LOW_BATTERY_LEVEL = 20; + + private final Application application; + + private static volatile boolean charging; + private static volatile boolean batteryNotLow; + + public ChargingAndBatteryIsNotLowConstraintObserver(@NonNull Application application) { + this.application = application; + } + + @Override + public void register(@NonNull Notifier notifier) { + Intent intent = application.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + boolean wasCharging = charging; + boolean wasBatteryNotLow = batteryNotLow; + + charging = isCharging(intent); + batteryNotLow = isBatteryNotLow(intent); + + if ((charging && !wasCharging) || (batteryNotLow && !wasBatteryNotLow)) { + notifier.onConstraintMet(REASON); + } + } + }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + + charging = isCharging(intent); + } + + public static boolean isCharging() { + return charging; + } + + public static boolean isBatteryNotLow() { + return batteryNotLow; + } + + private static boolean isCharging(@Nullable Intent intent) { + if (intent == null) { + return false; + } + + int status = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, STATUS_BATTERY); + return status != STATUS_BATTERY; + } + + private static boolean isBatteryNotLow(@Nullable Intent intent) { + if (intent == null) { + return false; + } + + int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); + + if (level <= 0 || scale <= 0) { + return false; + } + + return ((int) Math.floor(level * 100 / (double) scale)) > LOW_BATTERY_LEVEL; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java index e67f71c7c8..206b6cae34 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java @@ -19,7 +19,7 @@ private ChargingConstraint() { @Override public boolean isMet() { - return ChargingConstraintObserver.isCharging(); + return ChargingAndBatteryIsNotLowConstraintObserver.isCharging(); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraintObserver.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraintObserver.java deleted file mode 100644 index 811b575ee0..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraintObserver.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.thoughtcrime.securesms.jobmanager.impl; - -import android.app.Application; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.jobmanager.ConstraintObserver; - -/** - * Observes the charging state of the device and notifies the JobManager system when appropriate. - */ -public class ChargingConstraintObserver implements ConstraintObserver { - - private static final String REASON = Log.tag(ChargingConstraintObserver.class); - private static final int STATUS_BATTERY = 0; - - private final Application application; - - private static volatile boolean charging; - - public ChargingConstraintObserver(@NonNull Application application) { - this.application = application; - } - - @Override - public void register(@NonNull Notifier notifier) { - Intent intent = application.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - boolean wasCharging = charging; - - charging = isCharging(intent); - - if (charging && !wasCharging) { - notifier.onConstraintMet(REASON); - } - } - }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - - charging = isCharging(intent); - } - - public static boolean isCharging() { - return charging; - } - - private static boolean isCharging(@Nullable Intent intent) { - if (intent == null) { - return false; - } - - int status = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, STATUS_BATTERY); - return status != STATUS_BATTERY; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/WifiConstraint.kt b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/WifiConstraint.kt index 157c4967f9..09bda207fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/WifiConstraint.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/WifiConstraint.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.jobmanager.impl import android.app.Application import android.app.job.JobInfo +import android.content.Context import org.thoughtcrime.securesms.jobmanager.Constraint import org.thoughtcrime.securesms.util.NetworkUtil @@ -17,10 +18,14 @@ class WifiConstraint(private val application: Application) : Constraint { companion object { const val KEY = "WifiConstraint" + + fun isMet(context: Context): Boolean { + return NetworkUtil.isConnectedWifi(context) + } } override fun isMet(): Boolean { - return NetworkUtil.isConnectedWifi(application) + return isMet(application) } override fun getFactoryKey(): String = KEY diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 719bd3d3ad..ea1acb3979 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -10,11 +10,12 @@ import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobMigration; import org.thoughtcrime.securesms.jobmanager.impl.AutoDownloadEmojiConstraint; +import org.thoughtcrime.securesms.jobmanager.impl.BatteryNotLowConstraint; import org.thoughtcrime.securesms.jobmanager.impl.CellServiceConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.ChangeNumberConstraint; import org.thoughtcrime.securesms.jobmanager.impl.ChangeNumberConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.ChargingConstraint; -import org.thoughtcrime.securesms.jobmanager.impl.ChargingConstraintObserver; +import org.thoughtcrime.securesms.jobmanager.impl.ChargingAndBatteryIsNotLowConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint; import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint; @@ -364,6 +365,7 @@ public static Map getJobFactories(@NonNull Application appl public static Map getConstraintFactories(@NonNull Application application) { return new HashMap() {{ put(AutoDownloadEmojiConstraint.KEY, new AutoDownloadEmojiConstraint.Factory(application)); + put(BatteryNotLowConstraint.KEY, new BatteryNotLowConstraint.Factory()); put(ChangeNumberConstraint.KEY, new ChangeNumberConstraint.Factory()); put(ChargingConstraint.KEY, new ChargingConstraint.Factory()); put(DataRestoreConstraint.KEY, new DataRestoreConstraint.Factory()); @@ -379,7 +381,7 @@ public static Map getConstraintFactories(@NonNull Ap public static List getConstraintObservers(@NonNull Application application) { return Arrays.asList(CellServiceConstraintObserver.getInstance(application), - new ChargingConstraintObserver(application), + new ChargingAndBatteryIsNotLowConstraintObserver(application), new NetworkConstraintObserver(application), new SqlCipherMigrationConstraintObserver(), new DecryptionsDrainedConstraintObserver(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt index 13204860d4..6ccf262717 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt @@ -19,7 +19,8 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.events.PartProgressEvent import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.JobLogger.format -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobmanager.impl.BatteryNotLowConstraint +import org.thoughtcrime.securesms.jobmanager.impl.WifiConstraint import org.thoughtcrime.securesms.jobs.protos.RestoreAttachmentJobData import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.MmsException @@ -107,9 +108,9 @@ class RestoreAttachmentJob private constructor( private constructor(messageId: Long, attachmentId: AttachmentId, manual: Boolean, queue: String) : this( Parameters.Builder() .setQueue(queue) - .addConstraint(NetworkConstraint.KEY) + .addConstraint(WifiConstraint.KEY) + .addConstraint(BatteryNotLowConstraint.KEY) .setLifespan(TimeUnit.DAYS.toMillis(30)) - .setMaxAttempts(3) .build(), messageId, attachmentId, @@ -129,7 +130,7 @@ class RestoreAttachmentJob private constructor( } @Throws(Exception::class) - public override fun onRun() { + override fun onRun() { doWork() if (!SignalDatabase.messages.isStory(messageId)) { @@ -170,7 +171,7 @@ class RestoreAttachmentJob private constructor( } else { Log.w(TAG, format(this, "onFailure() messageId: $messageId attachmentId: $attachmentId")) - markFailed(messageId, attachmentId) + markFailed(attachmentId) } } @@ -255,7 +256,7 @@ class RestoreAttachmentJob private constructor( } } catch (e: InvalidAttachmentException) { Log.w(TAG, "Experienced exception while trying to download an attachment.", e) - markFailed(messageId, attachmentId) + markFailed(attachmentId) } catch (e: NonSuccessfulResponseCodeException) { if (SignalStore.backup.backsUpMedia) { if (e.code == 404 && !forceTransitTier && attachment.remoteLocation?.isNotBlank() == true) { @@ -269,30 +270,30 @@ class RestoreAttachmentJob private constructor( } Log.w(TAG, "Experienced exception while trying to download an attachment.", e) - markFailed(messageId, attachmentId) + markFailed(attachmentId) } catch (e: MmsException) { Log.w(TAG, "Experienced exception while trying to download an attachment.", e) - markFailed(messageId, attachmentId) + markFailed(attachmentId) } catch (e: MissingConfigurationException) { Log.w(TAG, "Experienced exception while trying to download an attachment.", e) - markFailed(messageId, attachmentId) + markFailed(attachmentId) } catch (e: InvalidMessageException) { Log.w(TAG, "Experienced an InvalidMessageException while trying to download an attachment.", e) if (e.cause is InvalidMacException) { Log.w(TAG, "Detected an invalid mac. Treating as a permanent failure.") - markPermanentlyFailed(messageId, attachmentId) + markPermanentlyFailed(attachmentId) } else { - markFailed(messageId, attachmentId) + markFailed(attachmentId) } } } - private fun markFailed(messageId: Long, attachmentId: AttachmentId) { - SignalDatabase.attachments.setTransferProgressFailed(attachmentId, messageId) + private fun markFailed(attachmentId: AttachmentId) { + SignalDatabase.attachments.setRestoreTransferState(attachmentId, AttachmentTable.TRANSFER_PROGRESS_FAILED) } - private fun markPermanentlyFailed(messageId: Long, attachmentId: AttachmentId) { - SignalDatabase.attachments.setTransferProgressPermanentFailure(attachmentId, messageId) + private fun markPermanentlyFailed(attachmentId: AttachmentId) { + SignalDatabase.attachments.setRestoreTransferState(attachmentId, AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE) } class Factory : Job.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt index 802f02c219..6ac3058917 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.keyvalue import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.coroutines.flow.Flow import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.backup.RestoreState import org.thoughtcrime.securesms.backup.v2.BackupFrequency @@ -65,7 +66,6 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { var nextBackupTime: Long by longValue(KEY_NEXT_BACKUP_TIME, -1) var lastBackupTime: Long by longValue(KEY_LAST_BACKUP_TIME, -1) var lastMediaSyncTime: Long by longValue(KEY_LAST_BACKUP_MEDIA_SYNC_TIME, -1) - var totalRestorableAttachmentSize: Long by longValue(KEY_TOTAL_RESTORABLE_ATTACHMENT_SIZE, 0) var backupFrequency: BackupFrequency by enumValue(KEY_BACKUP_FREQUENCY, BackupFrequency.MANUAL, BackupFrequency.Serializer) var backupTier: MessageBackupTier? by enumValue(KEY_BACKUP_TIER, null, MessageBackupTier.Serializer) @@ -96,6 +96,11 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { var backupsInitialized: Boolean by booleanValue(KEY_BACKUPS_INITIALIZED, false) + private val totalRestorableAttachmentSizeValue = longValue(KEY_TOTAL_RESTORABLE_ATTACHMENT_SIZE, 0) + var totalRestorableAttachmentSize: Long by totalRestorableAttachmentSizeValue + val totalRestorableAttachmentSizeFlow: Flow + get() = totalRestorableAttachmentSizeValue.toFlow() + val isRestoreInProgress: Boolean get() = totalRestorableAttachmentSize > 0 diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegates.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegates.kt index cf4c68af57..2b2d680e57 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegates.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegates.kt @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.keyvalue import com.squareup.wire.ProtoAdapter +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import org.signal.core.util.LongSerializer import kotlin.reflect.KProperty @@ -45,12 +47,22 @@ internal fun SignalStoreValues.protoValue(key: String, adapter: ProtoAdapter * class to callers and protect the individual implementations as private behind the various extension functions. */ sealed class SignalStoreValueDelegate(private val store: KeyValueStore) { + + private var flow: Lazy> = lazy { MutableStateFlow(getValue(store)) } + operator fun getValue(thisRef: Any?, property: KProperty<*>): T { return getValue(store) } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { setValue(store, value) + if (flow.isInitialized()) { + flow.value.tryEmit(value) + } + } + + fun toFlow(): Flow { + return flow.value } internal abstract fun getValue(values: KeyValueStore): T diff --git a/app/src/main/res/drawable/symbol_backup_error_24.xml b/app/src/main/res/drawable/symbol_backup_error_24.xml new file mode 100644 index 0000000000..de289d084e --- /dev/null +++ b/app/src/main/res/drawable/symbol_backup_error_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 253ed4a59f..87762db825 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7288,7 +7288,23 @@ - Free up %1$s of space to download your media. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + diff --git a/core-util-jvm/src/main/java/org/signal/core/util/ByteExtensions.kt b/core-util-jvm/src/main/java/org/signal/core/util/ByteExtensions.kt index ad7ec621c3..6d372cd126 100644 --- a/core-util-jvm/src/main/java/org/signal/core/util/ByteExtensions.kt +++ b/core-util-jvm/src/main/java/org/signal/core/util/ByteExtensions.kt @@ -5,6 +5,9 @@ package org.signal.core.util +import java.text.NumberFormat +import kotlin.math.min + inline val Long.bytes: ByteSize get() = ByteSize(this) @@ -15,41 +18,41 @@ inline val Long.kibiBytes: ByteSize get() = (this * 1024).bytes inline val Int.kibiBytes: ByteSize - get() = (this * 1024).bytes + get() = (this.toLong() * 1024L).bytes inline val Long.mebiBytes: ByteSize - get() = (this * 1024).kibiBytes + get() = (this * 1024L).kibiBytes inline val Int.mebiBytes: ByteSize - get() = (this * 1024).kibiBytes + get() = (this.toLong() * 1024L).kibiBytes inline val Long.gibiBytes: ByteSize - get() = (this * 1024).mebiBytes + get() = (this * 1024L).mebiBytes inline val Int.gibiBytes: ByteSize - get() = (this * 1024).mebiBytes + get() = (this.toLong() * 1024L).mebiBytes inline val Long.tebiBytes: ByteSize - get() = (this * 1024).gibiBytes + get() = (this * 1024L).gibiBytes inline val Int.tebiBytes: ByteSize - get() = (this * 1024).gibiBytes + get() = (this.toLong() * 1024L).gibiBytes class ByteSize(val bytes: Long) { val inWholeBytes: Long get() = bytes val inWholeKibiBytes: Long - get() = bytes / 1024 + get() = bytes / 1024L val inWholeMebiBytes: Long - get() = inWholeKibiBytes / 1024 + get() = inWholeKibiBytes / 1024L val inWholeGibiBytes: Long - get() = inWholeMebiBytes / 1024 + get() = inWholeMebiBytes / 1024L val inWholeTebiBytes: Long - get() = inWholeGibiBytes / 1024 + get() = inWholeGibiBytes / 1024L val inKibiBytes: Float get() = bytes / 1024f @@ -63,16 +66,35 @@ class ByteSize(val bytes: Long) { val inTebiBytes: Float get() = inGibiBytes / 1024f - fun getLargestNonZeroValue(): Pair { + fun getLargestNonZeroValue(): Pair { return when { - inWholeTebiBytes > 0L -> inWholeTebiBytes to Size.TEBIBYTE - inWholeGibiBytes > 0L -> inWholeGibiBytes to Size.GIBIBYTE - inWholeMebiBytes > 0L -> inWholeMebiBytes to Size.MEBIBYTE - inWholeKibiBytes > 0L -> inWholeKibiBytes to Size.KIBIBYTE - else -> inWholeBytes to Size.BYTE + inWholeTebiBytes > 0L -> inTebiBytes to Size.TEBIBYTE + inWholeGibiBytes > 0L -> inGibiBytes to Size.GIBIBYTE + inWholeMebiBytes > 0L -> inMebiBytes to Size.MEBIBYTE + inWholeKibiBytes > 0L -> inKibiBytes to Size.KIBIBYTE + else -> inWholeBytes.toFloat() to Size.BYTE } } + fun toUnitString(maxPlaces: Int = 1, spaced: Boolean = true): String { + val (size, unit) = getLargestNonZeroValue() + + val formatter = NumberFormat.getInstance().apply { + minimumFractionDigits = 0 + maximumFractionDigits = when (unit) { + Size.BYTE, + Size.KIBIBYTE -> 0 + + Size.MEBIBYTE -> min(1, maxPlaces) + + Size.GIBIBYTE, + Size.TEBIBYTE -> min(2, maxPlaces) + } + } + + return "${formatter.format(size)}${if (spaced) " " else ""}${unit.label}" + } + enum class Size(val label: String) { BYTE("B"), KIBIBYTE("KB"), From 66e6b5506e2a6598f5175e6890128dd4c943a249 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 2 Oct 2024 10:19:34 -0400 Subject: [PATCH 36/53] Improve thread delete performance. --- .../database/MessageSendLogTables.kt | 50 +++++++++++++-- .../securesms/database/MessageTable.kt | 62 ++++++++++++++++--- .../securesms/database/SearchTable.kt | 30 +++++++-- .../securesms/database/ThreadTable.kt | 25 +++----- .../jobs/DeleteAbandonedAttachmentsJob.kt | 50 +++++++++++++++ .../securesms/jobs/JobManagerFactories.java | 1 + 6 files changed, 183 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/DeleteAbandonedAttachmentsJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt index eeeadb7701..658db43b01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt @@ -89,13 +89,16 @@ class MessageSendLogTables constructor(context: Context?, databaseHelper: Signal "CREATE INDEX msl_payload_date_sent_index ON $TABLE_NAME ($DATE_SENT)" ) + const val AFTER_MESSAGE_DELETE_TRIGGER_NAME = "msl_message_delete" + const val AFTER_MESSAGE_DELETE_TRIGGER = """ + CREATE TRIGGER $AFTER_MESSAGE_DELETE_TRIGGER_NAME AFTER DELETE ON ${MessageTable.TABLE_NAME} + BEGIN + DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MslMessageTable.PAYLOAD_ID} FROM ${MslMessageTable.TABLE_NAME} WHERE ${MslMessageTable.MESSAGE_ID} = old.${MessageTable.ID}); + END + """ + val CREATE_TRIGGERS = arrayOf( - """ - CREATE TRIGGER msl_message_delete AFTER DELETE ON ${MessageTable.TABLE_NAME} - BEGIN - DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MslMessageTable.PAYLOAD_ID} FROM ${MslMessageTable.TABLE_NAME} WHERE ${MslMessageTable.MESSAGE_ID} = old.${MessageTable.ID}); - END - """, + AFTER_MESSAGE_DELETE_TRIGGER, """ CREATE TRIGGER msl_attachment_delete AFTER DELETE ON ${AttachmentTable.TABLE_NAME} BEGIN @@ -381,6 +384,41 @@ class MessageSendLogTables constructor(context: Context?, databaseHelper: Signal db.delete(MslPayloadTable.TABLE_NAME, query, args) } + /** + * Drop the trigger for updating the [MslPayloadTable] on message deletes. Should only be used for expected large deletes. + * The caller must be in a transaction and called with a matching [restoreAfterMessageDeleteTrigger] before the transaction + * completes. + * + * Note: The caller is not responsible for performing the missing trigger operations and they will be performed in + * [restoreAfterMessageDeleteTrigger]. + */ + fun dropAfterMessageDeleteTrigger() { + check(SignalDatabase.inTransaction) + writableDatabase.execSQL("DROP TRIGGER IF EXISTS ${MslPayloadTable.AFTER_MESSAGE_DELETE_TRIGGER_NAME}") + } + + /** + * Restore the trigger for updating the [MslPayloadTable] on message deletes. Must only be called within the same transaction after calling + * [dropAfterMessageDeleteTrigger]. + */ + fun restoreAfterMessageDeleteTrigger() { + check(SignalDatabase.inTransaction) + + val restoreDeleteMessagesOperation = """ + DELETE FROM ${MslPayloadTable.TABLE_NAME} + WHERE ${MslPayloadTable.TABLE_NAME}.${MslPayloadTable.ID} IN ( + SELECT ${MslMessageTable.TABLE_NAME}.${MslMessageTable.PAYLOAD_ID} + FROM ${MslMessageTable.TABLE_NAME} + WHERE ${MslMessageTable.TABLE_NAME}.${MslMessageTable.MESSAGE_ID} NOT IN ( + SELECT ${MessageTable.TABLE_NAME}.${MessageTable.ID} FROM ${MessageTable.TABLE_NAME} + ) + ) + """ + + writableDatabase.execSQL(restoreDeleteMessagesOperation) + writableDatabase.execSQL(MslPayloadTable.AFTER_MESSAGE_DELETE_TRIGGER) + } + override fun remapRecipient(oldRecipientId: RecipientId, newRecipientId: RecipientId) { val values = ContentValues().apply { put(MslRecipientTable.RECIPIENT_ID, newRecipientId.serialize()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index e3c0b8ac7a..cf32a949fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -77,7 +77,6 @@ import org.thoughtcrime.securesms.database.SignalDatabase.Companion.distribution import org.thoughtcrime.securesms.database.SignalDatabase.Companion.groupReceipts import org.thoughtcrime.securesms.database.SignalDatabase.Companion.groups import org.thoughtcrime.securesms.database.SignalDatabase.Companion.mentions -import org.thoughtcrime.securesms.database.SignalDatabase.Companion.messageLog import org.thoughtcrime.securesms.database.SignalDatabase.Companion.messages import org.thoughtcrime.securesms.database.SignalDatabase.Companion.reactions import org.thoughtcrime.securesms.database.SignalDatabase.Companion.recipients @@ -2099,7 +2098,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat deletedAttachments = attachments.deleteAttachmentsForMessage(messageId) mentions.deleteMentionsForMessage(messageId) - messageLog.deleteAllRelatedToMessage(messageId) + SignalDatabase.messageLog.deleteAllRelatedToMessage(messageId) reactions.deleteReactions(MessageId(messageId)) deleteGroupStoryReplies(messageId) disassociateStoryQuotes(messageId) @@ -3456,17 +3455,62 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun deleteMessagesInThreadBeforeDate(threadId: Long, date: Long, inclusive: Boolean): Int { val condition = if (inclusive) "<=" else "<" + val extraWhere = "AND ${TABLE_NAME}.$DATE_RECEIVED $condition $date" - return writableDatabase - .delete(TABLE_NAME) - .where("$THREAD_ID = ? AND $DATE_RECEIVED $condition $date", threadId) - .run() + return deleteMessagesInThread(listOf(threadId), extraWhere) + } + + fun deleteMessagesInThread(threadIds: Collection, extraWhere: String = ""): Int { + var deletedCount = 0 + + writableDatabase.withinTransaction { db -> + SignalDatabase.messageSearch.dropAfterMessageDeleteTrigger() + SignalDatabase.messageLog.dropAfterMessageDeleteTrigger() + + for (threadId in threadIds) { + val subSelect = "SELECT ${TABLE_NAME}.$ID FROM $TABLE_NAME WHERE ${TABLE_NAME}.$THREAD_ID = $threadId $extraWhere" + + // Bulk deleting FK tables for large message delete efficiency + db.delete(StorySendTable.TABLE_NAME) + .where("${StorySendTable.TABLE_NAME}.${StorySendTable.MESSAGE_ID} IN ($subSelect)") + .run() + + db.delete(ReactionTable.TABLE_NAME) + .where("${ReactionTable.TABLE_NAME}.${ReactionTable.MESSAGE_ID} IN ($subSelect)") + .run() + + db.delete(CallTable.TABLE_NAME) + .where("${CallTable.TABLE_NAME}.${CallTable.MESSAGE_ID} IN ($subSelect)") + .run() + + // Must delete rows from FTS table before deleting from main table due to FTS requirement when deleting by rowid + db.delete(SearchTable.FTS_TABLE_NAME) + .where("${SearchTable.FTS_TABLE_NAME}.${SearchTable.ID} IN ($subSelect)") + .run() + + // Actually delete messages + deletedCount += db.delete(TABLE_NAME) + .where("$THREAD_ID = ? $extraWhere", threadId) + .run() + } + + SignalDatabase.messageSearch.restoreAfterMessageDeleteTrigger() + SignalDatabase.messageLog.restoreAfterMessageDeleteTrigger() + } + + return deletedCount } - fun deleteAbandonedMessages(): Int { + fun deleteAbandonedMessages(threadId: Long? = null): Int { + val where = if (threadId == null) { + "$THREAD_ID NOT IN (SELECT ${ThreadTable.TABLE_NAME}.${ThreadTable.ID} FROM ${ThreadTable.TABLE_NAME} WHERE ${ThreadTable.ACTIVE} = 1)" + } else { + "$THREAD_ID = $threadId AND (SELECT ${ThreadTable.TABLE_NAME}.${ThreadTable.ACTIVE} FROM ${ThreadTable.TABLE_NAME} WHERE ${ThreadTable.TABLE_NAME}.${ThreadTable.ID} = $threadId) != 1" + } + val deletes = writableDatabase .delete(TABLE_NAME) - .where("$THREAD_ID NOT IN (SELECT _id FROM ${ThreadTable.TABLE_NAME} WHERE ${ThreadTable.ACTIVE} = 1)") + .where(where) .run() if (deletes > 0) { @@ -3494,7 +3538,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat .readToSingleLongOrNull() } - fun deleteMessages(messagesToDelete: List): List { + fun deleteMessages(messagesToDelete: List): List { val threads = mutableSetOf() val unhandled = mutableListOf() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt index 7194d90b7c..dc9a0b1769 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt @@ -46,6 +46,11 @@ class SearchTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa private const val TRIGGER_AFTER_INSERT = "message_ai" private const val TRIGGER_AFTER_DELETE = "message_ad" private const val TRIGGER_AFTER_UPDATE = "message_au" + private const val AFTER_MESSAGE_DELETE_TRIGGER = """ + CREATE TRIGGER $TRIGGER_AFTER_DELETE AFTER DELETE ON ${MessageTable.TABLE_NAME} BEGIN + INSERT INTO $FTS_TABLE_NAME($FTS_TABLE_NAME, $ID, $BODY, $THREAD_ID) VALUES('delete', old.${MessageTable.ID}, old.${MessageTable.BODY}, old.${MessageTable.THREAD_ID}); + END; + """ @Language("sql") val CREATE_TRIGGERS = arrayOf( @@ -54,11 +59,7 @@ class SearchTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa INSERT INTO $FTS_TABLE_NAME($ID, $BODY, $THREAD_ID) VALUES (new.${MessageTable.ID}, new.${MessageTable.BODY}, new.${MessageTable.THREAD_ID}); END; """, - """ - CREATE TRIGGER $TRIGGER_AFTER_DELETE AFTER DELETE ON ${MessageTable.TABLE_NAME} BEGIN - INSERT INTO $FTS_TABLE_NAME($FTS_TABLE_NAME, $ID, $BODY, $THREAD_ID) VALUES('delete', old.${MessageTable.ID}, old.${MessageTable.BODY}, old.${MessageTable.THREAD_ID}); - END; - """, + AFTER_MESSAGE_DELETE_TRIGGER, """ CREATE TRIGGER $TRIGGER_AFTER_UPDATE AFTER UPDATE ON ${MessageTable.TABLE_NAME} BEGIN INSERT INTO $FTS_TABLE_NAME($FTS_TABLE_NAME, $ID, $BODY, $THREAD_ID) VALUES('delete', old.${MessageTable.ID}, old.${MessageTable.BODY}, old.${MessageTable.THREAD_ID}); @@ -137,6 +138,25 @@ class SearchTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa } } + /** + * Drop the trigger for updating the search table on deletes. Should only be used for expected large deletes. + * The caller must be in a transaction, update the search table manually before message deletes because of FTS indexing + * requirements, and be called with a matching [restoreAfterMessageDeleteTrigger] before the transaction completes. + */ + fun dropAfterMessageDeleteTrigger() { + check(SignalDatabase.inTransaction) + writableDatabase.execSQL("DROP TRIGGER IF EXISTS $TRIGGER_AFTER_DELETE") + } + + /** + * Restore the trigger for updating the search table on message deletes. Must only be called within the same transaction + * after calling [dropAfterMessageDeleteTrigger] and performing the dropped trigger's actions manually. + */ + fun restoreAfterMessageDeleteTrigger() { + check(SignalDatabase.inTransaction) + writableDatabase.execSQL(AFTER_MESSAGE_DELETE_TRIGGER) + } + /** * Re-adds every message to the index. It's fine to insert the same message twice; the table will naturally de-dupe. * diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt index a1c1f6aba1..0e2593c4f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -33,7 +33,6 @@ import org.signal.libsignal.zkgroup.groups.GroupMasterKey import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter import org.thoughtcrime.securesms.database.MessageTable.MarkedMessageInfo import org.thoughtcrime.securesms.database.SignalDatabase.Companion.attachments -import org.thoughtcrime.securesms.database.SignalDatabase.Companion.calls import org.thoughtcrime.securesms.database.SignalDatabase.Companion.drafts import org.thoughtcrime.securesms.database.SignalDatabase.Companion.groupReceipts import org.thoughtcrime.securesms.database.SignalDatabase.Companion.mentions @@ -50,6 +49,7 @@ import org.thoughtcrime.securesms.database.model.serialize import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.groups.BadGroupIdException import org.thoughtcrime.securesms.groups.GroupId +import org.thoughtcrime.securesms.jobs.DeleteAbandonedAttachmentsJob import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSyncJob import org.thoughtcrime.securesms.jobs.OptimizeMessageSearchIndexJob import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -345,17 +345,14 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa } } - val deletes = writableDatabase.withinTransaction { + writableDatabase.withinTransaction { messages.deleteAbandonedMessages() attachments.trimAllAbandonedAttachments() groupReceipts.deleteAbandonedRows() mentions.deleteAbandonedMentions() - return@withinTransaction attachments.deleteAbandonedAttachmentFiles() } - if (deletes > 0) { - Log.i(TAG, "Trim all threads caused $deletes attachments to be deleted.") - } + DeleteAbandonedAttachmentsJob.enqueue() if (syncThreadTrimDeletes && threadTrimsToSync.isNotEmpty()) { MultiDeviceDeleteSyncJob.enqueueThreadDeletes(threadTrimsToSync, isFullDelete = false) @@ -378,18 +375,15 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa } var threadTrimToSync: ThreadDeleteSyncInfo? = null - val deletes = writableDatabase.withinTransaction { + writableDatabase.withinTransaction { threadTrimToSync = trimThreadInternal(threadId, syncThreadTrimDeletes, length, trimBeforeDate, inclusive) - messages.deleteAbandonedMessages() + messages.deleteAbandonedMessages(threadId) attachments.trimAllAbandonedAttachments() groupReceipts.deleteAbandonedRows() mentions.deleteAbandonedMentions() - return@withinTransaction attachments.deleteAbandonedAttachmentFiles() } - if (deletes > 0) { - Log.i(TAG, "Trim thread $threadId caused $deletes attachments to be deleted.") - } + DeleteAbandonedAttachmentsJob.enqueue() if (syncThreadTrimDeletes && threadTrimToSync != null) { MultiDeviceDeleteSyncJob.enqueueThreadDeletes(listOf(threadTrimToSync!!), isFullDelete = false) @@ -1164,12 +1158,11 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa db.deactivateThread(query) } - messages.deleteAbandonedMessages() + messages.deleteMessagesInThread(selectedConversations) attachments.trimAllAbandonedAttachments() groupReceipts.deleteAbandonedRows() mentions.deleteAbandonedMentions() drafts.clearDrafts(selectedConversations) - attachments.deleteAbandonedAttachmentFiles() synchronized(threadIdCache) { for (recipientId in recipientIds) { threadIdCache.remove(recipientId) @@ -1177,6 +1170,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa } } + DeleteAbandonedAttachmentsJob.enqueue() + if (syncThreadDeletes) { MultiDeviceDeleteSyncJob.enqueueThreadDeletes(addressableMessages, isFullDelete = true) } @@ -1199,7 +1194,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa messages.deleteAllThreads() drafts.clearAllDrafts() db.deactivateThreads() - calls.deleteAllCalls() + SignalDatabase.calls.deleteAllCalls() synchronized(threadIdCache) { threadIdCache.clear() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/DeleteAbandonedAttachmentsJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/DeleteAbandonedAttachmentsJob.kt new file mode 100644 index 0000000000..7d090a9dd0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/DeleteAbandonedAttachmentsJob.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.attachments +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import kotlin.time.Duration.Companion.days + +/** + * Deletes attachment files that are no longer referenced in the database. + */ +class DeleteAbandonedAttachmentsJob private constructor(parameters: Parameters) : Job(parameters) { + + companion object { + private val TAG = Log.tag(DeleteAbandonedAttachmentsJob::class) + const val KEY = "DeleteAbandonedAttachmentsJob" + + fun enqueue() { + AppDependencies.jobManager.add(DeleteAbandonedAttachmentsJob()) + } + } + + constructor() : this( + parameters = Parameters.Builder() + .setMaxInstancesForFactory(2) + .setLifespan(1.days.inWholeMilliseconds) + .build() + ) + + override fun serialize(): ByteArray? = null + override fun getFactoryKey(): String = KEY + override fun onFailure() = Unit + + override fun run(): Result { + val deletes = attachments.deleteAbandonedAttachmentFiles() + Log.i(TAG, "Deleted $deletes abandoned attachments.") + return Result.success() + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): DeleteAbandonedAttachmentsJob { + return DeleteAbandonedAttachmentsJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index ea1acb3979..ff25fb735f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -140,6 +140,7 @@ public static Map getJobFactories(@NonNull Application appl put(ConversationShortcutUpdateJob.KEY, new ConversationShortcutUpdateJob.Factory()); put(CopyAttachmentToArchiveJob.KEY, new CopyAttachmentToArchiveJob.Factory()); put(CreateReleaseChannelJob.KEY, new CreateReleaseChannelJob.Factory()); + put(DeleteAbandonedAttachmentsJob.KEY, new DeleteAbandonedAttachmentsJob.Factory()); put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory()); put(DonationReceiptRedemptionJob.KEY, new DonationReceiptRedemptionJob.Factory()); put(DownloadLatestEmojiDataJob.KEY, new DownloadLatestEmojiDataJob.Factory()); From 91115fa0bd3535cd2a7773be1b4f650df903c5cd Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 2 Oct 2024 15:23:15 -0300 Subject: [PATCH 37/53] Allow users to cancel during pending donation. --- ...outFlowActivityTest__RecurringDonations.kt | 48 +++++++++++++++++++ .../donate/DonateToSignalFragment.kt | 2 +- .../donate/DonateToSignalState.kt | 3 +- .../donate/DonateToSignalViewModel.kt | 2 + .../manage/ManageDonationsFragment.kt | 4 +- 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivityTest__RecurringDonations.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivityTest__RecurringDonations.kt index b3d233f4ec..0b75e87c36 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivityTest__RecurringDonations.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivityTest__RecurringDonations.kt @@ -5,6 +5,7 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled import androidx.test.espresso.matcher.ViewMatchers.isSelected import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText @@ -79,6 +80,16 @@ class CheckoutFlowActivityTest__RecurringDonations { onView(withText(R.string.StripePaymentInProgressFragment__cancelling)).check(matches(isDisplayed())) } + @Test + fun givenAPendingRecurringDonation_whenILoadScreen_thenIExpectDisabledUpgradeButton() { + initialiseConfigurationResponse() + initialisePendingSubscription() + + ActivityScenario.launch(intent) + onView(withText(R.string.SubscribeFragment__update_subscription)).check(matches(isDisplayed())) + onView(withText(R.string.SubscribeFragment__update_subscription)).check(matches(isNotEnabled())) + } + private fun initialiseConfigurationResponse() { InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers( Get("/v1/subscription/configuration") { @@ -130,4 +141,41 @@ class CheckoutFlowActivityTest__RecurringDonations { } ) } + + private fun initialisePendingSubscription() { + val currency = Currency.getInstance("USD") + val subscriber = InAppPaymentSubscriberRecord( + subscriberId = SubscriberId.generate(), + currency = currency, + type = InAppPaymentSubscriberRecord.Type.DONATION, + requiresCancel = false, + paymentMethodType = InAppPaymentData.PaymentMethodType.CARD + ) + + InAppPaymentsRepository.setSubscriber(subscriber) + SignalStore.inAppPayments.setSubscriberCurrency(currency, subscriber.type) + + InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers( + Get("/v1/subscription/${subscriber.subscriberId.serialize()}") { + MockResponse().success( + ActiveSubscription( + ActiveSubscription.Subscription( + 200, + currency.currencyCode, + BigDecimal.ONE, + System.currentTimeMillis().milliseconds.inWholeSeconds + 30.days.inWholeSeconds, + false, + System.currentTimeMillis().milliseconds.inWholeSeconds + 30.days.inWholeSeconds, + false, + "incomplete", + "STRIPE", + "CARD", + false + ), + null + ) + ) + } + ) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt index 65be79c59a..a0e4146443 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt @@ -277,7 +277,7 @@ class DonateToSignalFragment : space(20.dp) - if (state.inAppPaymentType == InAppPaymentType.RECURRING_DONATION && state.monthlyDonationState.isSubscriptionActive) { + if (state.inAppPaymentType == InAppPaymentType.RECURRING_DONATION && (state.monthlyDonationState.isSubscriptionActive || state.monthlyDonationState.isSubscriptionInProgress)) { primaryButton( text = DSLSettingsText.from(R.string.SubscribeFragment__update_subscription), isEnabled = state.canUpdate, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt index ebc49aee02..4019515aea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt @@ -82,7 +82,7 @@ data class DonateToSignalState( val canUpdate: Boolean get() = when (inAppPaymentType) { InAppPaymentType.ONE_TIME_DONATION -> false - InAppPaymentType.RECURRING_DONATION -> areFieldsEnabled && monthlyDonationState.isSelectionValid + InAppPaymentType.RECURRING_DONATION -> areFieldsEnabled && monthlyDonationState.isSelectionValid && monthlyDonationState.isSubscriptionActive && !monthlyDonationState.transactionState.isInProgress else -> error("This flow does not support $inAppPaymentType") } @@ -124,6 +124,7 @@ data class DonateToSignalState( val transactionState: TransactionState = TransactionState() ) { val isSubscriptionActive: Boolean = _activeSubscription?.isActive == true + val isSubscriptionInProgress: Boolean = _activeSubscription?.isInProgress == true val activeLevel: Int? = _activeSubscription?.activeSubscription?.level val activeSubscription: ActiveSubscription.Subscription? = _activeSubscription?.activeSubscription val isActiveSubscriptionEnding: Boolean = _activeSubscription?.isActive == true && _activeSubscription.activeSubscription.willCancelAtPeriodEnd() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt index 3c9324973a..1311bcf4aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt @@ -119,6 +119,8 @@ class DonateToSignalViewModel( fun updateSubscription() { val snapshot = store.state + + check(snapshot.canUpdate) if (snapshot.areFieldsEnabled) { actionDisposable += createInAppPayment(snapshot).subscribeBy { _actions.onNext(DonateToSignalAction.UpdateSubscription(it, snapshot.isUpdateLongRunning)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt index 55a7c69522..c3cf911d5c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt @@ -278,9 +278,7 @@ class ManageDonationsFragment : activeSubscription = activeSubscription, subscriberRequiresCancel = state.subscriberRequiresCancel, onRowClick = { - if (it != ManageDonationsState.RedemptionState.IN_PROGRESS) { - launcher.launch(InAppPaymentType.RECURRING_DONATION) - } + launcher.launch(InAppPaymentType.RECURRING_DONATION) }, onPendingClick = { displayPendingDialog(it) From a02c2e393f2a5179eb19ed9cdff9a864e01298b3 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 2 Oct 2024 17:04:00 -0400 Subject: [PATCH 38/53] Fix stories being restored in Signal chat. --- .../backup/v2/database/MessageTableArchiveExtensions.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt index 7aa2e6fe0d..459830137d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt @@ -62,6 +62,7 @@ fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, medi OR (${MessageTable.EXPIRES_IN} > 0 AND (${MessageTable.EXPIRE_STARTED} + ${MessageTable.EXPIRES_IN}) > $backupTime + ${TimeUnit.DAYS.toMillis(1)}) ) + AND ${MessageTable.STORY_TYPE} = 0 """ ) .orderBy("${MessageTable.DATE_RECEIVED} ASC") From 754dc63f7008704f1b45f3b8ae06b68561710e35 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 2 Oct 2024 17:06:24 -0400 Subject: [PATCH 39/53] Add simple backup/restore tools in backup playground. --- .../InternalBackupPlaygroundFragment.kt | 38 ++++++++++++------- .../InternalBackupPlaygroundViewModel.kt | 11 +++++- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundFragment.kt index 506edd4e8d..a92d407f96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundFragment.kt @@ -55,12 +55,14 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.signal.core.ui.Buttons import org.signal.core.ui.Dividers import org.signal.core.ui.Snackbars @@ -121,6 +123,7 @@ class InternalBackupPlaygroundFragment : ComposeFragment() { @Composable override fun FragmentContent() { + val context = LocalContext.current val state by viewModel.state val mediaState by viewModel.mediaState @@ -172,7 +175,13 @@ class InternalBackupPlaygroundFragment : ComposeFragment() { validateFileLauncher.launch(intent) }, onTriggerBackupJobClicked = { viewModel.triggerBackupJob() }, - onRestoreFromRemoteClicked = { viewModel.restoreFromRemote() } + onWipeDataAndRestoreClicked = { + MaterialAlertDialogBuilder(context) + .setTitle("Are you sure?") + .setMessage("This will delete all of your chats! Make sure you've finished a backup first, we don't check for you. Only do this on a test device!") + .setPositiveButton("Wipe and restore") { _, _ -> viewModel.wipeAllDataAndRestoreFromRemote() } + .show() + } ) }, mediaContent = { snackbarHostState -> @@ -265,7 +274,7 @@ fun Screen( onUploadToRemoteClicked: () -> Unit = {}, onCheckRemoteBackupStateClicked: () -> Unit = {}, onTriggerBackupJobClicked: () -> Unit = {}, - onRestoreFromRemoteClicked: () -> Unit = {} + onWipeDataAndRestoreClicked: () -> Unit = {} ) { val scrollState = rememberScrollState() @@ -278,6 +287,20 @@ fun Screen( .verticalScroll(scrollState) .padding(16.dp) ) { + Buttons.LargePrimary( + onClick = onTriggerBackupJobClicked + ) { + Text("Enqueue remote backup") + } + + Buttons.LargeTonal( + onClick = onWipeDataAndRestoreClicked + ) { + Text("Wipe data and restore") + } + + Dividers.Default() + Row( verticalAlignment = Alignment.CenterVertically ) { @@ -305,13 +328,6 @@ fun Screen( Text("Export to backup directory") } - Buttons.LargePrimary( - onClick = onTriggerBackupJobClicked, - enabled = !state.backupState.inProgress - ) { - Text("Trigger Backup Job") - } - Dividers.Default() Buttons.LargeTonal( @@ -407,10 +423,6 @@ fun Screen( Spacer(modifier = Modifier.height(8.dp)) - Buttons.LargePrimary(onClick = onRestoreFromRemoteClicked) { - Text("Restore from remote") - } - when (state.uploadState) { BackupUploadState.NONE -> { StateLabel("") diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt index 882c595dd1..09f71c6b28 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt @@ -18,6 +18,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.subscribeBy import io.reactivex.rxjava3.schedulers.Schedulers +import org.signal.core.util.concurrent.SignalExecutors import org.signal.libsignal.zkgroup.profiles.ProfileKey import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.DatabaseAttachment @@ -208,7 +209,15 @@ class InternalBackupPlaygroundViewModel : ViewModel() { } } - fun restoreFromRemote() { + fun wipeAllDataAndRestoreFromRemote() { + SignalExecutors.BOUNDED_IO.execute { + SignalDatabase.threads.deleteAllConversations() + AppDependencies.messageNotifier.updateNotification(AppDependencies.application) + restoreFromRemote() + } + } + + private fun restoreFromRemote() { _state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS) disposables += Single.fromCallable { From fbb72448bfc7a4195fef81b594d0bec40d0ae23b Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 2 Oct 2024 15:00:22 -0400 Subject: [PATCH 40/53] Use URLEncoder for attachment path. --- .../signalservice/internal/push/PushServiceSocket.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 251cd150e4..50c73c6b53 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -131,14 +131,14 @@ import org.whispersystems.signalservice.internal.configuration.SignalUrl; import org.whispersystems.signalservice.internal.crypto.AttachmentDigest; import org.whispersystems.signalservice.internal.push.exceptions.CaptchaRejectedException; -import org.whispersystems.signalservice.internal.push.exceptions.InAppPaymentProcessorError; -import org.whispersystems.signalservice.internal.push.exceptions.InAppPaymentReceiptCredentialError; import org.whispersystems.signalservice.internal.push.exceptions.ForbiddenException; import org.whispersystems.signalservice.internal.push.exceptions.GroupExistsException; import org.whispersystems.signalservice.internal.push.exceptions.GroupMismatchedDevicesException; import org.whispersystems.signalservice.internal.push.exceptions.GroupNotFoundException; import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException; import org.whispersystems.signalservice.internal.push.exceptions.GroupStaleDevicesException; +import org.whispersystems.signalservice.internal.push.exceptions.InAppPaymentProcessorError; +import org.whispersystems.signalservice.internal.push.exceptions.InAppPaymentReceiptCredentialError; import org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException; import org.whispersystems.signalservice.internal.push.exceptions.MismatchedDevicesException; import org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException; @@ -1007,7 +1007,9 @@ public void retrieveAttachment(int cdnNumber, Map headers, Signa if (cdnPath instanceof SignalServiceAttachmentRemoteId.V2) { path = String.format(Locale.US, ATTACHMENT_ID_DOWNLOAD_PATH, ((SignalServiceAttachmentRemoteId.V2) cdnPath).getCdnId()); } else if (cdnPath instanceof SignalServiceAttachmentRemoteId.V4) { - path = String.format(Locale.US, ATTACHMENT_KEY_DOWNLOAD_PATH, ((SignalServiceAttachmentRemoteId.V4) cdnPath).getCdnKey()); + //noinspection CharsetObjectCanBeUsed + String urlEncodedKey = URLEncoder.encode(((SignalServiceAttachmentRemoteId.V4) cdnPath).getCdnKey(), StandardCharsets.UTF_8.name()); + path = String.format(Locale.US, ATTACHMENT_KEY_DOWNLOAD_PATH, urlEncodedKey); } else if (cdnPath instanceof SignalServiceAttachmentRemoteId.Backup) { SignalServiceAttachmentRemoteId.Backup backupCdnId = (SignalServiceAttachmentRemoteId.Backup) cdnPath; path = String.format(Locale.US, ARCHIVE_MEDIA_DOWNLOAD_PATH, backupCdnId.getBackupDir(), backupCdnId.getMediaDir(), backupCdnId.getMediaId()); From 8b7dcd51aec8af9bb3dcd3ee86c94b72b1de27e5 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 2 Oct 2024 22:03:02 -0400 Subject: [PATCH 41/53] Reset recipient cache after a backup restore. --- .../org/thoughtcrime/securesms/backup/v2/BackupRepository.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index 08735439d4..ce346fd9da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -456,6 +456,9 @@ object BackupRepository { } } + AppDependencies.recipientCache.clear() + AppDependencies.recipientCache.warmUp() + Log.d(TAG, "import() ${eventTimer.stop().summary}") val groupJobs = SignalDatabase.groups.getGroups().use { groups -> From 321c344e77dc1c246992a95f8bfee6cb87e57b8c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 2 Oct 2024 22:33:04 -0400 Subject: [PATCH 42/53] Ensure all tables are cleaned up the same before backup import. --- .../thoughtcrime/securesms/backup/v2/BackupRepository.kt | 5 +++-- .../backup/v2/database/AttachmentTableArchiveExtensions.kt | 2 ++ .../backup/v2/database/CallLinkTableArchiveExtensions.kt | 7 +++++++ .../backup/v2/database/CallTableArchiveExtensions.kt | 7 +++++++ .../backup/v2/database/ChatColorsTableArchiveExtensions.kt | 2 ++ .../v2/database/DistributionListTablesArchiveExtensions.kt | 3 +++ .../v2/database/InAppPaymentTableArchiveExtensions.kt | 2 ++ .../backup/v2/database/ReactionTableArchiveExtensions.kt | 2 ++ .../backup/v2/database/StickerTableArchiveExtensions.kt | 2 ++ 9 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index ce346fd9da..5ae22165b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackup import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackupRestore import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter import org.thoughtcrime.securesms.backup.v2.processor.AccountDataArchiveProcessor @@ -386,8 +387,6 @@ object BackupRepository { return ImportResult.Failure } - // Note: Without a transaction, bad imports could lead to lost data. But because we have a transaction, - // writes from other threads are blocked. This is something to think more about. SignalDatabase.rawDatabase.withinTransaction { SignalDatabase.recipients.clearAllDataForBackupRestore() SignalDatabase.distributionLists.clearAllDataForBackupRestore() @@ -398,6 +397,8 @@ object BackupRepository { SignalDatabase.reactions.clearAllDataForBackupRestore() SignalDatabase.inAppPayments.clearAllDataForBackupRestore() SignalDatabase.chatColors.clearAllDataForBackupRestore() + SignalDatabase.calls.clearAllDataForBackup() + SignalDatabase.callLinks.clearAllDataForBackup() // Add back self after clearing data val selfId: RecipientId = SignalDatabase.recipients.getAndPossiblyMerge(selfData.aci, selfData.pni, selfData.e164, pniVerified = true, changeSelf = true) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableArchiveExtensions.kt index 3f62008b3e..52394176ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableArchiveExtensions.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.backup.v2.database +import org.signal.core.util.SqlUtil import org.signal.core.util.deleteAll import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.AttachmentId @@ -12,6 +13,7 @@ import org.thoughtcrime.securesms.database.AttachmentTable fun AttachmentTable.clearAllDataForBackupRestore() { writableDatabase.deleteAll(AttachmentTable.TABLE_NAME) + SqlUtil.resetAutoIncrementValue(writableDatabase, AttachmentTable.TABLE_NAME) } fun AttachmentTable.restoreWallpaperAttachment(attachment: Attachment): AttachmentId? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt index 719f798996..0ed2e934a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallLinkTableArchiveExtensions.kt @@ -5,6 +5,8 @@ package org.thoughtcrime.securesms.backup.v2.database +import org.signal.core.util.SqlUtil +import org.signal.core.util.deleteAll import org.signal.core.util.select import org.thoughtcrime.securesms.database.CallLinkTable @@ -16,3 +18,8 @@ fun CallLinkTable.getCallLinksForBackup(): CallLinkArchiveExporter { return CallLinkArchiveExporter(cursor) } + +fun CallLinkTable.clearAllDataForBackup() { + writableDatabase.deleteAll(CallLinkTable.TABLE_NAME) + SqlUtil.resetAutoIncrementValue(writableDatabase, CallLinkTable.TABLE_NAME) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt index 1c780545d6..5ed0cfe8b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableArchiveExtensions.kt @@ -5,6 +5,8 @@ package org.thoughtcrime.securesms.backup.v2.database +import org.signal.core.util.SqlUtil +import org.signal.core.util.deleteAll import org.signal.core.util.select import org.thoughtcrime.securesms.database.CallTable @@ -17,3 +19,8 @@ fun CallTable.getAdhocCallsForBackup(): AdHocCallArchiveExporter { .run() ) } + +fun CallTable.clearAllDataForBackup() { + writableDatabase.deleteAll(CallTable.TABLE_NAME) + SqlUtil.resetAutoIncrementValue(writableDatabase, CallTable.TABLE_NAME) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableArchiveExtensions.kt index cdaeb387c2..e4458492c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatColorsTableArchiveExtensions.kt @@ -5,9 +5,11 @@ package org.thoughtcrime.securesms.backup.v2.database +import org.signal.core.util.SqlUtil import org.signal.core.util.deleteAll import org.thoughtcrime.securesms.database.ChatColorsTable fun ChatColorsTable.clearAllDataForBackupRestore() { writableDatabase.deleteAll(ChatColorsTable.TABLE_NAME) + SqlUtil.resetAutoIncrementValue(writableDatabase, ChatColorsTable.TABLE_NAME) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt index db5232ec1e..ec13aa231f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesArchiveExtensions.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.backup.v2.database +import org.signal.core.util.SqlUtil import org.signal.core.util.deleteAll import org.signal.core.util.select import org.signal.core.util.withinTransaction @@ -42,4 +43,6 @@ fun DistributionListTables.getMembersForBackup(id: DistributionListId): List Date: Thu, 3 Oct 2024 10:06:04 -0300 Subject: [PATCH 43/53] Add biometric prompt to reveal backup key from settings and other fixes. --- .../MessageBackupsCheckoutActivity.kt | 13 +- .../MessageBackupsFlowFragment.kt | 42 +++- .../MessageBackupsFlowViewModel.kt | 6 +- .../settings/app/AppSettingsFragment.kt | 2 +- .../app/backups/BackupsSettingsFragment.kt | 5 +- .../remote/BackupKeyDisplayFragment.kt | 28 +++ .../remote/RemoteBackupsSettingsFragment.kt | 97 ++++++-- .../remote/RemoteBackupsSettingsViewModel.kt | 2 +- .../type/BackupsTypeSettingsFragment.kt | 227 ------------------ .../backups/type/BackupsTypeSettingsState.kt | 17 -- .../type/BackupsTypeSettingsViewModel.kt | 46 ---- .../app/chats/ChatsSettingsFragment.kt | 5 +- .../UpgradeToEnableOptimizedStorageSheet.kt | 5 +- .../MessageBackupsCheckoutLauncher.kt | 3 +- app/src/main/res/navigation/app_settings.xml | 10 +- .../app_settings_with_change_number.xml | 10 +- app/src/main/res/values/strings.xml | 7 + 17 files changed, 188 insertions(+), 337 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsFragment.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsState.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsViewModel.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivity.kt index 13ef1a4e52..f2131345fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutActivity.kt @@ -8,8 +8,10 @@ package org.thoughtcrime.securesms.backup.v2.ui.subscription import android.content.Context import android.content.Intent import androidx.activity.result.contract.ActivityResultContract +import androidx.core.content.IntentCompat import androidx.fragment.app.Fragment import org.signal.core.util.getParcelableExtraCompat +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.components.FragmentWrapperActivity import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity.Result @@ -20,15 +22,18 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.donate.Ch class MessageBackupsCheckoutActivity : FragmentWrapperActivity() { companion object { + private const val TIER = "tier" private const val RESULT_DATA = "result_data" } - override fun getFragment(): Fragment = MessageBackupsFlowFragment() + override fun getFragment(): Fragment = MessageBackupsFlowFragment.create( + IntentCompat.getSerializableExtra(intent, TIER, MessageBackupTier::class.java) + ) - class Contract : ActivityResultContract() { + class Contract : ActivityResultContract() { - override fun createIntent(context: Context, input: Unit): Intent { - return Intent(context, MessageBackupsCheckoutActivity::class.java) + override fun createIntent(context: Context, input: MessageBackupTier?): Intent { + return Intent(context, MessageBackupsCheckoutActivity::class.java).putExtra(TIER, input) } override fun parseResult(resultCode: Int, intent: Intent?): Result? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt index ddabf9e879..a490d61339 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt @@ -14,10 +14,12 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext +import androidx.core.os.bundleOf import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import kotlinx.coroutines.rx3.asFlowable +import org.signal.core.util.getSerializableCompat import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentCheckoutDelegate @@ -33,7 +35,21 @@ import org.thoughtcrime.securesms.util.viewModel */ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelegate.ErrorHandlerCallback { - private val viewModel: MessageBackupsFlowViewModel by viewModel { MessageBackupsFlowViewModel() } + companion object { + + private const val TIER = "tier" + + fun create(messageBackupTier: MessageBackupTier?): MessageBackupsFlowFragment { + return MessageBackupsFlowFragment().apply { + arguments = bundleOf(TIER to messageBackupTier) + } + } + } + + private val viewModel: MessageBackupsFlowViewModel by viewModel { + MessageBackupsFlowViewModel(requireArguments().getSerializableCompat(TIER, MessageBackupTier::class.java)) + } + private val errorHandler = InAppPaymentCheckoutDelegate.ErrorHandler() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -65,6 +81,17 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega ) } + LaunchedEffect( + state.selectedMessageBackupTier, + state.selectedMessageBackupTierLabel, + state.availableBackupTypes + ) { + if (state.selectedMessageBackupTierLabel == null && state.selectedMessageBackupTier != null && state.availableBackupTypes.isNotEmpty()) { + val type = state.availableBackupTypes.firstOrNull { it.tier == state.selectedMessageBackupTier } ?: return@LaunchedEffect + viewModel.onMessageBackupTierUpdated(type.tier, getTypeLabel(type)) + } + } + Nav.Host( navController = navController, startDestination = state.startScreen.name @@ -105,12 +132,8 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega availableBackupTypes = state.availableBackupTypes.filter { it.tier == MessageBackupTier.FREE || state.hasBackupSubscriberAvailable }, onMessageBackupsTierSelected = { tier -> val type = state.availableBackupTypes.first { it.tier == tier } - val label = when (type) { - is MessageBackupsType.Free -> requireContext().resources.getQuantityString(R.plurals.MessageBackupsTypeSelectionScreen__text_plus_d_days_of_media, type.mediaRetentionDays, type.mediaRetentionDays) - is MessageBackupsType.Paid -> requireContext().getString(R.string.MessageBackupsTypeSelectionScreen__text_plus_all_your_media) - } - viewModel.onMessageBackupTierUpdated(tier, label) + viewModel.onMessageBackupTierUpdated(tier, getTypeLabel(type)) }, onNavigationClick = viewModel::goToPreviousStage, onReadMoreClicked = {}, @@ -141,6 +164,13 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega } } + private fun getTypeLabel(type: MessageBackupsType): String { + return when (type) { + is MessageBackupsType.Free -> requireContext().resources.getQuantityString(R.plurals.MessageBackupsTypeSelectionScreen__text_plus_d_days_of_media, type.mediaRetentionDays, type.mediaRetentionDays) + is MessageBackupsType.Paid -> requireContext().getString(R.string.MessageBackupsTypeSelectionScreen__text_plus_all_your_media) + } + } + override fun onUserLaunchedAnExternalApplication() = error("Not supported by this fragment.") override fun navigateToDonationPending(inAppPayment: InAppPaymentTable.InAppPayment) = error("Not supported by this fragment.") diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt index 0a13d5ffee..e140e0298e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt @@ -45,7 +45,9 @@ import org.thoughtcrime.securesms.util.RemoteConfig import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration import kotlin.time.Duration.Companion.seconds -class MessageBackupsFlowViewModel : ViewModel() { +class MessageBackupsFlowViewModel( + initialTierSelection: MessageBackupTier? +) : ViewModel() { companion object { private val TAG = Log.tag(MessageBackupsFlowViewModel::class) @@ -54,7 +56,7 @@ class MessageBackupsFlowViewModel : ViewModel() { private val internalStateFlow = MutableStateFlow( MessageBackupsFlowState( availableBackupTypes = emptyList(), - selectedMessageBackupTier = SignalStore.backup.backupTier, + selectedMessageBackupTier = initialTierSelection ?: SignalStore.backup.backupTier, startScreen = if (SignalStore.backup.backupTier == null) MessageBackupsStage.EDUCATION else MessageBackupsStage.TYPE_SELECTION ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt index 560e291f76..af380361cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt @@ -204,7 +204,7 @@ class AppSettingsFragment : DSLSettingsFragment( if (RemoteConfig.messageBackups) { clickPref( title = DSLSettingsText.from(R.string.preferences_chats__backups), - // TODO [message-backups] -- icon + icon = DSLSettingsIcon.from(R.drawable.symbol_backup_24), onClick = { findNavController().safeNavigate(R.id.action_appSettingsFragment_to_backupsSettingsFragment) }, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt index 135ec715d3..cd7e4a75df 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt @@ -41,6 +41,7 @@ import org.signal.core.ui.SignalPreview import org.signal.core.ui.Texts import org.signal.core.util.money.FiatMoney import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType import org.thoughtcrime.securesms.components.settings.app.subscription.MessageBackupsCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.compose.ComposeFragment @@ -57,7 +58,7 @@ import kotlin.time.Duration.Companion.seconds */ class BackupsSettingsFragment : ComposeFragment() { - private lateinit var checkoutLauncher: ActivityResultLauncher + private lateinit var checkoutLauncher: ActivityResultLauncher private val viewModel: BackupsSettingsViewModel by viewModels() @@ -86,7 +87,7 @@ class BackupsSettingsFragment : ComposeFragment() { } BackupsSettingsState.EnabledState.Never -> { - checkoutLauncher.launch(Unit) + checkoutLauncher.launch(null) } else -> Unit diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt new file mode 100644 index 0000000000..40dce01740 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.backups.remote + +import androidx.compose.runtime.Composable +import androidx.navigation.fragment.findNavController +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsKeyRecordScreen +import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.Util + +/** + * Fragment which only displays the backup key to the user. + */ +class BackupKeyDisplayFragment : ComposeFragment() { + @Composable + override fun FragmentContent() { + MessageBackupsKeyRecordScreen( + backupKey = SignalStore.svr.getOrCreateMasterKey().deriveBackupKey(), + onNavigationClick = { findNavController().popBackStack() }, + onCopyToClipboardClick = { Util.copyToClipboard(requireContext(), it) }, + onNextClick = { findNavController().popBackStack() } + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt index efeea40c24..f7417a5d71 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt @@ -5,9 +5,14 @@ package org.thoughtcrime.securesms.components.settings.app.backups.remote +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.activity.result.ActivityResultLauncher +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricPrompt import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -49,9 +54,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties -import androidx.fragment.app.setFragmentResultListener +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.signal.core.ui.Buttons import org.signal.core.ui.Dialogs import org.signal.core.ui.Dividers @@ -62,20 +69,23 @@ import org.signal.core.ui.SignalPreview import org.signal.core.ui.Snackbars import org.signal.core.ui.Texts import org.signal.core.ui.theme.SignalTheme +import org.signal.core.util.logging.Log import org.signal.core.util.money.FiatMoney +import org.thoughtcrime.securesms.BiometricDeviceAuthentication import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.ArchiveUploadProgress import org.thoughtcrime.securesms.backup.v2.BackupFrequency import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType -import org.thoughtcrime.securesms.components.settings.app.backups.type.BackupsTypeSettingsFragment import org.thoughtcrime.securesms.components.settings.app.subscription.MessageBackupsCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.dependencies.GooglePlayBillingDependencies import org.thoughtcrime.securesms.fonts.SignalSymbols import org.thoughtcrime.securesms.fonts.SignalSymbols.SignalSymbol import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.ServiceUtil import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.viewModel @@ -90,13 +100,19 @@ import kotlin.time.Duration.Companion.seconds */ class RemoteBackupsSettingsFragment : ComposeFragment() { + companion object { + private val TAG = Log.tag(RemoteBackupsSettingsFragment::class) + private const val AUTHENTICATE_REQUEST_CODE = 1 + } + private val viewModel by viewModel { RemoteBackupsSettingsViewModel() } private val args: RemoteBackupsSettingsFragmentArgs by navArgs() - private lateinit var checkoutLauncher: ActivityResultLauncher + private lateinit var checkoutLauncher: ActivityResultLauncher + private lateinit var biometricDeviceAuthentication: BiometricDeviceAuthentication @Composable override fun FragmentContent() { @@ -125,7 +141,21 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { } override fun onBackupTypeActionClick(tier: MessageBackupTier) { - // TODO [message-backups] + when (tier) { + MessageBackupTier.FREE -> checkoutLauncher.launch(MessageBackupTier.PAID) + MessageBackupTier.PAID -> lifecycleScope.launch(Dispatchers.Main) { + val uri = Uri.parse( + getString( + R.string.backup_subscription_management_url, + GooglePlayBillingDependencies.getProductId(), + requireContext().applicationInfo.packageName + ) + ) + + val intent = Intent(Intent.ACTION_VIEW, uri) + startActivity(intent) + } + } } override fun onBackUpUsingCellularClick(canUseCellular: Boolean) { @@ -160,11 +190,24 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { viewModel.turnOffAndDeleteBackups() } - override fun onBackupsTypeClick() { - findNavController().safeNavigate(R.id.action_remoteBackupsSettingsFragment_to_backupsTypeSettingsFragment) + override fun onViewBackupKeyClick() { + if (!biometricDeviceAuthentication.authenticate(requireContext(), true, this@RemoteBackupsSettingsFragment::showConfirmDeviceCredentialIntent)) { + displayBackupKey() + } } } + private fun displayBackupKey() { + findNavController().safeNavigate(R.id.action_remoteBackupsSettingsFragment_to_backupKeyDisplayFragment) + } + + private fun showConfirmDeviceCredentialIntent() { + val keyguardManager = ServiceUtil.getKeyguardManager(requireContext()) + val intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.RemoteBackupsSettingsFragment__unlock_to_view_backup_key), "") + + startActivityForResult(intent, AUTHENTICATE_REQUEST_CODE) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) checkoutLauncher = createBackupsCheckoutLauncher { backUpLater -> @@ -173,22 +216,41 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { } } - setFragmentResultListener(BackupsTypeSettingsFragment.REQUEST_KEY) { _, bundle -> - val backUpLater = bundle.getBoolean(BackupsTypeSettingsFragment.REQUEST_KEY) - if (backUpLater) { - viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.BACKUP_WILL_BE_CREATED_OVERNIGHT) - } - } - if (savedInstanceState == null && args.backupLaterSelected) { viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.BACKUP_WILL_BE_CREATED_OVERNIGHT) } + + val biometricManager = BiometricManager.from(requireContext()) + val biometricPrompt = BiometricPrompt(this, AuthListener()) + val promptInfo: BiometricPrompt.PromptInfo = BiometricPrompt.PromptInfo.Builder() + .setAllowedAuthenticators(BiometricDeviceAuthentication.ALLOWED_AUTHENTICATORS) + .setTitle(getString(R.string.RemoteBackupsSettingsFragment__unlock_to_view_backup_key)) + .build() + + biometricDeviceAuthentication = BiometricDeviceAuthentication(biometricManager, biometricPrompt, promptInfo) } override fun onResume() { super.onResume() viewModel.refresh() } + + private inner class AuthListener : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationFailed() { + Log.w(TAG, "onAuthenticationFailed") + Toast.makeText(requireContext(), R.string.authentication_required, Toast.LENGTH_SHORT).show() + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + Log.i(TAG, "onAuthenticationSucceeded") + displayBackupKey() + } + + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + Log.w(TAG, "onAuthenticationError: $errorCode, $errString") + onAuthenticationFailed() + } + } } /** @@ -196,7 +258,6 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { */ private interface ContentCallbacks { fun onNavigationClick() = Unit - fun onBackupsTypeClick() = Unit fun onBackupTypeActionClick(tier: MessageBackupTier) = Unit fun onBackUpUsingCellularClick(canUseCellular: Boolean) = Unit fun onBackupNowClick() = Unit @@ -206,6 +267,7 @@ private interface ContentCallbacks { fun onSnackbarDismissed() = Unit fun onSelectBackupsFrequencyChange(newFrequency: BackupFrequency) = Unit fun onTurnOffAndDeleteBackupsConfirm() = Unit + fun onViewBackupKeyClick() = Unit } @Composable @@ -309,6 +371,13 @@ private fun RemoteBackupsSettingsContent( ) } + item { + Rows.TextRow( + text = stringResource(R.string.RemoteBackupsSettingsFragment__view_backup_key), + onClick = contentCallbacks::onViewBackupKeyClick + ) + } + item { Dividers.Default() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt index 3bf3fbd45a..0ae28905e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt @@ -51,7 +51,7 @@ class RemoteBackupsSettingsViewModel : ViewModel() { if (activeSubscription.isSuccess) { val subscription = activeSubscription.getOrThrow().activeSubscription - if (subscription.isActive && subscription != null) { + if (subscription != null) { _state.update { it.copy(renewalTime = subscription.endOfCurrentPeriod.seconds) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsFragment.kt deleted file mode 100644 index 1e1772333f..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsFragment.kt +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.backups.type - -import android.os.Bundle -import android.view.View -import androidx.activity.result.ActivityResultLauncher -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.pluralStringResource -import androidx.compose.ui.res.stringResource -import androidx.core.os.bundleOf -import androidx.fragment.app.setFragmentResult -import androidx.navigation.fragment.findNavController -import org.signal.core.ui.Previews -import org.signal.core.ui.Rows -import org.signal.core.ui.Scaffolds -import org.signal.core.ui.SignalPreview -import org.signal.core.util.money.FiatMoney -import org.signal.donations.PaymentSourceType -import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType -import org.thoughtcrime.securesms.components.settings.app.subscription.MessageBackupsCheckoutLauncher.createBackupsCheckoutLauncher -import org.thoughtcrime.securesms.compose.ComposeFragment -import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord -import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.payments.FiatMoneyUtil -import org.thoughtcrime.securesms.util.DateUtils -import org.thoughtcrime.securesms.util.viewModel -import java.math.BigDecimal -import java.util.Locale - -/** - * Allows the user to modify their backup plan - */ -class BackupsTypeSettingsFragment : ComposeFragment() { - - companion object { - const val REQUEST_KEY = "BackupsTypeSettingsFragment__result" - } - - private val viewModel: BackupsTypeSettingsViewModel by viewModel { - BackupsTypeSettingsViewModel() - } - - private lateinit var checkoutLauncher: ActivityResultLauncher - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - checkoutLauncher = createBackupsCheckoutLauncher { backUpLater -> - findNavController().popBackStack() - setFragmentResult(REQUEST_KEY, bundleOf(REQUEST_KEY to backUpLater)) - } - } - - @Composable - override fun FragmentContent() { - val contentCallbacks = remember { - Callbacks() - } - - val state by viewModel.state.collectAsState() - - BackupsTypeSettingsContent( - state = state, - contentCallbacks = contentCallbacks - ) - } - - private inner class Callbacks : ContentCallbacks { - override fun onNavigationClick() { - findNavController().popBackStack() - } - - override fun onChangeOrCancelSubscriptionClick() { - checkoutLauncher.launch(Unit) - } - } - - override fun onResume() { - super.onResume() - viewModel.refresh() - } -} - -private interface ContentCallbacks { - fun onNavigationClick() = Unit - fun onPaymentHistoryClick() = Unit - fun onChangeOrCancelSubscriptionClick() = Unit -} - -@Composable -private fun BackupsTypeSettingsContent( - state: BackupsTypeSettingsState, - contentCallbacks: ContentCallbacks -) { - if (state.messageBackupsType == null) { - return - } - - Scaffolds.Settings( - title = "Backup Type", - onNavigationClick = contentCallbacks::onNavigationClick, - navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24) - ) { - LazyColumn( - modifier = Modifier.padding(it) - ) { - item { - BackupsTypeRow( - messageBackupsType = state.messageBackupsType, - nextRenewalTimestamp = state.nextRenewalTimestamp - ) - } - - item { - PaymentSourceRow( - paymentSourceType = state.paymentSourceType - ) - } - - item { - Rows.TextRow( - text = stringResource(id = R.string.BackupsTypeSettingsFragment__change_or_cancel_subscription), - onClick = contentCallbacks::onChangeOrCancelSubscriptionClick - ) - } - - item { - Rows.TextRow( - text = stringResource(id = R.string.BackupsTypeSettingsFragment__payment_history), - onClick = contentCallbacks::onPaymentHistoryClick - ) - } - } - } -} - -@Composable -private fun BackupsTypeRow( - messageBackupsType: MessageBackupsType, - nextRenewalTimestamp: Long -) { - val resources = LocalContext.current.resources - val formattedAmount = remember(messageBackupsType) { - val amount = when (messageBackupsType) { - is MessageBackupsType.Paid -> messageBackupsType.pricePerMonth - else -> FiatMoney(BigDecimal.ZERO, SignalStore.inAppPayments.getSubscriptionCurrency(InAppPaymentSubscriberRecord.Type.BACKUP)) - } - - FiatMoneyUtil.format(resources, amount, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) - } - - val title = when (messageBackupsType) { - is MessageBackupsType.Paid -> stringResource(id = R.string.MessageBackupsTypeSelectionScreen__text_plus_all_your_media) - is MessageBackupsType.Free -> pluralStringResource(id = R.plurals.MessageBackupsTypeSelectionScreen__text_plus_d_days_of_media, count = messageBackupsType.mediaRetentionDays, messageBackupsType.mediaRetentionDays) - } - - val renewal = remember(nextRenewalTimestamp) { - DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), nextRenewalTimestamp) - } - - Rows.TextRow(text = { - Column { - Text(text = title) - Text( - text = stringResource(id = R.string.BackupsTypeSettingsFragment__s_month_renews_s, formattedAmount, renewal), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - }) -} - -@Composable -private fun PaymentSourceRow(paymentSourceType: PaymentSourceType) { - val paymentSourceTextResId = remember(paymentSourceType) { - when (paymentSourceType) { - is PaymentSourceType.GooglePlayBilling -> R.string.BackupsTypeSettingsFragment__google_play - is PaymentSourceType.Stripe.CreditCard -> R.string.BackupsTypeSettingsFragment__credit_or_debit_card - is PaymentSourceType.Stripe.IDEAL -> R.string.BackupsTypeSettingsFragment__iDEAL - is PaymentSourceType.Stripe.GooglePay -> R.string.BackupsTypeSettingsFragment__google_pay - is PaymentSourceType.Stripe.SEPADebit -> R.string.BackupsTypeSettingsFragment__bank_transfer - is PaymentSourceType.PayPal -> R.string.BackupsTypeSettingsFragment__paypal - is PaymentSourceType.Unknown -> R.string.BackupsTypeSettingsFragment__unknown - } - } - - Rows.TextRow(text = { - Column { - Text(text = "Payment method") // TOD [message-backups] Final copy - Text( - text = stringResource(id = paymentSourceTextResId), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - }) -} - -@SignalPreview -@Composable -private fun BackupsTypeSettingsContentPreview() { - Previews.Preview { - BackupsTypeSettingsContent( - state = BackupsTypeSettingsState( - messageBackupsType = MessageBackupsType.Free( - mediaRetentionDays = 30 - ) - ), - contentCallbacks = object : ContentCallbacks {} - ) - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsState.kt deleted file mode 100644 index 52dde1e5e9..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsState.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.backups.type - -import androidx.compose.runtime.Stable -import org.signal.donations.PaymentSourceType -import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType - -@Stable -data class BackupsTypeSettingsState( - val messageBackupsType: MessageBackupsType? = null, - val paymentSourceType: PaymentSourceType = PaymentSourceType.Unknown, - val nextRenewalTimestamp: Long = 0 -) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsViewModel.kt deleted file mode 100644 index 54c16164e5..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/type/BackupsTypeSettingsViewModel.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.backups.type - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.thoughtcrime.securesms.backup.v2.BackupRepository -import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository -import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository.toPaymentSourceType -import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord -import org.thoughtcrime.securesms.keyvalue.SignalStore - -class BackupsTypeSettingsViewModel : ViewModel() { - private val internalState = MutableStateFlow(BackupsTypeSettingsState()) - - val state: StateFlow = internalState - - init { - refresh() - } - - fun refresh() { - viewModelScope.launch { - val tier = SignalStore.backup.backupTier - val paymentMethod = withContext(Dispatchers.IO) { - InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.BACKUP) - } - - internalState.update { - it.copy( - messageBackupsType = if (tier != null) BackupRepository.getBackupsType(tier) else null, - paymentSourceType = paymentMethod.toPaymentSourceType() - ) - } - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt index 7e34176914..53925dbe45 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.navigation.Navigation import androidx.navigation.fragment.findNavController import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment import org.thoughtcrime.securesms.components.settings.DSLSettingsText @@ -17,7 +18,7 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__chats) { private lateinit var viewModel: ChatsSettingsViewModel - private lateinit var checkoutLauncher: ActivityResultLauncher + private lateinit var checkoutLauncher: ActivityResultLauncher override fun onResume() { super.onResume() @@ -98,7 +99,7 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch if (state.canAccessRemoteBackupsSettings) { Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_remoteBackupsSettingsFragment) } else { - checkoutLauncher.launch(Unit) + checkoutLauncher.launch(null) } } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt index 31b9214410..45d7048f94 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt @@ -33,6 +33,7 @@ import org.signal.core.ui.Buttons import org.signal.core.ui.Previews import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.ui.BackupsIconColors import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeBlock @@ -49,7 +50,7 @@ class UpgradeToEnableOptimizedStorageSheet : ComposeBottomSheetDialogFragment() private val viewModel: UpgradeToEnableOptimizedStorageViewModel by viewModels() - private lateinit var checkoutLauncher: ActivityResultLauncher + private lateinit var checkoutLauncher: ActivityResultLauncher override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -62,7 +63,7 @@ class UpgradeToEnableOptimizedStorageSheet : ComposeBottomSheetDialogFragment() UpgradeToEnableOptimizedStorageSheetContent( messageBackupsType = type, onUpgradeNowClick = { - checkoutLauncher.launch(Unit) + checkoutLauncher.launch(MessageBackupTier.PAID) dismissAllowingStateLoss() }, onCancelClick = { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/MessageBackupsCheckoutLauncher.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/MessageBackupsCheckoutLauncher.kt index 70ae6cb962..9e246e7ac6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/MessageBackupsCheckoutLauncher.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/MessageBackupsCheckoutLauncher.kt @@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription import androidx.activity.result.ActivityResultLauncher import androidx.fragment.app.Fragment import org.signal.core.util.getSerializableCompat +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.ui.CreateBackupBottomSheet import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsCheckoutActivity import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentProcessorAction @@ -17,7 +18,7 @@ object MessageBackupsCheckoutLauncher { fun Fragment.createBackupsCheckoutLauncher( onCreateBackupBottomSheetResultListener: OnCreateBackupBottomSheetResultListener = {} as OnCreateBackupBottomSheetResultListener - ): ActivityResultLauncher { + ): ActivityResultLauncher { childFragmentManager.setFragmentResultListener(CreateBackupBottomSheet.REQUEST_KEY, viewLifecycleOwner) { requestKey, bundle -> if (requestKey == CreateBackupBottomSheet.REQUEST_KEY) { val result = bundle.getSerializableCompat(CreateBackupBottomSheet.REQUEST_KEY, CreateBackupBottomSheet.Result::class.java) diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index ae627bd003..967988588e 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -985,8 +985,8 @@ android:name="org.thoughtcrime.securesms.components.settings.app.backups.remote.RemoteBackupsSettingsFragment"> - - + android:id="@+id/backupKeyDisplayFragment" + android:name="org.thoughtcrime.securesms.components.settings.app.backups.remote.BackupKeyDisplayFragment" /> diff --git a/app/src/main/res/navigation/app_settings_with_change_number.xml b/app/src/main/res/navigation/app_settings_with_change_number.xml index a1b616ebfb..7b5386c0d7 100644 --- a/app/src/main/res/navigation/app_settings_with_change_number.xml +++ b/app/src/main/res/navigation/app_settings_with_change_number.xml @@ -999,8 +999,8 @@ android:name="org.thoughtcrime.securesms.components.settings.app.backups.remote.RemoteBackupsSettingsFragment"> - - + android:id="@+id/backupKeyDisplayFragment" + android:name="org.thoughtcrime.securesms.components.settings.app.backups.remote.BackupKeyDisplayFragment" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87762db825..71bfbdd9df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,9 @@ https://support.signal.org/hc/articles/360031949872#pending https://support.signal.org/hc/articles/360031949872#donate + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Yes No Delete @@ -7390,6 +7393,10 @@ Backup frequency Back up using cellular + + View backup key + + Unlock to view backup key Turn off and delete backup From 703e1cd06528ee75cc025a59753e0f09c72e7384 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 3 Oct 2024 10:12:55 -0300 Subject: [PATCH 44/53] Remove chat settings path to enter remote backups. --- .../app/chats/ChatsSettingsFragment.kt | 32 ++++--------------- .../settings/app/chats/ChatsSettingsState.kt | 3 +- .../app/chats/ChatsSettingsViewModel.kt | 25 ++------------- app/src/main/res/navigation/app_settings.xml | 7 ---- .../app_settings_with_change_number.xml | 7 ---- 5 files changed, 10 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt index 53925dbe45..d4914134b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt @@ -1,15 +1,12 @@ package org.thoughtcrime.securesms.components.settings.app.chats -import androidx.activity.result.ActivityResultLauncher import androidx.lifecycle.ViewModelProvider import androidx.navigation.Navigation import androidx.navigation.fragment.findNavController import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.backup.v2.MessageBackupTier 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.MessageBackupsCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter @@ -18,7 +15,6 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__chats) { private lateinit var viewModel: ChatsSettingsViewModel - private lateinit var checkoutLauncher: ActivityResultLauncher override fun onResume() { super.onResume() @@ -27,10 +23,6 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch @Suppress("ReplaceGetOrSet") override fun bindAdapter(adapter: MappingAdapter) { - checkoutLauncher = createBackupsCheckoutLauncher { - findNavController().safeNavigate(ChatsSettingsFragmentDirections.actionChatsSettingsFragmentToRemoteBackupsSettingsFragment().setBackupLaterSelected(it)) - } - viewModel = ViewModelProvider(this).get(ChatsSettingsViewModel::class.java) viewModel.state.observe(viewLifecycleOwner) { @@ -87,31 +79,19 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch } ) - dividerPref() + if (!RemoteConfig.messageBackups) { + dividerPref() - sectionHeaderPref(R.string.preferences_chats__backups) + sectionHeaderPref(R.string.preferences_chats__backups) - if (RemoteConfig.messageBackups || state.canAccessRemoteBackupsSettings) { clickPref( - title = DSLSettingsText.from(R.string.RemoteBackupsSettingsFragment__signal_backups), - summary = DSLSettingsText.from(if (state.canAccessRemoteBackupsSettings) R.string.arrays__enabled else R.string.arrays__disabled), + title = DSLSettingsText.from(R.string.preferences_chats__chat_backups), + summary = DSLSettingsText.from(if (state.localBackupsEnabled) R.string.arrays__enabled else R.string.arrays__disabled), onClick = { - if (state.canAccessRemoteBackupsSettings) { - Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_remoteBackupsSettingsFragment) - } else { - checkoutLauncher.launch(null) - } + Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_backupsPreferenceFragment) } ) } - - clickPref( - title = DSLSettingsText.from(R.string.preferences_chats__chat_backups), - summary = DSLSettingsText.from(if (state.localBackupsEnabled) R.string.arrays__enabled else R.string.arrays__disabled), - onClick = { - Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_backupsPreferenceFragment) - } - ) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt index 27ef9e4fa0..ed174592c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt @@ -6,6 +6,5 @@ data class ChatsSettingsState( val keepMutedChatsArchived: Boolean, val useSystemEmoji: Boolean, val enterKeySends: Boolean, - val localBackupsEnabled: Boolean, - val canAccessRemoteBackupsSettings: Boolean + val localBackupsEnabled: Boolean ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt index c0765af6a4..f726738063 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt @@ -2,11 +2,6 @@ package org.thoughtcrime.securesms.components.settings.app.chats import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.Single -import io.reactivex.rxjava3.kotlin.subscribeBy -import io.reactivex.rxjava3.schedulers.Schedulers -import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.util.BackupUtil @@ -27,24 +22,12 @@ class ChatsSettingsViewModel @JvmOverloads constructor( keepMutedChatsArchived = SignalStore.settings.shouldKeepMutedChatsArchived(), useSystemEmoji = SignalStore.settings.isPreferSystemEmoji, enterKeySends = SignalStore.settings.isEnterKeySends, - localBackupsEnabled = SignalStore.settings.isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(AppDependencies.application), - canAccessRemoteBackupsSettings = SignalStore.backup.areBackupsEnabled + localBackupsEnabled = SignalStore.settings.isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(AppDependencies.application) ) ) val state: LiveData = store.stateLiveData - private val disposable = Single.fromCallable { BackupRepository.canAccessRemoteBackupSettings() } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy { canAccessRemoteBackupSettings -> - store.update { it.copy(canAccessRemoteBackupsSettings = canAccessRemoteBackupSettings) } - } - - override fun onCleared() { - disposable.dispose() - } - fun setGenerateLinkPreviewsEnabled(enabled: Boolean) { store.update { it.copy(generateLinkPreviews = enabled) } SignalStore.settings.isLinkPreviewsEnabled = enabled @@ -76,12 +59,10 @@ class ChatsSettingsViewModel @JvmOverloads constructor( fun refresh() { val backupsEnabled = SignalStore.settings.isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(AppDependencies.application) - val remoteBackupsEnabled = SignalStore.backup.areBackupsEnabled - if (store.state.localBackupsEnabled != backupsEnabled || - store.state.canAccessRemoteBackupsSettings != remoteBackupsEnabled + if (store.state.localBackupsEnabled != backupsEnabled ) { - store.update { it.copy(localBackupsEnabled = backupsEnabled, canAccessRemoteBackupsSettings = remoteBackupsEnabled) } + store.update { it.copy(localBackupsEnabled = backupsEnabled) } } } } diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 967988588e..21cfae37b9 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -352,13 +352,6 @@ app:exitAnim="@anim/fragment_open_exit" app:popEnterAnim="@anim/fragment_close_enter" app:popExitAnim="@anim/fragment_close_exit" /> - - Date: Thu, 3 Oct 2024 10:41:39 -0400 Subject: [PATCH 45/53] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 126 +++- app/src/main/res/values-ar/strings.xml | 786 +++++++++++---------- app/src/main/res/values-az/strings.xml | 122 +++- app/src/main/res/values-bg/strings.xml | 122 +++- app/src/main/res/values-bn/strings.xml | 122 +++- app/src/main/res/values-bs/strings.xml | 122 +++- app/src/main/res/values-ca/strings.xml | 122 +++- app/src/main/res/values-cs/strings.xml | 122 +++- app/src/main/res/values-da/strings.xml | 122 +++- app/src/main/res/values-de/strings.xml | 122 +++- app/src/main/res/values-el/strings.xml | 122 +++- app/src/main/res/values-es/strings.xml | 308 ++++---- app/src/main/res/values-et/strings.xml | 122 +++- app/src/main/res/values-eu/strings.xml | 122 +++- app/src/main/res/values-fa/strings.xml | 128 +++- app/src/main/res/values-fi/strings.xml | 122 +++- app/src/main/res/values-fr/strings.xml | 252 ++++--- app/src/main/res/values-ga/strings.xml | 122 +++- app/src/main/res/values-gl/strings.xml | 122 +++- app/src/main/res/values-gu/strings.xml | 122 +++- app/src/main/res/values-hi/strings.xml | 122 +++- app/src/main/res/values-hr/strings.xml | 122 +++- app/src/main/res/values-hu/strings.xml | 122 +++- app/src/main/res/values-in/strings.xml | 122 +++- app/src/main/res/values-it/strings.xml | 122 +++- app/src/main/res/values-iw/strings.xml | 122 +++- app/src/main/res/values-ja/strings.xml | 122 +++- app/src/main/res/values-ka/strings.xml | 122 +++- app/src/main/res/values-kk/strings.xml | 122 +++- app/src/main/res/values-km/strings.xml | 122 +++- app/src/main/res/values-kn/strings.xml | 122 +++- app/src/main/res/values-ko/strings.xml | 122 +++- app/src/main/res/values-ky/strings.xml | 122 +++- app/src/main/res/values-lt/strings.xml | 122 +++- app/src/main/res/values-lv/strings.xml | 124 +++- app/src/main/res/values-mk/strings.xml | 122 +++- app/src/main/res/values-ml/strings.xml | 122 +++- app/src/main/res/values-mr/strings.xml | 122 +++- app/src/main/res/values-ms/strings.xml | 122 +++- app/src/main/res/values-my/strings.xml | 122 +++- app/src/main/res/values-nb/strings.xml | 122 +++- app/src/main/res/values-nl/strings.xml | 288 ++++---- app/src/main/res/values-pa/strings.xml | 122 +++- app/src/main/res/values-pl/strings.xml | 122 +++- app/src/main/res/values-pt-rBR/strings.xml | 122 +++- app/src/main/res/values-pt/strings.xml | 122 +++- app/src/main/res/values-ro/strings.xml | 122 +++- app/src/main/res/values-ru/strings.xml | 122 +++- app/src/main/res/values-sk/strings.xml | 122 +++- app/src/main/res/values-sl/strings.xml | 122 +++- app/src/main/res/values-sq/strings.xml | 122 +++- app/src/main/res/values-sr/strings.xml | 122 +++- app/src/main/res/values-sv/strings.xml | 122 +++- app/src/main/res/values-sw/strings.xml | 122 +++- app/src/main/res/values-ta/strings.xml | 124 +++- app/src/main/res/values-te/strings.xml | 122 +++- app/src/main/res/values-th/strings.xml | 122 +++- app/src/main/res/values-tl/strings.xml | 122 +++- app/src/main/res/values-tr/strings.xml | 122 +++- app/src/main/res/values-ug/strings.xml | 124 +++- app/src/main/res/values-uk/strings.xml | 226 +++--- app/src/main/res/values-ur/strings.xml | 122 +++- app/src/main/res/values-vi/strings.xml | 122 +++- app/src/main/res/values-yue/strings.xml | 122 +++- app/src/main/res/values-zh-rCN/strings.xml | 122 +++- app/src/main/res/values-zh-rHK/strings.xml | 122 +++- app/src/main/res/values-zh-rTW/strings.xml | 122 +++- app/static-ips.gradle.kts | 6 +- 68 files changed, 6398 insertions(+), 3048 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 3bcb97fbba..7e06804ec1 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ja Nee Skrap @@ -260,6 +263,7 @@ Tik vir foto, hou vir video + Vang Verander kamera Maak galery oop @@ -346,7 +350,7 @@ Hierdie is nie ’n geldige oproepskakel nie. Maak seker die volledige skakel is heel en korrek voor jy probeer aansluit. - You are already in a call + Jy is reeds in \'n oproep @@ -403,6 +407,7 @@ Kan nie \'n program vind om hierdie media mee oop te maak nie. %1$s gekopieer van %1$s + aan %1$s   Lees Meer   Laai Meer Af @@ -436,6 +441,7 @@ Stuur wysiging Skryf boodskap Jammer, daar was \'n fout tydens die opstel van jou aanhangsel. + Ontvanger is nie \'n geldige SMS- of e-pos-adres nie Boodskap is leeg! Groeplede @@ -576,6 +582,7 @@ Besig om %1$d aanhangsels na stoorruimte te stoor… Hangend… + Data (Signal) MMS SMS @@ -747,7 +754,7 @@ Geargiveerde kletse (%1$d) - Geverifiëer + Geverifieer Jy @@ -1006,6 +1013,7 @@ Ontkoppel \'%1$s\'? As u hierdie toestel ontkoppel, kan dit nie meer boodskappe stuur of ontvang nie. Netwerkverbinding misluk + Probeer weer Ontkoppel toestel… Besig om die toestel te ontkoppel @@ -1408,7 +1416,7 @@ Groepskakel Deel Stel skakel terug - Require admin approval + Vereis admin-goedkeuring ’n Admin moet nuwe lede goedkeur wat d.m.v. die groepskakel aansluit. Is jy seker jy wil die groepskakel terugstel? Mense sal nie meer met die huidige skakel by die groep kan aansluit nie. @@ -1507,6 +1515,7 @@ Stuur %1$d SMS uitnodigings? Kom ons skakel oor na Signal: %1$s + Dit lyk asof jy geen programme het om mee te deel nie. @@ -1931,6 +1940,7 @@ Laat %1$s toe om vir jou boodskappe stuur, en deel jou naam en foto met hulle? Tot jy hulle ontsper, sal jy geen boodskappe ontvang nie. Laat %1$s toe om vir jou boodskappe stuur? Tot jy hulle ontsper, sal jy geen boodskappe ontvang nie. + Wil jy bywerkings en nuus van %1$s kry? Tot jy hulle ontsper, sal jy geen bywerkings ontvang nie. Wil jy jou klets met hierdie groep voortsit en jou naam en foto met sy lede deel? Hierdie Legaatgroep kan nie meer gebruik word nie. Skep \'n nuwe groep om nuwe kenmerke soos @vermeldings en admins te aktiveer. @@ -2245,6 +2255,7 @@ Hier is niemand anders nie %1$s is in hierdie oproep + %1$s is in hierdie oproep %1$s en %2$s is in hierdie oproep @@ -2334,7 +2345,7 @@ Signal-kontak - Geverifiëer + Geverifieer Geen direkte boodskappe nie met %1$s @@ -2647,7 +2658,7 @@ Ons sal later weer probeer. Signal suksesvol opgedateer Jy is outomaties na weergawe %1$s opgedateer. - You updated to version %1$s. + Jy het \'n opdatering na weergawe %1$s gedoen. Stuur boodskap? @@ -3067,6 +3078,7 @@ Aanhegsel-duimnaelskakel Skakel snel-kameraanhegselkassie aan of af Neem oudio-aanhegsel op en stuur + Sluit opname van oudio-aanhegsel Boodskap kon nie gestuur word nie. Kontroleer jou verbinding en probeer weer. @@ -3085,6 +3097,7 @@ Boodskap gelees + Kontakfoto @@ -3169,6 +3182,7 @@ Alles lyk nou goed! Om oproepkennisgewings te ontvang, tik hier en skakel \"Wys kennisgewings\" aan. Om oproepkennisgewings te ontvang, tik hier, skakel kennisgewings aan en maak seker Klank en Opwip is geaktiveer. + Om oproepkennisgewings te ontvang, tik hier en skakel agtergrondaktiwiteit in \"Battery-instellings\" aan. Instellings Om oproepkennisgewings te ontvang, tik op Instellings en skakel \"Wys kennisgewings\" aan. @@ -3306,7 +3320,7 @@ Ek rus tans Werk tans aan iets nuuts - One or more characters is invalid. + Een of meer karakters is ongeldig. Redigeer groep @@ -3405,6 +3419,7 @@ + Ondersteuningsinligting Signal Android ondersteuningsversoek @@ -3493,6 +3508,7 @@ Haal skakelvoorskoue direk van die webwerwe af vir boodskappe wat jy stuur. Verander wagwoordfrase Verander jou wagwoordfrase + Aktiveer wagwoordfrase-skermslot Sluit skerm en kennisgewings met \'n wagwoordfrase Skermsekuriteit @@ -3505,6 +3521,7 @@ LED-flikkerpatroon Pasmaak Verander klank en vibrasie + Klank Stilte Verstek @@ -5378,6 +5395,7 @@ Voeg by storie by Voeg ’n boodskap toe + Voeg ’n antwoord toe Stuur na Eenkeerkyk-media @@ -5982,6 +6000,8 @@ %1$d antwoord %1$d antwoorde + + Story no longer hidden Voeg toe @@ -7267,7 +7287,23 @@ - Stel %1$s ruimte beskikbaar om jou media af te laai. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Goed - - - Betalingsgeskiedenis - - Rugsteun van teks en alle media - - Betalingsbesonderhede - - Rugsteuntipe - - Datum betaal - - Deel + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Gereeldheid van rugsteun Rugsteun met behulp van mobiele data + + View backup key + + Unlock to view backup key Skakel rugsteun af en skrap dit @@ -7370,13 +7406,27 @@ Rugsteun sal oornag geskep word. - Rugsteuntipe + Backup plan Rugsteun gedeaktiveer - - %1$s · %2$s/maand - - Aktiveer rugsteun + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Jou rugsteunsleutel - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Jou rugsteunsleutel is \'n 64-syfer-kode wat jou toelaat om jou rugsteun te herstel wanneer jy Signal herinstalleer. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + As jy jou sleutel vergeet, sal jy nie jou rugsteun kan herstel nie. Signal kan jou nie help om jou rugsteun te herstel nie. - Next + Volgende - Record your backup key + Maak \'n aantekening van jou rugsteunsleutel - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Hierdie sleutel is nodig om jou rekening en data te herstel. Bewaar hierdie sleutel iewers waar dit veilig is. As jy dit verloor, sal jy nie jou rekening kan herstel nie. - Copy to clipboard + Kopieer na knipbord - Next + Volgende - Keep your key safe + Hou jou sleutel veilig - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal sal jou nie kan help om jou rugsteun te herstel as jy jou sleutel verloor nie. Bewaar dit iewers waar dit veilig is, en moet dit nie met ander mense deel nie. - I\'ve recorded my key + Ek het \'n aantekening van my sleutel gemaak - Continue + Gaan voort - See key again + Wys sleutel weer diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 749cd28fbe..f6818ca3fc 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + نعم لا حذف @@ -268,6 +271,7 @@ انقر لالتقاط صورة، أواضغط باستمرار لتصوير الفيديو + التقاط تغيير الكاميرا فتح المعرض @@ -358,7 +362,7 @@ رابط المكالمة هذا غير صالح. تأكَّد من سلامة وصحة الرابط بأكمله قبل مُحاولة الانضمام. - You are already in a call + أنت بالأصل تجري مكالمة الآن. @@ -415,6 +419,7 @@ لم يتم العثور على تطبيق قادر على فتح الملف. تم نسخ %1$s من %1$s + إلى %1$s   قراءة المزيد   تنزيل المزيد @@ -448,6 +453,7 @@ أرسِل التعديل إنشاء رسالة عذرًا، حصل خطأ أثناء رفع الملف المُرفَق. + المُستلِم ليس رسالة SMS أو عنوان بريد إلكتروني صالح! الرسالة فارغة أعضاء المجموعة @@ -620,6 +626,7 @@ جارٍ حفظ %1$d ملفٍ مُرفَقٍ في ذاكرة التخزين… في حالة انتظار… + بيانات (سيجنال) رسالة وسائط متعددة (MMS) رسالة قصيرة (SMS) @@ -1035,7 +1042,7 @@ تتم مُزامنة رسائل سيجنال مع تطبيق سيجنال على هاتفك المحمول بعد ربطه. لن تظهر سجلات رسائلك السابقة. - على الجهاز الذي ترغب بربطه، تفضّل بزيارة %1$s لتثبيت سيجنال + على الجهاز الذي ترغب بربطه، قُم بزيارة %1$s لتثبيت سيجنال الأجهزة المرتبطة @@ -1086,6 +1093,7 @@ أترغبُ بإلغاء ربط \'%1$s\'؟ لن يكون إرسال واستقبال الرسائل مُمكِنًا بمُجرَّد إلغاء ربط الجهاز. فَشِلَ الاتصال بالشبكة + حاوِل مرة أخرى. جارٍ إلغاء الارتباط بالجهاز… جارٍ إلغاء الارتباط بالجهاز @@ -1323,29 +1331,29 @@ يُمكنك إيقاف تحسينات استخدام البطارية لتطبيق سيجنال لضمان عدم تأخُّر إشعارات الرسائل. - قد تتأخر الإشعارات في الوصول بسبب بعض التحسينات في البطارية + قد تتأخر الإشعارات في الوصول بسبب بعض التحسينات في استخدام البطارية. - يُمكنك إيقاف تحسينات البطارية لسيجنال لِضمان وصول إشعارات الرسائل في مواعيدها. انقر \"متابعة\" للاطلاع على الإرشادات الخاصة بالجهاز. + يُمكنك إيقاف تحسينات استخدام البطارية لسيجنال لضمان عدم تأخر وصول إشعارات الرسائل. انقر \"متابعة\" للاطِّلاع على الإرشادات الخاصة بالجهاز. - لا شكراً + لا، شكرًا - مواصلة + متابعة - قد تكون غير قادر على التوصل بالرسائل. + قد تكون غير قادر على استلام الرسائل. - إعادة تشغيل جهازك قد يُساعد على حل مشكل التوصل بالرسائل. إذا استمر المشكل، يُرجى الاتصال بدعم سيجنال. + إعادة تشغيل جهازك قد يُساعد على حل مشكلة عدم استلام الرسائل. إذا استمر المشكلة، يُرجى التواصل مع قسم الدعم في سيجنال. - عُلم + تم متابعة - لا شكراً + لا، شكرًا - الطلبات و الدعوات + الطلبات والدعوات الطلبات الدعوات الأشخاص الذين دعوتهم @@ -1411,7 +1419,7 @@ حدّدتَ جهة اتصال لا تدعم مجموعات سيجنال، لذلك ستُحوّل هذه المجموعة إلى رسائل الوسائط المتعددة المخصصة (MMS). ستكون أسماء وصور مجموعة رسائل الوسائط المتعددة المخصصة (MMS) مرئية لك فقط. - لقد اخترت جهة اتصال لا تدعم مجموعات سيجنال، لِذلك هذه المجموعة ستتواصل بتقنية MMS. أسماء وصور مجموعة MMS المُخصصة ستكون مرئية لك فقط. سَتتم إزالة دعم مجموعات MMS قريبًا للتركيز على الرسائل المشفرة. + اخترتَ جهة اتصال لا تدعم مجموعات سيجنال، لذلك فهذه المجموعة ستتواصل بتقنية MMS. أسماء وصور مجموعة MMS المُخصصة ستكون مرئية لك فقط. سَتتم إزالة دعم مجموعات MMS قريبًا للتركيز على الرسائل المشفرة. من يمكنه إضافة أعضاء جدد؟ @@ -1435,39 +1443,39 @@ فشل تحديث المجموعة بسبب خلل في الشبكة. يُرجى المحاولة لاحقًا. تعديل الاسم والصورة - مجموعة سابقة + مجموعة بنمط سابق - هذه مجموعة من الطراز القديم. إن الميزات مثل خاصية المشرفين على المجموعة، متاحة فقط في المجموعات الجديدة. + هذه مجموعة ذات نمط سابق. إن الميزات مثل خاصية المشرفين على المجموعة متاحة فقط في المجموعات الجديدة. - هذه مجموعة من الطراز القديم. للوصول إلى ميزات مثل ‎@mentions‏ والمشرفين، - لا يمكن ترقية هذه المجموعة القديمة الطراز إلى مجموعة جديدة لأنها كبيرة جدا. الحد الأقصى للمجموعة هو %1$d. - يُرجى ترقية هذه المجموعة. - إن مجموعة MMS هذه غير آمنة. للتحدث بخصوصية، يُرجى دعوة جهات اتصالك إلى سيجنال. + هذه مجموعة ذات نمط سابق. للوصول إلى ميزات جديدة مثل الإشارة بتاغ ‎@mentions‏ والمشرفين، + لا يمكن ترقية هذه المجموعة ذات النمط السابق إلى مجموعة بنمط جديد لأنها كبيرة جدًا. الحد الأقصى للمجموعة هو %1$d. + قُم بترقية هذه المجموعة. + هذه المجموعة التي تعمل بنمط MMS غير آمنة. للتحدُّث بخصوصية، يُرجى دعوة جهات اتصالك إلى سيجنال. الدعوة اﻵن المزيد إضافة وصف للمجموعة… - نقل انطلاقا من جهاز أندرويد + نقل من جهاز أندرويد - حوّل حسابك ورسائلك من جهاز أندرويد القديم. + انقل حسابك ورسائلك من جهاز أندرويد القديم. - تسجيل الدخول بدون نقل + تسجيل الدخول من دون نقل - اِستِمر دُون نقلِ رسائلك والوسائط + اِستِمر من دُون نقلِ رسائلك والوسائط - استعادة نسخ احتياطية محلية + استعادة نسخة احتياطية محلية - استعد رسائلك من الملف الاحتياطي الذي حفظته على جهازك. + استعِد رسائلك من ملف نسخة احتياطية حفظته على جهازك. - يَجري تنزيل النسخة الاحتياطية… + جارٍتنزيل النسخة الاحتياطية… - تجري استعادة الرسائل… + جارية استعادة الرسائل… - تجري الاستعادة… + جارية الاستعادة… %1$s من %2$s (%3$s) @@ -1487,47 +1495,47 @@ نبّهني عندما يذكر أحدهم اسمي - تلقِّي الإشعارات عندما يُذكَر اسمك في الدردشات الصامتة؟ - نبّهني دوماً + أترغبُ بتلقِّي الإشعارات عندما يُذكَر اسمك في الدردشات الصامتة؟ + نبّهني دومًا لا تنبّهني - اسم المُستخدم الخاص بك ليس مرئيًا على حسابك الشخصي، وكذلك رمز ورابط الاستجابة السريعة الخاصين بك. شارك اسم المُستخدم الخاص بك فقط مع الناس الذين تثق بهم. + اسم المُستخدم الخاص بك، وكود الـ QR والرابط ليسوا مرئيين على حسابك الشخصي. شارِك اسم المُستخدم الخاص بك فقط مع الناس الذين تثق بهم. - يُمكن للناس مراسلتك الآن باستخدام اسم المُستخدم الاختياري الخاص بك حتى لا تضطر إلى إعطاء رقم هاتفك. + يُمكن للناس مراسلتك الآن باستخدام اسم المُستخدم الاختياري الخاص بك، بحيث لا تحتاج إلى إعطاء رقم هاتفك. اسم الحساب اسم المُستخدم حول - فشل ضبط صورة حسابك + فشل ضبط الصورة الرمزية الشارات - رمز أو رابط الاستجابة السريع - تحرير الصورة + كود الـ QR أو الرابط + تعديل الصورة - شارك اسم المُستخدم الخاص بك + شارِك اسم المُستخدم الخاص بك - اسمح للآخرين ببدء محادثة معك عبر مشاركة رمز أو رابط الاستجابة السريع الفريد الخاص بك. + اسمح للآخرين ببدء دردشة معك عبر مشاركة رابط أو كود QR فريد خاص بك. - تم إنشاء اسم المُستخدم + تمَّ إنشاء اسم المُستخدم - تم نسخ اسم المُستخدم + تمَّ نسخ اسم المُستخدم - تعذّر حذف اسم المُستخدم. يُرجى المحاولة مرة أخرى لاحقًا. + تعذَّر حذف اسم المُستخدم. حاوِل مرّة أخرى لاحقًا. - تم حذف اسم المُستخدم + تمَّ حذف اسم المُستخدم - حذف اسم المُستخدم؟ + أترغبُ بحذف اسم المُستخدم؟ - "سيُؤدي هذا إلى إزالة اسم المُستخدم الخاص بك وتعطيل رمز الاستجابة السريع والرابط الخاصين بك. \"%1$s\" سيُصبح متاحًا للآخرين ليستفيدوا منه. هل أنت متأكد؟" + "سيُؤدي هذا إلى إزالة اسم المُستخدم الخاص بك وتعطيل كود الـ QR والرابط الخاصين بك. \"%1$s\" سيُصبح متاحًا للآخرين ليستفيدوا منه. هل أنت متأكد؟" - حدثت مشكلة مع اسم المُستخدم الخاص بك ولم يعد مخصصًا لحسابك. يمكنك محاولة تعيينه من جديد أو اختيار اسم مُستخدم جديد. + حدثت مشكلة ما مع اسم المُستخدم الخاص بك ولم يعد مُخصَّصًا لحسابك. يمكنك محاولة تعيينه من جديد واختيار اسم مُستخدم جديد. - حدث خطأ ما مع رمز الاستجابة السريع ورابط اسم المُستخدم الخاصين بك، حيث لم يعد صالحًا. أنشئ رابطًا جديدًا للمشاركة مع الآخرين. + حدث خطأ ما مع كود الـ QR ورابط اسم المُستخدم الخاصين بك، حيث لم يعد صالحًا. أنشِئ رابطًا جديدًا للمشاركة مع الآخرين. - إصلاح الآن + الإصلاح الآن @@ -1535,7 +1543,7 @@ %1$d مجموعة مشتركة %1$d مجموعة مشتركة - %1$d مجموعات مشتركة + %1$d مجموعتان مشتركتان %1$d مجموعات مشتركة %1$d مجموعة مشتركة %1$d مجموعة مشتركة @@ -1551,124 +1559,124 @@ - إشعارات مخصصة + إشعارات مُخصَّصة الرسائل - استخدم إشعارات مخصصة + استخدم إشعارات مُخصَّصة صوت التنبيه الاهتزاز تخصيص تغيير الصوت و الاهتزاز إعدادات المكالمات - الرنة + النغمة افتراضي غير معروف - وصلة المجموعة - شارك - إعادة تعيين الوصلة - Require admin approval - يتطلب هذا اﻹجراء موافقة المشرف لقبول انضمام أعضاء جدد عبر وصلة المجموعة. - أأنت على يقين من قيامك بإعادة تعيين وصلة المجموعة ؟ لن يتمكن الأشخاص بعد الآن من الانضمام إلى المجموعة باستخدام الوصلة الحالية. + رابط المجموعة + مشاركة + إعادة تعيين الرابط + يتطلَّب موافقة المُشرِف. + يتطلب هذا اﻹجراء موافقة المُشرِف لقبول انضمام أعضاء جدُد عبر رابط المجموعة. + هل ترغب فعلًا بإعادة تعيين رابط المجموعة؟ لن يتمكن الأشخاص بعد الآن من الانضمام إلى المجموعة باستخدام الرابط الحالي. - الرمز المربع - سيتمكن الأشخاص الذين يقومون بالمسح الضوئي لهذا الرمز من الانضمام إلى مجموعتك. سيبقى على عاتق المشرفين الموافقة على الأعضاء الجدد إذا كان هذا الإعداد مُشغَّلا. - مشاركة الرمز + كود الـ QR + سيتمكن الأشخاص الذين يقومون بالمسح الضوئي لهذا الكود من الانضمام إلى مجموعتك. مايزال يحتاج المُشرِفون إلى الموافقة على الأعضاء الجدُد إذا كان هذا الإعداد في وضع تشغيل. + مشاركة الكود - هل تريد سحب الدعوة التي أرسلتها إلى %1$s؟ + هل تريد إلغاء الدعوة التي أرسلتها إلى %1$s؟ - هل تريد سحب %2$d دعوة مُرسلة من طرف %1$s؟ - هل تريد سحب الدعوة المُرسلة من طرف %1$s؟ - هل تريد سحب %2$d دعوتين مُرسلتين من طرف %1$s؟ - هل تريد سحب %2$d دعوات مُرسلة من طرف %1$s؟ - هل تريد سحب %2$d دعوة مُرسلة من طرف %1$s؟ - هل تريد سحب %2$d دعوة مُرسلة من طرف %1$s؟ + هل تريد إلغاء %2$d دعوة مُرسَلة من طرف %1$s؟ + هل تريد إلغاء الدعوة المُرسَلة من طرف %1$s؟ + هل تريد إلغاء %2$d دعوتين مُرسَلتين من طرف %1$s؟ + هل تريد إلغاء %2$d دعوات مُرسَلة من طرف %1$s؟ + هل تريد إلغاء %2$d دعوة مُرسَلة من طرف %1$s؟ + هل تريد إلغاء %2$d دعوة مُرسَلة من طرف %1$s؟ أنت عضو بالفعل انضم - أرسل طلب للانضمام - تعذر الانضمام إلى المجموعة. يُرجى إعادة المحاولة لاحقا - حدث خطأ فى الشبكة - وصلة هذه المجموعة غير مُفعَّلة + أرسِل طلب للانضمام + تعذَّر الانضمام إلى المجموعة. يُرجى إعادة المحاولة لاحقًا. + حدث خطأ فى الشبكة. + رابط هذه المجموعة غير مُفعَّل. - تعذر الانضمام للمجموعة. + تعذَّر الانضمام للمجموعة. - لا يمكنك الانضمام لهذه المجموعة من خلال الرابط لأن أحد المشرفين أزالك. + لا يمكنك الانضمام لهذه المجموعة من خلال رابط المجموعة لأن أحد المُشرِفين قام بإزالتك. - لم يعُد رابط المجموعة هذه صالحًا. + لم يعُد رابط المجموعة هذا صالحًا. خطأ في الرابط - فشِل الانضمام عبر هذا الرابط. يُرجى محاولة الانضمام لاحقًا. + فشِل الانضمام عبر هذا الرابط. حاوِل الانضمام لاحقًا. هل تريد الانضمام إلى هذه المجموعة ومشاركة اسمك وصورتك مع أعضائها؟ - لتستطيع الإنضمام إلى هذه المجموعة يجب أن يوافق مشرف من هذه المجموعة على طلب انضمامك. عندما ترسل طلب انضمام، سيتم مشاركة اسمك وصورتك مع أعضاء المجموعة. + لتستطيع الانضمام إلى هذه المجموعة، يجب أن يوافق مُشرِف من هذه المجموعة على طلب انضمامك. عندما ترسل طلب انضمام، سيتم مشاركة اسمك وصورتك مع أعضاء المجموعة. مجموعة · %1$d عضو مجموعة · %1$d عضو - مجموعة · %1$d أعضاء + مجموعة · %1$d عضوان مجموعة · %1$d أعضاء - مجموعة · %1$d دعوة + مجموعة · %1$d عضوًا مجموعة · %1$d عضو - قم بتحديث سيجنال لاستخدام وصلات المجموعة - لا يدعم إصدار سيجنال المُستعمَل وصلة المجموعة هذه. لذا، يُرجى التحديث إلى آخر إصدار للانضمام إلى هذه المجموعة عبر وصلة. - قم بتحديث سيجنال - وصلة المجموعة غير صالحة + قُم بتحديث سيجنال لاستخدام روابط المجموعة. + لا يدعم إصدار سيجنال المُستعمَل رابط المجموعة هذا. لذا، قُم بالتحديث إلى آخر إصدار للانضمام إلى هذه المجموعة عبر رابط. + قُم بتحديث سيجنال + رابط المجموعة غير صالح - دعوة الأصدقاء - شارك أصدقاءك بوصلة للسماح لهم بالانضمام لهذه المجموعة بسرعة. + اُدْعُ الأصدقاء + شارِك رابطًا مع أصدقائك للسماح لهم بالانضمام بسرعة لهذه المجموعة. - تفعيل ومشاركة الوصلة - مشاركة الوصلة + تفعيل ومشاركة رابط + مشاركة رابط - تعذر تفعيل وصلة المجموعة. يُرجى إعادة المحاولة لاحقا - حدث خطأ فى الشبكة - ليس لديك الصلاحية لتفعيل وصلة المجموعة. يُرجى الطلب من مشرف القيام بذلك. - لست عضوا في المجموعة حاليا. + تعذَّر تفعيل رابط المجموعة. يُرجى إعادة المحاولة لاحقًا. + حدث خطأ فى الشبكة. + ليس لديك الصلاحية لتفعيل رابط المجموعة. يُرجى الطلب من أحد المشرفين القيام بذلك. + لستَ عضوًا في المجموعة حاليًا. - إضافة \"%1$s\" إلى المجموعة ؟ - رفض طلب \"%1$s\" ؟ + أترغبُ بإضافة \"%1$s\" إلى المجموعة؟ + أترغبُ برفض طلب \"%1$s\"؟ - رفض طلب انضمام المستخدم \"%1$s\"؟ لن يتمكن المُستخدم من إرسال طلب الانضمام مرة أخرى للمجموعة بواسطة رابط للمجموعة. + أترغبُ برفض طلب انضمام المستخدم \"%1$s\"؟ لن يتمكن المُستخدم من إرسال طلب الانضمام مرّة أخرى بواسطة رابط للمجموعة. إضافة رفض - ضبّب الوجوه - جديد: ضبّب الوجوه أو ارسم في أي مكان لتضبيبه - ارسم في أي مكان لتضبيبه - ارسم لتضبيب مزيدٍ من الوجوه والأماكن + تمويه الوجوه + جديد: قُم بتمويه الوجوه أو ارسم في أي مكان لتمويهه + ارسم في أي مكان لتمويهه + ارسم لتمويه مزيدٍ من الوجوه والأماكن - انقر وأضغط لتسجيل رسالة صوتية، اسحب إصبعك للإرسال + انقر واضغط لتسجيل رسالة صوتية، ارفع إصبعك للإرسال تعديل الرسالة - تجاهل المسودّة؟ + أترغبُ بتجاهل المسودّة؟ لا يُمكن التراجع عن هذا الإجراء. تجاهل - شارك - المشاركة مع جهات الاتصال - المشاركة عبر… + مشاركة + مشاركة مع جهات الاتصال + مشاركة عبر… إلغاء - إرسال… - تم إرسال الدعوات! - الدعوة إلى سيجنال + جارٍ الإرسال… + تمَّ إرسال الدعوات! + اُدْعُ إلى سيجنال إرسال رسالة قصيرة (%1$d) هل تريد إرسال %1$d دعوات عبر الرسائل القصيرة ؟ @@ -1679,15 +1687,16 @@ هل تريد إرسال %1$d دعوات عبر رسائل SMS؟ هيا ننتقل إلى سيجنال %1$s + يبدو عدم وجود أي تطبيقات لديك للمشاركة من خلالها. - لمعرفة المزيد + اعرف المزيد - قراءة المزيد + اقرأ المزيد - تعذر العثور على الرسالة + تعذَّر العثور على الرسالة رسالة من %1$s رسالتك @@ -1698,36 +1707,36 @@ الوسائط الملفات - الصوتيات + الملفات الصوتية الجميع - حذف العنصر المختار؟ - حذف العنصر المختار؟ - حذف العنصرين المختارين؟ - حذف العناصر المختارة؟ - حذف العناصر المختارة؟ - حذف العناصر المختارة؟ + أترغبُ بحذف العنصر المُحدَّد؟ + أترغبُ بحذف العنصر المُحدَّد؟ + أترغبُ بحذف العنصرين المُحدَّدين؟ + أترغبُ بحذف العناصر المُحدَّدة؟ + أترغبُ بحذف العناصر المُحدَّدة؟ + أترغبُ بحذف العناصر المُحدَّدة؟ هذا سيحذف جميع الـ %1$d ملف التي تمّ اختياره. سيتم حذف أيضًا أي رسالة نصية ذات صلة بهذه العناصر. هذا سيحذف الملف الذي تمّ اختياره. سيتم حذف أيضًا أي رسالة نصية ذات صلة بهذه العناصر. - هذا سيحذف %1$d الملفين اللذين تمّ اختيارهما. سيتم حذف أيضًا أي رسالة نصية ذات صلة بهذه العناصر. + هذا سيحذف الملفين اللذين تمّ اختيارهما. سيتم حذف أيضًا أي رسالة نصية ذات صلة بهذه العناصر. هذا سيحذف جميع الـ %1$d ملفات التي تمّ اختيارها. سيتم حذف أيضًا أي رسالة نصية ذات صلة بهذه العناصر. هذا سيحذف جميع الـ %1$d ملفًا التي تمّ اختيارها. سيتم حذف أيضًا أي رسالة نصية ذات صلة بهذه العناصر. هذا سيحذف جميع الـ %1$d ملف التي تمّ اختيارها. سيتم حذف أيضًا أي رسالة نصية ذات صلة بهذه العناصر. - جار الحذف - جار حذف الرسائل… - جارٍ جمع المرفقات… + جارٍ الحذف + جارٍ حذف الرسائل… + جارٍ جمع الملفات المُرفَقة… الترتيب بحسب الأحدث الأقدم مساحة التخزين المستخدمة جميع مساحة التخزين المستخدمة عرض شبكي - عرض القائمة - مختار - اختيار الجميع + عرض كقائمة + مُحدَّد + تحديد الجميع حفظ حفظ @@ -1747,16 +1756,16 @@ - تم تحديد %1$d (%2$s) - تم تحديد %1$d (%2$s) - تم تحديد %1$d (%2$s) - تم تحديد %1$d (%2$s) - تم تحديد %1$d (%2$s) - تم تحديد %1$d (%2$s) + تمَّ تحديد %1$d (%2$s) + تمَّ تحديد %1$d (%2$s) + تمَّ تحديد %1$d (%2$s) + تمَّ تحديد %1$d (%2$s) + تمَّ تحديد %1$d (%2$s) + تمَّ تحديد %1$d (%2$s) ملف - صوت - فيديو + ملف صوتي + مقطع فيديو صورة @@ -1767,15 +1776,15 @@ مرسل منك إلى %1$s - ذكرني لاحقاً - التحقق من ‫رقم سيجنال التعريفي الشخصي‬ - قد نسألك أحيانا للتحقق من رقمك التعريفي الشخصي ليصبح بإمكانك تذكره. - التحقق من الرقم التعريفي الشخصي - لنبدأ + ذكِّرني لاحقًا + التحقُّق من ‫رقم التعريف الشخصي (PIN) + قد نسألك أحيانًا للتحقُّق من رقم التعريف الشخصي (PIN) ليصبح بإمكانك تذكُّره. + التحقُّق من رقم التعريف الشخصي (PIN) + ابدأ مجموعة جديدة - دعوة الأصدقاء + اُدْعُ الأصدقاء لون الدردشة - صورة حساب شخصي + إضافة صورة للحساب الإجابات @@ -1788,15 +1797,15 @@ إلغاء المكالمة - تفعيل الإشعارات؟ + أترغبُ بتفعيل الإشعارات؟ عدم تفويت أي رسالة من جهات اتصالك ومجموعاتك. - شغِّل + تشغيل ليس الآن رسالة وسائط متعددة - تنزيل رسالة متعددة الوسائط  - خطأ في تنزيل رسالة متعددة الوسائط، المس لإعادة المحاولة + تنزيل رسالة متعددة الوسائط + خطأ في تنزيل رسالة متعددة الوسائط، انقر لإعادة المحاولة فتح الكاميرا @@ -1809,11 +1818,11 @@ الكاميرا - مجهول - لقد استلمت رسالة مُعمَّاة من نسخة سيجنال قديمة وتوقف دعمها. يرجى أن تطلب من المرسل تحديث نسخة التطبيق ومن ثمّ إعادة إرسال الرسالة. - لقد تركت المجموعة. - قمت بتحديث المجموعة. - تم تحديث المجموعة بنجاح. + غير معروف + استلمتَ رسالةً مُشفَّرة من نسخة سيجنال قديمة كان قد توقَّف دعمها. يُرجى أن تطلب من المُرسِل تحديث نسخة التطبيق ومن ثمّ إعادة إرسال الرسالة. + تركتَ المجموعة. + قمتَ بتحديث المجموعة. + تمَّ تحديث المجموعة. مكالمة صوتية صادرة @@ -1833,197 +1842,197 @@ %1$s . %2$s %1$s قام بتحديث المجموعة. - %1$sمتواجد على سيجنال! - قمت بتعطيل الرسائل المختفية. - قام %1$s بتعطيل الرسائل المختفية. - لقد حددت مدّة ظهور الرّسائل قبل اختفائها ب %1$s. - %1$s حدد مدّة ظهور الرّسائل قبل اختفائها ب %2$s. - تم ضبط مؤقت اختفاء الرسائل بعد انقضاء %1$s. - حُدّثت هذه المجموعة إلى مجموعة جديدة. - تعذرت إضافتك إلى المجموعات الجديدة والآن تمت دعوتك إليها. - تم إنعاش جلسة الدردشة + %1$s متواجد على سيجنال! + قمتَ بتعطيل ميزة إخفاء الرسائل. + قام %1$s بتعطيل ميزة إخفاء الرسائل. + حددتَ مؤقِّت إخفاء الرسالة إلى %1$s. + %1$s حدَّد مؤقِّت إخفاء الرسالة إلى %2$s. + تمَّ ضبط مؤقِّت إخفاء الرسالة إلى %1$s. + تمَّ تحديث هذه المجموعة إلى \"مجموعة جديدة\". + تعذَّرت إضافتك إلى \"المجموعة الجديدة\"، وقد تمَّت دعوتك للانضمام. + تمَّ تحديث جلسة الدردشة. - تعذّر إضافة %1$s عضو إلى المجموعة الجديدة وقد تمت دعوته إليها. - تعذّر إضافة عضو واحد إلى المجموعة الجديدة وقد تمت دعوته إليها. - تعذّر إضافة %1$s عضوين إلى المجموعة الجديدة وقد تمت دعوتهما إليها. - تعذّر إضافة %1$s أعضاء إلى المجموعة الجديدة وقد تمت دعوتهم إليها. - تعذّر إضافة %1$s عضوًا إلى المجموعة الجديدة وقد تمت دعوتهم إليها. - تعذّر إضافة %1$s عضو إلى المجموعة الجديدة وقد تمت دعوتهم إليها. + تعذّرت إضافة %1$s عضو إلى \"المجموعة الجديدة\"، وقد تمَّت دعوته إليها. + تعذّرت إضافة عضو واحد إلى \"المجموعة الجديدة\"، وقد تمَّت دعوته إليها. + تعذّرت إضافة %1$s عضوين إلى \"المجموعة الجديدة\"، وقد تمَّت دعوتهما إليها. + تعذّرت إضافة %1$s أعضاء إلى \"المجموعة الجديدة\"، وقد تمَّت دعوتهم إليها. + تعذّرت إضافة %1$s عضوًا إلى \"المجموعة الجديدة\"، وقد تمَّت دعوتهم إليها. + تعذّرت إضافة %1$s عضو إلى\" المجموعة الجديدة\"، وقد تمَّت دعوتهم إليها. - تعذّر إضافة %1$s عضو إلى المجموعة الجديدة وقد تمت إزالته. - تعذّر إضافة عضو واحد إلى المجموعة الجديدة وقد تمت إزالته. - تعذّر إضافة %1$s عضوين إلى المجموعة الجديدة وقد تمت إزالتهما. - تعذّر إضافة %1$s أعضاء إلى المجموعة الجديدة وقد تمت إزالتهم. - تعذّر إضافة %1$s عضوًا إلى المجموعة الجديدة وقد تمت إزالتهم. - تعذّر إضافة %1$s عضو إلى المجموعات الجديدة وقد تمت إزالتهم. + تعذَّرت إضافة %1$s عضو إلى المجموعة الجديدة، وقد تمَّت إزالته. + تعذَّرت إضافة عضو واحد إلى المجموعة الجديدة، وقد تمَّت إزالته. + تعذَّرت إضافة عضوين إلى المجموعة الجديدة، وقد تمَّت إزالتهما. + تعذَّرت إضافة %1$s أعضاء إلى المجموعة الجديدة، وقد تمَّت إزالتهم. + تعذَّرت إضافة %1$s عضوًا إلى المجموعة الجديدة، وقد تمَّت إزالتهم. + تعذَّرت إضافة %1$s عضو إلى المجموعات الجديدة، وقد تمَّت إزالتهم. - غيّر %1$s اسم حسابه الشخصي إلى %2$s. - غيّر %1$s اسم حسابه الشخصي من %2$s إلى %3$s. - غيّر %1$s صفحة حسابه الشخصي. + غيَّر %1$s اسم حسابه الشخصي إلى %2$s. + غيَّر %1$s اسم حسابه الشخصي من %2$s إلى %3$s. + غيَّر %1$s صفحة حسابه الشخصي. - لقد بدأت دردشة مع %1$s. + بدأتَ دردشة مع %1$s. - لقد أنشأتَ مجموعة. - تم تحديث المجموعة. - قم بدعوة الأصدقاء لهذه المجموعة باستخدام وصلة المجموعة + أنشأتَ مجموعة. + تمَّ تحديث المجموعة. + قُم بدعوة الأصدقاء لهذه المجموعة باستخدام رابط المجموعة. - أضفت %1$s. + أضفتَ %1$s. %1$s أضاف %2$s. %1$s أضافك إلى المجموعة. - لقد انضممت إلى المجموعة. + انضممتَ إلى المجموعة. %1$s انضموا إلى المجموعة. - لقد قمت بإزالة %1$s . - %1$s أزال %2$s . + قمتَ بإزالة %1$s . + %1$s أزالَ %2$s . %1$s أزالك من المجموعة. - لقد غادرت المجموعة. - %1$s غادر المجموعة. - لم تعد عضواً في هذه المجموعة. - %1$s لم يعد عضواً في هذه المجموعة. + غادرتَ المجموعة. + %1$s غادرَ المجموعة. + لم تعد عضوًا في هذه المجموعة. + %1$s لم يعد عضوًا في هذه المجموعة. - لقد جعلت %1$s مشرفاً. - لقد جعل %1$s %2$s مشرفاً. - لقد جعلك %1$s مشرفاً. - لقد قمت بإلغاء امتيازات الإشراف على المجموعة من %1$s . - لقد أبطل العضو %1$s امتيازات إشرافك على المجموعة. - قام العضو %1$s بإلغاء امتيازات الإشراف على المجموعة من %2$s. - أصبح العضو %1$s مشرفا. - أنت الآن هو المشرف. - لم يعد العضو %1$s مشرفا. - لم تعد مشرفاً. + جعلتَ %1$s مشرفًا. + قامَ %1$s بتعيين %2$s كمشرفًا. + قام %1$s بتعيينك مُشرِفًا. + قمتَ بإلغاء امتيازات الإشراف على المجموعة من %1$s. + أبطلَ العضو %1$s امتيازات إشرافك على المجموعة. + قامَ العضو %1$s بإلغاء امتيازات الإشراف على المجموعة من %2$s. + أصبح العضو %1$s مُشرِفًا الآن. + أنت الآن هو المُشرِف. + لم يعد العضو %1$s مُشرِفًا. + لم تعد مُشرِفًا. - لقد دعوت %1$s إلى المجموعة. + قمتَ بدعوة %1$s إلى المجموعة. دعاك %1$s إلى المجموعة. - دعا %1$s %2$d شخص إلى المجموعة. - دعا %1$s شخصًا واحدًا إلى المجموعة. - دعا %1$s %2$d شخصين إلى المجموعة. - دعا %1$s %2$d أشخاص إلى المجموعة. - دعا %1$s %2$d شخصًا إلى المجموعة. - دعا %1$s %2$d شخصٍ إلى المجموعة. - - لقد دُعيت إلى المجموعة. + قام %1$s بدعوة %2$d شخص إلى المجموعة. + قام %1$s بدعوة شخصًا واحدًا إلى المجموعة. + قام %1$s بدعوة %2$d شخصين إلى المجموعة. + قام %1$s بدعوة %2$d أشخاص إلى المجموعة. + قام %1$s بدعوة %2$d شخصًا إلى المجموعة. + قام %1$s بدعوة %2$d شخصٍ إلى المجموعة. + + دُعيتَ إلى المجموعة. - دُعي %1$d شخص إلى المجموعة. - دُعي شخص واحد إلى المجموعة. - دُعي %1$d شخصان إلى المجموعة. - دُعي %1$d أشخاص إلى المجموعة. - دُعي %1$d شخصًا إلى المجموعة. - دُعي %1$d شخص إلى المجموعة. + دُعيَ %1$d شخص إلى المجموعة. + دُعيَ شخص واحد إلى المجموعة. + دُعيَ %1$d شخصان إلى المجموعة. + دُعيَ %1$d أشخاص إلى المجموعة. + دُعيَ %1$d شخصًا إلى المجموعة. + دُعيَ %1$d شخص إلى المجموعة. - لقد أبطلت %1$d دعوة إلى مجموعة. - لقد أبطلت دعوة واحدة إلى المجموعة. - لقد أبطلت %1$d دعوتين إلى المجموعة. - لقد أبطلت %1$d دعوات إلى المجموعة. - لقد أبطلت %1$d دعوة إلى المجموعة. - لقد أبطلت %1$d دعوة إلى المجموعة. + أبطلتَ %1$d دعوة إلى مجموعة. + أبطلتَ دعوة واحدة إلى المجموعة. + أبطلتَ %1$d دعوتين إلى المجموعة. + أبطلتَ %1$d دعوات إلى المجموعة. + أبطلتَ %1$d دعوةً إلى المجموعة. + أبطلتَ %1$d دعوة إلى المجموعة. - أبطل %1$s %2$d دعوة إلى المجموعة. - أبطل %1$s دعوة واحدة إلى المجموعة. - أبطل %1$s %2$d دعوتين إلى المجموعة. - أبطل %1$s %2$d دعوات إلى المجموعة. - أبطل %1$s %2$d دعوة إلى المجموعة. - أبطل %1$s %2$d دعوة إلى المجموعة. + أبطلَ %1$s %2$d دعوة إلى المجموعة. + أبطلَ %1$s دعوة واحدة إلى المجموعة. + أبطلَ %1$s %2$d دعوتين إلى المجموعة. + أبطلَ %1$s %2$d دعواتٍ إلى المجموعة. + أبطلَ %1$s %2$d دعوة إلى المجموعة. + أبطلَ %1$s %2$d دعوة إلى المجموعة. رفض شخص ما الدعوة إلى المجموعة. - لقد رفضت الدعوة إلى المجموعة. - أبطل العضو %1$s دعوتك للمجموعة. - أبطل المشرف دعوتك للمجموعة. + رفضتَ الدعوة إلى المجموعة. + أبطلَ العضو %1$s دعوتك إلى المجموعة. + أبطلَ المُشرِف دعوتك للمجموعة. - تم إبطال %1$d دعوة للمجموعة. - تم إبطال دعوة للمجموعة. - تم إبطال %1$d دعوتين للمجموعة. - تم إبطال %1$d دعوات للمجموعة. - تم إبطال %1$d دعوة للمجموعة. - تم إبطال %1$d دعوة للمجموعة. + تمَّ إبطال %1$d دعوة للمجموعة. + تمَّ إبطال دعوة للمجموعة. + تمَّ إبطال %1$d دعوتين للمجموعة. + تمَّ إبطال %1$d دعواتٍ للمجموعة. + تمَّ إبطال %1$d دعوةً للمجموعة. + تمَّ إبطال %1$d دعوة للمجموعة. - لقد قبلت الدعوة إلى المجموعة. - قبل العضو %1$s الدعوة إلى المجموعة. - أضفت العضو المدعوّ %1$s. - أضاف العضو %1$s العضو %2$s الذي دُعي من قبل. + قبلتَ الدعوة إلى المجموعة. + قبلَ العضو %1$s الدعوة إلى المجموعة. + أضفتَ العضو %1$s الذي تمَّت دعوته. + أضاف %1$s العضو %2$s الذي دُعيَ من قبل. - لقد قمت بتغيير اسم المجموعة إلى \"%1$s\". - قام الغضو %1$s بتغيير اسم المجموعة إلى \"%2$s \". - تم تغيير اسم المجموعة إلى \"%1$s\". + قمتَ بتغيير اسم المجموعة إلى \"%1$s\". + قام العضو %1$s بتغيير اسم المجموعة إلى \"%2$s \". + تمَّ تغيير اسم المجموعة إلى \"%1$s\". - لقد غيَّرت وصف المجموعة. - غيَّر %1$s وصف المجموعة. - لقد تغير وصف المجموعة. + غيَّرتَ وصف المجموعة. + قامَ %1$s بتغيير وصف المجموعة. + تمَّ تغيير وصف المجموعة. - لقد قمت بتغيير صورة المجموعة. - قام الغضو %1$s بتغيير صورة للمجموعة. - تم تغيير صورة المجموعة. + قمتَ بتغيير الصورة الرمزية للمجموعة. + قامَ العضو %1$s بتغيير الصورة الرمزية للمجموعة. + تمَّ تغيير الصورة الرمزية للمجموعة. - لقد قمت بتغيير من يمكنه تحرير معلومات المجموعة ليصبح : \"%1$s\". - لقد غير الغضو%1$s من يمكنه تحرير معلومات المجموعة ليصبح : \"%2$s\". - تم تغيير من يمكنهم تعديل معلومات المجموعة ليصبح : \"%1$s\". + قمتَ بتغيير من يمكنه تعديل معلومات المجموعة ليصبح \"%1$s\". + غيَّر العضو %1$s من يمكنه تحرير معلومات المجموعة ليصبح \"%2$s\". + تمَّ تغيير من يمكنهم تعديل معلومات المجموعة ليصبح \"%1$s\". - أنت غيرت الأشخاص الذين يستطيعون تعديل عضوية المجموعة إلى \"%1$s\". - غير %1$s أشخاص الذين يستطيعون تعديل عضوية المجموعة إلى \"%2$s\". - تم تغيير من يمكنهم تعديل عضوية المجموعة ليصبح : \"%1$s\". + قمتَ بتغيير الأشخاص الذين يستطيعون تعديل عضوية المجموعة إلى \"%1$s\". + غيَّر %1$s الأشخاص الذين يستطيعون تعديل عضوية المجموعة إلى \"%2$s\". + تمَّ تغيير من يمكنهم تعديل عضوية المجموعة ليصبح \"%1$s\". - لقد قمت بتغيير إعدادات المجموعة للسماح للمُشرفين فقط بإرسال الرسائل. - لقد قمت بتغيير إعدادات المجموعة للسماح للمُشرفين فقط بإرسال الرسائل. - لقد غيَّر العضو %1$s إعدادات المجموعة للسماح لجميع الأعضاء بإرسال الرسائل. - لقد غيَّر العضو %1$s إعدادات المجموعة للسماح للمُشرفين فقط بإرسال الرسائل. - لقد تغيرت إعدادات المجموعة للسماح لجميع الأعضاء بإرسال الرسائل. - لقد تغيرت إعدادات المجموعة للسماح فقط للمُشرفين بإرسال الرسائل. + قمتَ بتغيير إعدادات المجموعة للسماح للمُشرِفين فقط بإرسال الرسائل. + قمتَ بتغيير إعدادات المجموعة للسماح للمُشرفين فقط بإرسال الرسائل. + غيَّر العضو %1$s إعدادات المجموعة للسماح لجميع الأعضاء بإرسال الرسائل. + غيَّر العضو %1$s إعدادات المجموعة للسماح للمُشرفين فقط بإرسال الرسائل. + تمَّ تغيير إعدادات المجموعة للسماح لجميع الأعضاء بإرسال الرسائل. + تمَّ تغيير إعدادات المجموعة للسماح فقط للمُشرِفين بإرسال الرسائل. - لقد قمت بتفعيل وصلة المجموعة مع إيقاف موافقة المشرف. - لقد قمت بتشغيل وصلة المجموعة مع تفعيل موافقة المشرف. - لقد عطلت وصلة المجموعة. - فعَّل العضو %1$s وصلة المجموعة مع إيقاف موافقة المشرف. - فعَّل العضو %1$s وصلة المجموعة مع تشغيل موافقة المشرف. - عطل العضو %1$s وصلة المجموعة. - تم تشغيل وصلة المجموعة مع إيقاف موافقة المشرف. - تم تشغيل وصلة المجموعة مع تفعيل موافقة المشرف. - عُطلَت وصلة المجموعة. - لقد أوقفت موافقة المشرف لوصلة المجموعة. - أوقف العضو %1$s موافقة المشرف لوصلة المجموعة. - أُوقِفت موافقة المشرف لوصلة المجموعة. - لقد شغّلت موافقة المشرف لوصلة المجموعة. - شغّل العضو %1$s موافقة المشرف لوصلة المجموعة. - فُعّلَت موافقة المشرف لوصلة المجموعة. + قمتَ بتفعيل رابط المجموعة بينما كانت موافقة المُشرِف في حالة إيقاف. + قمتَ بتشغيل رابط المجموعة بينما كانت موافقة المُشرِف في حالة تشغيل. + قمتَ بتعطيل رابط المجموعة. + فعَّل العضو %1$s رابط المجموعة بينما كانت موافقة المُشرِف في حالة إيقاف. + فعَّل العضو %1$s رابط المجموعة بينما كانت موافقة المُشرِف في حالة تشغيل. + عطَّل العضو %1$s رابط المجموعة. + تمَّ تشغيل رابط المجموعة بينما كانت موافقة المُشرِف في حالة إيقاف. + تمَّ تشغيل رابط المجموعة بينما كانت موافقة المُشرِف في حالة تشغيل. + تمَّ إيقاف رابط المجموعة. + أوقفتَ موافقة المُشرِف لرابط المجموعة. + أوقفَ العضو %1$s موافقة المُشرِف لرابط المجموعة. + تمَّ إيقاف موافقة المُشرِف لرابط المجموعة. + قمتَ بتفعيل موافقة المُشرِف لرابط المجموعة. + شغَّل العضو %1$s موافقة المُشرِف لرابط المجموعة. + تمَّ تفعيل موافقة المُشرِف لرابط المجموعة. - لقد أعدت تعيين وصلة المجموعة. - أعاد العضو %1$s تعيين وصلة المجموعة. - لقد أُعيد تعيين وصلة المجموعة. + أعدتَ تعيين رابط المجموعة. + أعاد العضو %1$s تعيين رابط المجموعة. + أُعيد تعيين رابط المجموعة. - لقد انضممت للمجموعة بواسطة وصلة المجموعة. - لقد انضم العضو %1$s للمجموعة بواسطة وصلة المجموعة. + انضمَمتَ للمجموعة بواسطة رابط المجموعة. + انضم العضو %1$s للمجموعة بواسطة رابط المجموعة. - لقد أرسلت طلب للانضمام للمجموعة. - طلب العضو %1$s الانضمام للمجموعة بواسطة وصلة المجموعة. + أرسلتَ طلب للانضمام إلى المجموعة. + طلب العضو %1$s الانضمام إلى المجموعة بواسطة رابط المجموعة. - أرسل %1$s %2$d طلب للانضمام وألغاه عبر رابط المجموعة. + أرسل %1$s طلب للانضمام وألغاه عبر رابط المجموعة. أرسل %1$s طلبًا للانضمام وألغاه عبر رابط المجموعة. - أرسل %1$s طلبين %2$d للانضمام وألغاهما عبر رابط المجموعة. + أرسل %1$s طلبين للانضمام وألغاهما عبر رابط المجموعة. أرسل %1$s %2$d طلبات للانضمام وألغاها عبر رابط المجموعة. أرسل %1$s %2$d طلبًا للانضمام وألغاها عبر رابط المجموعة. أرسل %1$s %2$d طلب للانضمام وألغاها عبر رابط المجموعة. @@ -2031,43 +2040,43 @@ قبِل العضو %1$s طلبك للانضمام للمجموعة. - قبِل العضو %1$s طلب %2$s انضمام للمجموعة. - لقد وافقت على طلب انضمام %1$s إلى المجموعة. - تمت الموافقة على طلبك للانضمام للمجموعة. - تمت الموافقة على طلب %1$s للانضمام للمجموعة. + قبِلَ %1$s طلب %2$s للانضمام للمجموعة. + وافقتَ على طلب انضمام %1$s إلى المجموعة. + تمَّت الموافقة على طلبك للانضمام للمجموعة. + تمَّت الموافقة على طلب %1$s للانضمام للمجموعة. - تمّ رفض طلبك للانضمام للمجموعة من قبل مشرف عليها. - رفض العضو %1$s طلب %2$s للانضمام للمجموعة. - رُفض طلب %1$s للانضمام للمجموعة. - لقد ألغيت طلب انضمامك للمجموعة. - ألغي طلب الانضمام للمجموعة من طرف %1$s. + تمَّ رفض طلبك للانضمام للمجموعة من قبل مُشرِف عليها. + رفضَ %1$s طلب %2$s للانضمام للمجموعة. + رُفِضَ طلب %1$s للانضمام للمجموعة. + ألغيتَ طلب انضمامك للمجموعة. + قام %1$s بإلغاء طلبه للانضمام إلى المجموعة. - تم تغيير رقم الأمان الخاص بينك وبين %1$s. - لقد قمت بالتحقق من رقم الأمان مع %1$s - لقد قمت بالإشعار بأنك تحققت من رقم الأمان مع %1$s عن طريق جهاز آخر - لقد قمت بالإشعار بأنّ رقم الأمان مع %1$s لم يتم التحقّق منه - لقد قمت بالإشعار بأنّ رقم الأمان مع %1$s لم يتم التحقّق منه عن طريق جهاز آخر - تعذر توصيل رسالة من %1$s - لقد تغير رقم هاتف %1$s. + تمَّ تغيير رقم الأمان الخاص بينك وبين %1$s. + قمتَ بتأكيد التحقُّق من رقم الأمان مع %1$s. + قمتَ بتأكيد التحقُّق من رقم الأمان مع %1$s عن طريق جهاز آخر. + قمتَ بتأكيد أنه لم يتم التحقُّق من رقم الأمان مع %1$s. + قمتَ بتأكيد أنه لم يتم التحقُّق من رقم الأمان مع %1$s عن طريق جهازٍ آخر. + تعذَّر توصيل رسالة من %1$s. + قام %1$s بتغيير رقم هاتفه. - أتعجبك هذه الميزة الجديدة؟ ادعم سيجنال بتقديمك تبرعًا لمرة واحدة. + أتعجبكُ هذه الميزة الجديدة؟ ادعم سيجنال بتقديمك تبرعًا لمرة واحدة. - تمّ دمج سِجّل رسائلك مع %1$s ورقمه %2$s. + تمَّ دمج سِجّل رسائلك مع %1$s ورقمه %2$s. - تمّ دمج سِجّل رسائلك مع %1$s ودردشة أخرى تخصه. + تمَّ دمج سِجّل رسائلك مع %1$s ودردشة أخرى تخصه. - يعود الرقم %1$s إلى %2$s + يعود الرقم %1$s إلى %2$s. - أرسلت لـ %1$s طلبًا لتفعيل عمليات الدفع + أرسلتَ لـ %1$s طلبًا لتفعيل عمليات الدفع. - يريدك %1$s أن تُفعّل عمليات الدفع. أرسل عمليات الدفع فقط للأشخاص الذين تثق بهم. + يريدك %1$s أن تُفعّل عمليات الدفع. أرسِل عمليات الدفع فقط للأشخاص الذين تثق بهم. - لقد فعّلت عمليات الدفع + فعّلتَ عمليات الدفع. - يُمكن لـ %1$s قبول عمليات الدفع الآن + يُمكن لـ %1$s قبول عمليات الدفع الآن. تفاصيل الدفع غير متوفرة @@ -2138,9 +2147,9 @@ - لن تتمكن من إرسال رسائل SMS من سيجنال قريبًا. قم بدعوة %1$s إلى سيجنال لإبقاء المحادثة هنا. + لن تتمكن قريبًا من إرسال رسائل SMS من سيجنال. قُم بدعوة %1$s إلى سيجنال لإبقاء المحادثة هنا. - لن تتمكن من إرسال رسائل SMS من سيجنال بعد الآن. قم بدعوة %1$s إلى سيجنال لإبقاء المحادثة هنا. + لن تتمكن بعد الآن من إرسال رسائل SMS من سيجنال. قُم بدعوة %1$s إلى سيجنال لإبقاء المحادثة هنا. عمليات الدفع: %1$s @@ -2157,56 +2166,57 @@ حظر رفع الحظر - هل تسمح لـ %1$s بمراسلتك ومشاركة اسمك وصورتك معهم؟ لقد حذفت هذا الشخص في الماضي. - هل تود السماح لـ%1$s بمراسلتك، بالإضافة إلى مشاركة اسمك وصورتك معه؟ لن يعرف أنك قرأت رسالته إلى أن تسمح له بذلك. + هل تسمح لـ %1$s بمراسلتك وهل توافق على مشاركة اسمك وصورتك معه؟ حذفت هذا الشخص في الماضي. + هل تود السماح لـ %1$s بمراسلتك وهل ترغب بمشاركة اسمك وصورتك معه؟ لن يعرف أنك قرأتَ رسالته إلى أن تسمح له بذلك. - هل تود السماح لـ %1$s بمراسلتك ومشاركة اسمك وصورتك معه؟ لن تصله أي رسالة حتى تزيل الحظر عنه. + هل تود السماح لـ %1$s بمراسلتك وهل ترغب بمشاركة اسمك وصورتك معه؟ لن تصلك أي رسائل حتى تزيل الحظر عنه. هل تود السماح لـ %1$s بإرسال رسالة لك؟ لن تتلقى أي رسالة منه حتى تُلغي حظرك له. - الحصول على الجديد والتحديثات من %1$s؟ لن تصلك أي تحديثات حتى يُرفَع حظرك عنها. - مُواصلة دردشاتك مع هذه المجموعة ومُشاركة اسمك وصورتك مع أعضاءها؟ - لم يعد ممكنًا استخدام هذه المجموعة القديمة. أنشئ مجموعة جديدة لتفعيل المزايا الجديدة مثل @الإشارات والمشرفين. - لا يمكن استخدام هذه المجموعة القديمة الطراز لأنها كبيرة جدا. الحد الأقصى للمجموعة هو %1$d. - مُواصلة هذه الدردشة مع %1$s ومُشاركة اسمك وصورتك معه؟ - الانضمام إلى هذه المجموعة ومشاركة اسمك وصورتك مع أعضائها؟ لن يعرفوا أنك قرأت رسالتهم حتى توافق. - هل تود الانضمام إلى هذه المجموعة ومشاركة اسمك وصورتك مع أعضائها؟ لن تظهر لك رسائلهم حتى تسمح بذلك. - هل تود الانضمام إلى هذه المجموعة؟ لن يعرفوا أنك قرأت رسائلهم إلا إذا وافقت. - إزالة الحظر عن هذه المجموعة ومشاركة اسمك وصورتك مع أعضائها؟ لن تتلقى أية رسائل حتى تُزيل الحظر. + + أترغبُ بالحصول على الأخبار والتحديثات من %1$s؟ لن تصلك أي تحديثات حتى ترفع الحظر عنه. + هل ترغبُ بمتابعة الدردشة مع هذه المجموعة ومُشاركة اسمك وصورتك مع أعضاءها؟ + لم يعد ممكنًا استخدام هذه المجموعة ذات النمط القديم. أنشِئ مجموعة جديدة لتفعيل المزايا الجديدة مثل الإشارة بتاغ @mentions والمُشرِفين. + لا يمكن استخدام هذه المجموعة ذات النمط السابق لأنها كبيرة جدًا. الحد الأقصى للمجموعة هو %1$d. + هل ترغب بمتابعة هذه الدردشة مع %1$s ومُشاركة اسمك وصورتك معه؟ + أترغبُ بالانضمام إلى هذه المجموعة ومشاركة اسمك وصورتك مع أعضائها؟ لن يعرفوا أنك قرأتَ رسائلهم حتى تقبل بذلك. + أترغبُ بالانضمام إلى هذه المجموعة ومشاركة اسمك وصورتك مع أعضائها؟ لن تظهر لك رسائلهم حتى تقبل بذلك. + هل ترغبُ بالانضمام إلى هذه المجموعة؟ لن يعرفوا أنك قرأت رسائلهم حتى تقبل بذلك. + هل ترغبُ بإزالة الحظر عن هذه المجموعة ومشاركة اسمك وصورتك مع أعضائها؟ لن تتلقَ أية رسائل حتى تُزيل الحظر عنها. - إظهار + عرض عضو في %1$s عضو في %1$s و%2$s عضو في %1$s و%2$s و%3$s %1$d عضو %1$d عضو - %1$d عضو + عضوان %1$d أعضاء - %1$d عضوا - %1$d عضو + %1$d عضوًا + %1$d عضوٍ %1$d أعضاء (%2$s) %1$d عضو (%2$s) - %1$d أعضاء (%2$s) + %1$d عضوان (%2$s) %1$d أعضاء (%2$s) %1$d أعضاء (%2$s) %1$d أعضاء (%2$s) - +%1$d مدعون + +%1$d مدعوون +%1$d مدعو - +%1$d مدعون - +%1$d مدعون - +%1$d مدعون + +%1$d مدعوان + +%1$d مدعوون + +%1$d مدعوون +%1$d مدعوون %1$d مجموعة إضافية %1$d مجموعة إضافية - %1$d مجموعات إضافية + %1$d مجموعتان إضافيتان %1$d مجموعات إضافية %1$d مجموعة إضافية %1$d مجموعة إضافية @@ -2216,11 +2226,11 @@ العبارات السرية غير متطابقة! - العبارة السرية القديمة غير صحيحة! + عبارة سرية قديمة وغير صحيحة! أدخل عبارة سرية جديدة! - ربط هذا الجهاز؟ + هل ترغبُ بربط هذا الجهاز؟ مواصلة سوف يتمكن من @@ -2229,21 +2239,21 @@ ربط الجهاز يجري ربط الجهاز الجديد… - تمت المصادقة على الجهاز. + تمَّت المصادقة على الجهاز. لم يتم العثور على أي جهاز. خطأ في الشبكة. - الرمز باطل. - عذرا، لديك عدة أجهزة مرتبطة. يرجى إزالة بعضها - عذرا، الرابط غير صحيح. - ربط جهاز سيجنال آخر ؟ - يبدو أنك تحاول ربط جهاز سيجنال باستخدام ماسح ضوئي من طرف ثالث. الرجاء مسح الرمز مرّة أخرى باستخدام سيجنال من أجل حمايتك. + كود الـ QR غير صالح. + عذرًا، لديك عدة أجهزة مرتبطة. يُرجى إزالة بعضها. + عذرًا، الرابط غير صحيح. + هل ترغبُ بربط جهاز يستخدم سيجنال؟ + يبدو أنك تحاول ربط جهاز يستخدم سيجنال باستخدام ماسح ضوئي من طرف ثالث. للحماية، امسح الكود مرّة أخرى باستخدام سيجنال. - يحتاج سيجنال إلى إذن الوصول إلى الكاميرا من أجل مسح الرمز المربع ضوئيا ولكن رُفض الإذن على نحو دائم، الرجاء زيارة إعدادات التطبيق، ثم اختيار « الصلاحيات »، ثم تفعيل « الكاميرا ». - لا يمكن مسح رمز الاستجابة السريع بدون منح إذن الوصول إلى الكاميرا + يحتاج سيجنال إلى إذن الوصول إلى الكاميرا من أجل مسح كود الـ QR ضوئيًا، ولكن رُفِض الإذن على نحو دائم. يُرجى زيارة \"الإعدادات\" في جهازك، وتحديد \"التطبيقات\"، ثم اختيار \"الأذونات\"، ثم تفعيل \"الكاميرا\". + لا يمكن مسح كود الـ QR بدون منح إذن الوصول إلى الكاميرا حدِّث الآن - تنتهي اليوم صلاحية إصدار سيجنال. يُرجى اللمس لتثبيت آخر نسخة من التطبيق. + تنتهي اليوم صلاحية إصدار سيجنال. قُم بتثبيت النسخة الأحدث. تنتهي صلاحية إصدار سيجنال خلال %1$d يوم. يُرجى التحديث لآخر إصدار. تنتهي صلاحية إصدار سيجنال غدًا. يُرجى التحديث لآخر إصدار. @@ -2541,6 +2551,7 @@ لا يوجد أحد هنا %1$s في هذه المكالمة + %1$s في هذه المكالمة العضوان %1$s و %2$s موجودان في هذه المكالمة @@ -2987,7 +2998,7 @@ سنحاول من جديد لاحقًا. تم تحديث سيجنال بنجاح تم تحديثك تلقائيًا إلى الإصدار %1$s. - You updated to version %1$s. + قمتَ بالتحديث إلى النسخة %1$s. هل تود إرسال الرسالة ؟ @@ -3435,6 +3446,7 @@ صورة للمرفق مُصغَّرة فتح دُرج مرفقات الكاميرا تسجيل وإرسال مرفق صوتي + قفل التسجيل الصوتي لم تُرسَل الرسالة، علبك التأكد من اتصالك بالانترنت ثم المحاولة مجددا. @@ -3453,6 +3465,7 @@ تمّت قراءة الرسالة + صورة جهة الاتصال @@ -3537,6 +3550,7 @@ يبدو أن كل شيء جيد الآن ! لتلقي إشعارات المكالمات، يكفي اللمس هنا وتشغيل « إظهار الإشعارات ». لتلقي إشعارات المكالمات، يُرجى اللمس هنا ثم تفعيل الإشعارات وبعدها التأكد من أن الأصوات والنوافذ المنبثقة مُفعَّلة. + لتلقي إشعارات المكالمات، يُرجى اللمس هنا ثم تفعيل أنشطة الخلفية في إعدادات البطارية. الإعدادات لتلقي إشعارات المكالمات، يكفي لمس « الإعدادات » ثم تفعيل « إظهار الإشعارات ». @@ -3702,7 +3716,7 @@ أنا في استراحة أعمل حاليا على شيء جديد - One or more characters is invalid. + رمز واحد أو أكثر غير صالح. تعديل المجموعة @@ -3801,6 +3815,7 @@ + معلومات الدعم طلب الدعم حول سيجنال Android @@ -3893,6 +3908,7 @@ يمكنك جلب معاينة وصلات المواقع مباشرة للرسائل التي سترسلها. غير العبارة السرية غير العبارة السرية + تفعيل عبارة السرية لقفل الشاشة قفل الشاشة وإخطارات الرسائل بعبارة سرية تأمين الشاشة @@ -3905,6 +3921,7 @@ نمط وميض LED تخصيص تغيير الصوت و الاهتزاز + صوت صامت افتراضي @@ -5886,6 +5903,7 @@ إضافة للقصة إضافة نص + إضافة رد إرسال إلى وسائط للمُشاهدة مرة واحدة @@ -6510,6 +6528,8 @@ %1$d جوابا %1$d جواب + + Story no longer hidden إضافة @@ -7899,7 +7919,23 @@ - حرّر %1$s من المساحة لتنزيل وسائطك. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7962,19 +7998,15 @@ موافق - - - تاريخ الدفع - - النسخ الاحتياطي للنص وكل الوسائط - - تفاصيل عمليات الدفع - - نوع النُسخ الاحتياطية - - تاريخ الدفع - - شارك + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7989,6 +8021,10 @@ وتيرة النسخ الاحتياطي إجراء النسخ الاحتياطي باستخدام البيانات الخلوية + + View backup key + + Unlock to view backup key إيقاف وحذف النسخ الاحتياطية @@ -8002,13 +8038,27 @@ سيتم إنشاء نسخة احتياطية خلال الليل. - نوع النُسخ الاحتياطية + Backup plan تعطيل النُسخ الاحتياطية - - %1$s · %2$s/شهريًا - - تفعيل النُسخ الإحتياطية + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -8058,33 +8108,33 @@ - Your backup key + مفتاح النسخة الاحتياطية الخاص بك - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + مفتاح النسخة الاحتياطية الخاص بك هو كود مُكوَّن من 64 رقمًا يَسمح لك باسترجاع نسختك الاحتياطية عندما تُعيد تثبيت سيجنال. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + إذا نسيتَ مفتاحك، لن تتمكَّن من استرجاع نسختك الاحتياطية. لا يُمكن لسيجنال مُساعدتك على استرجاع نسختك الاحتياطية. - Next + التالي - Record your backup key + احتفِظ بنسخة عن مفتاح النسخة الاحتياطية الخاص بك. - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + هذا المفتاح مطلوب لاسترجاع بياناتك وحسابك. احتفِظ بهذا المفتاح في مكانٍ آمن. إذا فقدته، لن تتمكّن من استرجاع حسابك. - Copy to clipboard + نسخ إلى الحافظة - Next + التالي - Keep your key safe + حافظ على مفتاحك في مكانٍ آمن. - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + لن يتمكّن سيجنال من مُساعدتك على استرجاع نسختك الاحتياطية إذا فقدتَ مفتاحك. احتفِظ بمفتاحك في مكانٍ آمن، ولا تُشاركه مع أي أحد. - I\'ve recorded my key + احتفظتُ بمفتاحي - Continue + متابعة - See key again + اطَّلِع على المفتاح مُجدَّدًا diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 126f6fcd1d..4b92c60a01 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Bəli Xeyr Sil @@ -260,6 +263,7 @@ Foto üçün toxunun, video üçün basıb saxlayın + Çək Kameranı dəyişdir Qalereyanı aç @@ -346,7 +350,7 @@ Bu zəng keçidi düzgün deyil. Qoşulma cəhdindən əvvəl bütün keçidin tam və düzgün olduğundan əmin olun. - You are already in a call + Artıq zəngdəsiniz @@ -403,6 +407,7 @@ Bu medianı açmaq üçün tətbiq tapıla bilmədi. %1$s kopyalandı Göndərən: %1$s + Alan:%1$s   Daha çox oxu   Daha çox endir @@ -436,6 +441,7 @@ Redaktə ediləni göndər Mesaj yarat Təəssüf ki, qoşmanız tənzimlənərkən xəta baş verdi. + Alıcı, etibarlı bir SMS və ya e-poçt ünvanı deyil! Mesaj boşdur! Qrup üzvləri @@ -576,6 +582,7 @@ %1$d qoşma yaddaş faylında saxlanılır… Gözlənilir… + Verilənlər (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \"%1$s\" ilə əlaqə kəsilsin? Bu cihazla əlaqəni kəsdikdən sonra, bu cihaz üzərindən mesaj ala və ya göndərə bilməyəcəksiniz. Şəbəkə ilə bağlantı qurulmadı + Yenidən sına Cihazla əlaqə kəsilir… Cihazla əlaqə kəsilir… @@ -1408,7 +1416,7 @@ Qrup bağlantısı Paylaş Bağlantını sıfırla - Require admin approval + Admin təsdiqi tələb olunur Qrup bağlantısı vasitəsilə qoşulan yeni üzvlərin admin tərəfindən təsdiqlənməsi tələb olunsun. Qrup bağlantısını sıfırlamaq istədiyinizə əminsiniz? İnsanlar, artıq hazırkı bağlantını istifadə edərək qrupa qoşula bilməyəcək. @@ -1507,6 +1515,7 @@ %1$d SMS dəvəti göndərilsin? Gəlin Signal-a keçək: %1$s + Bunu paylaşmaq üçün heç bir tətbiqiniz yoxdur kimi görünür. @@ -1931,6 +1940,7 @@ %1$s adlı istifadəçinin sizə mesaj göndərməsinə icazə verərək adınızı və fotonuzu onunla paylaşırsınız? Blokdan çıxaranadək heç bir mesaj almayacaqsınız. %1$s sizə mesaj göndərsin? Onu blokdan çıxaranadək heç bir mesaj almayacaqsınız. + %1$s üzərindən yeniliklər və xəbərlər alınsın? Blokdan çıxaranadək heç bir yenilik almayacaqsınız. Bu qrupla çata davam edib adınızı və fotonuzu üzvlərlə paylaşmaq istəyirsiniz? Bu köhnə qrupdan artıq istifadə etmək mümkün deyil. @xatırlatmalar və adminlər kimi yeni xüsusiyyətləri aktivləşdirmək üçün yeni bir qrup yaradın. @@ -2245,6 +2255,7 @@ Burada heç kim yoxdur %1$s bu zəngdədir + %1$s bu zəngdədir %1$s və %2$s bu zəngdədir @@ -2647,7 +2658,7 @@ Daha sonra yenidən cəhd edəcəyik. Signal uğurla yeniləndi %1$s versiyası avtomatik yeniləndi. - You updated to version %1$s. + %1$s versiyasına yenilədiniz. Mesaj göndərilsin? @@ -3067,6 +3078,7 @@ Qoşma eskizi Cəld kamera siyirməsini aç/bağla Səs yaz və göndər + Səsyazmanı kilidlə Mesaj göndərilə bilmir. Bağlantınızı yoxlayıb yenidən sınayın. @@ -3085,6 +3097,7 @@ Mesaj oxundu + Əlaqə fotosu @@ -3169,6 +3182,7 @@ İndi hər şey qaydasında görünür! Zəng bildirişlərini almaq üçün bura toxunun və \"Bildirişləri göstər\"i işə salın. Zəng bildirişlərini almaq üçün bura toxunun və bildirişləri fəallaşdırın, Səs və Açılan pəncərələrin fəal olduğuna əmin olun. + Zəng bildirişlərini almaq üçün bura toxunun və \"Batareya\" tənzimləmələrində arxaplan fəaliyyətini fəallaşdırın. Tənzimləmələr Zəng bildirişlərini almaq üçün Tənzimləmələrə toxunun və \"Bildirişləri göstər\"i işə salın. @@ -3306,7 +3320,7 @@ Dincəlir Yeni bir şeylər üzərində işləyir - One or more characters is invalid. + Bir və ya daha çox simvol səhvdir. Qrupa düzəliş et @@ -3405,6 +3419,7 @@ + Dəstək məlumatı Signal Android Dəstək Tələbi @@ -3493,6 +3508,7 @@ Göndərdiyiniz mesajlar üçün bağlantı önbaxışlarını birbaşa veb saytlardan alın. Parolu dəyişdir Parolunuzu dəyişdirin + Ekran kilid parolunu fəallaşdır Parol ilə ekranı və bildirişləri kilidləyin Ekran təhlükəsizliyi @@ -3505,6 +3521,7 @@ LED yanıb sönmə forması Özəlləşdir Səs və titrəməni dəyişdir + Səs Səssiz İlkin @@ -5378,6 +5395,7 @@ Hekayəyə əlavə et Bir mesaj əlavə et + Bir cavab əlavə et Göndər Bir dəfə göstərilən media faylı @@ -5982,6 +6000,8 @@ %1$d cavab %1$d cavab + + Story no longer hidden Əlavə et @@ -7267,7 +7287,23 @@ - Media faylınızı endirmək üçün %1$s həcmində yer boşaldın. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Oldu - - - Ödəniş tarixçəsi - - Mətn və bütün medianın ehtiyat nüsxəsi - - Ödəniş təfsilatları - - Ehtiyat nüsxə növü - - Ödəniş tarixi - - Paylaş + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Ehtiyat nüsxənin tezliyi Mobil cihazdan istifadə edərək ehtiyat nüsxə çıxarma + + View backup key + + Unlock to view backup key Ehtiyat nüsxəni söndür və sil @@ -7370,13 +7406,27 @@ Ehtiyat nüsxə gecə çıxarılacaq. - Ehtiyat nüsxə növü + Backup plan Ehtiyat nüsxələr qeyri-aktivdir - - %1$s · %2$s/aylıq - - Nüsxələməni fəallaşdır + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Ehtiyat nüsxə şifrəniz - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Ehtiyat nüsxə şifrəsi, Signal-ı təkrar quraşdırdıqda ehtiyat nüsxənizi bərpa etmənizə imkan verən 64 rəqəmli bir koddur. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Şifrənizi unutmusunuzsa, ehtiyat nüsxəsini bərpa edə bilməyəcəksiniz. Signal ehtiyat nüsxənizi bərpa etmənizə kömək edə bilmir. - Next + Növbəti - Record your backup key + Ehtiyat nüsxə şifrəsini qeyd edin - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Bu şifrə hesabınızı və verilənlərinizi bərpa etmək üçün tələb olunur. Bu şifrəni etibarlı bir yerdə saxlayın. Onu itirsəniz, hesabınızı bərpa edə bilməyəcəksiniz. - Copy to clipboard + Lövhəyə kopyala - Next + Növbəti - Keep your key safe + Şifrənizin təhlükəsizliyini qoruyun - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Şifrənizi itirsəniz Signal ehtiyat nüsxələrinizi yenidən bərpa etməniz üçün sizə kömək edə bilməyəcək. Onu təhlükəsiz və etibarlı bir yerdə saxlayın və başqaları ilə paylaşmayın. - I\'ve recorded my key + Şifrəmi qeyd etmişən - Continue + Davam et - See key again + Şifrəyə yenidən bax diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 03b750f3ae..ab7c62d3f5 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Да Не Изтриване @@ -260,6 +263,7 @@ Натисни за снимка, задръж за видео + Запечатване Смяна на камерата Отвори галерията @@ -346,7 +350,7 @@ Това не е валиден линк на повикване. Уверете се, че целият линк е правилен и не е прекъснат, преди да се опитате да се присъедините. - You are already in a call + Вече сте в обаждане @@ -403,6 +407,7 @@ Неуспешно откриване на приложение за отваряне на този файл. %1$s e копирано от %1$s + до %1$s Прочети Изтегли повече @@ -436,6 +441,7 @@ Изпращане на редакция Ново съобщение За съжаление, настъпи грешка при прикачването. + Получателят не е с валиден SMS или имейл! Съобщението е празно! Членове на групата @@ -576,6 +582,7 @@ Запазване на %1$d прикачени файла в хранилището… Предстоящ… + Данни (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Премахни връзката \'%1$s\'? Ако прекратиш връзката с това устройство, няма да можеш да изпращаш и получаваш съобщения от него. Връзката с мрежата е неуспешна + Опитай отново Прекратяване на връзката с устройството… Прекратяване на връзката с устройството @@ -1408,7 +1416,7 @@ Връзка към групата Споделяне Промяна на връзката - Require admin approval + Изискване на одобрение от администратор Изисквайте от администратор да одобри присъединяването на нови членове чрез връзката към групата. Наистина ли искате да промените груповата връзка? Хората вече няма да могат да се присъединят към групата чрез текущата връзка. @@ -1507,6 +1515,7 @@ Изпащане на %1$d SMS покани? Да преминем към Signal %1$s + Изглежда нямате приложения, с които да можете да споделите. @@ -1931,6 +1940,7 @@ Позволете на %1$s да ви праща съобщения и споделете вашето име и снимка с тях? Няма да получавате съобщения, докато не ги отблокирате. Позволявате ли на %1$s да ви изпраща съобщения? Няма да получавате съобщения, преди да отблокирате потребителя. + Получаване на актуализации и новини от %1$s? Няма да получавате актуализации, преди да отблокирате потребителя. Ще продължите ли чата с тази група и ще споделите ли името и снимката си с нейните членове? Тази група от стар вид повече не може да се използва. Създайте нова група, за да активирате нови функции като @споменавания и администратори. @@ -2245,6 +2255,7 @@ Никой друг не е тук %1$s е в този разговор + %1$s са в този разговор %1$s и %2$s са в този разговор @@ -2647,7 +2658,7 @@ Ще опитаме отново по-късно. Signal е актуализиран успешно Получихте автоматична актуализация до версия %1$s. - You updated to version %1$s. + Актуализирахте до версия %1$s. Изпращане на съобщение? @@ -3067,6 +3078,7 @@ Изображение на прикачен файл Затваряне/отваряне на чекмеджето за прикачване на файл от камера Записване и изпращане на прикачено аудио + Заключване на запис на прикачен файл с аудио Съобщението не можа да бъде изпратено. Проверете връзката си и опитайте отново. @@ -3085,6 +3097,7 @@ Съобщението е прочетено + Снимка на контакта @@ -3169,6 +3182,7 @@ Вече всичко изглежда добре! За да получавате известия за разговори, натиснете тук и включете \"Показвай известия\". За да получавате известия за повиквания, докоснете тук, включете известията и се уверете, че функциите за звук и изскачащ прозорец са разрешени. + За да получавате известия за повиквания, докоснете тук и разрешете активността във фонов режим от настройките за батерията. Настройки За да получавате известия за повиквания, докоснете „Настройки“ и включете „Показване на известия“. @@ -3306,7 +3320,7 @@ В почивка Работи по нещо ново - One or more characters is invalid. + Един или повече знаци са невалидни. Промяна на групата @@ -3405,6 +3419,7 @@ + Информация за поддръжка Заявка за поддръжка на Signal за Android @@ -3493,6 +3508,7 @@ Извличайте визуализации на връзки директно от уебсайтове за съобщения, които изпращате. Смени паролата Сменете паролата си + Активиране на парола за защита на екрана Заключване на екрана и известията с парола Сигурност на екрана @@ -3505,6 +3521,7 @@ LED известяване Персонализиране Промяна на звук и вибрация + Мелодия Тих По подразбиране @@ -5378,6 +5395,7 @@ Добавяне към историята Добави съобщение + Добавяне на отговор Изпращане до Мултимедия за еднократно гледане @@ -5982,6 +6000,8 @@ %1$d отговор %1$d отговора + + Story no longer hidden Добави @@ -7267,7 +7287,23 @@ - Освободете до %1$s място, за да изтеглите мултимедията. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ОК - - - История на плащанията - - Резервно копиране на текстови съобщения и мултимедия - - Подробности за плащане - - Вид резервно копие - - Дата на плащане - - Споделяне + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Честота на резервно копиране Резервно копиране с мобилна мрежа + + View backup key + + Unlock to view backup key Изключване и изтриване на резервното копие @@ -7370,13 +7406,27 @@ Резервното копие ще бъде създадено през нощта. - Вид резервно копие + Backup plan Резервните копия са деактивирани - - %1$s · %2$s/месец - - Активиране на архиви + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Вашият резервен ключ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Вашият резервен ключ е 64-цифрен код, който ви позволява да възстановите резервното копие, когато инсталирате Signal отново. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ако забравите своя ключ, няма да можете да възстановите резервното си копие. Signal не може да ви помогне да възстановите резервното си копие. - Next + Напред - Record your backup key + Запишете своя резервен ключ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Този ключ е необходим за възстановяване на акаунта и данните ви. Съхранявайте този ключ на сигурно място. Ако го изгубите, няма да можете да възстановите акаунта си. - Copy to clipboard + Копиране - Next + Напред - Keep your key safe + Пазете ключа си на сигурно място - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal няма да може да ви помогне да възстановите своето резервно копие, ако изгубите ключа си. Съхранявайте го на сигурно и защитено място и не го споделяйте с други хора. - I\'ve recorded my key + Записах ключа си - Continue + Продължаване - See key again + Вижте ключа отново diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 05d3f6787e..5bd48d501c 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + হ্যাঁ না মুছে ফেলুন @@ -260,6 +263,7 @@ ছবির জন্য আলতো চাপুন, ভিডিও\'র জন্য ধরে রাখুন + ক্যাপচার ক্যামেরা পরিবর্তন গ্যালারী খুলুন @@ -346,7 +350,7 @@ কলের এই লিংকটি কার্যকর নয়। যোগ দিতে চেষ্টা করার আগে নিশ্চিত করুন যে পুরো লিংকটি অক্ষত ও সঠিক আছে। - You are already in a call + আপনি ইতিমধ্যেই একটি কলে আছেন @@ -403,6 +407,7 @@ এই মিডিয়াটি খোলার জন্য কোন অ্যাপ খুঁজে পাওয়া যায়নি। %1$s প্রতিলিপি করা হয়েছে %1$s থেকে + %1$s এ  আরো পড়ুন  আরো ডাউনলোড করুন @@ -436,6 +441,7 @@ এডিট পাঠান বার্তা তৈরি করুন দুঃখিত, আপনার সংযুক্তি সেট করার সময় একটি ত্রুটি হয়েছিল। + প্রাপক কোনও কার্যকর এসএমএস বা ইমেল ঠিকানা নয়! বার্তা খালি! গ্রুপের সদস্যগণ @@ -576,6 +582,7 @@ %1$dটি সংযুক্ত ফাইল স্টোরেজে সংরক্ষণ করা হচ্ছে… অমীমাংসিত… + ডাটা (Signal) এমএমএস এসএমএস @@ -1006,6 +1013,7 @@ \'%1$s\' বিচ্ছিন্ন করা হবে? এই ডিভাইস বিচ্ছিন্ন করা হলে, এটি আর বার্তা প্রেরণ বা গ্রহণ করতে সক্ষম হবে না। নেটওয়ার্ক সংযোগ ব্যর্থ + আবারো চেষ্টা করুন ডিভাইস বিচ্ছিন্ন করা হচ্ছে… সংযোগ বিচ্ছিন্ন করা হচ্ছে @@ -1408,7 +1416,7 @@ গ্রুপের লিংক শেয়ার করুন লিঙ্কটি পুনরায় সেট করুন - Require admin approval + অ্যাডমিনের অনুমোদন প্রয়োজন গ্রুপ লিঙ্কের মাধ্যমে নতুন সদস্যদের যোগদানের অনুমোদনের জন্য একজন এডমিন প্রয়োজন। আপনি কি নিশ্চিত যে আপনি গ্রুপের লিঙ্কটি পুনরায় সেট করতে চান? বর্তমান লিঙ্কটি ব্যবহার করে কেউ আর গ্রুপে যোগ দিতে পারবে না। @@ -1507,6 +1515,7 @@ %1$d টি নিমন্ত্রন এসএমএস পাঠান? চলুন Signal এ যোগ দেইঃ %1$s + আপনার কাছে শেয়ার করতে পারবেন এমন কোন অ্যাপ নেই। @@ -1931,6 +1940,7 @@ %1$s-কে কি আপনাকে ম্যাসেজ পাঠাতে এবং আপনার নাম ও ছবি শেয়ার করতে দেবেন? আপনি তাদের আনব্লক না করা পর্যন্ত তাদের পাঠানো কোনো ম্যাসেজ পাবেন না। %1$s-কে ম্যাসেজ পাঠানোর অনুমতি দিবেন? যতক্ষণ না আপনি তাদের আনব্লক করবেন ততক্ষণ আপনি কোনো ম্যাসেজ পাবেন না। + %1$s-এর কাছ থেকে আপডেট এবং খবর পেতে চান? যতক্ষণ না আপনি তাদের আনব্লক করবেন ততক্ষণ আপনি কোনো আপডেট পাবেন না। এই গ্রুপের সাথে চ্যাট চালিয়ে যাবেন এবং এর সদস্যদের সাথে আপনার নাম ও ছবি শেয়ার করবেন? এই পুরনো গ্রুপটি আর ব্যবহার করা যাবে না। @mentions ও অ্যাডমিন-এর মতো নতুন ফিচার সক্রিয় করতে একটি নতুন গ্রুপ তৈরি করুন। @@ -2245,6 +2255,7 @@ এখানে আর কেউ নেই %1$s এই কল-এ আছেন + %1$s এই কলে আছে %1$s ও %2$s এই কল-এ আছেন @@ -2647,7 +2658,7 @@ আমরা পরে আবার চেষ্টা করবো। Signal সফলভাবে আপডেট করা হয়েছে আপনি স্বয়ংক্রিয়ভাবে %1$s সংস্করণে আপডেট হয়েছেন। - You updated to version %1$s. + আপনি %1$s সংস্করণে আপডেট করেছেন। বার্তা পাঠান? @@ -3067,6 +3078,7 @@ সংযুক্তি \'র চেহারা দ্রুত ক্যামেরা সংযুক্তি ড্রয়ারটি টগল করুন অডিও সংযুক্তি রেকর্ড এবং প্রেরণ করুন + অডিও সংযুক্তি রেকর্ডিং লক্ করুন মেসেজ পাঠানো যায়নি। আপনার সংযোগটি পরীক্ষা করুন ও পুনরায় চেষ্টা করুন। @@ -3085,6 +3097,7 @@ বার্তা পঠিত + পরিচিতির ছবি @@ -3169,6 +3182,7 @@ সবকিছু এখন ঠিক দেখাচ্ছে! কল নোটিফিকেশন পেতে, এখানে ট্যাপ করুন এবং “নোটিফিকেশন দেখান” অপশনটি চালু করুন। কল নোটিফিকেশন পেতে, এখানে ট্যাপ করুন ও নোটিফিকেশন অপশনটি চালু করুন এবং নিশ্চিত করুন যে সাউন্ড ও পপ-আপ চালু রয়েছে। + কল নোটিফিকেশন পেতে, এখানে ট্যাপ করুন এবং “ব্যাটারি” সেটিংস-এ গিয়ে ব্যাকগ্রাউন্ড কার্যকলাপ চালু করুন। সেটিংস কল নোটিফিকেশন পেতে, সেটিংস-এ ট্যাপ করুন এবং “নোটিফিকেশন দেখান” অপশনটি চালু করুন। @@ -3306,7 +3320,7 @@ একটি বিরতি নিচ্ছে নতুন কিছুতে কাজ করছে - One or more characters is invalid. + এক বা একাধিক ক্যারেক্টার অকার্যকর। গ্রুপ সংশোধন করুন @@ -3405,6 +3419,7 @@ + সহায়ক তথ্য Signal অ্যানড্রয়েড সহায়তা আবেদন @@ -3493,6 +3508,7 @@ আপনার পাঠানো মেসেজের জন্য ওয়েবসাইটগুলি থেকে সরাসরি লিঙ্কের প্রিভিউ পুনরুদ্ধার করুন। পাসফ্রেজ পরিবর্তন করুন আপনার পাসফ্রেজ পরিবর্তন করুন + পাসফ্রেজ স্ক্রীন এর লক্ সক্ষম করুন একটি পাসফ্রেজ দিয়ে স্ক্রিন এবং নোটিফিকেশনসমুহ লক্ করুন স্ক্রীন সুরক্ষা @@ -3505,6 +3521,7 @@ এলইডি মিটমিট করার প্যাটার্ন কাস্টোমাইজ করুন শব্দ এবং ভাইব্রেশন পরিবর্তন করুন + শব্দ নিঃশব্দ সচারচর @@ -5378,6 +5395,7 @@ স্টোরি যোগ করুন একটি বার্তা যোগ করুন + একটি জবাব যোগ করুন পাঠান একবার-দেখার উপযোগী মিডিয়া @@ -5982,6 +6000,8 @@ %1$d উত্তর %1$d উত্তর + + Story no longer hidden যোগ করুন @@ -7267,7 +7287,23 @@ - আপনার মিডিয়া ডাউনলোড করতে %1$s জায়গা খালি করুন। + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ঠিক আছে - - - পেমেন্টের ইতিহাস - - টেক্সট এবং সকল মিডিয়ার ব্যাকআপ - - পেমেন্টের বিস্তারিত - - ব্যাকআপের ধরন - - পরিশোধের তারিখ - - শেয়ার করুন + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ ব্যাকআপ ফ্রিকোয়েন্সি সেলুলার ব্যবহার করে ব্যাকআপ করুন + + View backup key + + Unlock to view backup key বন্ধ করুন এবং ব্যাকআপ মুছুন @@ -7370,13 +7406,27 @@ রাতের বেলা ব্যাকআপ তৈরি করা হবে। - ব্যাকআপের ধরন + Backup plan ব্যাকআপ নিষ্ক্রিয় করা হয়েছে - - %1$s · %2$s/মাস - - ব্যাকআপ সক্ষম করুন + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + আপনার ব্যাকআপ \'কি\' - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + আপনার ব্যাকআপ \'কি\' একটি 64-সংখ্যার কোড যা আপনাকে Signal পুনরায় ইনস্টল করার সময় আপনার ব্যাকআপ পুনরুদ্ধার করতে সাহায্য করে। - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + \'কি\' ভুলে গেলে, আপনি আপনার ব্যাকআপ পুনরুদ্ধার করতে পারবেন না। Signal আপনাকে আপনার ব্যাকআপ পুনরুদ্ধার করতে সাহায্য করতে পারবে না। - Next + পরবর্তী - Record your backup key + আপনার ব্যাকআপ \'কি\' সংরক্ষণ করুন - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + আপনার অ্যাকাউন্ট এবং ডেটা পুনরুদ্ধার করতে এই \'কি\' প্রয়োজন। এই \'কি\'-টি নিরাপদ কোথাও সংরক্ষণ করুন। এটি হারিয়ে ফেললে, আপনি আপনার অ্যাকাউন্টের ব্যাকআপ পুনরুদ্ধার করতে পারবেন না। - Copy to clipboard + ক্লিপবোর্ডে অনুলিপি করুন - Next + পরবর্তী - Keep your key safe + আপনার \'কি\' নিরাপদ রাখুন - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + আপনি আপনার \'কি\' হারিয়ে ফেললে Signal আপনাকে আপনার ব্যাকআপ পুনরুদ্ধারে সাহায্য করতে সক্ষম হবে না। এটিকে নিরাপদ ও সুরক্ষিত স্থানে সংরক্ষণ করুন এবং অন্যদের সাথে শেয়ার করবেন না। - I\'ve recorded my key + আমি আমার \'কি\' সংরক্ষণ করেছি - Continue + চালিয়ে যান - See key again + \'কি\'-টি আবার দেখুন diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 2e313c4d69..d0185584ed 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Da Ne Izbriši @@ -264,6 +267,7 @@ Dotaknite za fotografiju, držite pritisnutim za video + Slikaj Promijeni kameru Otvori galeriju @@ -352,7 +356,7 @@ Ovo nije važeća poveznica za poziv. Prije nego se pokušate pridružiti provjerite je li cijela poveznica netaknuta i ispravna. - You are already in a call + Već učestvujete u pozivu @@ -409,6 +413,7 @@ Nije pronađena aplikacija koja može otvoriti ovu datoteku. Kopirano %1$s od %1$s + u %1$s   Saznaj više   Preuzmi više @@ -442,6 +447,7 @@ Slanje uređene poruke Napiši poruku Nažalost, došlo je do greške prilikom obrađivanja priloga. + Primalac nije valjana SMS ili email adresa! Poruka je prazna! Članovi grupe @@ -598,6 +604,7 @@ %1$d priloga se pohranjuje u memoriju uređaja… U obradi… + Internet (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Prekinuti vezu sa \'%1$s\'? Ako prekinete vezu s ovim uređajem, preko njega više nećete moći slati i primati poruke. Prekinuta konekcija + Pokušajte ponovo Prekidam vezu s uređajem… Prekidam vezu s uređajem @@ -1488,7 +1496,7 @@ Link za grupu Dijeli Kreiraj novi link - Require admin approval + Zatraži odobrenje administratora Zahtijevaj da novim članovima administrator odobri pristupanje grupi putem linka. Jeste li sigurni da želite iznova kreirati link za grupu? Novi članovi više neće moći pristupiti grupi putem trenutnog linka. @@ -1593,6 +1601,7 @@ Poslati %1$d SMS pozivnica? Pređimo na Signal: %1$s + Izgleda da nemate nijednu aplikaciju s kojom je moguće dijeljenje. @@ -2047,6 +2056,7 @@ Želite li dopustiti da vam %1$s šalje poruke i vidi vaše ime i sliku? Nećete primati poruke dok ih ne odblokirate. Želite li dopustiti da vam %1$s šalje poruke? Nećete primati poruke dok ih ne prestanete blokirati. + Želite li primati ažuriranja i novosti od %1$s? Nećete primati nikakve novosti dok ovu osobu ne odblokirate. Continue your chat with this group and share your name and photo with its members? Ova starija grupa se više ne može koristiti. Kreirajte novu grupu da aktivirate nove funkcije kao što su @spominjanja i administratori. @@ -2393,6 +2403,7 @@ Niko drugi nije tu %1$s učestvuje u pozivu + %1$s učestvuje u pozivu %1$s i %2$s učestvuju u pozivu @@ -2817,7 +2828,7 @@ Pokušat ćemo ponovo kasnije. Signal je uspješno ažuriran Automatski ste ažurirani na verziju %1$s. - You updated to version %1$s. + Ažurirali ste na verziju %1$s. Poslati poruku? @@ -3251,6 +3262,7 @@ Sličica priloga Uključi/isključi ladicu za priloge brze kamere Snimi i pošalji zvučni prilog + Zaključaj snimanje zvučnog priloga Nije bilo moguće poslati poruku. Provjerite svoju konekciju i pokušajte ponovo. @@ -3269,6 +3281,7 @@ Poruka pročitana + Slika kontakta @@ -3353,6 +3366,7 @@ Sad sve izgleda kako treba! Da biste bili obaviješteni o pozivima, pritisnite ovdje i uključite \"Prikaži obavještenja\". Da biste bili obaviješteni o pozivima, pritisnite ovdje i uključite obavještenja. Dobro provjerite i potvrdite da su Zvuk i Iskočni okviri uključeni. + Da biste bili obaviješteni o pozivima, pritisnite ovdje i uključite aktivnost u pozadini među podešavanjima za bateriju. Podešavanja Da biste bili obaviješteni o pozivima, pritisnite Podešavanja i uključite \"Prikaži obavještenja\". @@ -3504,7 +3518,7 @@ Na pauzi Radim na nečemu novom - One or more characters is invalid. + Jedan ili više znakova je nevažeći. Uredi grupu @@ -3603,6 +3617,7 @@ + Informacije o podršci Zahtjev za pomoć (Signal Android) @@ -3693,6 +3708,7 @@ Preuzeti pregled linkova do internet-stranica u porukama koje šaljete. Promijeni lozinku Promijenite svoju lozinku + Aktivirajte zaključavanje ekrana lozinkom Zaključaj ekran i obavještenja lozinkom Sigurnost ekrana @@ -3705,6 +3721,7 @@ LED treptanja Prilagodi Promijeni zvuk i vibraciju + Zvuk Stišan Standardno @@ -5632,6 +5649,7 @@ Dodaj u priču Napiši poruku + Odgovor Pošalji za Mediji koji se mogu pogledati jednom @@ -6246,6 +6264,8 @@ %1$d odgovora %1$d odgovora + + Story no longer hidden Dodaj @@ -7583,7 +7603,23 @@ - Oslobodite %1$s prostora da preuzmete medije. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ OK - - - Historija plaćanja - - Sigurnosna kopija poruka i svih medija - - Detalji plaćanja - - Vrsta sigurnosne kopije - - Datum plaćanja - - Dijeli + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Učestalost kreiranja rezervne kopije Kreirajte sigurnosnu kopiju pomoću mobilne mreže + + View backup key + + Unlock to view backup key Isključiti i izbrisati sigurnosne kopije? @@ -7686,13 +7722,27 @@ Sigurnosna kopija će biti kreirana tokom noći. - Vrsta rezervne kopije + Backup plan Rezervne kopije su onemogućene. - - %1$s · %2$s/mjesečno - - Aktiviraj + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Vaš rezervni ključ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Vaš rezervni ključ je 64-cifreni kod koji vam omogućava da vratite rezervnu kopiju kada ponovo instalirate Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ako zaboravite ključ, nećete moći vratiti sigurnosnu kopiju. Signal vam ne može pomoći da vratite sigurnosnu kopiju. - Next + Dalje - Record your backup key + Snimite rezervni ključ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Ovaj ključ je potreban za oporavak vašeg računa i podataka. Čuvajte ovaj ključ na sigurnom mjestu. Ako ga izgubite, nećete moći oporaviti svoj račun. - Copy to clipboard + Kopiraj u međuspremnik - Next + Dalje - Keep your key safe + Čuvajte svoj ključ - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal vam neće moći pomoći da vratite rezervnu kopiju ako izgubite ključ. Čuvajte ga na sigurnom mjestu i nemojte ga dijeliti s drugima. - I\'ve recorded my key + Snimio/la sam svoj ključ - Continue + Nastavi - See key again + Vidi ključ ponovo diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index fd46568241..15233522b0 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + No Esborra @@ -260,6 +263,7 @@ Un toc per fer una fotografia, pressió contínua per fer vídeo + Captura Canvia la càmera Obre la galeria @@ -346,7 +350,7 @@ Aquest no és un enllaç de trucada vàlid. Assegura\'t que l\'enllaç estigui intacte i sigui correcte abans d\'intentar afegir-t\'hi. - You are already in a call + Ja ets a una trucada. @@ -403,6 +407,7 @@ No s\'ha trobat una aplicació que pugui obrir aquest fitxer. S\'ha copiat %1$s de %1$s + a %1$s   Més informació   Baixa\'n més @@ -436,6 +441,7 @@ Enviar versió editada Escriviu un missatge S\'ha produït un error en afegir el fitxer adjunt. + El destinatari no és una adreça electrònica o SMS vàlid! El missatge és buit. Membres del grup @@ -576,6 +582,7 @@ Desant %1$d fitxers a l\'emmagatzematge… Pendent… + Dades (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Voleu desenllaçar «%1$s»? En desenllaçar aquest dispositiu, no es podran rebre ni enviar missatges. La connexió de xarxa ha fallat + Torna a provar-ho Es desenllaça el dispositiu… Es desenllaça el dispositiu @@ -1408,7 +1416,7 @@ Enllaç del grup Compartir-lo Restableix l\'enllaç - Require admin approval + Sol·licitar l\'aprovació de l\'administrador Requereix que un administrador aprovi els membres nous que s’afegeixin per l’enllaç del grup. Segur que voleu restablir l\'enllaç del grup? La gent ja no podrà afegir-se al grup amb l’enllaç actual. @@ -1507,6 +1515,7 @@ Voleu enviar %1$d invitacions SMS? Canviem al Signal: %1$s + Sembla que no teniu cap aplicació on compartir-ho. @@ -1931,6 +1940,7 @@ Vols permetre que %1$s t\'enviï missatges i compartir-hi el nom i la fotografia? No rebràs cap missatge fins que no el desbloquegis. Vols permetre que %1$s t\'enviï missatges? No en rebràs cap missatge fins que no el desbloquegis. + Vols rebre actualitzacions i notícies de %1$s? No en rebràs cap actualització fins que no les desbloquegis. Vols continuar el xat amb aquest grup i compartir-hi el nom i la fotografia? Aquest és un grup antic i ja no pot utilitzar-se. Crea un grup nou per activar les noves funcions com ara les @mencions i els admins. @@ -2245,6 +2255,7 @@ Aquí no hi ha ningú més. %1$s és en aquesta trucada. + %1$s és en aquesta trucada %1$s i %2$s són en aquesta trucada. @@ -2647,7 +2658,7 @@ Ho tornarem a provar més tard. Signal s\'ha actualitzat correctament S\'ha actualitzat automàticament a la versió %1$s. - You updated to version %1$s. + S\'ha actualitzat a la versió %1$s. Voleu enviar el missatge? @@ -3067,6 +3078,7 @@ Miniatura de l\'adjunt Desplega la pestanya d\'adjunts de càmera ràpida Enregistra i envia un àudio adjunt + Bloqueja la gravació d\'un adjunt d\'àudio No s\'ha pogut enviar el missatge. Comproveu la connexió i torneu-ho a provar. @@ -3085,6 +3097,7 @@ Missatge llegit + Foto del contacte @@ -3169,6 +3182,7 @@ Ara tot està bé! Per rebre notificacions de trucades, toqueu aquí i activeu Mostra notificacions. Per rebre notificacions de trucades, toqueu aquí i activeu les notificacions i assegureu-vos que el so i les finestres emergents estiguin activades. + Per rebre notificacions de trucades, toqueu aquí i activeu l\'activitat en segon pla a la configuració Bateria. Configuració Per rebre notificacions de trucades, toqueu Configuració i activeu Mostra notificacions. @@ -3306,7 +3320,7 @@ Faig un descans. Treballo en una cosa nova. - One or more characters is invalid. + Un o més caràcters no son vàlids. Edita el grup @@ -3405,6 +3419,7 @@ + Informació de suport Petició de suport de Signal d\'Android @@ -3493,6 +3508,7 @@ Recupereu les previsualitzacions d’enllaços directament des de qualsevol lloc web per als missatges que envieu. Canvia la contrasenya Canvia la contrasenya + Activa la contrasenya del blocatge de la pantalla Bloca la pantalla i les notificacions amb una contrasenya Seguretat de la pantalla @@ -3505,6 +3521,7 @@ Patró lluminós per al LED Personalitza Canvia el so i la vibració + So Silenciós Per defecte @@ -5378,6 +5395,7 @@ Afegeix-ho a la història Afegiu-hi un missatge + Afegiu-hi una resposta Enviar a Contingut d\'una sola visualització @@ -5982,6 +6000,8 @@ %1$d resposta %1$d respostes + + Story no longer hidden Afegeix @@ -7267,7 +7287,23 @@ - Allibera %1$s d\'espai per descarregar els teus arxius. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ D\'acord - - - Historial de pagaments - - Còpia de seguretat de text i de tots els arxius multimèdia - - Detalls de pagament - - Tipus de còpia de seguretat - - Data de pagament - - Comparteix + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Freqüència de còpia de seguretat Fer una còpia de seguretat amb el mòbil + + View backup key + + Unlock to view backup key Desactivar i eliminar les còpies de seguretat @@ -7370,13 +7406,27 @@ La còpia de seguretat es crearà durant la nit. - Tipus de còpia de seguretat + Backup plan Còpies de seguretat desactivades - - %1$s · %2$s/mes - - Activa les còpies de seguretat + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + La teva clau de còpia de seguretat - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + La teva clau de còpia de seguretat és un codi de 64 dígits que et permetrà restaurar la còpia de seguretat quan tornis a instal·lar Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Si oblides la teva clau, no podràs restaurar la còpia de seguretat. Signal no et pot ajudar a recuperar la còpia de seguretat. - Next + Següent - Record your backup key + Guarda la teva clau de còpia de seguretat - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Aquesta clau és necessària per recuperar el teu compte i les teves dades. Guarda-la en un lloc segur. Si la perds, no podràs recuperar el teu compte. - Copy to clipboard + Copia al porta-retalls - Next + Següent - Keep your key safe + Guarda bé la teva clau - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Si perds la teva clau, Signal no et podrà ajudar a restaurar la còpia de seguretat. Guarda-la en un lloc segur i no la comparteixis amb ningú més. - I\'ve recorded my key + He guardat la meva clau - Continue + Continuar - See key again + Tornar a veure la clau diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index be1bef4ad3..9ee855228c 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ano Ne Odstranit @@ -264,6 +267,7 @@ Klepnout pro fotografii, přidržet pro video + Pořídit Změnit kameru Otevřít galerii @@ -352,7 +356,7 @@ Toto není platný odkaz na hovor. Než se pokusíte připojit, ujistěte se, že je odkaz neporušený a správný. - You are already in a call + Již jste v hovoru @@ -409,6 +413,7 @@ Nemohu nalézt aplikaci pro otevření tohoto typu dat. Zkopírováno %1$s od %1$s + %1$s Číst dál Stáhnout další @@ -442,6 +447,7 @@ Publikovat úpravu Napište zprávu Omlouváme se, ale došlo k chybě při zpracování přílohy. + Adresa příjemce není ve formátu SMS nebo e-mail! Zpráva je prázdná! Členové skupiny @@ -598,6 +604,7 @@ Ukládání %1$d příloh do úložiště… Probíhající: + Data (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Zrušit propojení s \"%1$s\"? Po odpojení tohoto zařízení nebude dále moci posílat a přijímat zprávy. Nepodařilo se připojit + Zkusit znovu Rozpojování zařízení… Rozpojuji zařízení @@ -1488,7 +1496,7 @@ Odkaz skupiny Sdílet Obnovit odkaz - Require admin approval + Vyžadovat schválení správcem Požadovat, aby správce schválil nové členy, kteří se připojují přes odkaz skupiny. Opravdu chcete obnovit odkaz skupiny? Pomocí současného odkazu se pak už lidé nebudou schopni ke skupině připojit. @@ -1593,6 +1601,7 @@ Poslat %1$d SMS pozvánek? Přejít na Signal: %1$s + Zdá se, že nemáte žádnou aplikaci pro sdílení. @@ -2047,6 +2056,7 @@ Nechat si od %1$s posílat zprávy a sdílet s nimi vaše jméno a fotografii? Dokud je neodblokujete, nebudete dostávat žádné zprávy. Nechat si od %1$s posílat zprávy? Dokud je neodblokujete, nebudete dostávat žádné zprávy. + Dostávat aktuality a novinky od %1$s? Dokud je neodblokujete, nebudete dostávat žádné zprávy. Pokračovat v chatu s touto skupinou a sdílet své jméno a fotografii s jejími členy? Tuto starší skupinu již nelze použít. K aktivaci nových funkcí, jako jsou @zmínky a správce, vytvořte novou skupinu. @@ -2393,6 +2403,7 @@ Nikdo další tady není %1$s se účastní tohoto hovoru + %1$s jsou v hovoru %1$s a %2$s se účastní tohoto hovoru @@ -2817,7 +2828,7 @@ Zkusíme to znovu později. Signal byl úspěšně aktualizován Byla provedena automatická aktualizace na verzi %1$s. - You updated to version %1$s. + Aktualizovali jste na verzi %1$s. Odeslat zprávu? @@ -3251,6 +3262,7 @@ Připojený náhled Vytvořit rychlou přílohu fotoaparátem Nahrát a poslat zvukovou přílohu + Zamknout nahrávání zvukové přílohy Zprávu nebylo možné odeslat. Zkontrolujte své připojení a zkuste to znovu. @@ -3269,6 +3281,7 @@ Zpráva přečtena + Fotografie kontaktu @@ -3353,6 +3366,7 @@ Teď všechno vypadá v pořádku! K obdržení oznámení o hovorech klepněte sem a zapněte \"Zobrazovat notifikace.\" K obdržení oznámení o hovorech klepněte sem a zapněte notifikace a ujistěte se, že jsou povoleny zvuky a vyskakovací okna. + K obdržení oznámení o hovorech klepněte sem a povolte aktivitu na pozadí v nastaveních baterie. Nastavení K obdržení oznámení o hovorech klepněte na Nastavení a zapněte \"Zobrazit notifikace.\" @@ -3504,7 +3518,7 @@ Dávám si pauzu Pracuji na něčem novém - One or more characters is invalid. + Jeden nebo více znaků je neplatných. Editovat skupinu @@ -3603,6 +3617,7 @@ + Informace o podpoře Požadavek na podporu Signal Android @@ -3693,6 +3708,7 @@ Pro zprávy, které odesíláte, získat náhledy odkazů přímo z webů. Změnit heslo Změna vašeho hesla + Povolit heslo zámku obrazovky Zamknout obrazovky a upozornění heslem Zabezpečení obrazovky @@ -3705,6 +3721,7 @@ Schéma blikání LED Upravit Změnit zvuk a vibrace + Zvuk Ticho Výchozí @@ -5632,6 +5649,7 @@ Přidat do příběhu Přidat zprávu + Přidat odpověď Odeslat uživateli Média, která lze zobrazit jen jednou @@ -6246,6 +6264,8 @@ %1$d odpovědí %1$d odpovědí + + Story no longer hidden Přidat @@ -7583,7 +7603,23 @@ - Uvolněte %1$s místa pro stahování médií. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ OK - - - Historie plateb - - Zálohování textových zpráv a všech médií - - Podrobnosti platby - - Typ zálohování - - Datum platby - - Sdílet + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Frekvence zálohování Zálohování pomocí mobilní sítě + + View backup key + + Unlock to view backup key Vypnout a odstranit zálohování @@ -7686,13 +7722,27 @@ Záloha bude vytvořena přes noc. - Typ zálohování + Backup plan Zálohování zakázáno - - %1$s · %2$s / měsíčně - - Povolit zálohování + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Váš záložní klíč - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Váš záložní klíč je 64místný kód, který vám umožní obnovit zálohu při opětovné instalaci aplikace Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Pokud svůj klíč zapomenete, nebude možné zálohu obnovit. Signal vám nemůže pomoci obnovit vaši zálohu. - Next + Další - Record your backup key + Zaznamenejte si svůj záložní klíč - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Tento klíč je nutný k obnovení vašeho účtu a dat. Uložte tento klíč na bezpečné místo. Pokud jej ztratíte, nebudete moci svůj účet obnovit. - Copy to clipboard + Zkopírovat do schránky - Next + Další - Keep your key safe + Uchovávejte svůj klíč v bezpečí - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Pokud klíč ztratíte, Signal vám s obnovením zálohy nemůže pomoct. Uložte jej na bezpečném místě a nesdělujte jej ostatním. - I\'ve recorded my key + Zaznamenal/a jsem svůj klíč - Continue + Pokračovat - See key again + Zobrazit klíč znovu diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 33aba4033b..3d5abac0bc 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ja Nej Slet @@ -260,6 +263,7 @@ Tryk for billede, tryk og hold for video + Tag billede Skift kamera Åbn galleri @@ -346,7 +350,7 @@ Dette er ikke et gyldigt opkaldslink. Sørg for, at hele linket er intakt og korrekt, inden du prøver at deltage. - You are already in a call + Du er allerede i et opkald @@ -403,6 +407,7 @@ Kan ikke finde en app, der kan åbne denne mediefil. Kopieret %1$s fra %1$s + til %1$s   Læs mere   Hent mere @@ -436,6 +441,7 @@ Send redigeret version Opret besked Beklager, der opstod en fejl ved vedhæftning af din fil. + Modtager er ikke en gyldig SMS eller e-mailadresse! Beskeden er tom! Gruppemedlemmer @@ -576,6 +582,7 @@ Gemmer %1$d vedhæftede filer i lagerplads… Afventer … + Data (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Frakobl \"%1$s\"? Enheden vil ikke længere være i stand til at sende og modtage beskeder, hvis den frakobles. Forbindelse til netværk fejlede + Prøv igen Frakobler enhed… Frakobler enhed @@ -1408,7 +1416,7 @@ Gruppelink Del Nulstil link - Require admin approval + Kræv godkendelse af administrator Kræv at en administrator godkender nye gruppemedlemmer, som tilslutter sig via gruppelinket. Er du sikker på, at du vil nulstille gruppelinket? Man vil ikke længere kunne tilslutte sig gruppen ved hjælp af det nuværende link. @@ -1507,6 +1515,7 @@ Send %1$d SMS-invitationer? Lad os skifte til Signal: %1$s + Det ser ud til, at du ikke har nogen apps at dele til. @@ -1931,6 +1940,7 @@ Lad %1$s sende dig beskeder og del dit navn og billede med vedkommende? Du modtager ikke nogen beskeder, før du fjerner blokeringen. Vil du give %1$s tilladelse til at sende dig beskeder? Du vil ingen beskeder modtage før blokeringen fjernes. + Få opdateringer og nyheder fra %1$s? Du vil ikke modtage opdateringer, før du ophæver din blokering. Vil du fortsætte chatten med gruppen og dele dit navn og billede med dens medlemmer? Denne legacygruppe kan ikke længere bruges. Opret en ny gruppe for at aktivere nye funktioner som @omtaler og administratorer. @@ -2245,6 +2255,7 @@ Ingen andre deltager %1$s deltager i dette opkald + %1$s deltager i dette opkald %1$s og %2$s deltager i dette opkald @@ -2647,7 +2658,7 @@ Vi prøver igen senere. Signal er hermed opdateret Du er blevet automatisk opdateret til version %1$s. - You updated to version %1$s. + Du opdaterede til version %1$s. Send besked? @@ -3067,6 +3078,7 @@ Vedhæftet skabelon Slå kamera til/fra Optag og send en lydbesked + Lås den vedhæftede lydoptagelse Beskeden kunne ikke sendes. Kontroller din forbindelse, og prøv igen @@ -3085,6 +3097,7 @@ Besked læst + Kontaktbillede @@ -3169,6 +3182,7 @@ Alt ser godt ud nu! For at modtage opkaldsnotifikationer, tryk her og aktiver \"Vis notifikationer\". For at modtage opkaldsnotifikationer, tryk her og aktiver notifikationer og sørg for, at lyd og pop-op er aktiveret. + For at modtage opkaldsnotifikationer, tryk her og aktiver baggrundsaktivitet i \"Batteri\"-indstillinger. Indstillinger For at modtage opkaldsnotifikationer, tryk på Indstillinger og aktiver \"Vis notifikationer\". @@ -3306,7 +3320,7 @@ Tager en pause Arbejder på noget nyt - One or more characters is invalid. + Et eller flere tegn er ugyldige. Redigér gruppe @@ -3405,6 +3419,7 @@ + Support Info Signal Android support-anmodning @@ -3493,6 +3508,7 @@ Hent forhåndsvisninger direkte fra websteder for links, du sender i beskeder. Skift adgangssætning Skift din adgangssætning + Aktiver skærmlås med adgangssætning Lås skærm og notifikationer med adgangssætning Skærmsikkerhed @@ -3505,6 +3521,7 @@ LED-blinkemønster Tilpas Skift lyd og vibration + Lyd Lydløs Standard @@ -5378,6 +5395,7 @@ Føj til historie Tilføj en besked + Tilføj et svar Send til Se medier én gang @@ -5982,6 +6000,8 @@ %1$d svar %1$d svar + + Story no longer hidden Tilføj @@ -7267,7 +7287,23 @@ - Frigør %1$s i lagerplads for at downloade dine medier. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Betalingshistorik - - Sikkerhedskopiering af tekst og alle medier - - Betalingsoplysninger - - Sikkerhedskopitype - - Dato for betaling - - Del + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Sikkerhedskopieringshyppighed Sikkerhedskopiér via mobildata + + View backup key + + Unlock to view backup key Slå fra og slet sikkerhedskopi @@ -7370,13 +7406,27 @@ Sikkerhedskopien oprettes i nat. - Sikkerhedskopitype + Backup plan Sikkerhedskopiering deaktiveret - - %1$s · %2$s/måned - - Aktiver sikkerhedskopier + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Din sikkerhedskopinøgle - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Din sikkerhedskopinøgle er en 64-cifret kode, som lader dig gendanne din sikkerhedskopi, når du geninstallerer Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Hvis du glemmer din nøgle, kan du ikke gendanne din sikkerhedskopi. Signal kan ikke hjælpe dig med at gendanne din sikkerhedskopi. - Next + Næste - Record your backup key + Skriv din sikkerhedskopinøgle ned - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Denne nøgle er påkrævet for at gendanne din konto og dine data. Opbevar denne nøgle et sikkert sted. Hvis du mister den, vil du ikke kunne gendanne din konto. - Copy to clipboard + Kopiér til udklipsholder - Next + Næste - Keep your key safe + Opbevar din nøgle sikkert - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal vil ikke kunne hjælpe dig med at gendanne din sikkerhedskopi, hvis du mister din nøgle. Opbevar den et sikkert sted, og del den ikke med andre. - I\'ve recorded my key + Jeg har skrevet min nøgle ned - Continue + Fortsæt - See key again + Se nøglen igen diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 81e19d342d..a25038b90a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ja Nein Löschen @@ -260,6 +263,7 @@ Für Foto antippen, für Video halten + Aufnehmen Kamera wechseln Galerie öffnen @@ -346,7 +350,7 @@ Dies ist kein gültiger Anruflink. Vergewissere dich, dass der Link vollständig und fehlerfrei ist, bevor du versuchst, teilzunehmen. - You are already in a call + Du nimmst bereits an einem Anruf teil @@ -403,6 +407,7 @@ Keine App zum Öffnen dieser Medieninhalte gefunden. %1$s kopiert von %1$s + an %1$s   Weiterlesen   Mehr herunterladen @@ -436,6 +441,7 @@ Bearbeitete Nachricht senden Nachricht verfassen Leider ist ein Fehler beim Hinzufügen des Anhangs aufgetreten. + Mobilnummer oder E-Mail-Adresse des Empfängers ist ungültig! Die Nachricht ist leer! Gruppenmitglieder @@ -576,6 +582,7 @@ %1$d Anhänge werden im Gerätespeicher gespeichert … Ausstehend … + Internet (Signal) MMS SMS @@ -1006,6 +1013,7 @@ »%1$s« entkoppeln? Wenn du dieses Gerät entkoppelst, kannst du keine weiteren Nachrichten darüber versenden oder empfangen. Keine Netzverbindung + Erneut versuchen Gerät wird entkoppelt … Gerät entkoppeln @@ -1408,7 +1416,7 @@ Gruppen-Link Teilen Link zurücksetzen - Require admin approval + Administrator-Genehmigung benötigt Ein Admin muss Beitrittsanfragen von neuen Mitgliedern über den Gruppen-Link bestätigen. Möchtest du den Gruppen-Link wirklich zurücksetzen? Über den derzeitigen Link wird der Gruppe dann niemand mehr beitreten können. @@ -1507,6 +1515,7 @@ %1$d SMS-Einladungen senden? Lass uns zu »Signal« wechseln: %1$s + Es sieht so aus, als hättest du keine zum Teilen geeignete Apps installiert. @@ -1931,6 +1940,7 @@ Darf %1$s dir Nachrichten schreiben und deinen Namen und dein Foto sehen? Du wirst keine Nachrichten erhalten, es sei denn, du erteilst eine Freigabe. Darf %1$s dir Nachrichten schreiben? Du wirst keine Nachrichten erhalten, es sei denn, du erteilst eine Freigabe. + Aktualisierungen und Neuigkeiten von %1$s erhalten? Du wirst keine Aktualisierungen erhalten, es sei denn, du erteilst eine Freigabe. Chat mit dieser Gruppe fortsetzen und deinen Namen und dein Foto mit deren Mitgliedern teilen? Diese Gruppe alten Typs kann nicht mehr verwendet werden. Erstelle eine neue Gruppe, um neue Funktionen wie @Erwähnungen und Administrator zu aktivieren. @@ -2245,6 +2255,7 @@ Niemand anderes ist hier %1$s nimmt an diesem Anruf teil + %1$s nimmt an diesem Anruf teil %1$s und %2$s nehmen an diesem Anruf teil @@ -2647,7 +2658,7 @@ Wir versuchen es später erneut. Signal wurde erfolgreich aktualisiert Du wurdest automatisch auf Version %1$s aktualisiert. - You updated to version %1$s. + Du hast auf Version %1$s aktualisiert. Nachricht senden? @@ -3067,6 +3078,7 @@ Vorschaubild Anhang Direktaufnahme Sprachnachricht aufnehmen und senden + Audioaufnahme arretieren Nachricht konnte nicht versendet werden. Überprüfe deine Internetverbindung und versuche es erneut. @@ -3085,6 +3097,7 @@ Nachricht gelesen + Kontaktbild @@ -3169,6 +3182,7 @@ Jetzt sieht alles gut aus! Um Benachrichtigungen für Anrufe zu erhalten, tippe hier und schalte »Benachrichtigungen anzeigen« ein. Um Benachrichtigungen für Anrufe zu erhalten, tippe hier, schalte Benachrichtigungen ein, und stelle sicher, dass »Ton« und »Pop-up« aktiviert sind. + Um Benachrichtigungen für Anrufe zu erhalten, tippe hier und lasse unter »Akku« die Hintergrundnutzung zu. Einstellungen Um Benachrichtigungen für Anrufe zu erhalten, tippe auf »Einstellungen« und schalte »Benachrichtigungen anzeigen« ein. @@ -3306,7 +3320,7 @@ Mache eine Pause Arbeite an etwas Neuem - One or more characters is invalid. + Ein oder mehrere Zeichen sind ungültig. Gruppe bearbeiten @@ -3405,6 +3419,7 @@ + Support-Informationen Support-Anfrage für Signal Android @@ -3493,6 +3508,7 @@ Für zu versendende Nachrichten Link-Vorschauen direkt von den verlinkten Webseiten abrufen. Passphrase ändern Deine Passphrase ändern + Passphrasengeschützte Bildschirmsperre aktivieren Bildschirm und Benachrichtungen mit einer Passphrase sperren Bildschirmschutz @@ -3505,6 +3521,7 @@ LED-Blinkmuster Anpassen Ton und Vibration ändern + Ton Lautlos Standard @@ -5378,6 +5395,7 @@ Zu Story hinzufügen Nachricht hinzufügen + Antwort hinzufügen Senden an Medien zur Einmalansicht @@ -5982,6 +6000,8 @@ %1$d Antwort %1$d Antworten + + Story no longer hidden Hinzufügen @@ -7267,7 +7287,23 @@ - Du musst %1$s Speicherplatz freigeben, um deine Medien herunterladen zu können. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Zahlungsverlauf - - Sicherung von Text und allen anderen Medien - - Zahlungsdetails - - Art der Datensicherung - - Zahlungsdatum - - Teilen + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Häufigkeit der Datensicherung Datensicherung über das Mobilfunknetz + + View backup key + + Unlock to view backup key Datensicherung ausschalten und löschen @@ -7370,13 +7406,27 @@ Datensicherung wird über Nacht erstellt. - Art der Datensicherung + Backup plan Datensicherung deaktiviert - - %1$s · %2$s pro Monat - - Sicherungen aktivieren + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Dein Datensicherungsschlüssel - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Dein Datensicherungsschlüssel ist ein 64-stelliger Code, mit dem du deine Datensicherung wiederherstellen kannst, wenn du Signal neu installierst. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ohne Schlüssel kannst du deine Datensicherung nicht wiederherstellen. Signal kann dir nicht helfen, deine Datensicherung wiederherzustellen. - Next + Weiter - Record your backup key + Sichere deinen Datensicherungsschlüssel - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Dieser Schlüssel wird benötigt, um dein Konto und deine Daten wiederherzustellen. Bewahre diesen Schlüssel sicher auf. Wenn du ihn verlierst, kannst du dein Konto nicht wiederherstellen. - Copy to clipboard + In Zwischenablage kopieren - Next + Weiter - Keep your key safe + Verwahre deinen Schlüssel sicher auf - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal kann dir nicht helfen, deine Datensicherung wiederherzustellen, wenn du deinen Schlüssel verlierst. Bewahre ihn an einem sicheren Ort auf und gib ihn nicht an andere weiter. - I\'ve recorded my key + Ich habe meinen Schlüssel aufgezeichnet - Continue + Fortfahren - See key again + Schlüssel erneut anzeigen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 05cc44e1d0..98285f1885 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ναι Όχι Διαγραφή @@ -260,6 +263,7 @@ Πάτα για φωτογραφία, παρατεταμένα για βίντεο + Λήψη Αλλαγή κάμερας Άνοιγμα συλλογής @@ -346,7 +350,7 @@ Ο σύνδεσμος κλήσης δεν είναι έγκυρος. Επιβεβαίωσε ότι ο σύνδεσμος είναι ακέραιος και σωστός πριν δοκιμάσεις να μπεις. - You are already in a call + Είσαι ήδη σε κάποια κλήση @@ -403,6 +407,7 @@ Δεν μπορεί να βρεθεί κατάλληλη εφαρμογή για το άνοιγμα αυτού του πολυμέσου. Αντιγράφτηκε: %1$s από %1$s + προς %1$s  Διάβασε περισσότερα  Κατέβασε περισσότερα @@ -436,6 +441,7 @@ Αποστολή με επεξεργασία Νέο μήνυμα Συγγνώμη, υπήρξε κάποιο σφάλμα με την επισύναψη. + Ο παραλήπτης δεν είναι έγκυρη διεύθυνση SMS ή email! Το μήνυμα είναι κενό! Μέλη ομάδας @@ -576,6 +582,7 @@ %1$d συνημμένα αποθηκεύονται στη μνήνη… Σε εκκρεμότητα… + Δεδομένα (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Αποσύνδεση του \'%1$s\'; Άν αποσυνδεθεί η συσκευή, δε θα μπορεί πλέον να λάβει ή να στείλει μηνύματα. Αποτυχία σύνδεσης στο δίκτυο + Ξαναπροσπάθησε Η συσκευή αποσυνδέεται… Η συσκευή αποσυνδέεται @@ -1408,7 +1416,7 @@ Σύνδεσμος ομάδας Μοιράσου Επαναφορά συνδέσμου - Require admin approval + Απαιτείται έγκριση διαχειριστή Να απαιτείται η έγκριση απο διαχειριστή/τρια για νέα μέλη που μπαίνουν από το σύνδεσμο της ομάδας. Είσαι σίγουρος/η πως θέλεις να επαναφέρεις το σύνδεσμο της ομάδας; Δεν θα μπορεί να μπει κανένας άλλος στην ομάδα χρησιμοποιώντας τον τωρινό σύνδεσμο. @@ -1507,6 +1515,7 @@ Αποστολή %1$d προσκλήσεων με SMS; Έλα να χρησιμοποιήσουμε το Signal: %1$s + Φαίνεται πως δεν έχεις κάποια εφαρμογή στην οποία μπορείς να μοιραστείς. @@ -1931,6 +1940,7 @@ Να επιτραπεί στον/στην %1$s να σου στείλει μηνύματα, και να μοιραστείς το όνομα και τη φωτογραφία σου μαζί του/της; Δεν θα λάβεις κανένα μήνυμα μέχρι να καταργήσεις τον αποκλεισμό του/της. Να επιτραπεί στον/στην %1$s να σου στείλει μηνύματα; Δεν θα λάβεις κανένα μήνυμα μέχρι να καταργήσεις τον αποκλεισμό του/της. + Λήψη ενημερώσεων και νέων από τον/την %1$s; Δεν θα λάβεις καμία ενημέρωση μέχρι να καταργήσεις τον αποκλεισμό του/της. Να συνεχίσεις τη συνομιλία με αυτή την ομάδα, και να μοιραστείς το όνομα και τη φωτογραφία σου με τα μέλη της; Αυτή η ομάδα παλαιού τύπου δεν μπορεί πλέον να χρησιμοποιηθεί. Δημιούργησε μια νέα ομάδα για να ενεργοποιήσεις νέες δυνατότητες όπως τις ονομαστικές αναφορές (@) και τους διαχειριστές. @@ -2245,6 +2255,7 @@ Δεν είναι κανένας άλλος εδώ Ο/Η %1$s είναι στην κλήση + %1$s είσαι στη κλήση Οι %1$s και %2$s είναι στην κλήση @@ -2647,7 +2658,7 @@ Θα προσπαθήσουμε ξανά αργότερα. Το Signal ενημερώθηκε με επιτυχία Έγινε αυτόματη ενημέρωση της εφαρμογής σου στην έκδοση %1$s. - You updated to version %1$s. + Έκανες ενημέρωση στην έκδοση %1$s. Αποστολή μηνύματος; @@ -3067,6 +3078,7 @@ Μικρογραφία συνημμένου Εναλλαγή συρταριού συνημμένων κάμερας Ηχογράφησε και στείλε το συνημμένο αρχείο ήχου + Κλείδωσε την ηχογράφηση του συνημμένου αρχείου ήχου Το μήνυμα δεν στάλθηκε. Έλεγξε τη σύνδεσή σου και ξαναπροσπάθησε. @@ -3085,6 +3097,7 @@ Το μήνυμα διαβάστηκε + Φωτογραφία επαφής @@ -3169,6 +3182,7 @@ Τώρα όλα φαίνοντει εντάξει! Για να λαμβάνεις ειδοποιήσεις κλήσεων, πάτα εδώ και ενεργοποίησε την \"Εμφάνιση ειδοποιήσεων\". Για να λαμβάνεις ειδοποιήσεις κλήσεων, πάτα εδώ, ενεργοποίησε τις ειδοποιήσεις και επιβεβαίωσε πως ο Ηχος και τα Pop-up είναι ενεργοποιημένα. + Για να λαμβάνεις ειδοποιήσεις κλήσεων, πάτα εδώ και ενεργοποίησε τη δραστηριότητα στο υπόβαθρο στις ρυθμίσεις Μπαταρίας. Ρυθμίσεις Για να λαμβάνεις ειδοποιήσεις κλήσεων, πάτα στις Ρυθμίσεις και ενεργοποίησε την \"Εμφάνιση ειδοποιήσεων\". @@ -3306,7 +3320,7 @@ Κάνω διάλειμμα Ετοιμάζω κάτι καινούργιο - One or more characters is invalid. + Ένας ή περισσότεροι χαρακτήρες δεν είναι έγκυροι. Επεξεργασία ομάδας @@ -3405,6 +3419,7 @@ + Πληροφορίες υποστήριξης Αίτημα υποστήριξης Signal Android @@ -3493,6 +3508,7 @@ Λάβε προεπισκόπεις συνδέσμων απ\' ευθείας από σελίδες για τα μηνύματα που στέλνεις. Αλλαγή συνθηματικού Αλλαγή του συνθηματικού σου + Ενεργοποίηση κλειδώματος οθόνης με συνθηματικό Κλείδωμα οθόνης και ειδοποιήσεων με συνθηματικό Ασφάλεια οθόνης @@ -3505,6 +3521,7 @@ Μοτίβο LED Προσαρμογή Αλλαγή ήχου και δόνησης + Ήχος Σε σίγαση Προκαθορισμένη @@ -5378,6 +5395,7 @@ Προσθήκη στην ιστορία Προσθήκη μηνύματος + Προσθήκη απάντησης Αποστολή σε Πολυμέσα μιας μόνο προβολής @@ -5982,6 +6000,8 @@ %1$d απάντηση %1$d απαντήσεις + + Story no longer hidden Προσθήκη @@ -7267,7 +7287,23 @@ - Απελευθέρωσε έως και %1$s χώρου και κάνε λήψη των πολυμέσων σου. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Ιστορικό πληρωμών - - Αντίγραφα ασφαλείας κειμένου και όλων των πολυμέσων - - Στοιχεία πληρωμής - - Τύπος αντιγράφου ασφαλείας - - Ημερομηνία πληρωμής - - Μοιράσου + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Συχνότητα αντιγράφου ασφαλείας Αντίγραφο ασφαλείας με δεδομένα κινητής + + View backup key + + Unlock to view backup key Απενεργοποίηση και διαγραφή αντιγράφων ασφαλείας @@ -7370,13 +7406,27 @@ Το αντίγραφο ασφαλείας θα δημιουργηθεί κατά τη διάρκεια της νύχτας. - Τύπος αντιγράφου ασφαλείας + Backup plan Τα αντίγραφα ασφαλείας απενεργοποιήθηκαν - - %1$s · %2$s/μήνα - - Ενεργοποίηση αντίγραφων ασφαλείας + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Το εφεδρικό κλειδί σου - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Το εφεδρικό κλειδί σου είναι ένας 64-ψήφιος κωδικός που σου επιτρέπει να επαναφέρεις το αντίγραφο ασφαλείας σου κατά την επανεγκατάσταση του Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Εάν ξεχάσεις τον κλειδί σου, δεν θα μπορέσεις να επαναφέρεις τα αντίγραφα ασφαλείας σου. Το Signal δεν μπορεί να σε βοηθήσει να ανακτήσεις τα αντίγραφα ασφαλείας σου. - Next + Επόμενο - Record your backup key + Καταγραφή εφεδρικού κλειδιού - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Αυτό το κλειδί απαιτείται για την ανάκτηση του λογαριασμού και των δεδομένων σου. Αποθήκευσε αυτό το κλειδί με ασφάλεια. Εάν το χάσεις, δεν θα μπορείς να ανακτήσεις τον λογαριασμό σου. - Copy to clipboard + Αντιγραφή στο πρόχειρο - Next + Επόμενο - Keep your key safe + Κράτησε το κλειδί σου ασφαλές - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Το Signal δεν θα μπορεί να σε βοηθήσει να επαναφέρεις το αντίγραφο ασφαλείας σου εάν χάσεις το κλειδί σου. Αποθήκευσέ το με ασφάλεια και μην το μοιράζεσαι με άλλους. - I\'ve recorded my key + Έχω καταγράψει το κλειδί μου - Continue + Συνέχεια - See key again + Επαναπροβολή κλειδιού diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 10ad691213..fbb9950b6f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + No Eliminar @@ -260,6 +263,7 @@ Toca para hacer una foto o mantén pulsado para grabar un vídeo + Capturar Cambiar cámara Abrir galería @@ -346,7 +350,7 @@ Enlace de llamada inválido. Asegúrate que el enlace es correcto antes de intentar unirte. - You are already in a call + Ya estás en una llamada @@ -403,6 +407,7 @@ No se puede encontrar una aplicación para mostrar este archivo. URL copiada: %1$s de %1$s + a %1$s   Leer más   Descargar más @@ -436,6 +441,7 @@ Enviar versión editada Escribir mensaje No se ha podido adjuntar el archivo. + La persona que intentas contactar no puede recibir SMS en su teléfono o no cuenta con una dirección de correo electrónico válida. El mensaje está vacío. Participantes del grupo @@ -576,6 +582,7 @@ Guardando %1$d archivos adjuntos… Pendiente… + Datos (Signal) MMS SMS @@ -1006,6 +1013,7 @@ ¿Desvincular %1$s? Si se desvincula este dispositivo, ya no podrás enviar ni recibir mensajes allí. Error en la conexión de red + Reintentar Desvinculando dispositivo… Desvinculando dispositivo @@ -1106,7 +1114,7 @@ Actualizar este grupo Los nuevos grupos incluyen funciones como @menciones y admins, y añadiremos más en el futuro. Se conservará todo el historial de mensajes y todos los archivos multimedia que tenías antes de la actualización. - Error en la conexión de red. Inténtalo de nuevo más tarde. + Se ha producido un error de red. Inténtalo de nuevo más tarde. No se ha podido actualizar. Esta persona tiene que aceptar la invitación para volver a unirse al grupo y no recibirá ningún mensaje del grupo hasta que acepte: @@ -1408,7 +1416,7 @@ Enlace al grupo Compartir Restablecer enlace - Require admin approval + Solicitar aprobación de admin Alguien que sea admin del grupo debe aprobar nuevas solicitudes para unirse mediante enlace. ¿Quieres restablecer el enlace al grupo? Si lo restableces, nadie podrá unirse al grupo usando el enlace actual. @@ -1429,7 +1437,7 @@ Unirse Solicitar unirse No es posible unirse al grupo. Inténtalo de nuevo más tarde. - Error en la conexión de red. + Se ha producido un error de red. El enlace a este grupo no está activo No es posible unirse al grupo @@ -1507,6 +1515,7 @@ ¿Enviar %1$d invitaciones por SMS? Pasémonos a Signal: %1$s + Parece que no hay ninguna aplicación para compartir este archivo. @@ -1526,7 +1535,7 @@ Archivos multimedia Archivos - Audio + Audios Todos ¿Eliminar elemento seleccionado? @@ -1536,7 +1545,7 @@ Se eliminará de forma permanente el archivo seleccionado. Todos los mensajes asociados con este archivo también se eliminarán. Se eliminarán de forma permanente los %1$d archivos seleccionados. Todos los mensajes asociados con estos archivos también se eliminarán. - Eliminar + Eliminando Eliminando mensajes… Recopilando archivos adjuntos… Ordenar por @@ -1618,7 +1627,7 @@ Desconocido - Has recibido un mensaje cifrado con una versión de Signal antigua que ya no está disponible. Pide a esta persona que actualice a la versión más reciente y reenvíe el mensaje. + Has recibido un mensaje cifrado con una versión de Signal antigua que ya no está disponible. Pide a esta persona que actualice a la versión más reciente y vuelva a enviar el mensaje. Has abandonado el grupo. Has actualizado el grupo. Se ha actualizado el grupo. @@ -1931,6 +1940,7 @@ ¿Quieres que %1$s te envíe mensajes y compartir tu nombre y foto? Esta persona no sabrá que has visto sus mensajes hasta lx desbloquees. ¿Quieres permitir a %1$s chatear contigo? No recibirás ningún mensaje si no desbloqueas el chat. + ¿Quieres recibir noticias de %1$s? No recibirás ninguna novedad hasta que desbloquees a esta persona. ¿Quieres chatear con este grupo y compartir tu nombre y foto con sus participantes? Este es un grupo antiguo y ya no puede usarse. Crea un nuevo grupo para activar funciones nuevas como @menciones y admins. @@ -2186,7 +2196,7 @@ Para habilitar el vídeo, permite a Signal acceso a la cámara. - Signal necesita permisos de micrófono para iniciar o unirte a una llamada. + Signal necesita acceso al micrófono para iniciar o unirte a una llamada. Signal necesita acceso a la cámara para habilitar tu vídeo @@ -2245,6 +2255,7 @@ No hay nadie más %1$s está en esta llamada + %1$s estás en esta llamada %1$s y %2$s están en esta llamada @@ -2334,9 +2345,9 @@ Contacto de Signal - Persona verificada + Identidad verificada - No hay mensajes privados con %1$s + No tienes mensajes privados con %1$s %1$s está en tus contactos del teléfono @@ -2427,7 +2438,7 @@ No hemos podido enviarte el código de verificación por SMS. Intenta recibir tu código a través de una llamada. - No se ha podido solicitar el código de verificación. Comprueba la conexión de red e inténtalo de nuevo. + No se ha podido solicitar un código de verificación. Comprueba la conexión de red e inténtalo de nuevo. Número en formato no estándar Parece que el número que has introducido (%1$s) tiene un formato no estándar.\n\n ¿Es %2$s el número correcto? @@ -2466,9 +2477,9 @@ • Asegúrate de que tu teléfono tenga señal para recibir tu SMS o llamada\n • Confirma que puedes recibir una llamada telefónica al número\n • Comprueba que has ingresado tu número de teléfono correctamente. - Para obtener más información, consulta %1$s o %2$s + Para obtener más información, consulta %1$s o selecciona %2$s - estos pasos de resolución de problemas + estos pasos de solución de problemas Contactar con asistencia @@ -2608,7 +2619,7 @@ La duración de los mensajes temporales se ha establecido en %1$s El número de seguridad ha cambiado Tu número de seguridad con %1$s ha cambiado. - Has verificado la identidad + Has verificado su identidad Has retirado la marca de verificación No se ha podido procesar el mensaje Fallo en la entrega @@ -2647,7 +2658,7 @@ Lo intentaremos de nuevo más tarde. Signal se ha actualizado con éxito Se actualizó automáticamente a la versión %1$s. - You updated to version %1$s. + Has actualizado a la versión %1$s. ¿Enviar mensaje? @@ -2667,7 +2678,7 @@ Alias Eliminar Alias eliminado con éxito. - Fallo en la conexión de red. + Se ha producido un error de red. Se han realizado demasiados intentos. Inténtalo de nuevo más tarde. Este alias ya existe. @@ -2697,7 +2708,7 @@ Si recuperas tu alias, se restablecerán tu código QR y tu enlace. ¿Quieres hacerlo de todos modos? - Si cambias tu alias, se restablecerán tu código QR y tu enlace actuales. ¿Cambiarlo de todos modos? + Si cambias tu alias, se restablecerán tu código QR y tu enlace actuales. ¿Quieres cambiarlo de todos modos? Continuar @@ -2752,9 +2763,9 @@ - Formato de archivo no compatible + Tipo de archivo no compatible Borrador - Signal necesita acceso al almacenamiento del teléfono para guardar archivos en el almacenamiento externo. Ve al menú Ajustes de tu dispositivo y selecciona Aplicaciones y notificaciones > Signal > Permisos de la aplicación > Almacenamiento > Permitir. Estos pasos pueden variar dependiendo de tu dispositivo. + Signal necesita acceso al almacenamiento para guardar archivos en el almacenamiento externo. Ve al menú Ajustes de tu dispositivo y selecciona Aplicaciones y notificaciones > Signal > Permisos de la aplicación > Almacenamiento > Permitir. Estos pasos pueden variar dependiendo de tu dispositivo. No se puede guardar en una unidad de almacenamiento externo si Signal no tiene acceso ¿Eliminar mensaje? Este mensaje se eliminará permanentemente. @@ -2828,7 +2839,7 @@ Para recibir notificaciones de nuevos mensajes: - 1. Toca \"Ajustes\" más abajo + 1. Toca el botón \"Ajustes\" que aparece más abajo 2. %1$s Activa las notificaciones @@ -2859,7 +2870,7 @@ - La función de respuesta rápida no está disponible cuando Signal está bloqueado. + La función de respuesta rápida no está disponible cuando la app Signal está bloqueada. No se ha podido enviar el mensaje @@ -3012,14 +3023,14 @@ Foto de contacto - Signal necesita acceso a los contactos en tu teléfono para mostrar tus contactos. Ve al menú Ajustes de tu dispositivo y selecciona Aplicaciones y notificaciones > Signal > Permisos de la aplicación > Contactos > Permitir. Estos pasos pueden variar dependiendo de tu dispositivo. + Signal necesita acceso a los contactos de tu teléfono para mostrar tus contactos. Ve al menú Ajustes de tu dispositivo y selecciona Aplicaciones y notificaciones > Signal > Permisos de la aplicación > Contactos > Permitir. Estos pasos pueden variar dependiendo de tu dispositivo. No se han podido encontrar contactos. Comprueba tu conexión de red. Alias no encontrado "%1$s no usa Signal. Comprueba que el alias sea correcto e inténtalo de nuevo." - No necesitas añadirte como participante en el grupo - Se alcanzó el número máximo de participantes en el grupo. + No necesitas añadirte al grupo como participante + El grupo ha alcanzado el número máximo de participantes Los grupos de Signal permiten un máximo de %1$d participantes. - Se alcanzó el límite máximo recomendado + Se ha alcanzado el límite recomendado de participantes Los grupos de Signal funcionan mejor con %1$d o menos participantes. Añadir más participantes causará retrasos para enviar y recibir mensajes. %1$d participante @@ -3062,11 +3073,12 @@ SIM %1$d Enviar - Redactar mensaje - Cambiar a teclado de emojis + Escribir mensaje + Activar teclado de emojis Miniatura del archivo adjunto - Activar panel rápido de la cámara + Activar panel de archivos adjuntos de la cámara Grabar y enviar archivo de audio + Fijar grabación de archivo de audio No se ha podido enviar el mensaje. Comprueba tu conexión e inténtalo de nuevo. @@ -3085,6 +3097,7 @@ Mensaje leído + Foto de contacto @@ -3096,7 +3109,7 @@ Volver a la llamada La llamada está completa Invitar personas - Habilitar notificaciones de llamada + Habilitar notificaciones de llamadas Actualizar contacto Vetar solicitud @@ -3160,15 +3173,16 @@ Es posible que las siguientes personas hayan reinstalado Signal o cambiado su dispositivo. Verifica tu número de seguridad con ellas para asegurar la privacidad de todos los mensajes. Ver - Verificado anteriormente + Identidad verificada anteriormente - Notificaciones de llamada habilitadas. - Habilitar notificaciones de llamada + Se han habilitado las notificaciones de llamadas. + Habilitar notificaciones de llamadas Habilitar actividad en segundo plano ¡Todo parece estar bien! Para recibir notificaciones de llamadas, toca aquí y activa \"Mostrar notificaciones\". Para recibir notificaciones de llamadas, toca aquí para activarlas y asegúrate de que el sonido y las notificaciones emergentes estén habilitados. + Para recibir notificaciones de llamadas, toca aquí y habilita la actividad en segundo plano en los ajustes de la batería de tu teléfono. Ajustes Para recibir notificaciones de llamadas, toca \"Ajustes\" y activa \"Mostrar notificaciones\". @@ -3226,7 +3240,7 @@ %1$d semanas - %1$d sem + %1$d sem. %1$s %2$s @@ -3234,9 +3248,9 @@ Tus números de seguridad con %1$s y %2$s ya no están verificados Tus números de seguridad con %1$s, %2$s y %3$s ya no están verificados - Tu número de seguridad con %1$s ha cambiado y ya no está verificado. Podría deberse a que alguien está tratando de interceptar la comunicación o, simplemente, que %1$s ha reinstalado Signal. - Tus números de seguridad con %1$s y %2$s ya no están verificados. Podría deberse a que alguien está tratando de interceptar la comunicación o, simplemente, que han reinstalado Signal. - Tus números de seguridad con %1$s, %2$s y %3$s ya no están verificados. Podría deberse a que alguien está tratando de interceptar la comunicación o, simplemente, que han reinstalado Signal. + Tu número de seguridad con %1$s ha cambiado y ya no está verificado. Podría deberse a que alguien está tratando de interceptar la comunicación o, simplemente, a que %1$s ha reinstalado Signal. + Tus números de seguridad con %1$s y %2$s ya no están verificados. Podría deberse a que alguien está tratando de interceptar la comunicación o, simplemente, a que han reinstalado Signal. + Tus números de seguridad con %1$s, %2$s y %3$s ya no están verificados. Podría deberse a que alguien está tratando de interceptar la comunicación o, simplemente, a que han reinstalado Signal. Tu número de seguridad con %1$s acaba de cambiar. Tus números de seguridad con %1$s y %2$s acaban de cambiar. @@ -3261,10 +3275,10 @@ Ver - Reenviar + Volver a enviar - La marca de tiempo de envío se ha copiado en el portapapeles. + La marca de tiempo de envío se ha copiado al portapapeles. Las actualizaciones de tu historia se mostrarán aquí. @@ -3306,7 +3320,7 @@ Tomándome un descanso Trabajando en cosas nuevas - One or more characters is invalid. + Uno o más caracteres no son válidos. Editar grupo @@ -3365,7 +3379,7 @@ Pendiente Enviado a - Enviado desde + Enviado por Entregado a Leído por No enviado @@ -3383,7 +3397,7 @@ Seleccionar contactos Cambiar clave de acceso Verificar número de seguridad - Previsualizar archivo + Vista previa del archivo Detalles del mensaje Dispositivos vinculados Invitar personas @@ -3405,8 +3419,9 @@ + Información de asistencia - Solicitud de asistencia de Signal Android + Solicitud de asistencia para Signal Android No se han podido cargar los registros Describe el problema detalladamente para ayudarnos a entender la incidencia. @@ -3461,7 +3476,7 @@ Imágenes Audios - Vídeo + Vídeos Documentos Pequeño @@ -3483,8 +3498,8 @@ Beta SMS y MMS - Usar fotos de la libreta de contactos - Mostrar fotos de la libreta de contactos si están disponibles + Usar fotos de contactos + Elige mostrar fotos de la libreta de contactos si están disponibles. Dejar archivados chats silenciados @@ -3493,10 +3508,11 @@ Elige mostrar las vistas previas de los enlaces directamente de los sitios web para los mensajes que envías. Cambiar clave de acceso Cambia tu clave de acceso + Activar bloqueo de pantalla con clave de acceso Bloquea la pantalla y las notificaciones con una clave de acceso Seguridad de la pantalla - Bloquea automáticamente Signal tras un intervalo de inactividad especificado + Bloquea automáticamente Signal tras el intervalo de inactividad que especifiques Contraseña de tiempo de inactividad Intervalo de tiempo de inactividad Notificaciones @@ -3505,8 +3521,9 @@ Patrón de parpadeo del LED Personalizar Cambiar sonido y vibración + Sonido - Silenciar + Silencio Predeterminado Repetir alertas Nunca @@ -3537,12 +3554,12 @@ Agente de usuario de MMS Configuración manual de MMS URL de MMSC - Proxy MMS - Puerto de proxy MMS - Alias de MMSC - Contraseña de MMSC - Confirmación de entrega de SMS - Solicita la confirmación de entrega de SMS para todos los SMS que envías + Servidor proxy MMS + Puerto del proxy MMS + Nombre de usuario del MMSC + Contraseña del MMSC + Informe de entrega de SMS + Solicita el informe de entrega de SMS para todos los mensajes SMS que envías Datos y almacenamiento Almacenamiento Pagos @@ -3566,9 +3583,9 @@ Cambiar el icono y el nombre de la aplicación a \"%1$s\" - Signal deberá cerrarse para cambiar el icono y el nombre de la aplicación. Las notificaciones siempre mostrarán el icono y el nombre predeterminados de Signal. + Signal deberá cerrarse para cambiar el icono y el nombre de la aplicación. En las notificaciones siempre se mostrará el icono y el nombre predeterminados de Signal. - Selecciona un icono y un nombre de la aplicación. Esta información se mostrará en la pantalla de inicio y en el menú de aplicaciones de tu teléfono. Las notificaciones siempre mostrarán el icono y el nombre predeterminados de Signal. Más información + Selecciona un icono y un nombre de la aplicación. Esta información se mostrará en la pantalla de inicio y en el menú de aplicaciones de tu teléfono. En las notificaciones siempre se mostrará el icono y el nombre predeterminados de Signal. Más información Los iconos y nombres de la aplicación se muestran en la pantalla de inicio y en el menú de aplicaciones. @@ -3579,7 +3596,7 @@ Gráfico que muestra dónde se verá el icono alternativo de la aplicación. Desactivar PIN Activar PIN - Si desactivas el PIN, perderás todos los datos de tu cuenta al volver a instalar Signal, a menos que hagas una copia de seguridad manual y la restaures. No puedes activar el bloqueo de registro mientras el PIN esté desactivado. + Si desactivas el PIN, perderás todos los datos de tu cuenta al volver a registrarte en Signal, a menos que hagas una copia de seguridad manual y la restaures. No puedes activar el bloqueo de registro si el PIN está desactivado. El PIN mantiene la información guardada en Signal cifrada para que solo tú tengas acceso. Tu perfil, ajustes y contactos se restaurarán al reinstalar Signal. No necesitarás el PIN para abrir la aplicación. Predeterminado del sistema Idioma @@ -3592,9 +3609,9 @@ Activa esta opción si tu dispositivo utiliza entrega de SMS/MMS por Wi-Fi (actívala solo cuando la opción \"Llamada por Wi-Fi\" esté activada en tu dispositivo) Teclado en modo incógnito Confirmaciones de lectura - Si las confirmaciones de lectura están desactivadas, no podrás comprobar cuándo se han leído tus mensajes. + Si desactivas las confirmaciones de lectura, no podrás ver las confirmaciones de lectura de nadie. Indicadores de escritura - Si desactivas los indicadores de escritura, no podrás ver cuándo otras personas están escribiendo. + Si desactivas los indicadores de escritura, no podrás ver los indicadores de escritura de nadie. Solicitar deshabilitar el aprendizaje personalizado del teclado. Aunque actives esta opción, es posible que tu teclado la ignore. @@ -3620,11 +3637,11 @@ Esto eliminará permanentemente todos los mensajes y adjuntos de este dispositivo. - Se eliminará de forma permanente todo el historial de mensajes y todos los archivos multimedia de este dispositivo y de cualquier dispositivo vinculado. + Se eliminará de forma permanente todo el historial de mensajes y todos los archivos multimedia de este dispositivo y de cualquier otro dispositivo vinculado. ¿Seguro que quieres eliminar todo tu historial de mensajes? El historial completo de mensajes se eliminará permanentemente. Esta acción no se puede deshacer. - El historial completo de mensajes se eliminará permanentemente de todos tus dispositivos. Esta acción no se puede deshacer. + El historial completo de mensajes se eliminará de forma permanente de todos tus dispositivos. Esta acción no se puede deshacer. Borrar todos ahora Para siempre 1 año @@ -3653,7 +3670,7 @@ Sonidos en el chat Mostrar Tono de llamada - Tamaño de fuente del mensaje + Tamaño de fuente de los mensajes Prioridad Solución de problemas con las notificaciones @@ -3663,7 +3680,7 @@ Evitar censura Si activas Evitar censura, Signal intentará enviar mensajes evitando la censura. Activa esta función solo si te encuentras en un país que bloquee Signal. - Evitar censura se ha activado automáticamente basándose en el número de teléfono de tu cuenta de Signal. + Evitar censura se ha activado automáticamente en función del número de teléfono de tu cuenta de Signal. Has desactivado manualmente la opción Evitar censura. @@ -3681,7 +3698,7 @@ Usa un proxy si no puedes conectarte a Signal con datos móviles ni con Wi-Fi. Compartir Guardar - Conectando con el proxy… + Conectándose al proxy… Se ha conectado al proxy Error de conexión No se ha podido conectar al proxy. Comprueba la dirección del proxy e inténtalo de nuevo. @@ -3729,7 +3746,7 @@ Desactivar pagos Frase de recuperación Ayuda - Tasa de limpieza de monedas + Tarifa por consolidación de monedas Pago enviado Pago recibido Procesando pago @@ -3799,7 +3816,7 @@ - 1. Toca \"Ajustes\" más abajo + 1. Toca el botón \"Ajustes\" que aparece más abajo 2. %1$s Activa el permiso @@ -3847,34 +3864,34 @@ Comisión de red Enviado por Enviado a %1$s - Tú (%2$s del %1$s) - %1$s (%3$s del %2$s) + Tú, %2$s del %1$s + %1$s, %3$s del %2$s Para De Los detalles de la transacción, incluidos el importe y la fecha del pago, se registran en el libro mayor de MobileCoin. - Tasa de limpieza de monedas - Se cobra una tasa de limpieza de monedas cuando las monedas que tienes no pueden combinarse para completar una transacción. La limpieza te permite continuar enviando pagos. + Tarifa por consolidación de monedas + Se cobra una tarifa por consolidación de monedas cuando las monedas que tienes no pueden combinarse para completar una transacción. La consolidación te permite continuar enviando pagos. No hay más detalles disponibles para esta transacción Pago enviado Pago recibido - Pago completado: %1$s + Pago completado %1$s Número de bloque Transferencia Escanear código QR - Para: escanear o introducir la dirección de la cartera + Para: Escanea o introduce la dirección de la cartera Puedes transferir MobileCoin haciendo una transferencia a la dirección de la cartera que se indica en la plataforma de intercambio. La dirección de la cartera es la serie de números y letras que suele aparecer debajo del código QR. Siguiente Dirección no válida Comprueba la dirección de la cartera a la que estás intentando transferir e inténtalo de nuevo. No puedes transferir a tu propia cartera de Signal. Introduce la dirección de la cartera de tu cuenta en una plataforma de intercambio que admita MobileCoin. Para escanear el código QR, Signal necesita acceso a la cámara. - Signal necesita acceso a la cámara para leer un código QR. Ve a \"Ajustes\" y selecciona Aplicaciones y notificaciones > Signal > Permisos de la aplicación > Cámara > Permitir. Estos pasos pueden variar dependiendo de tu dispositivo. - Para escanear el código QR, Signal necesita acceso a la cámara. + Signal necesita acceso a la cámara para leer códigos QR. Ve al menú Ajustes de tu dispositivo y selecciona Aplicaciones y notificaciones > Signal > Permisos de la aplicación > Cámara > Permitir. Estos pasos pueden variar dependiendo de tu dispositivo. + Para escanear códigos QR, Signal necesita acceso a la cámara. Ajustes @@ -3882,10 +3899,10 @@ Escanea el código QR de la persona que recibirá el pago - Solicitud + Solicitar Pagar Saldo disponible: %1$s - Mostrar + Alternar 1 2 3 @@ -4067,7 +4084,7 @@ Toca dos veces para editar - Tuca rápidamente dos veces tus mensajes para editarlos. Puedes editar tus mensajes hasta 24 horas después de haberlos enviado. + Toca rápidamente dos veces tus mensajes para editarlos. Puedes editar tus mensajes hasta 24 horas después de haberlos enviado. Entendido @@ -4104,7 +4121,7 @@ - Previsualizar archivo + Vista previa del archivo Actualizar @@ -4143,7 +4160,7 @@ No se ha podido crear el PIN Tu PIN no se ha guardado. Volveremos a pedirte que crees un PIN en otro momento. PIN creado. - Vuelve a introducir tu PIN. + Vuelve a introducir tu PIN Creando PIN… @@ -4226,13 +4243,13 @@ Videollamada de Signal entrante - Llamada grupal entrante en Signal + Llamada grupal entrante de Signal Llamada de Signal en curso Videollamada de Signal en curso - Llamada grupal en curso en Signal + Llamada grupal de Signal en curso Cargando… @@ -4242,7 +4259,7 @@ Ahora no Migrar base de datos de Signal Clave de acceso a la copia de seguridad - Las copias de seguridad se guardarán en una unidad de almacenamiento externo y se cifrarán con la clave de acceso que aparece debajo. Necesitas esta clave para poder restaurar la copia de seguridad. + Las copias de seguridad se guardarán en una unidad de almacenamiento externo y se cifrarán con la clave de acceso que aparece más abajo. Necesitas esta clave para poder restaurar la copia de seguridad. Necesitas esta clave para poder restaurar la copia de seguridad. Carpeta He anotado esta clave. Comprendo que sin ella no podré restaurar la copia de seguridad. @@ -4258,7 +4275,7 @@ No se pueden importar nuevas copias de seguridad La copia de seguridad contiene datos mal formados - La clave de la copia de seguridad es incorrecta + Clave de acceso a la copia de seguridad incorrecta Comprobando… %1$d mensajes completados… ¿Restaurar desde copia de seguridad? @@ -4274,7 +4291,7 @@ Selecciona una carpeta para habilitar las copias de seguridad. Las copias se guardarán allí. Seleccionar carpeta Se ha copiado al portapapeles - No hay un selector de archivos disponible. + No hay selector de archivos disponible. Introduce tu clave de acceso a la copia de seguridad para verificarla Verificar ¡Clave de acceso correcta! @@ -4284,7 +4301,7 @@ Verificando copia de seguridad de Signal… Error en la copia de seguridad Parece que tu carpeta de copias de seguridad se ha eliminado o movido. - El archivo de copia de seguridad es demasiado grande para guardarlo en este dispositivo. + Tu archivo de copia de seguridad es demasiado grande para guardarlo en este dispositivo. No hay suficiente espacio para guardar tu copia de seguridad. No se ha podido crear ni verificar tu copia de seguridad más reciente. Crea una nueva. @@ -4350,8 +4367,8 @@ Debes introducir tu PIN de bloqueo de registro Tu PIN debe contener al menos %1$d dígitos o caracteres Demasiados intentos - Has realizado demasiados intentos incorrectos del PIN de bloqueo de registro. Inténtalo de nuevo en 24 horas. - Demasiados intentos fallidos. Inténtalo de nuevo más tarde. + Has realizado demasiados intentos con un PIN de bloqueo de registro incorrecto. Inténtalo de nuevo en 24 horas. + Has realizado demasiados intentos. Inténtalo de nuevo más tarde. Error de conexión con el servicio de Signal Copias de seguridad @@ -4467,7 +4484,7 @@ Reintentar Enviar registros de depuración Verificar código - Comprueba que el código que aparece abajo coincide en ambos dispositivos. Luego, toca Continuar. + Comprueba que el código que aparece más abajo coincide en ambos dispositivos. Luego, toca Continuar. Los números no coinciden Continuar Si los números de tus dispositivos no coinciden, es posible que no te hayas conectado al dispositivo correcto. Para solucionarlo, detén la transferencia e inténtalo de nuevo. Mantén ambos dispositivos cerca. @@ -4475,7 +4492,7 @@ No se puede encontrar el dispositivo anterior No se puede encontrar el dispositivo nuevo Asegúrate de tener activados estos servicios y permisos: - Acesso a la ubicación + Acceso a la ubicación Servicios de ubicación Wi-Fi En la pantalla de Wi-Fi Direct, elimina todos los grupos guardados y desvincula cualquier dispositivo invitado o conectado. @@ -4495,7 +4512,7 @@ Transfiriendo datos - Mantén próximos ambos dispositivos. No los apagues y mantén la aplicación de Signal abierta. La transferencia entre ambos dispositivos está cifrada de extremo a extremo. + Mantén ambos dispositivos cerca uno del otro. No los apagues y mantén la aplicación Signal abierta. La transferencia entre ambos dispositivos está cifrada de extremo a extremo. %1$d mensajes completados… %1$s %% de los mensajes completado… @@ -4611,13 +4628,13 @@ Revisar solicitud - %1$d participante del grupo tiene el mismo nombre. Revisa la lista de participantes debajo y selecciona la opción que desees. - %1$d participantes del grupo tienen el mismo nombre. Revisa la lista de participantes y selecciona la opción que desees. + %1$d participante del grupo tiene el mismo nombre. Revisa la siguiente lista de participantes debajo y selecciona la opción que desees. + %1$d participantes del grupo tienen el mismo nombre. Revisa la siguiente lista de participantes y selecciona la opción que desees. - Si no sabes quién ha enviado esta solicitud, revisa el contacto debajo y selecciona la opción que desees. - Si no sabes quién ha enviado esta solicitud, revisa la lista de contactos y selecciona la opción que desees. + Si no sabes quién ha enviado esta solicitud, revisa el siguiente contacto y selecciona la opción que desees. + Si no sabes quién ha enviado esta solicitud, revisa la siguiente lista de contactos y selecciona la opción que desees. No tienes otros grupos en común. No tienes grupos en común. @@ -5378,6 +5395,7 @@ Añadir a la historia Añadir un mensaje + Añadir una respuesta Enviar a Contenido de visualización única @@ -5982,6 +6000,8 @@ %1$d respuesta %1$d respuestas + + Story no longer hidden Añadir @@ -6005,7 +6025,7 @@ Habilita las confirmaciones de visualización para saber quién ha visto tus historias. - Ir a ajustes + Ir a Ajustes Eliminar @@ -6432,7 +6452,7 @@ - Las historias desaparecerán automáticamente después de 24 horas. Elige quién puede ver tu historia o crea historias nuevas para compartir con personas o grupos específicos. + Las historias desaparecerán automáticamente después de 24 horas. Elige quién puede ver tus historias o crea historias nuevas para compartir con personas o grupos específicos. Desactivar historias @@ -6489,7 +6509,7 @@ Para volver a habilitar las copias de seguridad: - Toca el botón \"Ir a Ajustes\" debajo + Toca el botón \"Ir a Ajustes\" que aparece más abajo Activar \"Permitir la configuración de alarmas y recordatorios\". @@ -7058,7 +7078,7 @@ No se ha podido encontrar este usuario. - Error en la conexión de red. Inténtalo de nuevo. + Se ha producido un error de red. Inténtalo de nuevo. No tienes acceso a la red. No se ha reiniciado tu enlace. Inténtalo de nuevo más tarde. @@ -7267,7 +7287,23 @@ - Libera %1$s de espacio para descargar tus archivos. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Aceptar - - - Historial de pagos - - Copia de seguridad de mensajes de texto y de todos los archivos multimedia - - Detalles de pago - - Tipo de copia de seguridad - - Fecha del pago - - Compartir + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Frecuencia de la copia de seguridad Copia de seguridad con conexión móvil + + View backup key + + Unlock to view backup key Desactivar y eliminar copias de seguridad @@ -7370,13 +7406,27 @@ La copia de seguridad se creará durante la noche. - Tipo de copia de seguridad + Backup plan Copias de seguridad desactivadas - - %1$s · %2$s/mes - - Activar copias + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + La clave de tu copia de seguridad - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + La clave de tu copia de seguridad es un código de 64 dígitos que te permite restaurar tu copia de seguridad cuando vuelves a instalar Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Si olvidas tu clave, no podrás restaurar tu copia de seguridad. Signal no puede ayudarte a recuperar tu copia de seguridad. - Next + Siguiente - Record your backup key + Guarda la clave de tu copia de seguridad - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Se necesita esta clave para recuperar tu cuenta y tus datos. Guárdala en un lugar seguro. Si la pierdes, no podrás recuperar tu cuenta. - Copy to clipboard + Copiar en el portapapeles - Next + Siguiente - Keep your key safe + Guarda bien tu clave - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal no podrá ayudarte a restaurar tu copia de seguridad si pierdes tu clave. Guárdala en un lugar seguro y no la compartas con otras personas. - I\'ve recorded my key + He guardado mi clave - Continue + Continuar - See key again + Volver a ver la clave diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 3f221d75ce..c701844a47 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Jah Ei Kustuta @@ -260,6 +263,7 @@ Koputa pildi, hoia all video tegemiseks + Pildista Vaheta kaamerat Ava galerii @@ -346,7 +350,7 @@ See ei ole kehtiv kõnelink. Kontrolli, kas kogu link on korrektne ja vajadusel paranda enne liitumiskatset. - You are already in a call + Sa oled juba kõnes @@ -403,6 +407,7 @@ Ei leia rakendust, mis oleks võimeline seda meediafaili avama. %1$s kopeeritud %1$s kaudu + SIMile %1$s   Loe rohkem   Laadi rohkem alla @@ -436,6 +441,7 @@ Saada muudatus Koosta sõnum Vabandust, sinu manuse seadmisega tekkis viga. + Saaja ei ole sobiv SMS või e-posti aadress! Sõnum on tühi! Grupiliikmed @@ -576,6 +582,7 @@ %1$d manuse salvestamine sisemällu… Ootel… + Andmeside (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Eemaldada \"%1$s\" linkimine? Eemaldades selle seadme lingi, ei ole see enam võimeline sõnumeid saatma ja vastu võtma. Võrguühendus ebaõnnestus + Proovi uuesti Seadme lingi eemaldamine… Eemaldan seadme lingi @@ -1408,7 +1416,7 @@ Grupilink Jaga Lähtesta link - Require admin approval + Küsi administraatori nõusolekut Nõua grupilingi kaudu liituvate uute liikmete administraatori poolt kinnitamist. Kas sa tõesti soovid vestluse linki lähtestada? Inimesed ei saa siis varasema lingi kaudu enam vestlusega liituda. @@ -1507,6 +1515,7 @@ Saadad %1$d SMS-kutset? Lähme Signalile üle: %1$s + Tundub, et sul pole ühtegi rakendust, kuhu jagada. @@ -1931,6 +1940,7 @@ Kas lubad kasutajal %1$s sulle sõnumeid saata ja jagad temaga oma nime ja fotot? Sa ei saa sõnumeid enne kui oled blokeeringu eemaldanud. Kas lubad kasutajal %1$s sulle sõnumeid saata? Sa ei saa sõnumeid enne kui oled blokeeringu eemaldanud. + Kas soovid näha kasutaja %1$s uuendusi ja uudiseid? Sa ei saa uuendusi enne kui oled blokeeringu eemaldanud. Kas jätkata vestlust selle grupiga ja jagada sinu nime ning fotot grupi liikmetele? Seda aegunud gruppi ei saa enam kasutada. Loo uus grupp, et aktiveerida uued funktsioonid nagu @mainimised ja administraatorid. @@ -2245,6 +2255,7 @@ Kedagi teist pole siin %1$s on selles kõnes + %1$s on selles kõnes %1$s ja %2$s on selles kõnes @@ -2647,7 +2658,7 @@ Proovime hiljem uuesti. Signal on edukalt uuendatud Sinu versioon on automaatselt uuendatud versioonile %1$s. - You updated to version %1$s. + Uuendasid versioonile %1$s. Saadad sõnumi? @@ -3067,6 +3078,7 @@ Manuse pisipilt Lülita kiire kaamera manuse sahtel sisse või välja Salvesta ja saada helimanus + Lukusta helimanuse salvestamine Sõnumi saatmine ei õnnestunud. Kontrolli internetiühendust ja proovi uuesti. @@ -3085,6 +3097,7 @@ Sõnum loetud + Kontakti foto @@ -3169,6 +3182,7 @@ Nüüd on kõik hästi! Kõnede teavituste saamiseks klõpsa siia ja lülita sisse \"Näita teavitusi\". Kõnede teavituste saamiseks klõpsa siia, luba teavitused ja kontrolli, et heli ja hüpikaknad oleks lubatud. + Kõnede teavituste saamiseks klõpsa Sätted ja luba aku sätetes taustategevused. Sätted Kõnede teavituste saamiseks klõpsa Sätted ja lülita sisse \"Näita teavitusi\". @@ -3306,7 +3320,7 @@ Puhkepaus Töötan millegi uue kallal - One or more characters is invalid. + Üks või rohkem tähemärke ei ole kasutatavad. Muuda gruppi @@ -3405,6 +3419,7 @@ + Kasutajatoe info Signali Androidi kasutajatoetaotlus @@ -3493,6 +3508,7 @@ Hangib lingieelvaated otse saitidelt, mida sõnumina saadad. Muuda salasõna Muuda oma salasõna + Luba salaväljendiga ekraanilukk Lukusta ekraan ja teavitused salasõnaga Kuva turvamine @@ -3505,6 +3521,7 @@ LEDi välkumismuster Kohanda Muuda heli ja vibratsiooni + Heli Vaikne Vaikimisi @@ -5378,6 +5395,7 @@ Lisa loosse Lisa sõnum + Lisa vastus Vali saaja Ühekordselt vaadatav meediasisu @@ -5982,6 +6000,8 @@ %1$d vastus %1$d vastust + + Story no longer hidden Lisa @@ -7267,7 +7287,23 @@ - Meedia allalaadimiseks vabasta %1$s ruumi. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Maksete ajalugu - - Sõnumite ja kogu meedia varukoopia - - Makse andmed - - Varukoopia tüüp - - Tasumiskuupäev - - Jaga + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Varundamise sagedus Varunda mobiiliside kaudu + + View backup key + + Unlock to view backup key Lülita välja ja kustuta varukoopia @@ -7370,13 +7406,27 @@ Varukoopia luuakse öösel. - Varukoopia tüüp + Backup plan Varundamine on keelatud - - %1$s · %2$s kuus - - Luba varundamine + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Sinu varukoopia võti - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Sinu varukoopia võti on 64-kohaline kood, mille abil saad varukoopia taastada, kui Signali uuesti paigaldad. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Kui oma võtme unustad, ei ole sul võimalik oma varukoopiat taastada. Signal ei saa sul aidata varukoopiat taastada. - Next + Edasi - Record your backup key + Pane oma võti kirja - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Seda võtit on vaja sinu andmete ja konto taastamiseks. Hoia seda turvalises kohas. Kui selle kaotad, ei ole sul võimalik oma kontot taastada. - Copy to clipboard + Kopeeri lõikelauale - Next + Edasi - Keep your key safe + Hoia oma võtit turvaliselt - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Kui oma võtme ära kaotad, ei saa Signal aidata sul varukoopiat taastada. Hoia seda turvalises kohas ning ära jaga teistega. - I\'ve recorded my key + Olen oma võtme üles kirjutanud - Continue + Jätka - See key again + Vaata võtit veelkord diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 3145c9dfb7..7bb9485109 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Bai Ez Ezabatu @@ -260,6 +263,7 @@ Sakatu argazki bat ateratzeko; eduki sakatuta bideo bat grabatzeko + Argazkia Aldatu kamera Ireki galeria @@ -346,7 +350,7 @@ Hau ez da baliozko dei-esteka bat. Ziurtatu esteka osoa osorik dagela eta zuzena dela sartzen saiatu aurretik. - You are already in a call + Deian zaude @@ -403,6 +407,7 @@ Ez da euskarri hau irekitzeko gai den aplikaziorik aurkitu. Kopiatuta %1$s %1$s-(e)tik + %1$s-(e)ra Irakurri gehiago Deskargatu gehiago @@ -436,6 +441,7 @@ Bidali editatutako mezua Mezua idatzi Barkatu, zure eranskina ezartzean errore bat izan da + Hartzailea SMS edo email baliogabea da! Mezua hutsik dago! Taldeko kideak @@ -576,6 +582,7 @@ %1$d eraskin biltegian gordetzen… Zain… + Datuak (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \'%1$s\'-rekin lotura ezabatu? Gailu hau desparekatu ondoren ezin ditzakezu mezuak bidal eta jaso. Internetera konektatzeak huts egin du + Saiatu berriro Gailuarekin lotura ezabatzen… Gailuarekin lotura ezabatzen @@ -1408,7 +1416,7 @@ Talderako esteka Elkarbanatu Berrezarri esteka - Require admin approval + Eskatu administratzaile baten onespena Kideak talde esteka bidez sartzen badira taldean, derrigorrezkotzat jo administratzaile batek onartzea. Ziur zaude talde esteka berrezarri nahi izateaz? Eginez gero, jendea ezingo da taldera gehitu oraingo esteka erabiliz. @@ -1507,6 +1515,7 @@ Bidali %1$d SMS gonbidapen? Signalera alda dezagun: %1$s + Dirudienez ez duzu partekatzeko aplikazio bakar bat ere. @@ -1931,6 +1940,7 @@ Zuri mezuak bidaltzeko baimena eman nahi diozu %1$s erabiltzaileari, eta zure izena eta argazkia harekin partzekatu? Desblokeatu arte, ez duzu jasoko haren mezurik. Zuri mezuak bidaltzeko baimena eman nahi diozu %1$s erabiltzaileari? Desblokeatu arte, ez duzu jasoko haren mezurik. + %1$s erabiltzailearen berritasun eta albisteak jaso nahi dituzu? Desblokeatzen duzun arte, ez duzu eguneratzerik jasoko. Talde honekin duzun txatean jarraitu, eta zure izena eta argazkia hartako kideekin partekatu nahi dituzu? Jarauntsitako talde hau ezin da erabili jada. Sortu beste talde bat eginbide berriak aktibatzeko; esaterako, @aipamenak eta administratzaileak. @@ -2245,6 +2255,7 @@ Beste inor ez dago hemen %1$s dei honetan dago + %1$s dei honetan zaude %1$s eta %2$s dei honetan daude @@ -2647,7 +2658,7 @@ Berriro saiatuko gara geroago. Eguneratu da Signal %1$s bertsioa automatikoki instalatu da. - You updated to version %1$s. + %1$s bertsiora eguneratu duzu aplikazioa. Mezua bidali? @@ -3067,6 +3078,7 @@ Eranskinerako koadro txikia Gaitu/ezgaitu kamera arinaren eranskinen tiradera Grabatu eta bidali audio eranskina + Finkatu audio atxikimenduen grabaketa audio mezu luzeak grabatzeko Mezua ezin izan da bidali. Egiazta ezazu zure konexioa eta saia zaitez berriro. @@ -3085,6 +3097,7 @@ Mezua irakurrita + Kontaktuaren argazkia @@ -3169,6 +3182,7 @@ Itxura ona du denak! Deien jakinarazpenak jasotzeko, sakatu hau eta aktibatu \"Erakutsi jakinarazpenak\" Deien jakinarazpenak jasotzeko, sakatu hau jakinarazpenak aktibatzeko, eta ziurtatu soinua eta leiho gainerakorrak gaituta daudela. + Deien jakinarazpenak jasotzeko, sakatu hau eta gaitu atzeko planoko jarduerak \"Bateria\" ataleko ezarpenetan. Ezarpenak Deien jakinarazpenak jasotzeko, sakatu Ezarpenak eta aktibatu \"Erakutsi jakinarazpenak\" @@ -3306,7 +3320,7 @@ Atsedena hartzen Zerbait berrian lanean - One or more characters is invalid. + Karaktere batek edo gehiagok ez dute balio. Taldea editatu @@ -3405,6 +3419,7 @@ + Laguntzarako Informazioa Eskaera Signal Android-ekin Laguntzeko @@ -3493,6 +3508,7 @@ Eskuratu esteken aurrebistak zuzenean webguneetatik eta erabili bidaltzen dituzun mezuetan. Aldatu pasaesaldia Aldatu zure pasaesaldia + Gaitu pasaesaldia blokeo-pantailan Blokeatu pantaila eta jakinarazpenak pasaesaldi batekin Pantailaren segurtasuna @@ -3505,6 +3521,7 @@ LEDaren keinuka eredua Pertsonalizatu Aldatu soinua eta dardara + Soinua Isildu Lehenetsia @@ -5378,6 +5395,7 @@ Gehitu istorioan Gehitu mezu bat + Gehitu erantzun bat Bidali honi: Behin ikusteko multimedia-elementua @@ -5982,6 +6000,8 @@ Erantzun %1$d %1$d erantzun + + Story no longer hidden Gehitu @@ -7267,7 +7287,23 @@ - Utzi %1$s libre multimedia-edukia deskargatzeko. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Ados - - - Ordainketen historia - - Testuaren eta mutimediaren babeskopia - - Ordainketaren xehetasunak - - Babeskopia mota - - Ordainketa data - - Partekatu + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Babeskopien maiztasuna Egin babeskopiak mugikorra erabiliz + + View backup key + + Unlock to view backup key Desaktibatu eta ezabatu babeskopiak @@ -7370,13 +7406,27 @@ Babeskopia gauez sortuko da. - Babeskopia mota + Backup plan Babeskopiak desgaituta daude - - %1$s · %2$s/hilabete - - Gaitu babeskopiak + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d / %2$d @@ -7426,33 +7476,33 @@ - Your backup key + Zure babeskopia-gakoa - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Babeskopia-gakoa 64 digituko kode bat da, eta Signal berriro instalatzean babeskopia leheneratzeko aukera ematen dizu. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Gakoa ahaztuz gero, ezingo duzu leheneratu babeskopia. Signal-ek ezin dizu lagundu babeskopia berreskuratzen. - Next + Hurrengoa - Record your backup key + Apuntatu babeskopia-gakoa - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Zure kontua eta datuak berreskuratzeko behar da gako hori. Gorde gakoa toki seguru batean. Galduz gero, ezingo duzu berreskuratu kontua. - Copy to clipboard + Kopiatu arbelera - Next + Hurrengoa - Keep your key safe + Mantendu gakoa seguru - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Gakoa galduz gero, Signal-ek ezingo dizu lagundu babeskopia leheneratzen. Gorde ezazu toki seguru batean, eta ez ezazu inorekin partekatu. - I\'ve recorded my key + Apuntatu dut gakoa - Continue + Jarraitu - See key again + Ikusi gakoa berriro diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 6095c9903d..28469a0355 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -22,12 +22,15 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + بله نه پاک کردن لطفاً صبر کنید… ذخیره - یادداشت به خود + یادداشت برای خود آب‌وهوا @@ -260,6 +263,7 @@ برای عکس ضربه بزنید، برای ویدئو نگه دارید + گرفتن تصویر تغییر دوربین باز کردن گالری @@ -346,7 +350,7 @@ این پیوند تماس معتبری نیست. قبل از تلاش برای پیوستن، مطمئن شوید که پیوند کامل و درست است. - You are already in a call + شما در حال حاضر در یک تماس هستید @@ -403,6 +407,7 @@ برنامه‌ای برای بازکردن این رسانه پیدا نشد. %1$s کپی شد از %1$s + به %1$s   بیشتر بخوانید  بارگیری بیشتر @@ -436,6 +441,7 @@ ارسال ویرایش نگارش پیام پوزش، در تنظیم پیوست شما خطایی رخ داد. + گیرنده یک نشانی ایمیل یا پیامک معتبر نیست! پیام خالی است! اعضا‌ی گروه @@ -576,6 +582,7 @@ در حال ذخیره‌سازی %1$d پیوست در حافظه… در انتظار… + اینترنت (سیگنال) پیام چندرسانه‌ای پیامک @@ -1006,6 +1013,7 @@ جدا کردن \'%1$s\'؟ با جدا کردن این دستگاه، دیگر قادر به ارسال و دریافت پیام نخواهد بود. اتصال شبکه ناموفق بود + دوباره امتحان کنید در حال جدا کردن دستگاه… در حال جدا کردن دستگاه @@ -1408,7 +1416,7 @@ پیوند گروه اشتراک‌گذاری بازنشانی پیوند - Require admin approval + نیازمند تأیید مدیر نیاز به یک مدیر برای تأیید اعضای جدیدی که از طریق پیوند گروه می‌پیوندند. آیا از بازنشانی پیوند گروه اطمینان دارید؟ افراد دیگر نمی‌توانند با استفاده از پیوند فعلی به گروه بپیوندند. @@ -1507,6 +1515,7 @@ ارسال %1$d پیامک دعوت؟ بیایید از سیگنال استفاده کنیم: %1$s + به نظر می‌رسد شما هیچ برنامه‌ای برای ‌اشتراک‌گذاری ندارید. @@ -1931,6 +1940,7 @@ می‌خواهید به %1$s اجازه دهید به شما پیام دهد و نام و عکستان را با او به اشتراک بگذارید؟ تا وقتی او را رفع مسدودیت نکنید هیچ پیامی دریافت نخواهید کرد. می‌خواهید اجازه دهید %1$s به شما پیام دهد؟ تا وقتی او را رفع مسدودیت نکنید، هیچ پیامی دریافت نخواهید کرد. + می‌خواهید به‌روزرسانی‌ها و تازه‌های %1$s را دریافت کنید؟ تا مسدودیت آن‌ها را رفع نکنید به‌روزرسانی دریافت نخواهید کرد. آیا به گفتگو‌ی خود با این گروه ادامه می‌دهید و نام و عکس خود را با اعضای آن به اشتراک می‌گذارید؟ این گروه قدیمی دیگر قابل استفاده نیست. برای فعال‌سازی قابلیت‌های جدید مانند @اشاره‌ها و مدیرها، گروه جدیدی ایجاد کنید. @@ -2245,6 +2255,7 @@ هیچ‌کس دیگری اینجا نیست %1$s در این تماس حضور دارد + %1$s نفر در این تماس هستند %1$s و %2$s در این تماس حضور دارند @@ -2647,7 +2658,7 @@ بعداً دوباره امتحان خواهیم کرد. سیگنال به‌روز شد به‌طور خودکار به نسخۀ %1$s به‌روزرسانی شده‌اید. - You updated to version %1$s. + شما به نسخه %1$s به روزرسانی کردید. ارسال پیام؟ @@ -3067,6 +3078,7 @@ ریزعکس پیوست باز و بسته کردن کشوی دوربین سریع ضبط و ارسال پیوست صوتی + قفل ضبط پیوست صوتی پیام ارسال نشد. اتصال خود را بررسی و دوباره امتحان کنید. @@ -3085,6 +3097,7 @@ پیام خوانده شد + تصویر مخاطب @@ -3169,6 +3182,7 @@ همه چیز حالا خوب بنظر می‌رسد! برای دریافت اعلان‌ها‌ی تماس، اینجا ضربه بزنید و «نمایش اعلان‌ها‌» را روشن کنید. برای دریافت اعلان‌ها‌ی تماس، اینجا ضربه بزنید و اعلان‌ها را روشن کنید و مطمئن شوید صدا و پاپ‌آپ فعال شده‌ اند. + برای دریافت اعلان‌ها‌ی تماس، اینجا ضربه بزنید و فعالیت پس‌زمینه را در تنظیمات «باتری» فعال کنید. تنظیمات برای دریافت اعلان‌ها‌ی تماس، روی تنظیمات ضربه بزنید و «نمایش اعلان‌ها» را روشن کنید. @@ -3306,7 +3320,7 @@ در حال استراحت در حال کار روی یک چیز جدید - One or more characters is invalid. + یک یا چند نویسه نامعتبر است. ویرایش گروه @@ -3405,6 +3419,7 @@ + اطلاعات پشتیبانی درخواست پشتیبانی برای سیگنال اندروید @@ -3493,6 +3508,7 @@ پیش‌نمایش‌های پیوند را به صورت مستقیم از وب‌سایت‌ها برای پیام‌هایی که ارسال می‌کنید، دریافت کنید. تغییر گذرواژه گذرواژهٔ خود را تغییر دهید + فعال‌سازی گذرواژهٔ قفل صفحه صفحه و اعلان‌ها را با یک گذرواژه قفل کنید امنیت صفحه @@ -3505,6 +3521,7 @@ الگوی چشمک زدن LED سفارشی‌سازی تغییر صدا و لرزش + زنگ بی‌صدا پیش‌فرض @@ -5378,6 +5395,7 @@ افزودن به استوری افزودن یک پیام + یک پاسخ اضافه کنید ارسال به فایل رسانه با قابلیت یک‌بار مشاهده @@ -5982,6 +6000,8 @@ %1$d پاسخ %1$d پاسخ + + Story no longer hidden افزودن @@ -6949,7 +6969,7 @@ ویرایش نام تماس - الزام تأیید مدیر + نیازمند تأیید مدیر اشتراک‌گذاری پیوند از طریق سیگنال @@ -7005,7 +7025,7 @@ افزودن نام تماس - الزام تأیید مدیر + نیازمند تأیید مدیر اشتراک‌گذاری پیوند @@ -7267,7 +7287,23 @@ - برای دانلود رسانه خود، %1$s فضای ذخیره‌سازی خالی کنید. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ تأیید - - - تاریخچه پرداخت - - پشتیبان‌گیری متن و تمام رسانه‌ها - - جزئیات پرداخت - - نوع پشتیبان - - تاریخ پرداخت - - اشتراک‌گذاری + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ تناوب پشتیبان پشتیبان‌گیری با استفاده از اینترنت همراه + + View backup key + + Unlock to view backup key خاموش کردن و پاک کردن پشتیبان @@ -7370,13 +7406,27 @@ پشتیبان‌گیری در طول شب انجام می‌شود. - نوع پشتیبان + Backup plan پشتیبان‌ها غیرفعال شده‌اند - - %1$s · %2$s/ماه - - فعال‌سازی پشتیبان‌ها + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + کلید پشتیبان شما - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + کلید پشتیبان شما یک کد ۶۴ رقمی است که این امکان را به شما می‌دهد هنگام نصب مجدد سیگنال، نسخه پشتیبان خود را بازیابی کنید. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + اگر کلید خود را فراموش کنید، نمی‌توانید نسخه پشتیبان خود را بازیابی کنید. سیگنال نمی‌تواند به شما کمک کند نسخه پشتیبان خود را بازیابی کنید. - Next + بعدی - Record your backup key + کلید پشتیبان خود را ضبط کنید - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + این کلید برای بازیابی حساب و اطلاعات شما ضروری است. این کلید را در مکانی امن نگهداری کنید. اگر آن را گم کنید، نمی‌توانید حساب خود را بازیابی کنید. - Copy to clipboard + کپی به کلیپ‌بورد - Next + بعدی - Keep your key safe + در مراقبت از کلید خود بکوشید - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + اگر کلید خود را گم کنید، سیگنال نمی‌تواند در بازیابی نسخه پشتیبان به شما کمک کند. آن را در مکانی امن و مطمئن نگهداری کنید و با دیگران به اشتراک نگذارید. - I\'ve recorded my key + من کلیدم را ضبط کرده‌ام - Continue + ادامه - See key again + مشاهده دوباره کلید diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 73520a3aa0..2d3e9c17c5 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Kyllä Ei Poista @@ -260,6 +263,7 @@ Ota kuva napauttamalla tai video painamalla pitkään + Ota kuva Vaihda kamera Avaa galleria @@ -346,7 +350,7 @@ Tämä ei ole kelvollinen puhelulinkki. Varmista, että koko linkki on ehjä ja oikea ennen kuin yrität liittyä. - You are already in a call + Olet jo puhelussa @@ -403,6 +407,7 @@ Median avaamiseen ei löytynyt sovellusta. Kopioitu: %1$s SIM-kortilta %1$s + vastaanotettu SIM-kortille %1$s   Lue lisää   Lataa lisää @@ -436,6 +441,7 @@ Lähetä muokkaus Kirjoita viesti Virhe liitteen lisäämisessä + Annettu numero tai sähköpostiosoite ei ole kelvollinen! Viesti on tyhjä! Ryhmän jäsenet @@ -576,6 +582,7 @@ Tallennetaan %1$d liitetiedostoa tallennustilaan… Käsiteltävänä… + Data (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Poista \'%1$s\'? Jos poistat tämän laitteen, sillä ei voi enää lähettää eikä vastaanottaa viestejä. Verkkoyhteyden muodostus epäonnistui + Yritä uudelleen Poistetaan laitetta Poistetaan laitetta @@ -1408,7 +1416,7 @@ Ryhmälinkki Jaa Uudelleenaseta linkki - Require admin approval + Vaadi järjestelmänvalvojan hyväksyntä Ylläpitäjän on hyväksyttävä ryhmälinkin kautta liittyvät uudet jäsenet. Haluatko varmasti asettaa ryhmälinkin uudelleen? Ryhmään ei voi enää liittyä nykyistä linkkiä käyttäen. @@ -1507,6 +1515,7 @@ Lähetetäänkö %1$d tekstiviestikutsua? Hei, vaihdetaan Signaliin: %1$s + Sinulla ole yhtään sovellusta, joihin voi jakaa sisältöä. @@ -1931,6 +1940,7 @@ Sallitaanko henkilön %1$s lähettää sinulle viestejä ja nähdä nimesi ja profiilikuvasi? Et saa viestejä, ennen kuin poistat eston. Sallitaanko henkilön %1$s lähettää sinulle viestejä? Et saa viestejä, ennen kuin poistat eston. + Haluatko päivityksiä ja uutisia henkilöltä %1$s? Et saa päivityksiä, ennen kuin poistat eston. Jatketaanko keskustelua tämän ryhmän kanssa ja näytetäänkö nimesi ja kuvasi sen jäsenille? Tätä vanhaa ryhmää ei voi enää käyttää. Luo uusi ryhmä ottaaksesi käyttöön uudet ominaisuudet, kuten @maininnat ja ryhmän ylläpitäjät. @@ -2245,6 +2255,7 @@ Kukaan muu ei ole täällä %1$s on tässä puhelussa + %1$s ovat tässä puhelussa %1$s ja %2$s ovat tässä puhelussa @@ -2647,7 +2658,7 @@ Yritämme myöhemmin uudelleen. Signalin päivitys onnistui Päivitettiin automaattisesti versioon %1$s. - You updated to version %1$s. + Päivitit versioon %1$s. Lähetetäänkö viesti? @@ -3067,6 +3078,7 @@ Liitteen esikatselu Avaa tai sulje pikakamera Nauhoita ja lähetä ääniviesti + Lukitse ääniviestin tallennus Viestin lähetys ei onnistunut. Tarkista verkkoyhteytesi ja yritä uudelleen. @@ -3085,6 +3097,7 @@ Viesti luettu + Yhteystiedon kuva @@ -3169,6 +3182,7 @@ Kaikki näyttää nyt hyvältä! Nähdäksesi ilmoitukset puheluista, napauta tästä ja valitse Näytä ilmoitukset Nähdäksesi ilmoitukset puheluista, napauta tästä, laita ilmoitukset päälle ja varmista että ääni ja ponnahdusikkuna ovat päällä. + Nähdäksesi ilmoitukset puheluista, napauta asetuksia ja salli toiminta taustalla. Asetukset Nähdäksesi ilmoitukset puheluista, napauta Asetukset ja valitse Näytä ilmoitukset @@ -3306,7 +3320,7 @@ Tauolla Luomassa uutta - One or more characters is invalid. + Yksi tai useampi merkki on virheellinen. Muokkaa ryhmää @@ -3405,6 +3419,7 @@ + Tukitiedot Signal Android -tukipyyntö @@ -3493,6 +3508,7 @@ Näytä lähettämiesi linkkien esikatselu verkkosivustoilta. Salalauseen vaihto Vaihda salalauseesi + Ota käyttöön näytön lukitus salalauseella Lukitse näyttö ja ilmoitukset salalauseella Näytön suojaus @@ -3505,6 +3521,7 @@ LED:in vilkkumistahti Mukauta Muuta ääni- ja värinäasetuksia + Ääni Ei ääntä Oletus @@ -5378,6 +5395,7 @@ Lisää tarinaan Lisää viesti + Lisää vastaus Lähetä henkilölle Kerran katsottava media @@ -5982,6 +6000,8 @@ %1$d vastausta %1$d vastausta + + Story no longer hidden Lisää @@ -7267,7 +7287,23 @@ - Vapauta %1$s tilaa, jotta voit ladata mediaa. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Maksuhistoria - - Tekstin ja kaiken median varmuuskopio - - Maksun tiedot - - Varmuuskopion tyyppi - - Maksun päivämäärä - - Jaa + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Varmuuskopiointitiheys Varmuuskopioi matkapuhelinverkon kautta + + View backup key + + Unlock to view backup key Kytke varmuuskopio pois käytöstä ja poista se @@ -7370,13 +7406,27 @@ Varmuuskopio luodaan yön aikana. - Varmuuskopion tyyppi + Backup plan Varmuuskopiot poistettu käytöstä - - %1$s · %2$s / kk - - Ota varmuuskopiot käyttöön + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Varmuuskopion avain - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Varmuuskopion avain on 64-numeroinen koodi, jonka avulla voit palauttaa varmuuskopion, kun asennat Signalin uudelleen. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Jos unohdat avaimen, et voi palauttaa varmuuskopiota. Signal ei voi auttaa sinua palauttamaan varmuuskopiota. - Next + Seuraava - Record your backup key + Tallenna varmuuskopion avain - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Tätä avainta tarvitaan tilisi ja tietojesi palauttamiseen. Säilytä avain turvallisessa paikassa. Jos kadotat sen, et voi palauttaa tiliäsi. - Copy to clipboard + Kopioi leikepöydälle - Next + Seuraava - Keep your key safe + Pidä avain turvassa - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal ei voi auttaa sinua palauttamaan varmuuskopiota, jos kadotat avaimesi. Säilytä avain turvallisessa paikassa äläkä jaa sitä muiden kanssa. - I\'ve recorded my key + Olen tallentanut avaimeni - Continue + Jatka - See key again + Näytä avain uudelleen diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6e2d7d45db..2bc55a3f69 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Oui Non Supprimer @@ -185,7 +188,7 @@ Faire un don de %1$s/mois - Faire un don de %1$s/mois + Payer %1$s/mois pour soutenir Signal @@ -260,6 +263,7 @@ Touchez pour une photo, maintenez pour une vidéo + Prendre une photo ou une vidéo Changer de caméra Ouvrir la galerie @@ -346,7 +350,7 @@ Ce lien d’appel n’est pas valide. Vérifiez qu’il n’est pas tronqué et qu’il s’agit du bon lien. - You are already in a call + Vous êtes déjà en communication @@ -403,6 +407,7 @@ Impossible de trouver une appli pour ouvrir ce média. Copié %1$s de %1$s + à %1$s   Lire la suite   Télécharger plus @@ -436,6 +441,7 @@ Envoyer les modifications Rédiger un message Oups ! Une erreur s’est produite lors de l’ajout de la pièce jointe. + Le destinataire n’est pas une adresse texto ou courriel valide. Le message est vide. Membres du groupe @@ -554,8 +560,8 @@ Impossible de retrouver l\'application Contacts. - Supprimer le message sélectionné ? - Supprimer les messages sélectionnés ? + Supprimer le message sélectionné ? + Supprimer les messages sélectionnés ? Enregistrer dans la mémoire ? @@ -576,6 +582,7 @@ Enregistrement de %1$d fichiers joints dans l\'espace de stockage… En attente… + Données (Signal) Message multimédia Texto @@ -1006,6 +1013,7 @@ Dissocier \"%1$s\" ? En annulant le lien avec cet appareil, il ne pourra plus ni envoyer ni recevoir de messages. Échec de connexion au réseau + Réessayer Annulation du lien avec l’appareil… Annulation du lien avec l’appareil @@ -1408,7 +1416,7 @@ Lien de groupe Partager Réinitialiser le lien - Require admin approval + Exiger l\'approbation d\'un admin Un administrateur doit approuver les nouveaux membres qui rejoignent le groupe via le lien. Voulez-vous vraiment réinitialiser le lien du groupe ? Personne ne pourra plus rejoindre le groupe via le lien actuel. @@ -1507,6 +1515,7 @@ Envoyer %1$d invitations par SMS ? Passons à Signal : %1$s + Vous semblez n’avoir aucune appli vers laquelle partager. @@ -1931,6 +1940,7 @@ Autoriser %1$s à vous envoyer des messages et partager vos nom et photo avec ce contact ? Vous ne recevrez aucun message tant que vous ne l’aurez pas débloqué. Autoriser %1$s à vous envoyer un message ? Vous ne recevrez aucun message tant que vous ne l’aurez pas débloqué. + Recevoir voir des nouvelles de%1$s ? Vous ne recevrez aucune nouvelle tant que vous ne l’aurez pas débloqué. Poursuivre votre conversation avec ce groupe et partager vos nom et photo avec ses membres ? Les groupes hérités ne peuvent plus être utilisés. Créez un nouveau groupe pour activer les nouvelles fonctionnalités telles que les @mentions et les administrateurs. @@ -2245,6 +2255,7 @@ Il n’y a personne d’autre ici %1$s est dans cet appel + %1$s sont dans cet appel %1$s et %2$s font partie de cet appel @@ -2647,7 +2658,7 @@ Nous réessaierons plus tard. Signal a été mis à jour avec succès La version %1$s a été automatiquement installée. - You updated to version %1$s. + Vous avez installé la version %1$s. Envoyer le message ? @@ -3067,6 +3078,7 @@ Imagette de fichiers joints Afficher/masquer le tiroir de l’appareil photo à basse résolution Enregistrer et envoyer un message audio en pièce jointe + Verrouiller l’enregistrement de pièces jointes au format audio Le message n’a pas pu être envoyé. Veuillez vérifier votre connexion et réessayer. @@ -3085,6 +3097,7 @@ Message lu + Photo du contact @@ -3169,6 +3182,7 @@ Tout semble en ordre maintenant ! Pour recevoir les notifications d\'appel, appuyez ici et activez l\'option \"Notifications\". Pour recevoir les notifications d’appel, touchez cette option, activez les notifications et vérifiez que le son et les pop-ups sont bien activés. + Pour recevoir les notifications d\'appel, appuyez ici et autorisez l\'activité en arrière-plan sous Paramètres > Batterie. Paramètres Pour recevoir les notifications d\'appel, appuyez sur \"Paramètres\" et activez l\'option \"Notifications\". @@ -3306,7 +3320,7 @@ Je prends une pause Je travaille sur quelque chose de nouveau - One or more characters is invalid. + Au moins un des caractères n\'est pas valide. Modifier le groupe @@ -3405,7 +3419,8 @@ - Infos d\'assistance + + Infos destinées à l\'assistance Demande d’assistance – Signal pour Android Impossible d’importer les journaux. @@ -3493,7 +3508,8 @@ Pour les messages que vous envoyez, récupérez des aperçus de lien directement des sites Web. Changer la phrase de passe Changer votre phrase de passe - Activer le verrouillage de l’écran avec phrase de passe + + Activer le verrouillage de l\'écran avec phrase de passe Verrouiller l’écran et les notifications avec une phrase de passe Sécurité de l’écran Verrouiller Signal automatiquement après un délai d’inactivité déterminé @@ -3505,6 +3521,7 @@ Rythme de clignotement de la DEL Personnaliser Modifier le son et les vibrations + Son Silencieux Valeur par défaut @@ -3615,8 +3632,8 @@ L’historique et les contenus multimédias des messages de plus de %1$s seront irrémédiablement supprimés de votre appareil. - Toutes les conversations seront réduites de manière permanente pour contenir le message le plus récent. - Toutes les conversations seront réduites de manière permanente pour contenir les %1$s messages les plus récents. + Les anciens messages seront définitivement supprimés de toutes les conversations. Celles-ci n\'afficheront plus que le dernier message reçu. + Les anciens messages seront définitivement supprimés de toutes les conversations. Celles-ci n\'afficheront plus que les %1$s derniers messages reçus. Tout l’historique et tous les contenus multimédias des messages seront irrémédiablement supprimés de votre appareil. @@ -3661,7 +3678,7 @@ Contournement de la censure Contournement de la censure - Si cette option est activée, Signal tentera de contourner la censure. N’activez cette fonction que si vous êtes situé en un lieu où Signal est censurée. + Lorsque cette option est activée, Signal tente de contourner la censure. Ne l\'activez que si vous vous trouvez dans une zone géographique où Signal est censuré. Le contournement de la censure a été activé d’après le numéro de téléphone de votre compte. @@ -3757,7 +3774,7 @@ Activer le blocage des paiements pour les prochains envois ? - Ajoutez une couche de sécurité et activez le verrouillage de l\'écran ou la demande de l\'empreinte digitale d\'Android pour effectuer un paiement. + Pour plus de sécurité, exigez l\'empreinte digitale ou la méthode de déverrouillage d\'écran Android pour effectuer des paiements. Activer @@ -4329,11 +4346,11 @@ Annuler Tout le monde Personne - Verrouillage de l’écran - Verrouiller l’accès à Signal avec l’empreinte digitale ou le verrouillage d’écran Android - Délai d’inactivité avant verrouillage de l’écran + Verrouillage de l\'écran + Verrouiller l\'accès à Signal avec l\'empreinte digitale ou le verrouillage d\'écran Android + Délai d\'inactivité avant verrouillage de l\'écran - Pour activer le verrouillage de l’écran, définissez un code PIN, un schéma de déverrouillage ou un mot de passe sur cet appareil. + Pour activer le verrouillage de l\'écran, définissez un code PIN, un schéma de déverrouillage ou un mot de passe sur cet appareil. Code PIN Signal Créer un code PIN Changer de code PIN @@ -4359,7 +4376,7 @@ Pour déverrouiller Signal, accédez aux paramètres de verrouillage de votre appareil Android. - Le verrouillage de l’écran est activé. L’accès à Signal est donc sécurisé grâce aux paramètres de verrouillage de votre appareil. Pour déverrouiller Signal, utilisez la même méthode que pour déverrouiller votre téléphone : reconnaissance faciale, empreinte digitale, code PIN, mot de passe ou schéma de déverrouillage. + Le verrouillage de l\'écran est activé. L\'accès à Signal est donc sécurisé grâce aux paramètres de verrouillage de votre appareil. Pour déverrouiller Signal, utilisez la même méthode que pour déverrouiller votre téléphone : reconnaissance faciale, empreinte digitale, code PIN, mot de passe ou schéma de déverrouillage. Contacter l’assistance @@ -4369,11 +4386,11 @@ Désactivé - Activer le verrouillage de l’écran + Activer le verrouillage de l\'écran Vous devrez utiliser la méthode de déverrouillage de votre appareil Android pour déverrouiller Signal lorsque vous quittez l’appli ou passez d’une appli à l’autre. Lorsque l’écran est verrouillé, les aperçus des notifications n’affichent pas le contenu des messages. - Verrouiller l’écran + Verrouiller l\'écran Immédiatement @@ -4385,9 +4402,9 @@ Délai personnalisé - Activer le verrouillage de l’écran Signal ? + Activer le verrouillage de l\'écran Signal ? - Désactiver le verrouillage de l’écran Signal ? + Désactiver le verrouillage de l\'écran Signal ? Inconnu @@ -5044,18 +5061,18 @@ Messagerie Messages éphémères - Sécurité de l’appli + Sécurité de l\'appli Bloquer les captures d’écran dans la liste des récents et dans l’appli Messages et appels Signal : \"Toujours relayer les appels\" et \"Expéditeur scellé\" Délai avant disparition Définissez le délai après lequel les messages éphémères disparaissent des nouvelles conversations que vous avez initiées. - Exiger le verrouillage d’écran Android ou l’empreinte digitale pour effectuer des paiements + Exiger le verrouillage d\'écran Android ou l\'empreinte digitale pour effectuer des paiements Impossible d’activer le verrouillage des paiements - Pour utiliser la fonction de blocage du paiement, vous devez d\'abord activer le verrouillage de l\'écran ou l\'identification digitale dans les paramètres de votre téléphone. + Pour utiliser la fonctionnalité de blocage des paiements, vous devez d\'abord activer le verrouillage de l\'écran ou l\'ID d\'empreinte digitale dans les paramètres de votre téléphone. Impossible de consulter les paramètres du système @@ -5067,8 +5084,8 @@ Afficher une icône dans les infos des messages lorsqu\'ils sont remis avec l\'option \"Expéditeur scellé\". - Si cette option est activée, les nouveaux messages envoyés et reçus dans les nouvelles conversations disparaîtront après avoir été lus. - Si cette option est activée, les nouveaux messages envoyés et reçus dans cette conversation disparaîtront après avoir été lus. + Si vous activez cette option et démarrez de nouvelles conversations, les nouveaux messages que vous enverrez et recevrez dans ces conversations disparaîtront une fois lus. + Si vous activez cette option, les nouveaux messages que vous enverrez et recevrez dans cette conversation disparaîtront une fois lus. Désactivé 4 semaines 1 semaine @@ -5142,7 +5159,7 @@ Faire un don à Signal - Signal dépend de personnes comme vous. Faites un don mensuel et recevez un macaron. + Signal dépend de personnes comme vous. Soutenez-nous grâce à un don mensuel et gagnez un macaron. Faire un don @@ -5165,7 +5182,7 @@ Devenir donateur mensuel - Signal dépend de personnes comme vous. Faites un don et recevez un macaron. + Signal dépend de personnes comme vous. Faites un don et gagnez un macaron. Plus tard Faire un don @@ -5235,7 +5252,7 @@ Conversation en sourdine définitivement Numéro de téléphone copié dans le presse-papier. Numéro de téléphone - Obtenez des macarons pour votre profil en soutenant Signal. Appuyez sur un macaron pour en savoir plus. + Soutenez Signal et gagnez des macarons à afficher sur votre profil. Appuyez sur un macaron pour en savoir plus. Ajouter des membres @@ -5378,12 +5395,13 @@ Ajouter à la story Ajouter un message + Ajouter une réponse Envoyer à Média à vue unique Un ou plusieurs fichiers sont trop volumineux. Un élément ou plus étaient invalides - Vous avez sélectionné un trop grand nombre d’éléments + Vous avez sélectionné un trop grand nombre d\'éléments Vue unique activée pour cette vidéo @@ -5426,8 +5444,8 @@ Appuyez pour supprimer Appuyez pour sélectionner - Abandonner - Abandonner les changements ? + Supprimer + Supprimer les modifications ? Vous perdrez toutes les modifications apportées à cette photo. @@ -5446,7 +5464,7 @@ Mes macarons - Macarons mis en avant + Macaron affiché Afficher les macarons sur le profil Échec de la mise à jour du profil @@ -5461,15 +5479,15 @@ %1$s soutient Signal - %1$s soutient Signal par un don mensuel. Signal est un organisme à but non lucratif, sans annonceurs ni investisseurs, soutenu uniquement par des personnes comme vous. + %1$s soutient Signal chaque mois grâce à un don mensuel. Signal est un organisme à but non lucratif. Ici, ni annonceurs, ni investisseurs. Signal n\'existe que grâce à des personnes comme vous. - %1$s a soutenu Signal par un don. Signal est un organisme à but non lucratif, sans annonceurs ni investisseurs, soutenu uniquement par des personnes comme vous. + %1$s soutient Signal grâce à un don. Signal est un organisme à but non lucratif. Ici, ni annonceurs, ni investisseurs. Signal n\'existe que grâce à des personnes comme vous. Macaron Annuler l’abonnement Confirmer l’annulation ? - Vous ne serez plus facturé. Votre macaron sera supprimé de votre profil à la fin de votre période de facturation. + Vous ne serez plus facturé. Votre macaron sera supprimé de votre profil à la fin de la période de facturation. Plus tard Confirmer Mettre à jour l’abonnement @@ -5495,16 +5513,16 @@ Merci pour votre soutien ! - Vous avez gagné un badge de donateur Signal ! Affichez-le sur votre profil pour montrer votre soutien. + Vous avez gagné un macaron de donateur Signal ! Affichez-le sur votre profil pour montrer que vous nous soutenez. Vous pouvez aussi devenir un donateur mensuel. Afficher sur le profil - Mettre en avant ce macaron + Afficher ce macaron Poursuivre - Si vous avez plus d’un macaron, vous pouvez choisir celui que les autres pourront voir sur votre profil. + Si vous avez plusieurs macarons, vous pouvez choisir celui que vous voulez afficher sur votre profil. - Obtenez des macarons pour votre profil en soutenant Signal. - Signal est un organisme sans but lucratif, sans annonceurs ni investisseurs, soutenu uniquement par des personnes comme vous. + Soutenez Signal et gagnez des macarons à afficher sur votre profil. + Signal est un organisme à but non lucratif. Ici, ni annonceurs, ni investisseurs. Signal n\'existe que grâce à des personnes comme vous. Faire un don à Signal @@ -5537,7 +5555,7 @@ Renouvellement %1$s Traitement de la transaction… - Impossible d’ajouter un macaron. %1$s + Impossible d\'ajouter un macaron. %1$s Veuillez contacter l’assistance. Paiement en attente @@ -5565,34 +5583,34 @@ Annuler - Macaron Coup de pouce expiré + Expiration du macaron Coup de pouce Don mensuel résilié - Votre macaron Coup de pouce a expiré et n’est plus visible sur votre profil. - Vous pouvez réactiver votre macaron Coup de pouce pour 30 jours supplémentaires en versant une somme unique. + Votre macaron Coup de pouce est arrivé à expiration et n\'est plus visible sur votre profil. + Vous pouvez réactiver votre macaron Coup de pouce pour 30 jours de plus grâce à un don ponctuel. Vous pouvez continuer à utiliser Signal, mais pour soutenir une technologie conçue pour vous, envisagez de devenir un donateur mensuel en faisant un don chaque mois. Devenir donateur mensuel Donner un Coup de pouce Plus tard - Votre don mensuel récurrent a été automatiquement annulé car vous êtes resté inactif trop longtemps. Votre macaron %1$s n’est plus visible sur votre profil. + Votre don mensuel récurrent a été automatiquement annulé car votre compte Signal est resté inactif trop longtemps. Votre macaron %1$s n\'est plus visible sur votre profil. - Votre don mensuel a automatiquement été résilié car nous n’avons pas pu traiter votre paiement. Votre macaron n’est plus visible sur votre profil. + Votre don mensuel a automatiquement été résilié car nous n\'avons pas pu traiter votre paiement. Votre macaron n\'est plus visible sur votre profil. - Votre don mensuel récurrent a été automatiquement annulé. %1$s Votre macaron %2$s n’est plus affiché sur votre profil. - Vous pouvez continuer à utiliser Signal. Cependant, afin de soutenir l’appli et réactiver votre macaron, veuillez renouveler votre abonnement. + Votre don mensuel récurrent a été automatiquement annulé. %1$s Votre macaron %2$s n\'est plus visible sur votre profil. + Vous pouvez bien sûr continuer d\'utiliser l\'appli, mais pour soutenir Signal et réactiver votre macaron, n\'hésitez pas à renouveler votre don. Renouveler l’abonnement Allez sur Google Pay Impossible de traiter le paiement de l’abonnement Nous avons des difficultés à encaisser le paiement de votre don mensuel à Signal. Vérifiez que votre mode de paiement est à jour. Si ce n’est pas le cas, mettez-le à jour dans Google Pay. Signal essaiera de traiter à nouveau le paiement dans quelques jours. - Ne plus montrer cela + Ne plus afficher Contacter l’assistance - Obtenez un macaron %1$s + Gagnez un macaron %1$s Don en cours de traitement… @@ -5622,20 +5640,20 @@ Erreur de traitement du don. %1$s Votre don n’a pas pu être traité et vous n’avez pas été débité. Veuillez réessayer. En cours de traitement - Impossible d’ajouter le macaron + Impossible d\'ajouter le macaron. - Échec de la validation du macaron + Validation du macaron impossible. Impossible de valider la réponse du serveur. Contactez l\'assistance. Échec de l\'envoi du don Votre don a été traité, mais Signal n\'a pas pu envoyer le message de confirmation. Veuillez contacter l’assistance. - Votre macaron n’a pas pu être ajouté à votre compte, mais vous avez peut-être été facturé. Veuillez contacter le service d’assistance. + Nous n\'avons pas pu ajouter votre macaron à votre compte, mais vous avez peut-être été facturé. Merci de contacter l\'assistance. Votre don est en cours de traitement. Cela peut prendre quelques minutes en fonction de votre connexion. Échec de l’annulation de l’abonnement L’annulation de l’abonnement nécessite une connexion Internet. - Votre appareil ne prend pas en charge Google Pay, vous ne pouvez donc pas vous abonner pour gagner un macaron. Vous pouvez toujours soutenir Signal en faisant un don sur notre site Web. + Votre appareil n\'est pas compatible avec Google Pay : vous ne pouvez donc pas vous abonner pour gagner un macaron, mais vous pouvez encore soutenir Signal et faire un don via notre site web. Erreur réseau. Vérifiez votre connexion et réessayez. Réessayer @@ -5982,6 +6000,8 @@ %1$d réponse %1$d réponses + + Story no longer hidden Ajouter @@ -6062,7 +6082,7 @@ Ne partager qu’avec… - Ne partager qu’avec les contacts sélectionnés + Ne partager qu\'avec les contacts sélectionnés %1$d personne @@ -6279,9 +6299,9 @@ Vous avez effectué un don à Signal de la part de %1$s. Il/elle aura la possibilité d\'afficher son soutien sur son profil. - Votre macaron a expiré + Votre macaron est arrivé à expiration - Votre macaron a expiré et n’est plus visible par les autres utilisateurs sur votre profil. + Votre macaron est arrivé à expiration et n\'est plus visible sur votre profil. Pour continuer à soutenir une technologie conçue pour vous, veuillez envisager de devenir donateur mensuel. @@ -6526,15 +6546,15 @@ - Faire un don de %1$s/par mois à Signal + Donner %1$s/par mois pour soutenir Signal - Obtenez un macaron %1$s + Gagnez un macaron %1$s Faire un don de %1$s à Signal - Obtenez un macaron %1$s pour %2$d jour - Obtenez un macaron %1$s pour %2$d jours + Gagnez un macaron %1$s pour %2$d jour + Gagnez un macaron %1$s pour %2$d jours Virement bancaire @@ -6655,9 +6675,9 @@ Don en attente - Votre don mensuel est en attente. Une fois votre don reçu, vous pourrez afficher le macaron %1$s dans votre profil. + Votre don mensuel est en attente. Vous pourrez afficher le macaron %1$s sur votre profil dès que nous recevrons votre don. - Votre don ponctuel est en attente. Une fois votre don reçu, vous pourrez afficher le macaron %1$s dans votre profil. + Votre don ponctuel est en attente. Vous pourrez afficher le macaron %1$s sur votre profil dès que nous recevrons votre don. Les virements sont généralement traités en 1 à 14 jours ouvrés. %1$s @@ -6677,7 +6697,7 @@ Don effectué - Votre virement a bien été reçu. Vous pouvez afficher ce macaron sur votre profil pour montrer votre soutien. + Nous avons bien reçu votre virement. Vous pouvez afficher ce macaron sur votre profil pour montrer que vous soutenez Signal. Terminé @@ -6792,7 +6812,7 @@ Définir - Le temps minimum avant que le verrouillage de l’écran ne s’applique est de 1 minute. + Le délai minimal avant avant verrouillage de l\'écran est de 1 minute. @@ -6921,7 +6941,7 @@ - Créer un lien d’appel + Créer un lien d\'appel Partager le lien d’un appel Signal @@ -7267,11 +7287,27 @@ - Libérez %1$s d’espace pour télécharger vos médias. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + - Google Play + Google Play Carte de crédit ou de débit @@ -7330,19 +7366,15 @@ OK - - - Historique des paiements - - Sauvegarde des messages et de tous les médias - - Détails des paiements - - Type de sauvegarde - - Date de paiement - - Partager + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Fréquence de sauvegarde Sauvegarder via les données cellulaires + + View backup key + + Unlock to view backup key Désactiver et supprimer la sauvegarde @@ -7370,13 +7406,27 @@ Signal créera une sauvegarde cette nuit. - Type de sauvegarde + Backup plan Sauvegardes désactivées - - %1$s · %2$s/mois - - Activer les sauvegardes + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Votre clé de sauvegarde - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Votre clé de sauvegarde est un code de 64 chiffres qui vous permet de restaurer votre sauvegarde après avoir réinstallé Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Si vous oubliez votre clé, vous ne pourrez pas restaurer votre sauvegarde et Signal ne pourra pas vous aider à la récupérer. - Next + Suivant - Record your backup key + Enregistrez votre clé de sauvegarde - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Vous devrez fournir cette clé pour récupérer votre compte et vos données. Conservez-la en lieu sûr. Si vous la perdez, vous ne pourrez pas récupérer votre compte. - Copy to clipboard + Copier dans le presse-papiers - Next + Suivant - Keep your key safe + Conservez votre clé en lieu sûr - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal ne pourra pas vous aider à restaurer votre sauvegarde si vous perdez votre clé. Conservez-la dans un endroit sécurisé et ne la partagez avec personne. - I\'ve recorded my key + J\'ai enregistré ma clé - Continue + Continuer - See key again + Afficher de nouveau la clé diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 09b0abd3ef..5b9ee20ed2 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Níl Scrios @@ -266,6 +269,7 @@ Cnag air do ghrianghraf, coinnigh síos é d\'fhíseán + Gabh é Athraigh an grianghrafadán Oscail an gailearaí @@ -355,7 +359,7 @@ Ní nasc glao bailí é seo. Cinntigh go bhfuil an nasc iomlán slán ceart roimh dhul isteach ann a iarraidh. - You are already in a call + Tá tú i mbun glao cheana féin @@ -412,6 +416,7 @@ Ní féidir feidhmchláirín atá in ann an meán seo a oscailt a aimsiú. Macasamhlaíodh %1$s ó %1$s + chuig %1$s   Léigh a thuilleadh   Íoslódáil tuilleadh @@ -445,6 +450,7 @@ Seol a bhfuil curtha in eagar Cum teachtaireacht Faraor, tharla botún nuair a bhí do cheangaltán á chur leis. + Ní SMS no ríomhsheoladh bailí é an faighteoir. Tá an teachtaireacht folamh! Comhaltaí na baicle @@ -609,6 +615,7 @@ %1$d ceangaltán á sábháil sa stóras… Ar feitheamh… + Sonraí (Signal) MMS SMS @@ -1066,6 +1073,7 @@ Dínasc \'%1$s\'? Tar éis dínascadh an gléas seo, ní féidir teachtaireachtaí a sheoladh nó a fháil a thuilleadh. Theip ar an nasc idirlín + Féach arís é Ag dínascadh gléas… Ag dínascadh gléas @@ -1528,7 +1536,7 @@ Group Link Comhroinn Reset Link - Require admin approval + Éiligh faomhadh riarthóra Require an admin to approve new members joining via the group link. Are you sure you want to reset the group link? People will no longer be able to join the group using the current link. @@ -1636,6 +1644,7 @@ Seol %1$d cuireadh SMS? Téimís i muinín Signal: %1$s + Tá an dealramh air nach bhfuil aon fheidhmchláirín agat lena roinnt leis. @@ -2105,6 +2114,7 @@ Lig do %1$s teachtaireachtaí a sheoladh chugat agus comhroinn d\'ainm agus grianghraf leis an duine sin? Ní bhfaighidh tú aon teachtaireacht go dtí go mbainfidh tú an bac de/di. Lig do %1$s teachtaireachtaí a sheoladh chugat? Ní bhfaighidh tú aon teachtaireachtaí go dtí go mbainfidh tú an bac de/di. + Faigh nuashonruithe agus nuacht ó %1$s? Ní bhfaighidh tú aon nuashonruithe go dtí go mbainfidh tú an bac de/di. Continue your chat with this group and share your name and photo with its members? Ní féidir an Grúpa Oidhreachta seo a úsáid a thuilleadh. Cruthaigh grúpa nua le gnéithe nua a ghníomhachtú, amhail @tráchtanna agus riarthóirí. @@ -2467,6 +2477,7 @@ Níl aon duine eile anseo %1$s is in this call + Tá %1$ssa ghlao seo %1$s and %2$s are in this call @@ -2902,7 +2913,7 @@ Triailfimid arís níos déanaí. D\'éirigh le nuashonrú Signal Nuashonraíodh go huathoibríoch chuig leagan %1$s. - You updated to version %1$s. + Rinn tú nuashonrú chuig an leagan %1$s. Seol an teachtaireacht? @@ -3343,6 +3354,7 @@ Mionshamhail an cheangaltáin Scoránaigh tarraiceán ceangaltán ceamara tapa Ceangaltán fuaime a thaifid agus a sheoladh + Glasáil taifeadadh den cheangaltán fuaime Níorbh fhéidir an teachtaireacht a sheoladh. Seiceáil do nasc agus triail arís. @@ -3361,6 +3373,7 @@ Teachtaireacht léite + Grianghraf den teagmhálaí @@ -3445,6 +3458,7 @@ Tá an dealramh air go bhfuil gach rud ar deil anois! Chun fógraí glaonna a fháil, tapáil anseo agus cas air \"Taispeáin fógraí\". Chun fógraí glaonna a fháil, tapáil anseo agus cas air fógraí agus cinntigh go bhfuil Fuaim agus Mír anuas cumasaithe. + Chun fógraí glaonna a fháil, tapáil anseo agus cumasaigh gníomhaíocht sa chúlra i socruithe \"Ceallra\". Socruithe Chun fógraí glaonna a fháil, tapáil Socruithe agus cas air \"Taispeáin fógraí\". @@ -3603,7 +3617,7 @@ Ag glacadh sosa Táim ag obair ar rud nua - One or more characters is invalid. + Tá carachtar amháin nó níos mó neamhbhailí. Cuir an Grúpa in Eagar @@ -3702,6 +3716,7 @@ + Faisnéis Thacaíochta Iarratas ar Thacaíocht Android Signal @@ -3793,6 +3808,7 @@ Aisghabh réamhamharcanna ar naisc díreach ó láithreáin gréasáin do na teachtaireachtaí a sheolann tú. Athraigh an nath faire Athraigh do nath faire + Cumasaigh pasfhrása don ghlas scáileáin Cuir glas ar an scáileán agus ar fhógraí le nath Slándáil an scáileáin @@ -3805,6 +3821,7 @@ Patrún caochta LED Saincheap Athraigh fuaim agus creathadh + Fuaim Tostach An réamhshocrú @@ -5759,6 +5776,7 @@ Cuir leis an scéal Cuir teachtaireacht leis + Cuir freagra leis Seol chuig Meáin amhairc aonuaire @@ -6378,6 +6396,8 @@ %1$d bhfreagra %1$d freagra + + Story no longer hidden Cuir leis @@ -7741,7 +7761,23 @@ - Saor %1$s de spás le do mheáin a íoslódáil. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7804,19 +7840,15 @@ CGL - - - Stair íocaíochtaí - - Cúltaca téacs agus gach meáin - - Sonraí na híocaíochta - - Cineál cúltaca - - Date paid - - Comhroinn + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7831,6 +7863,10 @@ Minicíocht an chúltaca Déan cúltaca le nasc móibíleach + + View backup key + + Unlock to view backup key Cas as agus scrios cúltaca @@ -7844,13 +7880,27 @@ Cruthófar cúltaca thar oíche. - Cineál cúltaca + Backup plan Cúltacaí díchumasaithe - - %1$s · %2$s/sa mhí - - Cumasaigh cúltacaithe + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d / %2$d @@ -7900,33 +7950,33 @@ - Your backup key + D\'eochair chúltaca - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Cód 64 digit is ea é d\'eochair chúltaca a ligeann duit do chúltaca a aischur nuair a athshuiteálann tú Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Má dhéanann tú dearmad ar d\'eochair ní bheidh tú in ann do chúltaca a aischur. Ní féidir le Signal cabhrú leat do chúltaca a aischur. - Next + Ar aghaidh - Record your backup key + Taifead d\'eochair chúltaca - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Tá an eochair seo ag teastáil chun do chuntas agus sonraí a aisghabháil. Stóráil an eochair seo in áit shábháilte. Má chailleann tú í, ní bheidh tú in ann do chuntas a aisghabháil. - Copy to clipboard + Macasamhlaigh chuig an ghearrthaisce - Next + Ar aghaidh - Keep your key safe + Coinnigh d\'eochair sábháilte - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Ní bheidh Signal in ann cabhrú leat do chúltaca a aischur má chailleann tú d\'eochair. Stóráil in áit shlán sábháilte í, ná chomhroinn le daoine eile í. - I\'ve recorded my key + Tá m\'eochair taifeadta agam - Continue + Ar aghaidh - See key again + Féach ar an eochair arís diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 1021e86b4f..f759398cc2 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Si Non Borrar @@ -260,6 +263,7 @@ Tocar para fotografía, arrastrar para vídeo + Capturar Cambiar cámara Abrir galería @@ -346,7 +350,7 @@ Non é unha ligazón de chamada válida. Asegúrate de que a ligazón está enteira e non ten ningún erro antes de unirte. - You are already in a call + Estás xa nunha chamada @@ -403,6 +407,7 @@ Non se atopa unha aplicación para abrir este contido multimedia. Copiouse %1$s de %1$s + a %1$s Ler máis Descargar máis @@ -436,6 +441,7 @@ Enviar edición Redactar mensaxe Sentímolo, hai un erro na configuración do teu anexo. + O destinatario non é un enderezo electrónico ou SMS válido! A mensaxe está baleira! Membros do grupo @@ -576,6 +582,7 @@ Gardando %1$d anexos no almacenamento… Pendente… + Datos (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Desvincular \'%1$s\'? Ao desvincular este dispositivo, non volverá estar dispoñible para enviar nin recibir mensaxes. Erro de conexión á rede + Volver tentar Desvinculando dispositivo… Desvinculando dispositivo @@ -1408,7 +1416,7 @@ Ligazón do grupo Compartir Restablecer ligazón - Require admin approval + Solicitar aprobación dun administrador Un administrador ten que aprobar os novos membros que acceden desde a ligazón. Estás seguro de querer restablecer a ligazón ao grupo? A xente non poderán unirse ao grupo a través da ligazón actual. @@ -1507,6 +1515,7 @@ Enviar %1$d invitacións pro SMS? Conversa comigo en Signal: %1$s + Semella que non hai ningunha aplicación coa que poidas compartir. @@ -1931,6 +1940,7 @@ Queres compartir o teu nome e foto con %1$s e que che envíe mensaxes? Non recibirás ningunha mensaxe ata que o desbloquees. Deixar que %1$s che envíe mensaxes? Non recibirás ningunha mensaxe ata que desbloquees o contacto. + Queres recibir novidades de %1$s? Non recibirás ningunha noticia ata que desbloquees o contacto. Continuar esta conversa con este grupo e compartir o teu nome e foto cos seus membros? Este Grupo Clásico xa non poderá usarse. Crea un novo grupo para activar a novas funcións como @mencións e administradores. @@ -2245,6 +2255,7 @@ Ninguén máis aquí %1$s nesta chamada + %1$s están na chamada %1$s e %2$s están nesta chamada @@ -2647,7 +2658,7 @@ Volveremos intentalo de novo máis tarde. Signal actualizouse con éxito Actualización automática á versión %1$s. - You updated to version %1$s. + Actualizaches a aplicación á versión %1$s. Enviar mensaxe? @@ -3067,6 +3078,7 @@ Miniatura do anexo Activar panel rápido de ficheiros da cámara Gravar e enviar un anexo de audio + Bloquear a gravación dun ficheiro anexo de audio Non se puido enviar a mensaxe. Comproba a túa conexión á rede e vólveo tentar. @@ -3085,6 +3097,7 @@ Mensaxe lida + Foto do contacto @@ -3169,6 +3182,7 @@ Semella que todo está ben! Para recibir notificacións de chamada, toca aquí e activa \"Mostrar notificacións.\" Para recibir notificacións de chamada, preme aquí e activa as notificacións. Asegúrate de que o son e as notificacións emerxentes están tamén activados. + Para recibir notificacións de chamada, preme aquí e activa a actividade en segundo plano na configuración de «Batería». Configuración Para recibir notificacións de chamada, preme en Configuración e activa «Mostrar notificacións». @@ -3306,7 +3320,7 @@ Tomando un descanso Traballando en algo novo - One or more characters is invalid. + Un ou máis caracteres non son válidos. Editar grupo @@ -3405,6 +3419,7 @@ + Información de soporte Solicitude de soporte para Signal Android @@ -3493,6 +3508,7 @@ Obter vistas previas das ligazóns directamente desde os sitios web para as mensaxes que envías. Cambiar frase de acceso Cambia a túa frase de acceso + Activar o bloqueo da pantalla cunha frase de acceso Bloquea a pantalla e as notificacións cunha frase de acceso Seguranza da pantalla @@ -3505,6 +3521,7 @@ Intermitencia do LED Personalizar Cambiar son e vibración + Son En silencio Por defecto @@ -5378,6 +5395,7 @@ Engadir á historia Engadir mensaxe + Engadir unha resposta Enviar a Arquivo dunha soa visualización @@ -5982,6 +6000,8 @@ %1$d resposta %1$d respostas + + Story no longer hidden Engadir @@ -7267,7 +7287,23 @@ - Libera %1$s de espazo para descargar os arquivos multimedia. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Aceptar - - - Historial de pagamentos - - Copia de seguranza de mensaxes de texto e arquivos multimedia - - Información do pagamento - - Tipo de copia de seguranza - - Data de pagamento - - Compartir + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Periodicidade da copia de seguranza Realizar cos datos móbiles + + View backup key + + Unlock to view backup key Desactivar e eliminar a copia de seguranza @@ -7370,13 +7406,27 @@ A copia de seguranza crearase durante a noite. - Tipo de copia de seguranza + Backup plan Copia de seguranza desactivada - - %1$s %2$s/mes - - Activar copias de seguranza + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + A túa clave de seguranza - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + A túa clave de seguranza é un código de 64 díxitos co que podes restaurar a túa copia de seguranza cando volves instalar Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Se perdes a túa clave, non poderás restablecer a copia e Signal non poderá axudarche a recuperala. - Next + Seguinte - Record your backup key + Garda a túa clave de seguranza - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Esta clave é necesaria para recuperar a túa conta e os datos asociados. Gárdaa nun lugar seguro. Se a perdes, non poderás recuperar a túa conta. - Copy to clipboard + Copiar ao portapapeis - Next + Seguinte - Keep your key safe + Non perdas a clave - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal non poderá axudarche a restaurar a túa copia de seguranza se perdes a túa clave. Gárdaa nun lugar seguro e non a compartas con ninguén. - I\'ve recorded my key + Anotei a miña clave - Continue + Continuar - See key again + Ver clave de novo diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 29feaa2abd..67926ee859 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + હા ના ડિલીટ કરો @@ -260,6 +263,7 @@ ફોટો માટે ટેપ કરો, વિડિયો માટે પકડી રાખો + કેપ્ચર કેમેરો બદલો ગેલેરી ખોલો @@ -346,7 +350,7 @@ આ માન્ય ગ્રૂપ લિંક નથી. જોડાવાનો પ્રયાસ કરતા પહેલાં ખાતરી કરો કે આખી લિંક અકબંધ અને સાચી છે. - You are already in a call + તમે પહેલેથી જ કૉલમાં છો @@ -403,6 +407,7 @@ આ મીડિયા ખોલવા માટે કોઈ એપ્લિકેશન મળતું નથી. નકલ કરેલ %1$s %1$s તરફથી + %1$s માટે  વધુ વાંચો  વધુ ડાઉનલોડ કરો @@ -436,6 +441,7 @@ ફેરફાર કરેલો મેસેજ મોકલો મેસેજ લખો માફ કરશો, તમારું જોડાણ સેટ કરવામાં ભૂલ આવી હતી. + પ્રાપ્તકર્તા માન્ય SMS અથવા ઇમેઇલ સરનામું નથી! મેસેજ ખાલી છે! ગ્રુપ ના સભ્યો @@ -576,6 +582,7 @@ સ્ટોરેજમાં %1$d જોડાણો સેવ થઈ રહ્યાં છે… બાકી + તારીખ (Signal) MMS SMS @@ -1006,6 +1013,7 @@ અનલિંક કરો \'%1$s\'? આ ડિવાઇસ ને અનલિંક કરીને, તે હવે મેસેજ મોકલવા અથવા પ્રાપ્ત કરવામાં સમર્થ રહેશે નહીં. નેટવર્ક કનેક્શન નિષ્ફળ થયું + ફરીથી પ્રયત્ન કરો અનલિંકિંગ ડિવાઇસ… અનલિંકિંગ ડિવાઇસ @@ -1408,7 +1416,7 @@ ગ્રુપ લિંક શેર કરો રીસેટ લિંક - Require admin approval + એડમિનની મંજૂરી ફરજીયાત કરો ગ્રુપ લિંક મારફતે જોડાનારા નવા સભ્યોને મંજૂરી આપવા માટે એડમિનની જરૂર છે. શું તમે ખરેખર ગ્રુપ લિંક રીસેટ કરવા માંગો છો? લોકો હવે વર્તમાન લિંકનો ઉપયોગ કરીને ગ્રુપમાં જોડાવા માટે સમર્થ હશે નહીં. @@ -1507,6 +1515,7 @@ SMS %1$d આમંત્રણો મોકલો? ચાલો Signal પર સ્વિચ કરીએ: %1$s + એવું લાગે છે કે તમારી પાસે શેર કરવા માટે કોઈ એપ્લિકેશન નથી. @@ -1931,6 +1940,7 @@ %1$sને તમને મેસેજ કરવા અને તેમની સાથે તમારું નામ અને ફોટો શેર કરવા દેવા છે? જ્યાં સુધી તમે તેમને અનબ્લૉક નહીં કરો ત્યાં સુધી તમને કોઈ મેસેજ પ્રાપ્ત થશે નહીં. %1$sને તમને મેસેજ કરવા દેવા છે? તમે તેમને અનબ્લૉક નહીં કરો ત્યાં સુધી તમને કોઈ મેસેજ પ્રાપ્ત થશે નહીં. + %1$s તરફથી અપડેટ અને સમાચાર મેળવવા માગો છે? તમે તેમને અનબ્લૉક નહીં કરો ત્યાં સુધી કોઈ અપડેટ મેળવશો નહીં. આ ગ્રુપ સાથે તમારી ચેટ ચાલુ રાખવી છે અને તેના સભ્યો સાથે તમારું નામ અને ફોટો શેર કરવા છે? આ એક લેગસી ગ્રૂપનો હવે ઉપયોગ થઈ શકતો નથી. @ઉલ્લેખો અને એડમિન જેવા નવા ફીચર સક્રિય કરવા માટે નવું ગ્રૂપ બનાવો. @@ -2245,6 +2255,7 @@ બીજું કોઈ અહીં નથી %1$s આ કોલમાં છે + %1$s આ કૉલમાં છે %1$s અને %2$s આ કોલમાં છે @@ -2647,7 +2658,7 @@ અમે પછીથી ફરી પ્રયાસ કરીશું. Signal સફળતાપૂર્વક અપડેટ થઈ ગયું તમને આપમેળે વર્ઝન %1$s પર અપડેટ કરવામાં આવ્યા હતા. - You updated to version %1$s. + તમે %1$s વર્ઝન પર અપડેટ કર્યું છે. મેસેજ મોકલો? @@ -3067,6 +3078,7 @@ જોડાણ થંબનેલ ક્વિક કેમેરા જોડાણ ડ્રોઅરને ટોગલ કરો રેકોર્ડ કરો અને ઓડિયો જોડાણ મોકલો + ઓડિયો જોડાણનું લૉક રેકોર્ડિંગ મેસેજ મોકલી શકાયો નથી. તમારું કનેક્શન તપાસો અને ફરી પ્રયાસ કરો. @@ -3085,6 +3097,7 @@ મેસેજ વાંચ્યો + સંપર્ક ફોટો @@ -3169,6 +3182,7 @@ હવે બધું સારું લાગે છે! કૉલ સૂચનાઓ મેળવવા માટે, અહીં ટેપ કરો અને \"સૂચનાઓ બતાવો\" ચાલુ કરો. કૉલ સૂચનાઓ મેળવવા માટે, અહીં ટેપ કરો અને સૂચનાઓ ચાલુ કરો અને ખાતરી કરો કે સાઉન્ડ અને પોપ-અપ સક્ષમ છે. + કૉલ સૂચનાઓ મેળવવા માટે, અહીં ટેપ કરો અને \"બેટરી\" સેટિંગ્સમાં બેકગ્રાઉંડ એક્ટિવીટી સક્ષમ કરો. સેટિંગ્સ કૉલ સૂચનાઓ પ્રાપ્ત કરવા માટે, સેટિંગ્સ ટેપ કરો અને \"સૂચનાઓ બતાવો\" ચાલુ કરો. @@ -3306,7 +3320,7 @@ વિરામ લઇ રહયા છે કંઈક નવીન પર કામ કરી રહ્યા છીએ - One or more characters is invalid. + એક કે વધુ અક્ષરો અમાન્ય છે. ગ્રુપમાં ફેરફાર કરો @@ -3405,6 +3419,7 @@ + સપૉટ માહિતી Signal Android વિનંતી સપોર્ટ @@ -3493,6 +3508,7 @@ તમે મોકલેલા મેસેજ માટે વેબસાઇટમાંથી સીધા લિંક પ્રિવ્યૂ પ્રાપ્ત કરો. પાસફ્રેઝ બદલો તમારો પાસફ્રેઝ બદલો + પાસફ્રેઝ સ્ક્રીન લૉક ને સક્ષમ કરો પાસફ્રેઝ સાથે લૉક સ્ક્રીન અને સૂચનાઓ સ્ક્રીન સુરક્ષા @@ -3505,6 +3521,7 @@ LED બ્લિંક પેટર્ન કસ્ટમાઇઝ કરો અવાજ અને કંપન બદલો + અવાજ મૌન ડિફોલ્ટ @@ -5378,6 +5395,7 @@ સ્ટોરીમાં ઉમેરો મેસેજ ઉમેરો + જવાબ ઉમેરો આમને મોકલો એકવાર જોઈ શકાય તેવા મીડિયા @@ -5982,6 +6000,8 @@ %1$d જવાબ %1$d જવાબો + + Story no longer hidden ઉમેરો @@ -7267,7 +7287,23 @@ - તમારા મીડિયાને ડાઉનલોડ કરવા માટે %1$s જગ્યા ખાલી કરો. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ઓકે - - - પેમેન્ટ હિસ્ટ્રી - - ટેક્સ્ટ અને બધા મીડિયાનું બેકઅપ - - પેમેન્ટની વિગતો - - બેકઅપનો પ્રકાર - - ચુકવણીની તારીખ - - શેર કરો + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ બેકઅપનું આવર્તન સેલ્યુલર ડેટાનો ઉપયોગ કરીને બેકઅપ લો + + View backup key + + Unlock to view backup key બેકઅપ બંધ કરો અને ડિલીટ કરો @@ -7370,13 +7406,27 @@ બેકઅપ રાત્રિ દરમિયાન લેવામાં આવશે. - બેકઅપનો પ્રકાર + Backup plan બેકઅપ અક્ષમ છે - - %1$s · %2$s/મહિને - - બૅકઅપ સક્ષમ કરીએ + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + તમારી બેકઅપ કી - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + તમારી બેકઅપ કી એ 64-અંકનો કોડ છે જે તમને Signalને ફરીથી ઇન્સ્ટોલ કરવા પર તમારા બેકઅપને રિસ્ટોર કરવા દે છે. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + જો તમે તમારી કી ભૂલી જાઓ, તો તમે તમારા બેકઅપને રિસ્ટોર કરી શકશો નહીં. Signal તમને તમારા બેકઅપને રિસ્ટોર કરવામાં મદદ કરી શકશે નહીં. - Next + આગળ - Record your backup key + તમારી બેકઅપ કીને રેકોર્ડ કરો - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + તમારા એકાઉન્ટ અને ડેટાને રિકવર કરવા માટે આ કી જરૂરી છે. આ કીને કોઈ સુરક્ષિત જગ્યાએ સ્ટોર કરો. જો તમે તેને ગુમાવો છો, તો તમે તમારું એકાઉન્ટ રિકવર કરી શકશો નહીં. - Copy to clipboard + ક્લિપબોર્ડ પર કૉપી કરેલું - Next + આગળ - Keep your key safe + તમારી કીને સુરક્ષિત રાખો - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + જો તમે તમારી કી ગુમાવશો તો Signal તમારું બેકઅપ રિસ્ટોર કરવામાં તમારી મદદ કરી શકશે નહીં. તેને કોઈ સલામત અને સુરક્ષિત જગ્યાએ સંગ્રહિત કરો અને તેને અન્ય લોકો સાથે શેર કરશો નહીં. - I\'ve recorded my key + મેં મારી કી રેકોર્ડ કરી છે - Continue + ચાલુ રાખો - See key again + કી ફરીથી જુઓ diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 3885ffeabe..7085341449 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + हाँ नहीं डिलीट करें @@ -260,6 +263,7 @@ फोटो के लिए टैप, वीडियो के लिए दबाए रखें + कैप्चर कैमरा बदलें गैलरी खोलें @@ -346,7 +350,7 @@ यह कॉल लिंक मान्य नहीं है। जुड़ने का प्रयास करने से पहले सुनिश्चित कर लें कि संपूर्ण लिंक सुरक्षित और सही है। - You are already in a call + आप पहले ही एक कॉल में हैं @@ -403,6 +407,7 @@ इस मीडिया को खोलने में सक्षम कोई ऐप नहीं मिल सकता है। %1$s की नकल की %1$s से + %1$s को और पढो और डाउनलोड करें @@ -436,6 +441,7 @@ संपादित भेजें मेसेज लिखें खेद है, आपका अनुलगनक लगाने में त्रुटि हुई। + प्राप्त करने वाला वैध SMS या ईमेल पता नहीं है! मेसेज खाली है! सदस्य @@ -576,6 +582,7 @@ स्टोरेज में %1$d अटैचमेंट सेव हो रहे हैं… रूका हुआ + डेटा (Signal) MMS SMS @@ -1006,6 +1013,7 @@ %1$sको अनलिंक? इस डिवाइस को अनलिंक करने के बाद, यह अब संदेश भेजने या प्राप्त करने में सक्षम नहीं होगा। नेटवर्क कनेक्शन असफल रहा + फिर से कोशिश करो डिवाइस अनलिंक किया जा रहा है डिवाइस अनलिंक किया जा रहा है @@ -1408,7 +1416,7 @@ समूह लिंक साझा करें रीसेट लिंक - Require admin approval + एडमिन की मंज़ूरी की ज़रूरत है ग्रुप लिंक के माध्यम से नए ग्रुप में शामिल होने वाले मेंबर्स को अनुमोदित करने के लिए एडमिन को आवश्यक करें। क्या आप वाकई में ग्रुप के लिंक को रीसेट करना चाहते हैं? लोग वर्तमान लिंक का उपयोग करते हुए ग्रुप में जुड़ नहीं सकेंगे। @@ -1507,6 +1515,7 @@ %1$d SMS आमंत्रण भेजें? चलो Signal पर स्विच करें: %1$s + ऐसा लगता है कि आपके पास साझा करने के लिए कोई ऐप नहीं है। @@ -1931,6 +1940,7 @@ %1$s को आपको मेसेज करने देना है और उनके साथ अपना नाम और तस्वीर शेयर करनी है? आप कोई भी मेसेज प्राप्त नहीं करेंगे जब तक आप उन्हें अनब्लॉक नहीं करते। %1$s को आपको संदेश भेजने दें? जब तक आप उनको अनब्लॉक नहीं करते, तब तक आपको कोई संदेश नहीं मिलेगा। + %1$s से अपडेट और खबरें पानी हैं? जब तक आप उनकी अनब्लॉक नहीं करते, तब तक आपको कोई अपडेट नहीं मिलेगा। इस ग्रुप के साथ अपनी चैट जारी रखनी है और इसके मेंबर्स के साथ अपना नाम और तस्वीर शेयर करनी है? इस लेगेसी ग्रुप का अब उपयोग नहीं किया जा सकता. @mentions और एडमिन जैसे नए फ़ीचर को सक्रिय करने के लिए एक नया ग्रुप बनाएं। @@ -2245,6 +2255,7 @@ यहाँ और कोई नहीं है %1$s इस कॉल में है + %1$s इस कॉल में हैं %1$s और %2$s इस कॉल में हैं @@ -2647,7 +2658,7 @@ हम बाद में पुनः प्रयास करेंगे। Signal सफलतापूर्वक अपडेट किया गया आपको अपने आप %1$s संस्करण पर अपडेट कर दिया गया है। - You updated to version %1$s. + आपने %1$s वर्ज़न पर अपडेट कर लिया है। मेसेज भेजें? @@ -3067,6 +3078,7 @@ अटैचमेंट थंबनेल त्वरित कैमरा अटैचमेंट दराज टॉगल करें रिकॉर्ड करें और ऑडियो अटैचमेंट भेजें + आवाज़ रिकॉर्ड करने का बटन लॉक करें मेसेज भेजा नहीं जा सका। अपना कनेक्शन देखें और दोबारा प्रयास करें। @@ -3085,6 +3097,7 @@ मेसेज पढ़ा + संपर्क फोटो @@ -3169,6 +3182,7 @@ अब सब कुछ अच्छा लग रहा है! कॉल सूचनाएं प्राप्त करने के लिए यहां टैप करें, और \"सूचनाएं दिखाएं\" चालू करें। कॉल सूचनाएं प्राप्त करने के लिए, यहां टैप करें और सूचनाएं चालू करें और सुनिश्चित करें कि ध्वनि और पॉप-अप चालू हैं। + कॉल सूचनाएं प्राप्त करने के लिए, यहां टैप करें और \"बैटरी\" सेटिंग में पृष्ठभूमि गतिविधि चालू करें। सेटिंग्स कॉल सूचनाएं प्राप्त करने के लिए, सेटिंग टैप करें और \"सूचनाएं दिखाएं\" चालू करें। @@ -3306,7 +3320,7 @@ ब्रेक पर हैं किसी नई चीज़ पर काम कर रहे हैं - One or more characters is invalid. + एक या ज़्यादा वर्ण अमान्य हैं। समूह संपादित करें @@ -3405,6 +3419,7 @@ + समर्थन संबंधी जानकारी Signal Android समर्थन अनुरोध @@ -3493,6 +3508,7 @@ आप जो मेसेज भेजते हैं, उनके लिए आप वेबसाइट से सीधा लिंक प्रिव्यु प्राप्त कर सकते हैं। पासफ्रेज बदलें अपना पासफ्रेज बदलें + पासफ्रेज़ स्क्रीन लॉक दर्ज करें एक पासफ्रेज के साथ स्क्रीन और अधिसूचना लॉक करें स्क्रीन सुरक्षा @@ -3505,6 +3521,7 @@ LED ब्लिंक पैटर्न कस्टमाइज़ करें ध्वनि व कंपन बदलें + आवाज़ म्युट डिफ़ॉल्ट @@ -5378,6 +5395,7 @@ स्टोरी में जोड़ें एक मेसेज शामिल करें + एक जवाब शामिल करें को भेजें एक बार मीडिया देखें @@ -5982,6 +6000,8 @@ %1$d जवाब %1$d जवाब + + Story no longer hidden जोड़ें @@ -7267,7 +7287,23 @@ - अपने मीडिया को डाउनलोड करने के लिए %1$s स्थान खाली करें। + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ठीक - - - भुगतान का इतिहास - - टेक्स्ट और सभी मीडिया बैकअप - - भुगतान विवरण - - बैकअप प्रकार - - भुगतान तिथि - - साझा करें + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ बैकअप फ़्रीक्वेंसी सेल्यूलर का उपयोग करके बैकअप लें + + View backup key + + Unlock to view backup key बैकअप बंद करें और डिलीट करें @@ -7370,13 +7406,27 @@ बैकअप को रात के समय बनाया जाएगा। - बैकअप प्रकार + Backup plan बैकअप बंद किया गया - - %1$s · %2$s/महीना - - बैकअप सक्षम करें + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + आपकी बैकअप की - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + आपकी बैकअप की 64 अंकों का कोड होती है जो आपको Signal फिर से इंस्टॉल करते समय अपना बैकअप रीस्टोर करने में मदद करती है। - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + अगर आप अपनी की भूल जाते हैं, तो आप अपना बैकअप रीस्टोर नहीं कर पाएँगे। Signal आपके बैकअप को रिकवर करने में मदद नहीं कर सकता। - Next + अगला - Record your backup key + अपनी बैकअप की रिकॉर्ड करें - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + आपके अकाउंट और डेटा को रिकवर करने के लिए यह की ज़रूरी है। इस की को किसी सुरक्षित जगह पर स्टोर करें। अगर यह खो जाती है, तो आप अपना अकाउंट रिकवर नहीं कर पाएँगे। - Copy to clipboard + क्लिपबोर्ड पर कॉपी करें - Next + अगला - Keep your key safe + अपनी की सुरक्षित रखें - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + अगर आपकी की खो जाती है, तो Signal आपका बैकअप रीस्टोर करने में आपकी मदद नहीं कर पाएगा। इसे किसी सुरक्षित जगह पर स्टोर करें और किसी और के साथ शेयर न करें। - I\'ve recorded my key + मैंने अपनी की रिकॉर्ड कर ली है - Continue + जारी रखें - See key again + फिर से की देखें diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index b6a38a4da8..4c2f0fc03f 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Da Ne Izbriši @@ -264,6 +267,7 @@ Dodirnite za fotografiju, držite za video + Slikaj Promijeni kameru Otvori galeriju @@ -352,7 +356,7 @@ Poveznica na poziv nije važeća. Provjerite je li cijela poveznica ispravna prije pokušaja pridruživanja. - You are already in a call + Već ste na pozivu @@ -409,6 +413,7 @@ Nije moguće pronaći aplikaciju za otvaranje ovog medijskog zapisa. Kopirano %1$s od %1$s + prema %1$s   Pročitaj više   Preuzmi više @@ -442,6 +447,7 @@ Pošalji uređenu poruku Sastavi poruku Došlo je do pogreške prilikom postavljanja privitka. + Primatelj nije valjana SMS ili adresa e-pošte! Poruka je prazna! Članovi grupe @@ -598,6 +604,7 @@ Spremanje %1$d privitaka u prostor za pohranu… U tijeku… + Internet (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Poništi vezu \'%1$s\'? Ovaj uređaj više neće moći slati i primati poruke ako je odspojen. Pogreška prilikom spajanja na mrežu + Pokušajte ponovno Poništavanje veze uređaja… Poništavanje veze uređaja @@ -1488,7 +1496,7 @@ Poveznica grupe Podijeli Poništi poveznicu - Require admin approval + Zatraži odobrenje administratora Zahtjeva da administrator odobri pridruživanje novih članova putem poveznice grupe. Jeste li sigurni da želite poništiti grupnu poveznicu? Osobe se više neće moći pridružiti grupi koristeći trenutnu poveznicu. @@ -1593,6 +1601,7 @@ Poslati %1$d SMS pozivnica? Prebacimo se na Signal: %1$s + Izgleda da nemate nijednu aplikaciju za dijeljenje. @@ -2047,6 +2056,7 @@ Želite li dopustiti da vam %1$s šalje poruke i vidi vaše ime i fotografiju? Nećete primati poruke od tog korisnika dok ga ne odblokirate. Želite li dopustiti da vam %1$s šalje poruke? Nećete primati poruke od tog korisnika dok ga ne odblokirate. + Želite li primati ažuriranja i novosti od korisnika %1$s? Nećete primati ažuriranja dok ga ne odblokirate. Želite li nastaviti razgovor s ovom grupom i podijeliti svoje ime i fotografiju s članovima? Naslijeđena grupa više se ne može koristiti. Stvorite novu grupu kako biste aktivirali nove značajke poput @spominjanja i administratora. @@ -2393,6 +2403,7 @@ Nitko drugi nije ovdje %1$s je na ovom pozivu + %1$s su u ovom pozivu %1$s i %2$s su na ovom pozivu @@ -2817,7 +2828,7 @@ Pokušat ćemo ponovno kasnije. Signal je uspješno ažuriran Aplikacija je automatski ažurirana na verziju %1$s. - You updated to version %1$s. + Aplikacija je ažurirana na verziju %1$s. Pošalji poruku? @@ -3251,6 +3262,7 @@ Sličica privitka Uključi/isključi ladicu privitaka kamere Snimi i pošalji zvučni privitak + Zaključaj snimanje zvučnog privitka Nije moguće poslati poruku. Provjerite internetsku vezu i pokušajte ponovo. @@ -3269,6 +3281,7 @@ Poruka je pročitana + Slika kontakta @@ -3353,6 +3366,7 @@ Sve izgleda dobro sada! Za primanje obavijesti za pozive, pritisnite ovdje i uključite \"Prikaži obavijesti\". Za primanje obavijesti za pozive, pritisnite ovdje i uključite obavijesti te provjerite jesu li zvuk i skočni prozori omogućeni. + Za primanje obavijesti za pozive, pritisnite ovdje i uključite pozadinsku aktivnost u postavkama \"Baterije\". Postavke Za primanje obavijesti za pozive, pritisnite na Postavke i uključite \"Prikaži obavijesti\". @@ -3504,7 +3518,7 @@ Na pauzi Radim na nečem novom - One or more characters is invalid. + Jedan ili više znakova nisu valjani. Uredi grupu @@ -3603,6 +3617,7 @@ + Informacije o podršci Signal Android zahtjev za podršku @@ -3693,6 +3708,7 @@ Preuzmite preglede poveznica izravno s web stranica za poruke koje šaljete. Promijeni lozinku Promijenite vašu lozinku + Omogući zaključavanje ekrana lozinkom Zaključajte zaslon i obavijesti uz pomoć lozinke Sigurnost zaslona @@ -3705,6 +3721,7 @@ LED uzorak treptanja Prilagodi Promjena zvuka i vibracije + Zvuk Bez zvuka Zadano @@ -5632,6 +5649,7 @@ Dodaj u priču Dodaj poruku + Dodajte odgovor Pošalji Medijski zapis koji nestaje nakon prikaza @@ -6246,6 +6264,8 @@ %1$d odgovora %1$d odgovora + + Story no longer hidden Dodaj @@ -7583,7 +7603,23 @@ - Oslobodite %1$s prostora da biste preuzeli svoje medijske zapise. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ U redu - - - Povijest plaćanja - - Sigurnosna kopija poruka i svih medijskih zapisa - - Pojedinosti o plaćanju - - Postavke sigurnosnog kopiranja - - Datum plaćanja - - Podijeli + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Učestalost sigurnosnog kopiranja Sigurnosno kopiranje putem mobilne mreže + + View backup key + + Unlock to view backup key Isključi i izbriši sigurnosnu kopiju @@ -7686,13 +7722,27 @@ Sigurnosna kopija bit će izrađena tijekom noćnih sati. - Postavke sigurnosnog kopiranja + Backup plan Sigurnosno kopiranje je onemogućeno - - %1$s · %2$s mjesečno - - Omogući sigurnosne kopije + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Vaš ključ za sigurnosnu kopiju - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Ključ za sigurnosnu kopiju je 64-znamenkasti kôd koji vam omogućuje vraćanje sigurnosne kopije kada ponovno instalirate Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ako zaboravite svoj ključ, neće biti moguće vratiti podatke iz sigurnosne kopije. Signal vam ne može pomoći vratiti sigurnosnu kopiju. - Next + Dalje - Record your backup key + Spremite svoj ključ za sigurnosnu kopiju - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Ovaj je ključ potreban za oporavak vašeg računa i vaših podataka. Spremite svoj ključ na sigurno mjesto. Ako ga izgubite ili zaboravite, nećete moći oporaviti svoj račun. - Copy to clipboard + Kopiraj u međuspremnik - Next + Dalje - Keep your key safe + Čuvajte svoj ključ - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal vam neće moći pomoći vratiti sigurnosnu kopiju ako izgubite svoj ključ. Čuvajte ga na sigurnom mjestu i nemojte ga dijeliti s drugima. - I\'ve recorded my key + Spremio/la sam svoj ključ - Continue + Nastavi - See key again + Ponovno prikaži ključ diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index cd262fe14c..b0c4ddaca9 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Igen Nem Törlés @@ -260,6 +263,7 @@ Koppints fotóhoz, tartsd nyomva videóhoz + Rögzítés Kamera váltása Galéria megnyitása @@ -346,7 +350,7 @@ Ez a híváshivatkozás nem érvényes. A csatlakozás megkísérlése előtt győződj meg róla, hogy a teljes hivatkozás sértetlen és helyes. - You are already in a call + Már beléptél a hívásba @@ -403,6 +407,7 @@ Nem található olyan alkalmazás, ami képes lenne megnyitni ezt a médiafájlt. %1$s másolva tőle: %1$s + neki: %1$s  Tovább  További letöltése @@ -436,6 +441,7 @@ Szerkesztés küldése Üzenetírás Sajnos hiba történt a melléklet csatolása során. + A címzett nem egy érvényes SMS vagy email cím! Üres az üzenet! Csoporttagok @@ -576,6 +582,7 @@ %1$d db melléklet mentése a háttértárba… Folyamatban… + Adat (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Társítás megszüntetése ezzel: \'%1$s\'? Az eszköz a társítás megszüntetését követően nem lesz képes üzenetek küldésére vagy fogadására. Hálózati kapcsolódás sikertelen + Újrapróbálkozás Eszköz társításának megszüntetése… Eszköz társításának megszüntetése @@ -1408,7 +1416,7 @@ Csoporthivatkozás Megosztás Hivatkozás cseréje - Require admin approval + Adminisztrátori jóváhagyás szükséges Admin általi jóváhagyás elvárása csoporthivatkozással csatlakozó tagok felvételéhez. Biztosan lecseréled a csoporthivatkozást? Utána már nem lehet a jelenlegi hivatkozás használatával csatlakozni a csoporthoz. @@ -1507,6 +1515,7 @@ Elküldesz %1$d db SMS meghívót? Váltsunk Signalra: %1$s + Úgy néz ki, hogy egy alkalmazásod sincs a megosztás kezeléséhez. @@ -1931,6 +1940,7 @@ Engedélyezed, hogy %1$s üzenetet küldjön számodra, és lássa a nevedet, valamint a profilképedet? Nem fogsz üzenetet kapni tőle, amíg fel nem oldod a tiltást. Engedélyezed, hogy %1$s üzenetet küldjön számodra? Amíg nem oldod fel a tiltást, nem kapsz tőle üzenetet. + Szeretnél híreket és újdonságokat kapni %1$s felhasználótól? Addig nem kapsz értesítéseket tőle, míg fel nem oldod a letiltását. Folytatod a csoporton belüli csevegést, és megosztod a neved, valamint profilképed a tagokkal? Ez a régi csoport már nem használható. Hozz létre egy új csoportot az olyan új funkciók aktiválásához, mint például a @mentions (személyek megemlítése) és adminisztrátorok kijelölése. @@ -2245,6 +2255,7 @@ Nincs itt senki %1$s ebben a hívásban van + %1$s ebben a hívásban %1$s és %2$s ebben a hívásban van @@ -2647,7 +2658,7 @@ Később újra megpróbáljuk. Signal sikeresen frissítve Automatikusan frissítettél a(z) %1$s verzióra. - You updated to version %1$s. + Frissítettél a(z) %1$s verzióra. Üzenet küldése? @@ -3067,6 +3078,7 @@ Melléklet előnézet Gyorskamera kapcsoló Hang melléklet felvétele és küldése + Hangcsatolmány rögzítésének zárolása Az üzenetet nem sikerült elküldeni. Ellenőrizd a hálózati kapcsolatot és próbáld újra! @@ -3085,6 +3097,7 @@ Üzenet elolvasva + Kontakt fotó @@ -3169,6 +3182,7 @@ Úgy tűnik, minden rendben van! A hívás-értesítések fogadásához koppints ide, és kapcsold be az \"Értesítések megjelenítése\" beállítást. A hívás-értesítések fogadásához koppints ide, és kapcsold be az az értesítéseket, és győződj meg róla, hogy a Hang és az Előugró ablak engedélyezve lett. + A hívás-értesítések engedélyezéséhez koppints ide, és engedélyezd a háttértevékenységeket az \"Akkumulátor\" beállításoknál. Beállítások A hívás-értesítések engedélyezéséhez koppints a Beállítások menüpontra, és kapcsold be az \"Értesítések megjelenítése\" beállítást. @@ -3306,7 +3320,7 @@ Lazulok pár percet Valami izgalmas újdonságon dolgozom - One or more characters is invalid. + Egy vagy több karakter érvénytelen. Csoport szerkesztése @@ -3405,6 +3419,7 @@ + Támogatás információ Signal Android támogatási kérés @@ -3493,6 +3508,7 @@ Előnézeti képek lekérése közvetlenül a weboldalról az elküldendő üzenetekhez. Jelmondat megváltoztatása Jelmondatod megváltoztatása + Jelmondattal védett képernyőzár engedélyezése Képernyő és értesítések zárolása jelmondattal Képernyőbiztonság @@ -3505,6 +3521,7 @@ LED villogás mintázata Testreszabás Hangjelzés és rezgés megváltoztatása + Dallam Csendes Alapértelmezett @@ -5378,6 +5395,7 @@ Hozzáadás a Történethez Szöveg hozzáadása + Válasz hozzáadása Címzett Egyszer megjelenő médiafájl megtekintése @@ -5982,6 +6000,8 @@ %1$d válasz %1$d válasz + + Story no longer hidden Hozzáadás @@ -7267,7 +7287,23 @@ - Szabadíts fel %1$s helyet a médiafájlok letöltéséhez. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Fizetési előzmények - - Szöveg és minden médiafájl biztonsági mentése - - Fizetés részletei - - Biztonsági mentés típusa - - Fizetés dátuma - - Megosztás + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Biztonsági mentés gyakorisága Biztonsági mentés mobilhálózaton keresztül + + View backup key + + Unlock to view backup key Biztonsági mentés kikapcsolása és törlése @@ -7370,13 +7406,27 @@ A biztonsági mentés éjszaka kerül létrehozásra. - Biztonsági mentés típusa + Backup plan Biztonsági mentés kikapcsolva - - %1$s · %2$s/hó - - Biztonsági mentések bekapcsolása + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + A biztonsági kulcsod - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + A biztonsági kulcs egy 64 számjegyű kód, amely lehetővé teszi a biztonsági másolat visszaállítását a Signal újratelepítésekor. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ha elfelejted a kulcsod, nem tudod visszaállítani a biztonsági másolatot. A Signal nem tud segíteni a biztonsági másolat helyreállításában. - Next + Tovább - Record your backup key + Jegyezd fel a biztonsági kulcsod - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Ez a kulcs szükséges a fiók és az adatok helyreállításához. Tárold biztonságos helyen ezt a kulcsot. Ha elveszíted, nem tudod helyreállítani a fiókodat. - Copy to clipboard + Másolás vágólapra - Next + Tovább - Keep your key safe + Tartsd biztonságban a kulcsod - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Ha elveszíted a kulcsod, a Signal nem tud segíteni a biztonsági másolat helyreállításában. Tárold biztonságos helyen, és ne oszd meg másokkal. - I\'ve recorded my key + Feljegyeztem a kulcsomat - Continue + Tovább - See key again + Kulcs újbóli megtekintése diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 64988bcfae..a3a92103eb 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ya Tidak Hapus @@ -258,6 +261,7 @@ Ketuk untuk foto, tahan untuk video + Ambil gambar Ganti kamera Buka galeri @@ -343,7 +347,7 @@ Tautan panggilan ini tidak valid. Pastikan seluruh tautan utuh dan benar sebelum mencoba untuk bergabung. - You are already in a call + Anda sudah berada dalam panggilan @@ -400,6 +404,7 @@ Tidak bisa menemukan aplikasi untuk membuka media ini. Disalin %1$s dari %1$s + ke %1$s   Baca Lebih Lanjut   Unduh Lebih Banyak @@ -433,6 +438,7 @@ Kirim editan Tulis pesan Maaf, terjadi kesalahan pada pengaturan lampiran anda. + Alamat penerima SMS atau email tidak valid! Pesan kosong! Anggota grup @@ -565,6 +571,7 @@ Menyimpan %1$d lampiran ke penyimpanan… Tertunda … + Data (Signal) MMS SMS @@ -986,6 +993,7 @@ Batalkan tautan \'%1$s\'? Dengan membuang kaitan perangkat ini, perangkat tidak akan bisa lagi mengirim atau menerima pesan. Sambungan jaringan gagal + Coba lagi Membatalkan tautan perangkat Membatalkan tautan perangkat @@ -1368,7 +1376,7 @@ Tautan grup Bagikan Atur ulang tautan - Require admin approval + Perlu persetujuan admin Dibutuhkan persetujuan admin untuk menerima anggota baru yang bergabung melalui tautan grup. Apakah Anda yakin untuk mengatur ulang tautan grup? Orang-orang tidak akan bisa bergabung ke dalam grup menggunakan tautan saat ini. @@ -1464,6 +1472,7 @@ Kirim %1$d undangan SMS? Ayo pindah ke Signal: %1$s + Sepertinya Anda tidak memiliki aplikasi untuk berbagi. @@ -1873,6 +1882,7 @@ Izinkan %1$s mengirimi Anda pesan dan bagikan nama dan foto Anda dengan mereka? Anda tidak akan menerima pesan apa pun sampai Anda membuka blokir mereka. Izinkan %1$s mengirimi Anda pesan? Anda tidak akan menerima pesan apa pun sampai Anda membuka blokir mereka. + Dapatkan pembaruan dan berita dari %1$s? Anda tidak akan menerima pembaruan apa pun sampai Anda membuka blokir mereka. Lanjutkan obrolan Anda dengan grup ini dan bagikan nama dan foto Anda dengan anggotanya? Grup Lama ini sudah tidak dapat digunakan. Buat grup baru untuk mengaktifkan fitur baru seperti @mention dan admin. @@ -2171,6 +2181,7 @@ Tidak ada orang lain di sini %1$s sedang berada dalam panggilan ini + %1$s berada dalam panggilan ini %1$s dan %2$s sedang berada dalam panggilan ini @@ -2562,7 +2573,7 @@ Kami akan coba lagi nanti. Signal berhasil diperbarui Aplikasi secara otomatis diperbarui ke versi %1$s. - You updated to version %1$s. + Signal diperbarui ke versi %1$s. Kirim pesan? @@ -2975,6 +2986,7 @@ Gambar Mini Lampiran Beralih ke laci lampiran kamera cepat Rekam dan kirim lampiran audio + Kunci rekaman dari lampiran audio Tidak dapat mengirim pesan. Cek koneksi Anda dan coba lagi. @@ -2993,6 +3005,7 @@ Pesan terbaca + Foto kontak @@ -3077,6 +3090,7 @@ Semua terlihat baik sekarang! Untuk menerima notifikasi panggilan, ketuk di sini dan nyalakan \"Tampilkan notifikasi.\" Untuk menerima notifikasi panggilan, ketuk di sini dan nyalakan notifikasi dan pastikan Suara dan Pop-up telah diaktifkan. + Untuk menerima notifikasi panggilan, ketuk di sini dan aktifkan aktivitas belakang layar pada pengaturan \"Baterai\". Pengaturan Untuk menerima notifikasi panggilan, ketuk Pengaturan dan nyalakan \"Tampilkan notifikasi.\" @@ -3207,7 +3221,7 @@ Istirahat Sedang mengerjakan sesuatu yang baru - One or more characters is invalid. + Satu atau beberapa karakter tidak valid. Sunting grup @@ -3306,6 +3320,7 @@ + Informasi Bantuan Permintaan Bantuan Signal Android @@ -3393,6 +3408,7 @@ Mengambil pratinjau dari tautan secara langsung dari situs web untuk pesan yang Anda kirimkan. Ganti frasa sandi Ubah frasa sandi Anda + Aktifkan frasa sandi untuk kunci layar Kunci layar dan notifikasi dengan frasa sandi Keamanan Layar @@ -3405,6 +3421,7 @@ Pola kedip LED KustomisasiKustomisasi Ubah suara dan getaran + Bunyi Diam Standar @@ -5251,6 +5268,7 @@ Tambahkan ke cerita Tambah pesan + Tambah balasan Kirim ke Media sekali lihat @@ -5850,6 +5868,8 @@ %1$d balasan + + Story no longer hidden Tambahkan @@ -7109,7 +7129,23 @@ - Kosongkan ruang sebesar %1$s untuk mengunduh media Anda. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ OKE - - - Riwayat pembayaran - - Pencadangan teks dan semua media - - Detail pembayaran - - Tipe pencadangan - - Tanggal dibayar - - Bagikan + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ Frekuensi pencadangan Cadangkan dengan data seluler + + View backup key + + Unlock to view backup key Nonaktifkan dan hapus pencadangan @@ -7212,13 +7248,27 @@ Pencadangan akan diproses dalam semalam. - Tipe pencadangan + Backup plan Pencadangan dinonaktifkan - - %1$s · %2$s/bulan - - Aktifkan pencadangan + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + Kunci cadangan Anda - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Kunci cadangan adalah kode 64 digit yang memungkinkan Anda memulihkan data cadangan saat menginstal ulang Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Jika lupa kunci tersebut, Anda tidak akan dapat memulihkan data cadangan. Signal tidak dapat membantu memulihkan data cadangan Anda. - Next + Berikutnya - Record your backup key + Simpan kunci cadangan Anda - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Kunci ini diperlukan untuk memulihkan akun dan data Anda. Simpan kunci di tempat yang aman. Jika kehilangan kunci, Anda tidak akan dapat memulihkan akun. - Copy to clipboard + Salin ke papan klip - Next + Berikutnya - Keep your key safe + Pastikan kunci Anda aman - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal tidak dapat membantu memulihkan data cadangan jika Anda kehilangan kunci. Simpan di tempat yang aman, dan jangan bagikan dengan orang lain. - I\'ve recorded my key + Saya telah menyimpan kunci saya - Continue + Lanjutkan - See key again + Lihat kunci lagi diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 4dca90fc23..630a5ba5ab 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + No Elimina @@ -260,6 +263,7 @@ Tocca per foto, tieni premuto per video + Scatta Cambia camera Apri galleria @@ -346,7 +350,7 @@ Questo link per la chiamata non è valido. Accertati di inserire il link giusto per intero prima di provare a unirti alla chiamata. - You are already in a call + Sei già in una chiamata @@ -403,6 +407,7 @@ Impossibile trovare un\'app per aprire il file. Copiato %1$s da %1$s + a %1$s   Leggi tutto   Scarica altro @@ -436,6 +441,7 @@ Invia messaggio modificato Componi messaggio Attenzione, c\'è stato un errore nell\'inviare il tuo allegato. + Il destinatario non è un indirizzo valido per l\'SMS o per l\'email! Il messaggio è vuoto! Membri del gruppo @@ -576,6 +582,7 @@ Salvataggio di %1$d allegati nella memoria… In attesa… + Dati (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Scollegare \'%1$s\'? Scollegando questo dispositivo non gli sarà più possibile inviare o ricevere messaggi. Connessione alla rete fallita + Riprova Scollegamento dispositivo… Scollegamento dispositivo @@ -1408,7 +1416,7 @@ Link del gruppo Condividi Reimposta link - Require admin approval + Richiedi l\'approvazione dell\'admin Richiedi a un amministratore di approvare i nuovi membri che si uniscono tramite il link del gruppo. Sei sicuro di voler reimpostare il link del gruppo? Le persone non saranno più in grado di unirsi al gruppo utilizzando il link attuale. @@ -1507,6 +1515,7 @@ Mandare %1$d inviti via SMS? Passiamo a Signal: %1$s + Sembra che tu non abbia applicazioni per condividere. @@ -1931,6 +1940,7 @@ Vuoi condividere il tuo nome e la tua foto con %1$s e permettere a questo utente di inviarti messaggi? Non riceverai messaggi da parte sua finché non sbloccherai il contatto. Vuoi consentire a %1$s di inviarti messaggi? Non riceverai messaggi da parte sua finché non sbloccherai il contatto. + Vuoi ricevere aggiornamenti e notizie da %1$s? Non riceverai aggiornamenti finché non sbloccherai il contatto. Continuare la tua chat con questo gruppo e condividere il tuo nome e la tua foto con i suoi membri? Questo Gruppo Legacy non può essere più usato. Crea un altro gruppo per attivare nuove funzioni come le @menzioni e gli admin. @@ -2245,6 +2255,7 @@ Nessun altro è qui %1$s è in questa chiamata + %1$s sono in questa chiamata %1$s e %2$s sono in questa chiamata @@ -2647,7 +2658,7 @@ Proveremo di nuovo più tardi. Aggiornamento di Signal riuscito! La tua app è stata automaticamente aggiornata alla versione %1$s. - You updated to version %1$s. + Hai aggiornato Signal alla versione %1$s. Inviare messaggio? @@ -3067,6 +3078,7 @@ Anteprima allegato Mostra/nascondi pannello fotocamera Registra ed invia allegato audio + Blocca la registrazione di un allegato audio Il messaggio non può essere inviato. Controlla la tua connessione e riprova. @@ -3085,6 +3097,7 @@ Messaggio letto + Foto del contatto @@ -3169,6 +3182,7 @@ Adesso sembra tutto a posto! Per ricevere le notifiche delle chiamate, clicca qui e attiva \"Mostra notifiche.\" Per ricevere le notifiche delle chiamate, clicca qui e attiva le notifiche e assicurati che Suono e Pop-up siano attivi. + Per ricevere le notifiche delle chiamate, clicca qui e attiva attività in background nelle impostazioni della \"Batteria\". Impostazioni Per ricevere le notifiche delle chiamate, clicca Impostazioni e attiva \"Mostra notifiche.\" @@ -3306,7 +3320,7 @@ Mi sto prendendo una pausa Al lavoro su qualcosa di nuovo - One or more characters is invalid. + Uno o più caratteri non sono validi. Modifica gruppo @@ -3405,6 +3419,7 @@ + Informazioni supporto Richiesta di supporto Signal Android @@ -3493,6 +3508,7 @@ Recupera le anteprime dei link direttamente dai siti web per i messaggi che invii. Cambia password Cambia la tua password + Abilita blocco schermo con password Blocca schermo e notifiche con password Sicurezza schermo @@ -3505,6 +3521,7 @@ Impostazioni LED Personalizza Cambia suono e vibrazione + Suono Silenzioso Predefinito @@ -5378,6 +5395,7 @@ Aggiungi alla Storia Aggiungi un messaggio + Aggiungi una risposta Invia a Media visibile una sola volta @@ -5982,6 +6000,8 @@ %1$d risposta %1$d risposte + + Story no longer hidden Aggiungi @@ -7267,7 +7287,23 @@ - Libera %1$s di spazio per scaricare i tuoi media. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Ok - - - Cronologia dei pagamenti - - Backup messaggi e media - - Informazioni sul pagamento - - Tipo di backup - - Data pagamento - - Condividi + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Frequenza dei backup Backup usando i dati cellulare + + View backup key + + Unlock to view backup key Disattiva ed elimina backup @@ -7370,13 +7406,27 @@ Il backup verrà creato stanotte. - Tipo di backup + Backup plan Backup disattivati - - %1$s · %2$s al mese - - Abilitare i backup + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + La tua chiave di backup - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + La chiave di backup è un codice di 64 cifre che ti consente di ripristinare il tuo backup quando installi di nuovo Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Se dimentichi la tua chiave, non potrai ripristinare il backup. Ricorda che Signal non può aiutarti a recuperare il backup. - Next + Avanti - Record your backup key + Annota la tua chiave di backup - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Questa chiave ti servirà per recuperare il tuo account e i tuoi dati. Salvala in un posto sicuro. Se la perdi, non potremo aiutarti a ripristinare il tuo account. - Copy to clipboard + Copia negli appunti - Next + Avanti - Keep your key safe + Tieni la tua chiave al sicuro - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal non potrà aiutarti a ripristinare il backup se perdi la chiave. Ti consigliamo di salvarla in un posto sicuro e di non condividerla mai con altre persone. - I\'ve recorded my key + Ho salvato la mia chiave - Continue + Continua - See key again + Vedi di nuovo la chiave diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 348d84240f..626481acb8 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + כן לא מחיקה @@ -264,6 +267,7 @@ הקש עבור תצלום, החזק עבור סרטון + לכוד שנה מצלמה פתח גלריה @@ -352,7 +356,7 @@ זה לא לינק שיחה תקף. יש לוודא שהלינק שלם ונכון לפני שמנסים להצטרף. - You are already in a call + אתה כבר בשיחה @@ -409,6 +413,7 @@ לא ניתן למצוא יישום המסוגל לפתוח מדיה זו. %1$s הועתק מאת %1$s + אל %1$s   קרא עוד   הורד עוד @@ -442,6 +447,7 @@ שליחת הודעה ערוכה חבר הודעה סליחה, הייתה שגיאה בהוספת הצרופה שלך. + המקבל אינו כתובת תקפה של מסרון או כתובת דוא״ל! ההודעה ריקה! חברי קבוצה @@ -598,6 +604,7 @@ שומרים %1$d קבצים מצורפים באחסון… ממתין… + נתונים (Signal) MMS מסרון @@ -1046,6 +1053,7 @@ לבטל קישור \'%1$s\'? ע״י ביטול קישור של מכשיר זה, הוא לא יוכל יותר לשלוח או לקבל הודעות. חיבור רשת נכשל + נסה שוב מבטל קישור מכשיר… מבטל קישור מכשיר @@ -1488,7 +1496,7 @@ קישור קבוצה שתף אפס קישור - Require admin approval + דרישת אישור מנהל/ת דרוש מנהלן כדי לאשר הצטרפות של חברי קבוצה חדשים באמצעות קישור הקבוצה. האם אתה בטוח שאתה רוצה לאפס את קישור הקבוצה? אנשים לא יוכלו עוד להצטרף אל הקבוצה ע״י שימוש בקישור הנוכחי. @@ -1593,6 +1601,7 @@ לשלוח %1$d הזמנות במסרון? בוא נחליף אל Signal: %1$s + נראה שאין לך יישומים כלשהם לשתף בהם. @@ -2047,6 +2056,7 @@ לאפשר ל%1$s לשלוח לך הודעות ולשתף איתו או איתה את השם והתמונה שלך? לא תוצג לך אף הודעה עד שהחסימה תבוטל. לאפשר ל%1$s לשלוח לך הודעות? לא תוצג לך אף הודעה עד שהחסימה תבוטל. + לקבל עדכונים וחדשות מ%1$s? לא יוצג לך אף עדכון עד שהחסימה תבוטל. להמשיך צ׳אט זה עם קבוצה זו ולשתף את השם והתמונה שלך עם חברי הקבוצה? לא ניתן להשתמש יותר בקבוצה הישנה הזו. צריך ליצור קבוצה חדשה כדי להפעיל פיצ׳רים חדשים כמו @אזכורים ומנהלים. @@ -2393,6 +2403,7 @@ אף אחד אחר לא כאן %1$s בשיחה זו + %1$s בשיחה זו %1$s וגם %2$s בשיחה זו @@ -2817,7 +2828,7 @@ ננסה שוב מאוחר יותר. Signal עודכנה בהצלחה עודכנת אוטומטית לגרסה %1$s. - You updated to version %1$s. + עדכנת לגרסה %1$s. לשלוח הודעה? @@ -3251,6 +3262,7 @@ תמונה ממוזערת של צרופה עורר מגירת צרופות מהירות של מצלמה הקלט ושלח צרופת שמע + נעל הקלטה של צרופת שמע הודעה לא יכלה להישלח. בדוק את החיבור שלך ונסה שוב. @@ -3269,6 +3281,7 @@ הודעה נקראה + תמונת איש קשר @@ -3353,6 +3366,7 @@ הכול נִרְאֶה כראוי עכשיו! כדי לקבל התראות שיחה, הקש כאן והפעל את \"הראה התראות\". כדי לקבל התראות שיחה, הקש כאן והפעל התראות ווודא שצליל וחלון קופץ מאופשרים. + כדי לקבל התראות שיחה, הקש כאן ואפשר את פעילות ברקע בהגדרות \"סוללה\". הגדרות כדי לקבל התראות שיחה, הקש על הגדרות והפעל את \"הראה התראות\". @@ -3504,7 +3518,7 @@ לוקח הפסקה עובד על משהו חדש - One or more characters is invalid. + תו אחד או יותר אינו חוקי. ערוך קבוצה @@ -3603,6 +3617,7 @@ + מידע תמיכה בקשת תמיכה של Signal Android @@ -3693,6 +3708,7 @@ אחזר תצוגות מקדימות של קישורים ישירות מאתרים עבור הודעות שאתה שולח. שנה משפט־סיסמה שנה את משפט־הסיסמה שלך + אפשר נעילת מסך עם משפט־סיסמה נעל מסך והתראות באמצעות משפט־סיסמה אבטחת מסך @@ -3705,6 +3721,7 @@ תבנית הבהוב LED התאם אישית שנה צליל ורטט + קול שקט ברירת מחדל @@ -5632,6 +5649,7 @@ הוספה לסטורי הוסף הודעה + הוסף תשובה שליחה אל מדיה לצפייה חד פעמית @@ -6246,6 +6264,8 @@ %1$d תשובות %1$d תשובות + + Story no longer hidden הוסף @@ -7583,7 +7603,23 @@ - יש לפנות שטח של %1$s כדי להוריד את המדיה שלך. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ אישור - - - היסטוריית תשלומים - - גיבוי טקסט וכל המדיה - - פרטי תשלום - - סוג גיבוי - - תאריך תשלום - - שתף + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ תדירות גיבוי גיבוי באמצעות נתונים ניידים + + View backup key + + Unlock to view backup key כיבוי ומחיקת גיבויים @@ -7686,13 +7722,27 @@ הגיבוי ייווצר במהלך הלילה. - סוג גיבוי + Backup plan גיבויים הושבתו - - %1$s · %2$s לחודש - - אפשר גיבויים + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + מפתח הגיבוי שלך - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + מפתח הגיבוי שלך הוא קוד בן 64 ספרות שמאפשר לך לשחזר את הגיבוי שלך בעת התקנה מחדש של Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + אם שכחת את המפתח שלך, לא תהיה לך אפשרות לשחזר את הגיבוי. Signal לא תוכל לעזור לך לשחזר את הגיבוי שלך. - Next + הבא - Record your backup key + תיעוד מפתח הגיבוי שלך - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + המפתח הזה נדרש כדי לשחזר את החשבון והנתונים שלך. חשוב לאחסן את המפתח הזה במקום בטוח. אם הוא נאבד, אי אפשר לשחזר את החשבון שלך. - Copy to clipboard + העתק ללוח העריכה - Next + הבא - Keep your key safe + יש לשמור על בטיחות המפתח שלך - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal לא תוכל לעזור לך לשחזר את הגיבוי שלך אם איבדת את המפתח שלך. חשוב לאחסן אותו במקום בטוח ומאובטח, ולא לשתף אותו עם אחרים. - I\'ve recorded my key + תיעדתי את המפתח שלי - Continue + המשך - See key again + צפייה חוזרת במפתח diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6eb7f3554a..9810b347b9 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + はい いいえ 消去する @@ -258,6 +261,7 @@ タップで写真、長押しで動画 + キャプチャ カメラを変更 ギャラリーを開く @@ -343,7 +347,7 @@ この通話リンクは有効ではありません。リンクに間違いがないことを確認してから、参加してください。 - You are already in a call + あなたはすでに通話中です @@ -400,6 +404,7 @@ このメディアを開けるアプリが見つかりません。 %1$s をコピーしました %1$s から + %1$s へ   続きを読む   さらにダウンロード @@ -433,6 +438,7 @@ 編集した内容を送信する メッセージ作成 添付ファイルの設定中にエラーが発生しました。 + 宛先のSMSまたはEメールアドレスが不正です! メッセージが空です! グループメンバー @@ -565,6 +571,7 @@ %1$d個の添付ファイルをストレージに保存しています… 保留中… + データ (Signal) MMS SMS @@ -986,6 +993,7 @@ 「%1$s」のリンクを解除しますか? リンクを解除すると、この端末はメッセージの送受信ができなくなります。 ネットワーク接続に失敗しました + もう一度試す 端末のリンクを解除しています… 端末のリンクを解除中 @@ -1368,7 +1376,7 @@ グループリンク 共有 リンクをリセット - Require admin approval + 管理者の承認を必要とする グループリンク経由での新メンバー参加時に、管理者の承認を要求します。 本当にグループリンクをリセットしますか?リセットすると現在のリンクからグループに参加できなくなります。 @@ -1464,6 +1472,7 @@ %1$d件のSMS招待状を送信しますか? メッセージアプリをSignalにしましょう: %1$s + 共有先として使えるアプリがないようです。 @@ -1873,6 +1882,7 @@ %1$s にあなたへのメッセージ送信や、名前とプロフィール画像の共有を許可しますか?ブロックを解除するまでメッセージは届きません。 %1$s からメッセージを受け取りますか?ブロックを解除するまでは受け取りません。 + %1$s から更新情報やニュースを受け取りますか?ブロックを解除するまでは受け取りません。 このグループとのチャットを継続して、あなたの名前と画像をそのメンバーと共有しますか? レガシーグループは現在使用できません。@メンションや管理者などの新機能を有効にするため、新しいグループを作成してください。 @@ -2171,6 +2181,7 @@ 誰も参加していません %1$s が参加中 + %1$s が参加中 %1$s と %2$s が参加中 @@ -2562,7 +2573,7 @@ 後で再度試してみます。 Signalが正常に更新されました バージョン%1$sに自動的に更新されました。 - You updated to version %1$s. + バージョン%1$sにアップデートされました。 メッセージを送信しますか? @@ -2975,6 +2986,7 @@ 添付ファイルのサムネイル クイックカメラの添付ドロワーを切り替える 音声を録音して送信する + 音声の録音をロック メッセージを送信できませんでした。接続を確認し、再度試してください。 @@ -2993,6 +3005,7 @@ 既読 + 連絡先の写真 @@ -3077,6 +3090,7 @@ すべて順調そうです! 着信通知を受けるには、ここをタップして「通知を表示する」を有効にします。 着信通知を受けるには、ここをタップして通知を有効にし、音とポップアップが有効になっていることを確認してください。 + 着信通知を受けるには、ここをタップして「バッテリー」設定でバックグラウンド動作を有効にします。 設定 着信通知を受けるには、設定をタップして「通知を表示する」を有効にします。 @@ -3207,7 +3221,7 @@ 休憩中 新しいことをしています - One or more characters is invalid. + 1つ以上の文字が無効です。 グループを編集 @@ -3306,6 +3320,7 @@ + サポート情報 Signal Androidサポートリクエスト @@ -3393,6 +3408,7 @@ 送信メッセージのリンクプレビューを、ウェブサイトから直接取得します。 パスフレーズを変更 パスフレーズを変更します + パスフレーズでの画面ロックを有効にする パスフレーズで画面と通知をロックします 画面のセキュリティ @@ -3405,6 +3421,7 @@ LED点滅パターン カスタマイズ サウンドとバイブの変更 + 着信音 無音 既定 @@ -5251,6 +5268,7 @@ ストーリーに追加 メッセージを追加してください + 返信を追加 宛先 使い捨てメディア @@ -5850,6 +5868,8 @@ 返信 %1$d件 + + Story no longer hidden 追加する @@ -7109,7 +7129,23 @@ - メディアをダウンロードするために、%1$sの空き容量を確保してください。 + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ OK - - - 決済履歴 - - テキストとすべてのメディアのバックアップ - - 決済の詳細 - - バックアップの種類 - - 支払い日 - - 共有 + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ バックアップの頻度 モバイルデータ通信を使ってバックアップする + + View backup key + + Unlock to view backup key バックアップをオフにして消去しますか? @@ -7212,13 +7248,27 @@ バックアップは夜間に作成されます。 - バックアップの種類 + Backup plan バックアップがオフになりました - - %1$s · %2$s/月 - - バックアップを有効にする + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + あなたのバックアップキー - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + バックアップキーは64桁のコードで、Signalを再インストールした際には、このコードを使ってバックアップを復元することができます。 - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + キーを忘れた場合、バックアップの復元はできません。Signalはバックアップの復元をサポートできません。 - Next + 次へ - Record your backup key + バックアップキーを保管する - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + このキーは、アカウントとデータを復元する際に必要です。このキーを安全な場所に保管してください。紛失した場合、アカウントを復元できなくなります。 - Copy to clipboard + クリップボードにコピー - Next + 次へ - Keep your key safe + キーの取り扱いは厳重にしてください - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + キーを紛失した場合、Signalはバックアップの復元をサポートできません。保管は安全な場所でおこない、第三者と共有しないでください。 - I\'ve recorded my key + キーを保管しました - Continue + 続行 - See key again + キーをもう一度見る diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 0efc7bba4e..b11997e0b7 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + დიახ არა წაშლა @@ -260,6 +263,7 @@ დააჭირე სურათისთვის, შეაკავე ვიდეოსთვის + გადაღება კამერის შეცვლა გალერეის გახსნა @@ -346,7 +350,7 @@ ეს ზარის ვალიდური ბმული არაა. შესვლის მცდელობამდე დარწმუნდი, რომ მთლიანი ბმული სწორი და ხელუხლებელია. - You are already in a call + შენ უკვე ამ ზარზე იმყოფები @@ -403,6 +407,7 @@ ამ მედია-ფაილის გამშვები აპლიკაცია ვერ მოიძებნა %1$s დაკოპირებულია %1$s დან + %1$s მდე  წაიკითხე მეტი   მეტის გადმოწერა @@ -436,6 +441,7 @@ რედაქტირების გაგზავნა შეტყობინების შექმნა სამწუხაროდ მიმაგრების დროს დაფიქსირდა შეცდომა. + მიმღები არ არის სწორი SMS ან ელ-ფოსტა! შეტყობინება ცარიელია! ჯგუფის წევრები @@ -576,6 +582,7 @@ %1$d დანართს მეხსიერებაში ვინახავთ… მოლოდინის რეჟიმში… + მონაცემები (Signal) MMS SMS @@ -1006,6 +1013,7 @@ ჩავხსნა კავშირი \'%1$s\'? ამ მოწყობილობასთან კავშირის ჩახსნის შემდეგ, ის ვეღარ შეძლებს შეტყობინებების გაგზავნასა და მიღებას. ქსელთან დაკავშირება ვერ მოხერხდა + ახლიდან სცადე მიბმული მოწყობილობის წაშლა მოწყობილობასთან კავშირის ჩახსნა @@ -1408,7 +1416,7 @@ ჯგუფის ბმული გაზიარება ბმულის შეცვლა - Require admin approval + ადმინისტრატორის დასტურის მოთხოვნა მოითხოვე, რომ ჯგუფის ბმულის მეშვეობით ახალი წევრები ადმინმა დაამტკიცოს. დარწმუნებული ხარ, რომ გსურს ჯგუფის ბმული შეცვალო? ხალხი ჯგუფში გაწევრიანებას ამჟამინდელი ბმულის საშუალებით ვეღარ შეძლებს. @@ -1507,6 +1515,7 @@ გავაგზავნოთ %1$d SMS-მოწვევა? მოდი, Signal-ზე გადავიდეთ: %1$s + როგორც ჩანს, არ გაქვს ისეთი აპი, იქ რომ გადააზიარო. @@ -1931,6 +1940,7 @@ მივცეთ უფლება %1$s-ს მოგწეროს და შენი სახელი და ფოტო გავუზიაროთ? წერილებს არ მიიღებ, სანამ მას არ განბლოკავ. მივცეთ უფლება %1$s-ს მოგწეროს? წერილებს არ მიიღებ, სანამ მას არ განბლოკავ. + გსურს შეიტყო სიახლეები %1$s-ის შესახებ? განახლებებს არ მიიღებ, სანამ მას არ განბლოკავ. გსურს გააგრძელო მიმოწერა ამ ჯგუფთან და გაუზიარო შენი სახელი და ფოტო მის წევრებს? ამ მოძველებული ჯგუფის გამოყენება აღარაა შესაძლებელი. შექმენი ახალი ჯგუფი ისეთი ფუნქციების გასააქტიურებლად, როგორიცაა @მონიშვნები და ადმინები. @@ -2245,6 +2255,7 @@ აქ არავინაა %1$s ამ ზარზეა + %1$s არიან ამ ზარზე %1$s და %2$s ამ ზარზე არიან @@ -2647,7 +2658,7 @@ მოგვიანებით ისევ ვცდით. Signal-ი წარმატებით განახლდა შენ ავტომატურად გადახვედი %1$s ვერსიაზე. - You updated to version %1$s. + შენ დააყენე ახალი ვერსია - %1$s. გავაგზავნოთ შეტყობინება? @@ -3067,6 +3078,7 @@ დანართის მინიატურა კამერის სწრაფი გადართვის პანელი აუდიო დანართის ჩაწერა და გაგზავნა + აუდიო დანართის ჩაწერის დალუქვა შეტყობინება ვერ გაიგზავნა. შეამოწმე შენი კავშირი და თავიდან სცადე. @@ -3085,6 +3097,7 @@ შეტყობინება წაკითხულია + კონტაქტის ფოტო @@ -3169,6 +3182,7 @@ ახლა ყველაფერი კარგად გამოიყურება! ზარის შეტყობინებების მისაღებად დააჭირე აქ და ჩართე „შეტყობინებების ჩვენება“. ზარის შეტყობინებების მისაღებად, დააჭირე აქ, ჩართე შეტყობინებები და დარწმუნდი, რომ ხმა და Pop-up ფანჯარა ჩართულია. + ზარის შეტყობინებების მისაღებად დააჭირე აქ და ჩართე ფონური აქტივობა „ბატარეის“ პარამეტრებში. Settings ზარის შეტყობინებების მისაღებად დააჭირე პარამეტრებს და ჩართე „შეტყობინებების ჩვენება“. @@ -3306,7 +3320,7 @@ Შესვენება რაღაც ახალზე მუშაობა - One or more characters is invalid. + ერთი ან მეტი სიმბოლო არასწორია. ჯგუფის რედაქტირება @@ -3405,6 +3419,7 @@ + მხარდაჭერის ინფორმაცია Signal Android-ის მხარდაჭერის მოთხოვნა @@ -3493,6 +3508,7 @@ მიიღე ბმულის წინასწარი ნახვა შენს მიერ გაგზავნილი შეტყობინებებისთვის პირდაპირ ვებსაიტებიდან. პაროლ-ფრაზის შეცვლა შეცვალე შენი პაროლ-ფრაზა + ჩართე პაროლ-ფრაზით ეკრანის დაბლოკვა ეკრანისა და შეტყობინებების დაბლოკვა პაროლ-ფრაზით ეკრანის უსაფრთხოება @@ -3505,6 +3521,7 @@ LED ციმციმის სიხშირე პერსონალიზირება ხმისა და ვიბრაციის შეცვლა + ხმა ჩუმი ძირითადი @@ -5378,6 +5395,7 @@ Story-ში დამატება შეტყობინების დამატება + პასუხის დამატება გაეგზავნოთ ჩამოთვლილებს ერთხელ სანახავი მედია-ფაილი @@ -5982,6 +6000,8 @@ %1$d პასუხი %1$d პასუხი + + Story no longer hidden დამატება @@ -7267,7 +7287,23 @@ - გაათავისუფლე %1$s ადგილი შენი მედია ფაილების გადმოსაწერად. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - ტრანზაქციის ისტორია - - ტექსტისა და ყველა მედია ფაილის სარეზერვო კოპია - - ტრანზაქციის დეტალები - - სარეზერვო კოპირების ტიპი - - გადახდის დღე - - გაზიარება + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ სარეზერვო კოპიების შექმნის სიხშირე სარეზერვო კოპიების შექმნა მობილური ინტერნეტის გამოყენებით + + View backup key + + Unlock to view backup key გამორთვა და სარეზერვო კოპიების წაშლა @@ -7370,13 +7406,27 @@ სარეზერვო კოპიები ღამით შეიქმნება. - სარეზერვო კოპირების ტიპი + Backup plan სარეზერვო კოპირება გამორთულია - - %1$s · %2$s/თვეში - - სარეზერვო კოპირების ჩართვა + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + შენი სათადარიგო გასაღები - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + შენი სათადარიგო გასაღები 64-რიცხვიანი კოდია, რომელიც საშუალებას გაძლევს, აღადგინო შენი ჩანაწერები, თუ Signal-ს თავიდან გადმოიწერ. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + თუ შენი პინ-კოდი დაგავიწყდება, სათადარიგო ჩანაწერების აღდგენას ვეღარ შეძლებ. Signal-ი შენი სათადარიგო ჩანაწერების აღდგენაში ვერ დაგეხმარება. - Next + შემდეგი - Record your backup key + ჩაიწერე შენი სათადარიგო ჩანაწერების გასაღები - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + ეს გასაღები საჭიროა შენი ანგარიშისა და მონაცემების აღსადგენად. შეინახე გასაღები უსაფრთხო ადგილას. თუ მას დაკარგავ, შენი ანგარიშის აღდგენას ვეღარ შეძლებ. - Copy to clipboard + ბუფერში ჩაკოპირება - Next + შემდეგი - Keep your key safe + უსაფრთხოდ შეინახე შენი გასაღები - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + გასაღების დაკარგვის შემთხვევაში, მის აღდგენაში Signal-ი ვერ დაგეხმარება. ის სადმე დაცულ ადგილას შეინახე და არავის გაუზიარო. - I\'ve recorded my key + ჩავინიშნე ჩემი გასაღები - Continue + გაგრძელება - See key again + გასაღების თავიდან ნახვა diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 905133dd91..723d80e016 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Иә Жоқ Жою @@ -260,6 +263,7 @@ Фотосуретке түсіру үшін түртіңіз, ал видео түсіргіңіз келсе басып тұрыңыз + Түсіру Камераны ауыстыру Галереяны ашу @@ -346,7 +350,7 @@ Бұл жарамсыз қоңырау сілтемесі. Қоңырауға қосылмас бұрын, сілтеменің дұрыс екеніне көз жеткізіңіз. - You are already in a call + Сіздің қоңырауға қатысып жатырсыз @@ -403,6 +407,7 @@ Бұл мультимедианы аша алатын қолданба табылмады. %1$s көшірілді кімнен: %1$s + кімге: %1$s   Толығырақ   Басқаларын жүктеп алу @@ -436,6 +441,7 @@ Өзгерісті жіберу Хат жазу Кешіріңіз, тіркемені орнату кезінде қате пайда болды. + Алушының SMS немесе электрондық мекенжайы дұрыс емес! Хатта ештеңе жоқ! Group members @@ -576,6 +582,7 @@ %1$d тіркеме жадқа сақталуда… Күтуде… + Дерек (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \"%1$s\" байланысын жою керек пе? Бұл құрылғымен байланысты жою арқылы ол хат жібере немесе қабылдай алмайтын болады. Желі байланысы орнатылмады + Қайталап көру Құрылғымен байланыс үзілуде… Құрылғымен байланысты үзу @@ -1408,7 +1416,7 @@ Топ сілтемесі Бөлісу Сілтемені қалпына келтіру - Require admin approval + Админнің мақұлдауы керек Топ сілтемесі арқылы жаңа мүшелердің қосылуын әкімші мақұлдау керек. Топ сілтемесін қалпына келтіргіңіз келе ме? Ағымдағы сілтеменің көмегімен адамдар енді топқа қосыла алмайды. @@ -1507,6 +1515,7 @@ %1$d SMS шақыруларын жіберу керек пе? Signal-ға ауысайық: %1$s + Бөлісетін бірде-бір қолданбаңыз жоқ сияқты. @@ -1931,6 +1940,7 @@ %1$s сізге хат жазуына рұқсат етіп, оған аты-жөніңіз бен фотосуретіңіздің көрсетілгенін қалайсыз ба? Оны блоктан шығармайынша, сіз одан бірде-бір хат алмайтын боласыз. %1$s сізге хат жазуына рұқсат етесіз бе? Сіз оны блоктан шығармайынша, ешқандай хат алмайсыз. + %1$s бойынша жаңартулар мен хабарлар алып тұрғыңыз келе ме? Оны блоктан шығармайынша, ешқандай жаңарту алмайсыз. Continue your chat with this group and share your name and photo with its members? Бұл ескі топты енді қолдана алмайсыз. @атап_өтілгендер және әкімшілер сияқты жаңа функцияларды іске қосу үшін жаңа топ құрыңыз. @@ -2245,6 +2255,7 @@ Бұл жерде ешкім жоқ %1$s осы қоңырауға қатысып отыр + %1$s осы қоңырауға қатысып отыр %1$s және %2$s осы қоңырауға қатысып отыр @@ -2647,7 +2658,7 @@ Кейінірек тағы қайталап көреміз. Signal жаңартылды %1$s нұсқасына автоматты түрде жаңартылды. - You updated to version %1$s. + Signal жаңартылды: %1$s. Хатты жіберу керек пе? @@ -3067,6 +3078,7 @@ Тіркеме нобайы Жылдам камераны тіркеу тартпасын ауыстыру Аудио тіркемені жазып, жіберу + Аудио тіркемесін жазу функциясын құлыптау Хат жіберілмеді. Байланысты тексеріп, қайта жіберіп көріңіз. @@ -3085,6 +3097,7 @@ Хат оқылды + Контакт фотосуреті @@ -3169,6 +3182,7 @@ Енді бәрі дұрыс болды! Қоңырау туралы хабарландырулар алу үшін осы жерді түртіп, \"Хабарландыруларды көрсету\" функциясын қосыңыз. Қоңырау туралы хабарландыруларды алу үшін осы жерді түртіп, хабарландыруларды қосыңыз және Дауыс және Қалқыма функциялары қосылып тұрғанына көз жеткізіңіз. + Қоңырау туралы хабарландырулар алу үшін осы жерді түртіп, \"Батарея\" параметрлерінде фондық әрекетті қосыңыз. Параметрлер Қоңырау туралы хабарландырулар алу үшін Параметрлер бөлімін басып, \"Хабарландыруларды көрсету\" функциясын қосыңыз. @@ -3306,7 +3320,7 @@ Үзіліс Жаңа жобамен жұмыс істеп жатырмын - One or more characters is invalid. + Бір немесе бірнеше таңба дұрыс емес. Топты өзгерту @@ -3405,6 +3419,7 @@ + Қолдау көрсету туралы ақпарат Signal Android қолдау көрсету өтініші @@ -3493,6 +3508,7 @@ Жіберетін хаттарыңыз үшін сілтеменің шағын көріністерін тікелей веб-сайттардан алыңыз. Құпия сөйлемді өзгерту Құпия сөйлеміңізді өзгертіңіз + Құпия сөйлеммен экранды құлыптау функциясын қосу Құпия сөйлеммен экранды және хабарландыруларды құлыптау Экран қауіпсіздігі @@ -3505,6 +3521,7 @@ LED жыпылықтау үлгісі Бейімдеу Дауыс пен дірілді өзгерту + Дауыс Дыбыссыз Әдепкі параметр @@ -5378,6 +5395,7 @@ Сторис қосу Хат қосу + Жауап қосу Алушы Бір рет көрілетін мультимедиа @@ -5982,6 +6000,8 @@ %1$d жауап %1$d жауап + + Story no longer hidden Қосу @@ -7267,7 +7287,23 @@ - Мультимедианы жүктеп алу үшін %1$s орын босатыңыз. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Төлемдер тарихы - - Мәтіннің және бүкіл мультимедианың резервтік көшірмесі - - Төлем туралы мәлімет - - Резервтік көшірме түрі - - Төленген күні - - Бөлісу + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Резервтік көшірме жиілігі Резервтік көшірмесін ұялы байланыс арқылы жасау + + View backup key + + Unlock to view backup key Резервтік көшірмелерді өшіру және жою @@ -7370,13 +7406,27 @@ Резервтік көшірме түнде жасалады. - Резервтік көшірме түрі + Backup plan Резервтік көшірме өшірілді - - %1$s · %2$s/ай - - Резервтік көшірмелерді қосу + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Резервтік кілтіңіз - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Резервтік кілтіңіз – 64 таңбалы код. Ол Signal қайта орнатылған кезде, резервтік көшірмені қалпына келтіруге мүмкіндік береді. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Кілтіңізді ұмытып қалсаңыз, резервтік көшірмені қалпына келтіре алмайсыз. Signal резервтік көшірмені қалпына келтіруге көмектесе алмайды. - Next + Келесі - Record your backup key + Резервтік кілтіңізді жазып алыңыз - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Бұл кілт аккаунт пен деректерді қалпына келтіру үшін қажет. Бұл кілтті қауіпсіз жерде сақтаңыз. Оны жоғалтсаңыз, аккаунтыңызды қалпына келтіре алмайсыз. - Copy to clipboard + Буферге көшіру - Next + Келесі - Keep your key safe + Кілтіңізді қауіпсіз сақтаңыз - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Кілтіңізді жоғалтып алсаңыз, Signal резервтік көшірмені қалпына келтіруге көмектесе алмайды. Оны қауіпсіз жерде сақтаңыз және ешкіммен бөліспеңіз. - I\'ve recorded my key + Кілтімді жазып алдым - Continue + Жалғастыру - See key again + Кілтті қайта көру diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index 48817fb4dc..c202ed4e84 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + មែន ទេ លុប @@ -258,6 +261,7 @@ ចុច សម្រាប់រូបភាព សង្កត់ សម្រាប់វីដេអូ + ថត ប្តូរកាមេរ៉ា បើកវិចិត្រសាល @@ -343,7 +347,7 @@ នេះមិនមែនជាតំណហៅត្រឹមត្រូវនោះទេ។ សូមប្រាកដថា តំណទាំងមូលនេះនៅមិនទាន់ខូច និងពិតជាត្រឹមត្រូវ មុនពេលព្យាយាមចូលរួម។ - You are already in a call + អ្នកស្ថិតនៅក្នុងការហៅទូរសព្ទស្រាប់ហើយ @@ -400,6 +404,7 @@ មិនអាចស្វែករកកម្មវិធី ដើម្បីបើកព័ត៌មាននេះទេ។ បានចម្លង %1$s ពី %1$s + ទៅ %1$s   អានបន្ថែម   ទាញយកបន្ថែម @@ -433,6 +438,7 @@ ផ្ញើសារដែលកែ បង្កើតសារថ្មី សុំទោស មានបញ្ហក្នុងការកំណត់ឯកសារភ្ជាប់របស់អ្នក។ + អ្នកទទួល មិនមានSMS ឬ អ៊ីម៉ែល ត្រឹមត្រូវ! សារទទេ! សមាជិកក្រុម @@ -565,6 +571,7 @@ កំពុងរក្សា %1$d ឯកសារភ្ជាប់ទៅកាន់ថាសផ្ទុក… មិនទាន់សម្រេច… + ទិន្នន័យ (Signal) MMS SMS @@ -986,6 +993,7 @@ ផ្តាត់តំណភ្ជាប់ \'%1$s\'? ពេលផ្តាច់តំណភ្ជាប់ឧបករណ៍នេះ អ្នកនឹងមិនអាចផ្ញើ និងទទួលសារទៀតទេ។ ការតភ្ជាប់បណ្តាញបរាជ័យ + ព្យាយាមម្តងទៀត កំពុងផ្តាច់ឧបករណ៍… កំពុងផ្តាច់ឧបករណ៍ @@ -1368,7 +1376,7 @@ តំណភ្ជាប់ក្រុម ចែករំលែក កំណត់តំណភ្ជាប់ឡើងវិញ - Require admin approval + ត្រូវមានការយល់ព្រមពីអ្នកគ្រប់គ្រង ទាមទារអភិបាល ដើម្បីអនុម័តសមាជិកថ្មី ចូលរួមតាមតំណភ្ជាប់ក្រុម។ តើអ្នកប្រាកដថាចង់កំណត់តំណភ្ជាប់ក្រុមឡើងវិញ? អ្នកដទៃ នឹងមិនអាចចូលរួមក្រុម ដោយប្រើប្រាស់តំណភ្ជាប់បច្ចុប្បន្នបានទេ។ @@ -1464,6 +1472,7 @@ ផ្ញើសារ SMS អញ្ជើញ %1$d ? តោះដូរទៅប្រើSignal ៖ %1$s + វាហាក់ដូចជាមិនមានកម្មវិធីណាមួយដើម្បីចែករំលែក។ @@ -1873,6 +1882,7 @@ អាចឲ្យ %1$s ផ្ញើសារមកអ្នកបាន និងចែករំលែកឈ្មោះ និងរូបថតរបស់អ្នកជាមួយពួកគេ? អ្នកនឹងមិនទទួលបានសារណាមួយឡើយ រហូតទាល់តែអ្នកឈប់ទប់ស្កាត់ពួកគេ។ អាចឲ្យ %1$s ផ្ញើសារមកអ្នកបាន? អ្នកនឹងមិនទទួលបានសារណាមួយឡើយ រហូតទាល់តែអ្នកឈប់ទប់ស្កាត់ពួកគេ។ + ទទួលបានបច្ចុប្បន្នភាព និងព័ត៌មានពី %1$s? អ្នកនឹងមិនទទួលបានបច្ចុប្បន្នភាពណាមួយឡើយ រហូតទាល់តែអ្នកឈប់ទប់ស្កាត់ពួកគេ។ បន្តការជជែករបស់អ្នកជាមួយក្រុមនេះ និងចែករំលែកឈ្មោះ និងរូបភាពរបស់អ្នកជាមួយសមាជិកក្រុម។ ក្រុមកេរដំណែលនេះមិនអាចប្រើបានទៀតឡើយ។ បង្កើតក្រុមថ្មីមួយ ដើម្បីបើកដំណើរការមុខងារថ្មីៗដូចជា @លើកឈ្មោះ និងអ្នកគ្រប់គ្រង។ @@ -2171,6 +2181,7 @@ គ្មាននរណាម្នាក់នៅទីនេះទេ %1$s នៅក្នុងការហៅ + %1$s នៅក្នុងការហៅនេះ %1$s និង %2$s នៅក្នុងការហៅ @@ -2562,7 +2573,7 @@ យើងនឹងព្យាយាមម្តងទៀតនៅពេលក្រោយ។ Signal ត្រូវបានដំឡើងកំណែដោយជោគជ័យ អ្នកត្រូវបានដំឡើងកំណែដោយស្វ័យប្រវត្តិទៅកំណែ %1$s។ - You updated to version %1$s. + អ្នកបានដំឡើងកំណែទៅកំណែ %1$s។ ផ្ញើសារ? @@ -2975,6 +2986,7 @@ ភ្ជាប់រូបភាពឯកសារ បិទបើកថតឯកសារភ្ជាប់កាមេរ៉ារហ័ស ថត​ និងផ្ញើឯកសារភ្ជាប់ជាសំឡេង + បិទការថតឯកសារសម្លេង សារមិនអាចផ្ញើ។ ពិនិត្យការតភ្ជាប់របស់អ្នក ហើយសាកល្បងម្តងទៀត។ @@ -2993,6 +3005,7 @@ សារបានអានរួច + រូបភាពទំនាក់ទំនង @@ -3077,6 +3090,7 @@ អ្វីគ្រប់យ៉ាងមើលទៅល្អឥឡូវនេះ! ដើម្បីទទួលសារជូនដំណឹងការហៅ​ ចុចទីនេះ និងបើក \"បង្ហាញសារជូនដំណឹង។\" ដើម្បីទទួលសារជូនដំណឹងការហៅ ចុចទីនេះ និងបើកសារជូនដំណឹង ហើយប្រាកដថា សំឡេង និងការលោតឡើង ត្រូវបានបើក។ + ដើម្បីទទួលសារជូនដំណឹងការហៅ ចុចទីនេះ និងបើកសកម្មភាពផ្ទៃក្រោយក្នុងការកំណត់ \"ថាមពលថ្ម\"។ ការកំណត់ ដើម្បីទទួលសារជូនដំណឹងការហៅ ចុចការកំណត់ និងបើក \"បង្ហាញសារជូនដំណឹង។\" @@ -3207,7 +3221,7 @@ សម្រាកបន្ថិច ធ្វើការលើអ្វីដែលថ្មី - One or more characters is invalid. + តួអក្សរមួយ ឬច្រើនមិនត្រឹមត្រូវទេ។ កែប្រែក្រុម @@ -3306,6 +3320,7 @@ + ព័ត៌មានជំនួយ ការស្នើជំនួយ Signal Android @@ -3393,6 +3408,7 @@ ទាញយកតំណភ្ជាប់មើលមុន ដោយផ្ទាល់ពីគេហទំព័រ សម្រាប់សារដែលអ្នកផ្ញើ។ ផ្លាស់ប្តូរឃ្លាសម្ងាត់ ផ្លាស់ប្តូរឃ្លាសម្ងាត់របស់អ្នក + បើកឃ្លាសម្ងាត់ចាក់សោរអេក្រង់ ចាក់សោរអេក្រង់ និងការជូនដំណឹងជាមួយឃ្លាសម្ងាត់ អេក្រង់សុវត្ថិភាព @@ -3405,6 +3421,7 @@ លំនាំលោតភ្លើង LED កំណត់ផ្ទាល់ខ្លួន ផ្លាស់ប្តូរសំឡេង និងរំញ័រ + សំឡេង ស្ងាត់ លំនាំដើម @@ -5251,6 +5268,7 @@ បញ្ចូលទៅរឿងរ៉ាវ បន្ថែមសារមួយ + បន្ថែមការឆ្លើយតប ផ្ញើទៅ មេឌៀមើលបានតែម្តង @@ -5850,6 +5868,8 @@ ចំនួនឆ្លើយតប %1$d + + Story no longer hidden បន្ថែម @@ -7109,7 +7129,23 @@ - បង្កើនទំហំផ្ទុក %1$s ដើម្បីទាញយកមេឌៀរបស់អ្នក។ + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ យល់ព្រម - - - ប្រវត្តិបង់ប្រាក់ - - ការបម្រុងទុកមេឌៀទាំងអស់ និងអត្ថបទ - - ព័ត៌មានលម្អិតអំពីការបង់ប្រាក់ - - ប្រភេទបម្រុងទុក - - កាលបរិច្ឆេទបង់ប្រាក់ - - ចែករំលែក + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ ភាពញឹកញាប់នៃការបម្រុងទុក បម្រុងទុកដោយប្រើអ៊ីនធឺណិតទូរសព្ទ + + View backup key + + Unlock to view backup key បិទ និងលុបការបម្រុងទុក @@ -7212,13 +7248,27 @@ ការបម្រុងទុកនឹងត្រូវបានបង្កើតឡើងក្នុងពេលមួយយប់។ - ប្រភេទបម្រុងទុក + Backup plan ការបម្រុងទុកត្រូវបានបិទ - - %1$s · %2$s/ខែ - - បើកការបម្រុងទុក + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + សោបម្រុងទុករបស់អ្នក - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + សោបម្រុងទុករបស់អ្នកគឺជាលេខកូដ 64 ខ្ទង់ដែលអនុញ្ញាតឱ្យអ្នកស្តារការបម្រុងទុករបស់អ្នកឡើងវិញនៅពេលអ្នកដំឡើង Signal ឡើងវិញ។ - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + ប្រសិនបើអ្នកភ្លេចសោរបស់អ្នក អ្នកនឹងមិនអាចស្ដារការបម្រុងទុករបស់អ្នកបានទេ។ Signal មិនអាចជួយអ្នកក្នុងការស្តារការបម្រុងទុករបស់អ្នកបានទេ។ - Next + បន្ទាប់ - Record your backup key + កត់ត្រាសោបម្រុងទុករបស់អ្នក - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + តម្រូវឱ្យមានសោនេះដើម្បីស្តារគណនី និងទិន្នន័យរបស់អ្នក។ ទុកសោនេះនៅកន្លែងដែលមានសុវត្ថិភាព។ ប្រសិនបើអ្នកបាត់វា អ្នកនឹងមិនអាចស្តារគណនីរបស់អ្នកមកវិញបានទេ។ - Copy to clipboard + ចម្លងទៅឃ្លីបបត - Next + បន្ទាប់ - Keep your key safe + រក្សាសោរបស់អ្នកឱ្យមានសុវត្ថិភាព - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal នឹងមិនអាចជួយអ្នកស្ដារការបម្រុងទុករបស់អ្នកឡើងវិញបានទេ ប្រសិនបើអ្នកបាត់សោរបស់អ្នក។ ទុកវានៅកន្លែងណាដែលមានសុវត្ថិភាព ហើយកុំចែករំលែកវាជាមួយអ្នកផ្សេង។ - I\'ve recorded my key + ខ្ញុំបានកត់ត្រាសោរបស់ខ្ញុំ - Continue + បន្ត - See key again + មើលសោម្តងទៀត diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index f831d86bb2..28f08a8777 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + ಹೌದು ಇಲ್ಲ ಅಳಿಸಿ @@ -260,6 +263,7 @@ ಫೋಟೋಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ, ವೀಡಿಯೊಗಾಗಿ ಹಿಡಿದುಕೊಳ್ಳಿ + ಸೆರೆಹಿಡಿಯಿರಿ ಕ್ಯಾಮರಾ ಬದಲಾಯಿಸಿ ಗ್ಯಾಲರಿ ತೆರೆಯಿರಿ @@ -346,7 +350,7 @@ ಇದು ಮಾನ್ಯವಾದ ಕರೆ ಲಿಂಕ್ ಅಲ್ಲ. ಸೇರಲು ಪ್ರಯತ್ನಿಸುವ ಮೊದಲು ಸಂಪೂರ್ಣ ಲಿಂಕ್ ಅಖಂಡವಾಗಿದೆ ಮತ್ತು ಸರಿಯಾಗಿದೆ ಎನ್ನುವುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ. - You are already in a call + ನೀವು ಈಗಾಗಲೇ ಕಾಲ್‌ನಲ್ಲಿದ್ದೀರಿ @@ -403,6 +407,7 @@ ಈ ಮೀಡಿಯಾ ತೆರೆಯಲು ಸಾಮರ್ಥ್ಯವಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ. %1$s ನಕಲಿಸಲಾಗಿದೆ ಇಂದ %1$s + ಗೆ %1$s  ಮತ್ತಷ್ಟು ಓದಿ  ಇನ್ನಷ್ಟು ಇಳಿಕೆ ಮಾಡಿ @@ -436,6 +441,7 @@ ಎಡಿಟ್ ಮಾಡಿದ್ದನ್ನು ಕಳುಹಿಸಿ ಸಂದೇಶ ರಚಿಸಿ ಕ್ಷಮಿಸಿ, ನಿಮ್ಮ ಲಗತ್ತನ್ನು ಹೊಂದಿಸುವಲ್ಲಿ ದೋಷ ಉಂಟಾಗಿದೆ. + ಸ್ವೀಕರಿಸುವವರ ಎಸ್‌‌ಎಂಸ್ ಅಥವಾ ಇಮೇಲ್ ವಿಳಾಸ ಮಾನ್ಯವಾದುದಲ್ಲ! ಸಂದೇಶ ಖಾಲಿಯಾಗಿದೆ! ಗುಂಪಿನ ಸದಸ್ಯರು @@ -576,6 +582,7 @@ ಸ್ಟೋರೇಜ್‍ ಗೆ %1$d ಲಗತ್ತುಗಳನ್ನು ಉಳಿಸಲಾಗುತ್ತಿದೆ… ಬಾಕಿ ಇದೆ… + ದತ್ತಾಂಶ (Signal) ಎಂಎಂಎಸ್ ಎಸ್‌ಎಂಎಸ್‌ @@ -1006,6 +1013,7 @@ %1$sಯನ್ನು ಅನ್‌ಲಿಂಕ್‌ ಮಾಡುವುದೇ? ಈ ಸಾಧನವನ್ನು ಅನ್‌ಲಿಂಕ್ ಮಾಡುವ ಮೂಲಕ, ಇದು ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಅಥವಾ ಪಡೆಯಲು ಇನ್ನು ಮುಂದೆ ಸಮರ್ಥವಾಗಿರುವುದಿಲ್ಲ. ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕ ವಿಫಲವಾಗಿದೆ + ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ ಸಾಧನ ಅನ್‌ಲಿಂಕ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ… ಸಾಧನ ಅನ್‌ಲಿಂಕ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ @@ -1408,7 +1416,7 @@ ಗುಂಪಿನ ಲಿಂಕ್ ಹಂಚಿಕೊಳ್ಳಿ ರೀಸೆಟ್ ಲಿಂಕ್ - Require admin approval + ಅಡ್ಮಿನ್ ಅನುಮೋದನೆ ಅಗತ್ಯವಿದೆ ಗುಂಪಿನ ಲಿಂಕ್ ಮೂಲಕ ಹೊಸ ಸದಸ್ಯರು ಸೇರುವುದನ್ನು ಅನುಮೋದಿಸಲು ಅಡ್ಮಿನ್ ಅಗತ್ಯವಿರುತ್ತದೆ. ನೀವು ಖಚಿತವಾಗಿ ಗುಂಪಿನ ಲಿಂಕ್ ಅನ್ನು ಮರುಹೊಂದಿಸಲು ಬಯಸುತ್ತೀರಾ? ಪ್ರಸ್ತುತ ಲಿಂಕ್ ಅನ್ನು ಬಳಸಿ ಇನ್ನು ಮುಂದೆ ಗುಂಪನ್ನು ಸೇರಲು ಜನರಿಗೆ ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. @@ -1507,6 +1515,7 @@ ಎಸ್‌ಎಂಎಸ್‌ ಆಹ್ವಾನಗಳನ್ನು %1$dಗೆ ಕಳುಹಿಸುವುದೇ? Signal ಗೆ ಬದಲಾಯಿಸೋಣ: %1$s + ಹಂಚಿಕೊಳ್ಳಲು ತಾವು ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್ ಗಳನ್ನು ಹೊಂದಿಲ್ಲ ಎಂದು ಕಂಡುಬರುತ್ತಿದೆ. @@ -1931,6 +1940,7 @@ %1$s ಅವರು ನಿಮಗೆ ಮೆಸೇಜ್ ಮಾಡಲು ಮತ್ತು ನಿಮ್ಮ ಹೆಸರು ಮತ್ತು ಫೋಟೋವನ್ನು ಅವರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಲು ಅನುಮತಿಸುತ್ತೀರಾ? ನೀವು ಅವರನ್ನು ಅನ್‌ಬ್ಲಾಕ್ ಮಾಡುವವರೆಗೆ ನೀವು ಯಾವುದೇ ಮೆಸೇಜ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸುವುದಿಲ್ಲ. %1$s ಅವರು ನಿಮಗೆ ಮೆಸೇಜ್ ಕಳುಹಿಸಬಹುದೇ? ನೀವು ಅವರನ್ನು ಅನ್‌ಬ್ಲಾಕ್ ಮಾಡುವವರೆಗೆ ನೀವು ಯಾವುದೇ ಮೆಸೇಜ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸುವುದಿಲ್ಲ. + %1$s ಅವರಿಂದ ಅಪ್‌ಡೇಟ್‌ಗಳು ಮತ್ತು ಸುದ್ದಿ ಪಡೆಯಬೇಕೇ? ನೀವು ಅವರನ್ನು ಅನ್‌ಬ್ಲಾಕ್ ಮಾಡುವವರೆಗೆ ನೀವು ಯಾವುದೇ ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸುವುದಿಲ್ಲ. ಈ ಗುಂಪಿನೊಂದಿಗೆ ನಿಮ್ಮ ಚಾಟ್ ಅನ್ನು ಮುಂದುವರಿಸುವುದೇ ಹಾಗೂ ನಿಮ್ಮ ಹೆಸರು ಮತ್ತು ಫೋಟೋವನ್ನು ಇದರ ಸದಸ್ಯರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಬೇಕೇ? ಈ ಲೆಗಸಿ ಗ್ರೂಪ್ ಅನ್ನು ಇನ್ನು ಮುಂದೆ ಬಳಸಲಾಗುವುದಿಲ್ಲ. @mentions ಮತ್ತು admins ನಂತಹ ಹೊಸ ಫೀಚರ್‌ಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ಹೊಸ ಗುಂಪನ್ನು ರಚಿಸಿ. @@ -2245,6 +2255,7 @@ ಇಲ್ಲಿ ಯಾರೂ ಇಲ್ಲ %1$s ಅವರು ಈ ಕರೆಯಲ್ಲಿದ್ದಾರೆ + %1$s ಅವರು ಈ ಕರೆಯಲ್ಲಿದ್ದಾರೆ %1$s ಮತ್ತು %2$s ಈ ಕರೆಯಲ್ಲಿದ್ದಾರೆ @@ -2647,7 +2658,7 @@ ನಾವು ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸುತ್ತೇವೆ. Signal ಯಶಸ್ವಿಯಾಗಿ ನವೀಕರಣಗೊಂಡಿದೆ ನೀವು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆವೃತ್ತಿ %1$s ಗೆ ನವೀಕರಣಗೊಂಡಿದ್ದೀರಿ. - You updated to version %1$s. + ನೀವು ಆವೃತ್ತಿ %1$s ಗೆ ಅಪ್‌ಡೇಟ್ ಮಾಡಿದ್ದೀರಿ. ಸಂದೇಶ ಕಳುಹಿಸುವುದೇ? @@ -3067,6 +3078,7 @@ ಥಂಬ್‌ನೇಲ್ ಲಗತ್ತು ತ್ವರಿತ ಕ್ಯಾಮರಾ ಅಟ್ಯಾಚ್‌ಮೆಂಟ್ ಡ್ರಾಯರ್ ಟಾಗಲ್ ಮಾಡಿ ಆಡಿಯೊ ಲಗತ್ತು ರೆಕಾರ್ಡ್ ಮಾಡಿ ಮತ್ತು ಕಳುಹಿಸಿ + ಆಡಿಯೊ ಲಗತ್ತು ರೆಕಾರ್ಡಿಂಗ್ ಲಾಕ್ ಮಾಡಿ ಮೆಸೇಜ್ ಕಳುಹಿಸಲಾಗಲಿಲ್ಲ. ನಿಮ್ಮ ಸಂಪರ್ಕ ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಪುನಃ ಪ್ರಯತ್ನಿಸಿ. @@ -3085,6 +3097,7 @@ ಸಂದೇಶವನ್ನು ಓದಲಾಗಿದೆ + ಸಂಪರ್ಕದ ಫೋಟೊ @@ -3169,6 +3182,7 @@ ಈಗ ಎಲ್ಲವೂ ಚೆನ್ನಾಗಿ ಕಾಣಿಸುತ್ತದೆ! ಕಾಲ್ ನೊಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು, ಇಲ್ಲಿ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು \"ನೊಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ತೋರಿಸಿ\" ಆನ್ ಮಾಡಿ. ಕಾಲ್ ನೊಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಪಡೆಯಲು, ಇಲ್ಲಿ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ನೊಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಆನ್ ಮಾಡಿ ಮತ್ತು ಧ್ವನಿ ಮತ್ತು ಪಾಪ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿ. + ಕಾಲ್ ನೊಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಪಡೆಯಲು, ಇಲ್ಲಿ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು \"ಬ್ಯಾಟರಿ\" ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ. ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಕಾಲ್ ನೊಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು \"ನೊಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ತೋರಿಸಿ\" ಆನ್ ಮಾಡಿ @@ -3306,7 +3320,7 @@ ವಿರಾಮ ತೆಗೆದುಕೊಳ್ಳುವುದು ಏನೋ ಹೊಸದರ ಕುರಿತು ಕೆಲಸ ಮಾಡುವುದು - One or more characters is invalid. + ಒಂದು ಅಥವಾ ಹೆಚ್ಚಿನ ಅಕ್ಷರಗಳು ಅಮಾನ್ಯವಾಗಿವೆ. ಗುಂಪನ್ನು ಬದಲಾಯಿಸಿ @@ -3405,6 +3419,7 @@ + ಬೆಂಬಲ ಮಾಹಿತಿ Signal ಆಂಡ್ರಾಯ್ಡ್ ಬೆಂಬಲ ವಿನಂತಿ @@ -3493,6 +3508,7 @@ ನೀವು ಕಳುಹಿಸುವ ಸಂದೇಶಗಳಿಗಾಗಿ ಲಿಂಕ್ ಪೂರ್ವವೀಕ್ಷಣೆಗಳನ್ನು ವೆಬ್‌ಸೈಟ್‌ಗಳಿಂದ ನೇರವಾಗಿ ಹಿಂಪಡೆಯಿರಿ. ಪಾಸ್‌‌ಫ್ರೇಸ್ ಬದಲಾಯಿಸಿ ನಿಮ್ಮ ಪಾಸ್‌‌ಫ್ರೇಸ್ ಬದಲಾಯಿಸಿ + ಪಾಸ್‌‌ಫ್ರೇಸ್ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಕ್ರಿಯಗೊಳಿಸಿ ಪಾಸ್‌ಫ್ರೇಸ್ ಜೊತೆಯಲ್ಲಿ ಲಾಕ್ ಸ್ಕ್ರೀನ್ ಮತ್ತು ಅಧಿಸೂಚನೆಗಳು ಸ್ಕ್ರೀನ್ ಸುರಕ್ಷತೆ @@ -3505,6 +3521,7 @@ ಎಲ್‌ಇಡಿ ಮಿನುಗುವ ಪ್ಯಾಟರ್ನ್‌ ಕಸ್ಟಮೈಝ್ ಮಾಡಿ ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಶನ್ ಬದಲಾಯಿಸಿ + ಶಬ್ದ ಮೌನ ಡೀಫಾಲ್ಟ್ @@ -5378,6 +5395,7 @@ ಸ್ಟೋರಿಗೆ ಸೇರಿಸಿ ಮೆಸೇಜ್ ಸೇರಿಸಿ + ಉತ್ತರ ಸೇರಿಸಿ ಇವರಿಗೆ ಕಳುಹಿಸಿ ಒಮ್ಮೆ ವೀಕ್ಷಿಸಬಹುದಾದ ಮೀಡಿಯಾ @@ -5982,6 +6000,8 @@ %1$d reply %1$d ಉತ್ತರಗಳು + + Story no longer hidden ಸೇರಿಸಿ @@ -7267,7 +7287,23 @@ - ನಿಮ್ಮ ಮೀಡಿಯಾವನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು %1$s ಸ್ಥಳಾವಕಾಶವನ್ನು ಮುಕ್ತಗೊಳಿಸಿ. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ಓಕೆ - - - ಪಾವತಿ ಇತಿಹಾಸ - - ಪಠ್ಯ ಮತ್ತು ಎಲ್ಲಾ ಮೀಡಿಯಾ ಬ್ಯಾಕಪ್ - - ಪಾವತಿ ವಿವರಗಳು - - ಬ್ಯಾಕಪ್ ಪ್ರಕಾರ - - ಪಾವತಿಸಿದ ದಿನಾಂಕ - - ಹಂಚಿಕೊಳ್ಳಿ + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ ಬ್ಯಾಕಪ್ ಆವರ್ತನ ಸೆಲ್ಯುಲರ್ ಬಳಸಿಕೊಂಡು ಬ್ಯಾಕಪ್ ಮಾಡಿ + + View backup key + + Unlock to view backup key ಬ್ಯಾಕಪ್ ಆಫ್ ಮಾಡಿ ಮತ್ತು ಅಳಿಸಿ @@ -7370,13 +7406,27 @@ ರಾತ್ರಿಯಲ್ಲಿ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಚಿಸಲಾಗುತ್ತದೆ. - ಬ್ಯಾಕಪ್ ಪ್ರಕಾರ + Backup plan ಬ್ಯಾಕಪ್‌ಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ - - %1$s · %2$s/ತಿಂಗಳು - - ಬ್ಯಾಕಪ್‌ಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d/ @@ -7426,33 +7476,33 @@ - Your backup key + ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀ 64-ಅಂಕಿಯ ಕೋಡ್ ಆಗಿದ್ದು, ನೀವು Signal ಅನ್ನು ಪುನಃ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿದಾಗ ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + ನಿಮ್ಮ ಕೀಯನ್ನು ನೀವು ಮರೆತರೆ, ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲು ನಿಮಗೆ ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಕವರ್ ಮಾಡಲು Signal ನಿಮಗೆ ಸಹಾಯ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. - Next + ಮುಂದೆ - Record your backup key + ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀಯನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + ನಿಮ್ಮ ಖಾತೆ ಮತ್ತು ಡೇಟಾವನ್ನು ರಿಕವರ್ ಮಾಡಲು ಈ ಕೀ ಅಗತ್ಯವಿದೆ. ಈ ಕೀಯನ್ನು ಎಲ್ಲಾದರೂ ಸುರಕ್ಷಿತವಾಗಿ ಸಂಗ್ರಹಿಸಿ. ನೀವು ಅದನ್ನು ಕಳೆದುಕೊಂಡರೆ, ನಿಮ್ಮ ಖಾತೆಯನ್ನು ರಿಕವರ್ ಮಾಡಲು ನಿಮಗೆ ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. - Copy to clipboard + ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ಗೆ ನಕಲಿಸು - Next + ಮುಂದೆ - Keep your key safe + ನಿಮ್ಮ ಕೀಯನ್ನು ಸುರಕ್ಷಿತವಾಗಿರಿಸಿ - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + ನಿಮ್ಮ ಕೀಯನ್ನು ನೀವು ಕಳೆದುಕೊಂಡರೆ ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲು Signal ನಿಮಗೆ ಸಹಾಯ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ಅದನ್ನು ಎಲ್ಲೋ ಸುರಕ್ಷಿತವಾಗಿ ಮತ್ತು ಭದ್ರವಾಗಿ ಸಂಗ್ರಹಿಸಿ ಹಾಗೂ ಅದನ್ನು ಇತರರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಬೇಡಿ. - I\'ve recorded my key + ನಾನು ನನ್ನ ಕೀಯನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿದ್ದೇನೆ - Continue + ಮುಂದುವರಿಸಿ - See key again + ಕೀಯನ್ನು ಮತ್ತೊಮ್ಮೆ ನೋಡಿ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 6ef9690cb5..1597d44f5c 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + 아니요 삭제 @@ -258,6 +261,7 @@ 사진은 탭하고, 동영상은 길게 누르세요. + 캡처 카메라 변경 갤러리 열기 @@ -343,7 +347,7 @@ 통화 링크가 올바르지 않습니다. 통화에 참여하기 전에 링크가 정확한지 확인하세요. - You are already in a call + 이미 통화에 참여 중입니다. @@ -400,6 +404,7 @@ 미디어를 열 수 있는 앱이 없습니다. %1$s 복사됨 %1$s에서 + %1$s에게 더 알아보기 더 다운로드 @@ -433,6 +438,7 @@ 수정한 메시지 보내기 메시지 작성 죄송합니다. 파일 첨부 설정 중 오류가 발생했습니다. + 수신자가 올바른 SMS 또는 이메일 주소가 아닙니다! 메시지가 비어 있습니다! 그룹 멤버 @@ -565,6 +571,7 @@ 첨부 파일 %1$d개를 저장 공간에 저장 중… 대기 중… + 데이터(Signal) MMS SMS @@ -986,6 +993,7 @@ \'%1$s\' 기기를 연결 해제하시겠습니까? 기기를 연결 해제하면 메시지를 보내거나 받을 수 없습니다. 네트워크 연결 실패 + 다시 시도해 주세요. 기기 연결 해제 중… 기기 연결 해제 중 @@ -1368,7 +1376,7 @@ 그룹 링크 공유 링크 초기화 - Require admin approval + 관리자 승인 필요 그룹 링크로 참가하려는 새 멤버를 승인하려면 관리자 권한이 필요합니다. 그룹 링크를 초기화하시겠습니까? 이 링크로 사람들이 그룹에 참가할 수 없게 됩니다. @@ -1464,6 +1472,7 @@ SMS 초대 %1$d개를 보내시겠습니까? Signal로 같이 옮겨요! %1$s + 공유하기 위해 사용할 수 있는 앱이 없습니다. @@ -1873,6 +1882,7 @@ %1$s 님이 내게 메시지를 보내고 내 이름과 사진을 공유할 수 있도록 허용할까요? 차단을 해제할 때까지 모든 메시지를 차단합니다. %1$s 님이 내게 메시지를 보내도록 허용할까요? 차단을 해제할 때까지 모든 메시지를 차단합니다. + %1$s의 업데이트와 메시지를 받아보시겠어요? 차단을 해제할 때까지 모든 업데이트를 차단합니다. 이 그룹과 대화를 계속하고 멤버들에게 이름과 사진을 공유하시겠어요? 이 레거시 그룹은 더 이상 사용할 수 없습니다. 새 그룹을 만들어 @멘션 및 관리자와 같은 새로운 기능을 활성화하세요. @@ -2171,6 +2181,7 @@ 아무도 없음 %1$s 님이 통화 중입니다. + %1$s이 이 통화에 있습니다 %1$s 및 %2$s 님이 통화에 참여했습니다. @@ -2562,7 +2573,7 @@ 나중에 다시 시도하겠습니다. Signal을 업데이트했습니다. %1$s 버전으로 자동 업데이트되었습니다. - You updated to version %1$s. + %1$s 버전으로 업데이트했습니다. 메시지를 보내시겠습니까? @@ -2975,6 +2986,7 @@ 첨부 파일 섬네일 빠른 카메라 첨부 파일 창 전환 오디오 첨부 파일 녹음 후 보내기 + 오디오 첨부 파일 녹음 잠금 메시지를 보낼 수 없습니다. 연결을 확인하고 다시 시도하세요. @@ -2993,6 +3005,7 @@ 메시지 읽음 + 연락처 사진 @@ -3077,6 +3090,7 @@ 모두 좋아보이네요! 전화 알림을 받으려면 여기를 탭하고 \'알림 표시\'를 켜세요. 전화 알림을 받으려면 여기를 탭하고 알림을 켜고 소리 및 팝업이 활성화되어 있는지 확인하세요. + 전화 알림을 받으려면 여기를 탭하고 \'배터리\' 설정에서 백그라운드 활동을 활성화하세요. 설정 전화 알림을 받으려면 설정을 탭하고 \'알림 표시\'를 켭니다. @@ -3207,7 +3221,7 @@ 쉬는 중 새로운 것에 도전하는 중 - One or more characters is invalid. + 잘못된 문자가 하나 이상 포함되어 있습니다. 그룹 편집 @@ -3306,6 +3320,7 @@ + 지원 정보 Signal Android 지원 요청 @@ -3393,6 +3408,7 @@ 보내는 메시지에 첨부된 웹 사이트의 링크 미리보기를 불러옵니다. 암호 변경 암호 변경 + 암호 화면 잠금 활성화 암호로 화면 및 알림 금기 화면 보안 @@ -3405,6 +3421,7 @@ LED 반복 패턴 사용자 정의 소리와 진동 변경하기 + 소리 무음 기본값 @@ -5251,6 +5268,7 @@ 스토리에 추가 메시지 추가 + 답장 추가 받는 사람 한 번만 볼 수 있는 미디어 @@ -5850,6 +5868,8 @@ 답장 %1$d개 + + Story no longer hidden 추가 @@ -7109,7 +7129,23 @@ - 미디어를 다운로드하려면 %1$s의 여유 공간을 확보하세요. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ 확인 - - - 결제 기록 - - 텍스트 및 모든 미디어 백업 - - 결제 세부 정보 - - 백업 종류 - - 지불 날짜 - - 공유 + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ 백업 빈도 백업 셀룰러 데이터 사용 + + View backup key + + Unlock to view backup key 백업 끄기 및 삭제 @@ -7212,13 +7248,27 @@ 밤사이 백업을 생성합니다. - 백업 종류 + Backup plan 백업 사용 안 함 - - %1$s · %2$s/월 - - 백업 활성화 + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + 내 백업 키 - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + 백업 키는 Signal을 다시 설치할 경우 백업을 복원하는 데 사용할 수 있는 64자리 코드입니다. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + 키를 잊어버리면 백업을 복원할 수 없습니다. Signal은 백업을 복원하도록 도와드릴 수 없습니다. - Next + 다음 - Record your backup key + 백업 키 기록 - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + 이 키는 계정과 데이터를 복원하는 데 필요합니다. 안전한 곳에 보관해 두세요. 키를 분실하면 계정을 복원할 수 없습니다. - Copy to clipboard + 클립보드에 복사 - Next + 다음 - Keep your key safe + 키를 안전하게 보관하세요 - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + 키를 분실할 경우 Signal은 백업을 복원하도록 도와드릴 수 없습니다. 안전한 곳에 키를 보관해 두고, 다른 사람과 공유하지 마세요. - I\'ve recorded my key + 키를 기록했음 - Continue + 계속 - See key again + 키 다시 보기 diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 593c7ae32b..0bc4fd8e68 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Жөнөтүү Жок Өчүрүү @@ -258,6 +261,7 @@ Сүрөт тартуу үчүн басыңыз, видео тартуу үчүн кое бербей басып туруңуз + Тартуу Камераны алмаштыруу Галереяны ачуу @@ -343,7 +347,7 @@ Чалуунун шилтемеси жараксыз. Шилтеменин толуктугун жана тууралыгын текшерип туруп кошулуңуз. - You are already in a call + Сиз чалуудасыз @@ -400,6 +404,7 @@ Ушул медиафайлды ача турган колдонмо табылган жок. %1$s көчүрүлдү Каяктан: %1$s + Каякка: %1$s Дагы   Дагы жүктөп алуу @@ -433,6 +438,7 @@ Оңдолгонду жөнөтүү Билдирүү жазуу Кечиресиз, тиркеме иштелип жатканда ката кетти. + Алуучунун дареги телефон номери да эмес, электрондук почта дареги да эмес! Билдирүү бош! Топтун мүчөлөрү @@ -565,6 +571,7 @@ %1$d тиркеме сактагычка сакталууда… Күтүлүүдө… + Интернет (Signal) MMS SMS @@ -986,6 +993,7 @@ \'%1$s\' ажыратасызбы? Бул түзмөктү ажыратсаңыз, башкалар менен жазыша албай каласыз. Интернет жок + Кайталап көрүңүз Түзмөк ажыратылууда… Түзмөктү ажыратуу @@ -1368,7 +1376,7 @@ Топко шилтеме Бөлүшүү Шилтемени кайра коюу - Require admin approval + Администратордун уруксаты керек Администратор топко шилтеме аркылуу кошулуп жаткан жаңы мүчөлөрдү ырасташы керек. Топко шилтемени кайра коесузбу? Адамдар учурдагы шилтеме менен топко кошула албай калышат. @@ -1464,6 +1472,7 @@ Бул байланышка (%1$d) SMS чакырууларын жөнөтөсүзбү? Signal\'га өтүп алыңыз: %1$s + Бөлүшө турган колдонмолоруңуз жок окшойт. @@ -1873,6 +1882,7 @@ %1$s сиз менен жазышып, атыңыз менен сүрөтүңүз ага көрүнө берсинби? Аны бөгөттөн чыгармайынча, билдирүүлөрдү албайсыз. %1$s сиз менен жазыша берсинби? Аны бөгөттөн чыгармайынча, билдирүүлөрдү албайсыз. + %1$s жаңыртууларын жана жаңылыктарын аласызбы? Аны бөгөттөн чыгармайынча, билдирүүлөрдү албайсыз. Бул топ менен маектешүүнү улантып, атыңыз менен сүрөтүңүз топтогуларга көрүнө берсинби? Бул эски топ мындан ары колдонулбайт. @айтыпөтүүлөр жана администраторлор сыяктуу жаңы функцияларды иштетүү үчүн жаңы топ түзүңүз. @@ -2171,6 +2181,7 @@ Азырынча эч ким жок %1$s ушул чалууда катышууда + %1$s ушул чалууда катышууда %1$s менен %2$s ушул чалууда катышууда @@ -2562,7 +2573,7 @@ Бир аздан соң аракет кылып көрөбүз. Signal ийгиликтүү жаңырды Автоматтык түрдө %1$s версиясына жаңырды. - You updated to version %1$s. + %1$s версиясына жаңырттыңыз. Билдирүү жөнөтөсүзбү? @@ -2975,6 +2986,7 @@ Тиркеме эскизи Ыкчам камера Аудио тиркемени жаздырып, жөнөтүү + Аудиобилдирүүнү жаздырууну бекитүү Билдирүү жөнөтүлгөн жок. Байланышыңызды текшерип, кайра жөнөтүңүз. @@ -2993,6 +3005,7 @@ Билдирүү окулду + Байланыштын сүрөтү @@ -3077,6 +3090,7 @@ Эми баары жайында! Чалуулар жөнүндө билдирмелерди алып туруу үчүн бул жерди басып, \"Билдирмелерди көрсөтүү\" дегенди иштетиңиз. Чалуулар жөнүндө билдирмелерди алып туруу үчүн бул жерди басып, билдирмелерди иштетип, \"Үн\" менен \"Калкып чыкма терезенин\" күйгүзүлгөнүн текшериңиз. + Чалуулар жөнүндө билдирмелерди алып туруу үчүн бул жерди басып, \"Батарея\" параметрлеринен фондук аракеттерди иштетиңиз. Параметрлер Чалуулар жөнүндө билдирмелерди алып туруу үчүн Параметрлерди басып, \"Билдирмелерди көрсөтүү\" дегенди иштетиңиз. @@ -3207,7 +3221,7 @@ Тыныгуу алдым Жаңы нерсенин үстүнөн иштеп жатам - One or more characters is invalid. + Бир же бир нече символ жарабайт. Топту оңдоо @@ -3306,6 +3320,7 @@ + Колдоо тууралуу маалымат Signal Android Кардарларын колдоо өтүнүчү @@ -3393,6 +3408,7 @@ Жөнөтүп жаткан билдирүүлөр үчүн түздөн түз сайттардан алдын ала көрүлүүчү шилтемелерди аласыз. Сыр сөз айкашын өзгөртүү Сыр сөз айкашыңызды өзгөртүңүз + Экранды сыр сөз айкашы менен кулпулоону иштетүү Экранды жана билдирмелерди сыр сөз айкашы менен кулпулайсыз Экранды коргоо @@ -3405,6 +3421,7 @@ LED кантип бүлбүлдөйт Тууралоо Үн менен дирилдөөнү өзгөртүү + Үн Үнсүз Демейки @@ -5251,6 +5268,7 @@ Окуяга кошуу Билдирүү кошуу + Жооп кошуу Кимге Бир жолу көрүлүүчү MMS @@ -5850,6 +5868,8 @@ %1$d жооп + + Story no longer hidden Кошуу @@ -7109,7 +7129,23 @@ - Медиа файлдарды жүктөп алуу үчүн %1$s чейин орун бошотуңуз. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ OK - - - Төлөмдөр - - Тексттик билдирүүлөр менен медиа файлдар - - Төлөмдүн чоо-жайы - - Камдык көчүрмөнүн түрү - - Төлөгөн күн - - Бөлүшүү + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ Канча убакытта сакталып турат Камдык көчүрмөлөрдү мобилдик байланыш аркылуу сактоо + + View backup key + + Unlock to view backup key Өчүрүп, камдык көчүрмөлөрдү жок кылуу @@ -7212,13 +7248,27 @@ Камдык көчүрмөсү түндө түзүлөт. - Камдык көчүрмөнүн түрү + Backup plan Камдык көчүрмөлөр өчүрүлгөн - - %1$s · айына %2$s - - Камдык көчүрмөлөрдү иштетүү + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + Камдык көчүрмөнүн ачкычы - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Камдык көчүрмөңүздүн ачкычы 64 орундуу код болуп, Signal\'ды кайра орнотконуңузда сакталган нерселерди калыбына келтиргенге жардам берет. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + PIN кодуңузду унутуп калсаңыз, камдык көчүрмөлөрүңүздү калыбына келтире албай каласыз. Signal да жардам бере албайт. - Next + Кийинки - Record your backup key + Камдык көчүрмөңүздүн ачкычын жаздырып алыңыз - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Бул ачкыч менен аккаунтуңузду жана андагы нерселерди калыбына келтиресиз. Аны коопсуз жерде сактаңыз. Жоготуп алсаңыз, аккаунтуңузду калыбына келтире албай каласыз. - Copy to clipboard + Буферге көчүрүү - Next + Кийинки - Keep your key safe + Ачкычыңызды коопсуз жерде сактаңыз - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Ачкычыңызды жоготуп алсаңыз, Signal камдык көчүрмөлөрүңүздү калыбына келтире албай калат. Аны эч ким көрбөгөн коопсуз жерде сактап, эч ким менен бөлүшпөңүз. - I\'ve recorded my key + Ачкычымды жаздырып алдым - Continue + Улантуу - See key again + Ачкычты кайра көрүү diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 99aa7c573a..f6d5f3a40d 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Taip Ne Ištrinti @@ -264,6 +267,7 @@ Bakstelėkite norėdami fotografuoti, laikykite norėdami filmuoti + Fotografuoti Keisti kamerą Atverti galeriją @@ -352,7 +356,7 @@ Neteisinga skambučio nuoroda. Įsitikink, kad nuoroda pilna ir teisinga, tuomet bandyk prisijungti. - You are already in a call + Jūs jau esate skambutyje @@ -409,6 +413,7 @@ Nepavyksta rasti programėlės, galinčios atverti šią mediją. %1$s nukopijuota nuo %1$s + iki %1$s   Skaityti daugiau   Atsisiųsti daugiau @@ -442,6 +447,7 @@ Siųsti redaguotą Rašyti žinutę Apgailestaujame, pridedant priedą, įvyko klaida. + Gavėjas nėra tinkamas SMS ar el. pašto adresas! Žinutė tuščia! Grupės nariai @@ -598,6 +604,7 @@ %1$d priedų įrašoma į atmintį… Laukiama… + Duomenys (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Atsieti „%1$s“? Atsiejus šį įrenginį, jis daugiau nebegalės siųsti ar gauti žinučių. Tinklo ryšys patyrė nesėkmę + Bandykite dar kartą Atsiejamas įrenginys… Atsiejamas įrenginys @@ -1488,7 +1496,7 @@ Grupės nuoroda Bendrinti Atstatyti nuorodą - Require admin approval + Reikalauti administratoriaus patvirtinimo Reikalauti, kad administratorius patvirtintų naujus, per grupės nuorodą prisijungiančius, narius. Ar tikrai norite atstatyti grupės nuorodą? Žmonės daugiau nebegalės prisijungti prie grupės naudodami dabartinę nuorodą. @@ -1593,6 +1601,7 @@ Siųsti %1$d SMS pakvietimų? Bendraukime per Signal: %1$s + Atrodo, kad neturite programėlių su kuriomis bendrinti. @@ -2047,6 +2056,7 @@ Ar nori leisti, kad %1$s rašytų tau žinutes, ir bendrinti savo vardą bei nuotrauką su šiuo žmogumi? Negausi žinučių, kol neatblokuosi. Ar nori leisti, kad %1$s siųstų tau žinutes? Negausi žinučių, kol neatblokuosi. + Ar gauti atnaujinimus ir naujienas iš %1$s? Negausi jokių atnaujinimų, kol neatblokuosi. Tęsti jūsų pokalbį su šia grupe ir bendrinti jūsų vardą ir nuotrauką su jos nariais? Šios pasenusios grupės naudoti nebegalima. Sukurkite naują grupę, jei norite aktyvinti tokias naujas funkcijas kaip @paminėjimai ar administratoriai. @@ -2393,6 +2403,7 @@ Čia daugiau nieko nėra %1$s yra šiame skambutyje + %1$s esate šiame skambutyje Šiame skambutyje yra %1$s ir %2$s @@ -2817,7 +2828,7 @@ Vėliau pabandysime dar kartą. „Signal“ sėkmingai atnaujinta Automatiškai atnaujinta į versiją %1$s. - You updated to version %1$s. + Atnaujinote į versiją %1$s. Siųsti žinutę? @@ -3251,6 +3262,7 @@ Priedo miniatiūra Perjungti sparčiosios kameros priedų stalčių Įrašyti ir siųsti garso įrašo priedą + Užrakinti garso įrašo priedo įrašymą Nepavyko išsiųsti žinutės. Patikrinkite savo ryšį ir bandykite dar kartą. @@ -3269,6 +3281,7 @@ Žinutė perskaityta + Adresato nuotrauka @@ -3353,6 +3366,7 @@ Dabar, viskas atrodo gerai! Norėdami gauti pranešimus apie skambučius, bakstelėkite čia ir įjunkite „Rodyti pranešimus“. Norėdami gauti pranešimus apie skambučius, bakstelėkite čia, įjunkite pranešimus ir užtikrinkite, kad Garsas ir Iškylantieji langai yra įjungti. + Norėdami gauti pranešimus apie skambučius, bakstelėkite čia ir „Akumuliatoriaus“ nustatymuose įjunkite foninę veiklą (išjunkite fono ribojimą). Nustatymai Norėdami gauti pranešimus apie skambučius, bakstelėkite ant Nustatymų ir įjunkite „Rodyti pranešimus“. @@ -3504,7 +3518,7 @@ Man pertrauka Dirbu ties kažkuo nauju - One or more characters is invalid. + Vienas ar daugiau simbolių yra netinkami. Taisyti grupę @@ -3603,6 +3617,7 @@ + Palaikymo informacija Signal „Android“ palaikymo užklausa @@ -3693,6 +3708,7 @@ Gauti savo siunčiamoms žinutėms nuorodų peržiūras tiesiogiai iš internetinių svetainių. Pakeisti slaptafrazę Pakeisti jūsų slaptafrazę + Įjungti ekrano užrakinimą slaptafraze Užrakinti ekraną ir pranešimus, naudojant slaptafrazę Ekrano saugumas @@ -3705,6 +3721,7 @@ Šviesos diodo mirksėjimo šablonas Tinkinti Keisti garsą ir vibraciją + Garsas Tyliai Numatytoji @@ -5632,6 +5649,7 @@ Pridėti į istoriją Pridėti žinutę + Pridėti atsakymą Kam siųsti Medija, kurią galima peržiūrėti vieną kartą @@ -6246,6 +6264,8 @@ %1$d atsakymų %1$d atsakymas + + Story no longer hidden Pridėti @@ -7583,7 +7603,23 @@ - Atlaisvinkite iki %1$s vietos, kad galėtumėte atsisiųsti savo įrašą. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ Gerai - - - Mokėjimų istorija - - Žinučių ir visų įrašų atsarginė kopija - - Mokėjimo informacija - - Atsarginio kopijavimo tipas - - Mokėjimo data - - Bendrinti + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Atsarginės kopijos atlikimo dažnis Daryti atsargines kopijas naudojant mobilųjį ryšį + + View backup key + + Unlock to view backup key Išjungti ir ištrinti atsargines kopijas @@ -7686,13 +7722,27 @@ Atsarginė kopija bus sukurta per naktį. - Atsarginio kopijavimo tipas + Backup plan Atsarginis kopijavimas išjungtas - - %1$s · %2$s/mėn. - - Įjungti atsargines kopijas + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Jūsų atsarginės kopijos raktas - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Atsarginis raktas yra 64 skaitmenų kodas, leidžiantis atkurti atsarginę kopiją iš naujo įdiegus „Signal“. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Pamiršę savo raktą atsarginės kopijos atkurti negalėsite. „Signal“ negali padėti jums atkurti atsarginės kopijos. - Next + Kitas - Record your backup key + Užsirašykite savo atsarginės kopijos raktą - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Šis raktas reikalingas norint atkurti paskyrą ir duomenis. Išsaugokite jį saugioje vietoje. Pamiršę raktą savo paskyros atkurti negalėsite. - Copy to clipboard + Kopijuoti į iškarpinę - Next + Kitas - Keep your key safe + Saugokite savo raktą - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + „Signal“ negalės padėti jums atkurti atsarginės kopijos, jei pamesite raktą. Išsaugokite jį saugioje vietoje ir nesidalykite juo su kitais. - I\'ve recorded my key + Aš įrašiau savo raktą - Continue + Tęsti - See key again + Rodyti raktą dar kartą diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 6ea9adb36a..d282e1f07b 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Dzēst @@ -262,6 +265,7 @@ Pieskarieties, lai uzņemtu bildi, turiet, lai ierakstītu video + Uzņemt Nomainīt kameru Atvērt galeriju @@ -349,7 +353,7 @@ Šī nav derīga zvana saite. Pirms mēģināt pievienoties, pārliecinieties, ka saite nav bojāta un ir pareiza. - You are already in a call + Jūs jau esat zvanā @@ -406,6 +410,7 @@ Nevarēja atrast aplikāciju, ar kuru iespējams atvērt šo failu. Nokopēts %1$s no %1$s + līdz %1$s   Lasīt vairāk.  Lejupielādēt vēl @@ -439,6 +444,7 @@ Sūtīt rediģētu Rakstīt ziņu Diemžēl pielikuma pievienošanas procesā gadījās kļūme + Adresātam nav derīga SMS vai e-pasta adrese! Ziņa ir tukša! Grupas dalībnieki @@ -587,6 +593,7 @@ Saglabā %1$d pielikumus krātuvē… Gaida… + Dati (Signal) MMS SMS @@ -1026,6 +1033,7 @@ Atvienot \"%1$s\" Atvienojot šo ierīci, no tās vairs nevarēs sūtīt vai saņemt ziņas. Tīkla savienojums neizdevās + Mēģiniet vēlreiz Atvieno ierīci… Atvieno ierīci @@ -1448,7 +1456,7 @@ Grupas saite Kopīgot Atstatīt saiti - Require admin approval + Administratora apstiprinājuma pieprasīšana Pieprasiet, lai administrators apstiprina jaunu dalībnieku pievienošanos, izmantojot grupas saiti. Vai tiešām vēlaties atiestatīt grupas saiti? Cilvēki vairs nevarēs pievienoties šai grupai, izmantojot pašreizējo saiti. @@ -1550,6 +1558,7 @@ Sūtīt %1$d SMS uzaicinājumus? Pārejam uz Signal: %1$s + Izskatās, ka jums nav nevienas aplikācijas, caur kuru kopīgot. @@ -1989,6 +1998,7 @@ Vai atļaut lietotājam %1$s ar jums sazināties un kopīgot jūsu vārdu un fotoattēlu? Jūs nesaņemsiet ziņas līdz viņu neatbloķēsiet. Vai atļaut lietotājam %1$s ar jums sazināties? Jūs nesaņemsiet ziņas līdz viņu neatbloķēsiet. + Vai saņemt jaunumus un ziņas no lietotāja %1$s? Jūs nesaņemsiet ziņas līdz viņu neatbloķēsiet. Vai turpināt tērzēšanu ar šo grupu un rādīt savu vārdu un fotoattēlu šīs grupas dalībniekiem? Šo novecojušo grupu vairs nevar izmantot. Izveidojiet jaunu grupu, lai aktivizētu jaunas funkcijas, piemēram, @pieminējumus un administratorus. @@ -2319,6 +2329,7 @@ Te neviena nav %1$s piedalās šajā zvanā + %1$s piedalās šajā zvanā %1$s un %2$s piedalās šajā zvanā @@ -2732,7 +2743,7 @@ Vēlāk mēģināsim vēlreiz. Signal ir sekmīgi atjaunināts Automātiski tika veikts atjauninājums uz versiju %1$s. - You updated to version %1$s. + Atjauninājums veiksmīgs. Jaunā versija: %1$s. Sūtīt ziņu? @@ -3159,6 +3170,7 @@ Pielikuma sīkattēls Pārslēgt uz ātro kameras pievienošanas atvilktni Ierakstīt un sūtīt audio pielikumus + Bloķēt audio pielikuma ierakstīšanu Ziņu nevarēja nosūtīt. Pārbaudiet savienojumu un mēģiniet vēlreiz. @@ -3177,6 +3189,7 @@ Ziņa izlasīta + Kontakta attēls @@ -3261,6 +3274,7 @@ Šķiet, ka tagad viss ir kārtībā! Lai saņemtu paziņojumus par zvaniem, pieskarieties šeit un ieslēdziet \"Rādīt paziņojumus\". Lai saņemtu paziņojumus par zvaniem, pieskarieties šeit un ieslēdziet paziņojumus, kā arī pārliecinieties, vai ir iespējota skaņa un uznirstošie elementi. + Lai saņemtu paziņojumus par zvaniem, pieskarieties šeit un akumulatora iestatījumu sadaļā iespējojiet fona darbības. Iestatījumi Lai saņemtu paziņojumus par zvaniem, pieskarieties pie Iestatījumi un ieslēdziet \"Rādīt paziņojumus\". @@ -3405,7 +3419,7 @@ Paņēmis pārtraukumu Strādāju pie kaut kā jauna - One or more characters is invalid. + Viena vai vairākas rakstzīmes nav derīgas. Rediģēt grupu @@ -3504,6 +3518,7 @@ + Atbalsta info Signal Android atbalsta pieprasījums @@ -3593,6 +3608,7 @@ Izgūstiet saišu priekšskatījumus nosūtītajām ziņām tieši no tīmekļa vietnēm. Nomainīt paroli Mainīt savu paroli + Iespējot ekrāna bloķēšanu ar paroli Aizslēgt ekrānu un paziņojumus ar ieejas frāzi Ekrāna aizsardzība @@ -3605,6 +3621,7 @@ LED mirgošanas biežums Pielāgot Mainīt skaņu un vibrāciju + Skaņa Klusums Noklusējuma @@ -5505,6 +5522,7 @@ Pievienot stāstam Pievienojiet ziņu + Pievienot atbildi Sūtīt Vienreiz skatāma multivide @@ -6114,6 +6132,8 @@ %1$d atbilde %1$d atbildes + + Story no longer hidden Pievienot @@ -7425,11 +7445,27 @@ - Lai lejupielādētu multividi, atbrīvojiet krātuvē vietu (%1$s). + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + - Google Play + Google Play Kredītkarte vai debetkarte @@ -7488,19 +7524,15 @@ Ok - - - Maksājumu vēsture - - Teksta un visas multivides rezerves kopija - - Maksājuma informācija - - Rezerves kopijas veids - - Samaksas datums - - Kopīgot + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7515,6 +7547,10 @@ Rezerves kopijas izveides biežums Izveidot rezerves kopiju, izmantojot mobilos datus + + View backup key + + Unlock to view backup key Izslēgt rezerves kopijas izveidi un dzēst to @@ -7528,13 +7564,27 @@ Rezerves kopija tiks izveidota pa nakti. - Rezerves kopijas veids + Backup plan Rezerves kopijas ir atspējotas - - %1$s · %2$s/mēnesī - - Iespējot rezerves kopēšanu + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7584,33 +7634,33 @@ - Your backup key + Rezerves kopijas atslēga - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Rezerves kopijas atslēga ir 64 ciparu kods, kas ļauj atjaunot rezerves kopiju, instalējot Signal atkārtoti. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ja aizmirsīsiet atslēgu, nevarēsiet atjaunot rezerves kopiju. Signal nevar palīdzēt atgūt rezerves kopiju. - Next + Tālāk - Record your backup key + Saglabājiet rezerves kopijas atslēgu - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Šī atslēga ir nepieciešama, lai atgūtu jūsu kontu un datus. Noglabājiet šo atslēgu drošā vietā. Ja to pazaudēsiet, nevarēsiet atgūt savu kontu. - Copy to clipboard + Kopēt starpliktuvē - Next + Tālāk - Keep your key safe + Glabājiet atslēgu drošā vietā - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Ja pazaudēsiet atslēgu, Signal nevarēs palīdzēt atjaunot rezerves kopiju. Glabājiet atslēgu drošā vietā un neizpaudiet to citiem. - I\'ve recorded my key + Saglabāju atslēgu - Continue + Turpināt - See key again + Skatīt atslēgu vēlreiz diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 8a95ef48f0..55064b6c9f 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Да Не Избриши @@ -260,6 +263,7 @@ Допрете за фотографија, држете за видео + Сними Смени камера Отвори галерија @@ -346,7 +350,7 @@ Ова не е валиден линк за повик. Бидете сигурни дека линкот е целосен и точен пред да пробате да се вклучите. - You are already in a call + Веќе сте на повик @@ -403,6 +407,7 @@ Не е пронајдена апликација која може да го отвори овој тип на медиумска датотека. Копирано %1$s од %1$s + до %1$s   Прочитај повеќе   Преземи повеќе @@ -436,6 +441,7 @@ Испрати ја измената Напишете порака Извинете, се појави грешка при прикачување на прилогот. + Адресата не е валидна SMS или e-mail адреса! Пораката е празна! Членови на групата @@ -576,6 +582,7 @@ Зачувувам %1$d прилози во складот… На чекање… + Интернет (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Да го одврзам \'%1$s\'? Ако го одврзете овој уред истиот повеќе нема да може да праќа и прима пораки. Мрежната врска е неуспешна + Обиди се повторно Одврзувам уред… Одврзувам уред @@ -1408,7 +1416,7 @@ Групен линк Сподели Ресетирај линк - Require admin approval + Барај одобрение од администратор Администраторот мора да ги одобри новите членови кои се приклучуваат преку групниот линк. Дали сте сигурни дека сакате да го ресетирате групниот линк? Луѓето повеќе нема да можат да се приклучат на групата со употреба на тековниот линк. @@ -1507,6 +1515,7 @@ Испрати %1$d SMS покани? Ајде да се префрлиме на Signal: %1$s + Изгледа дека немате апликации преку кои може да споделите. @@ -1931,6 +1940,7 @@ Сакате да дозволите %1$s да може да ви пишува и да ви ги гледа името и сликата? Нема да добивате пораки додека не го дебклокирате овој контакт. Сакате да дозволите %1$s да може да ви пишува? Нема да добивате пораки додека не го дебклокирате овој контакт. + Сакате да добивате новости од %1$s? Нема да добивате новости додека не го деблокирате овој контакт. Continue your chat with this group and share your name and photo with its members? Оваа застарена група не може да се користи повеќе. Создаjте нова група за да активирате функции како @спомнувања и администратори. @@ -2245,6 +2255,7 @@ Нема никој %1$s е на овој повик + %1$s е на овој повик %1$s и %2$s се на овој повик @@ -2647,7 +2658,7 @@ Ќе се обидеме повторно подоцна. Signal се ажурираше успешно Апликацијата автоматски се ажурираше на верзија %1$s. - You updated to version %1$s. + Ажуриравте на верзија %1$s. Да се испрати пораката? @@ -3067,6 +3078,7 @@ Сликичка на прилогот Вклучи/исклучи фиока за прилози за брза камера Сними и испрати аудио прилог + Заклучи снимање на аудио прилог Пораката не може да биде испратена. Проверете ја Вашата интернет конекција и обидете се повторно. @@ -3085,6 +3097,7 @@ Пораката е прочитана + Слика на контакт @@ -3169,6 +3182,7 @@ Сѐ изгледа добро сега! За да примате известувања за повици, допрете овде и вклучете „Прикажи известувања“. За да примате известувања за повици, допрете овде и вклучете ги известувањата и проверете дали звук и скокачки прозорци се вклучени. + За да примате известувања за повици, допрете овде и вклучете „активност во заднина“ во поставувањата за \"Батерија\". Поставувања За да примате известувања за повици, допрете на Поставувања и вклучете „Прикажи известувања“. @@ -3306,7 +3320,7 @@ На пауза Работам на нешто ново - One or more characters is invalid. + Еден или повеќе знаци беа неважечки. Уреди група @@ -3405,6 +3419,7 @@ + Информации за поддршка Барање за поддршка за Signal Android @@ -3493,6 +3508,7 @@ Преземај прегледи за линкови директно од веб страниците за пораките што ги испраќате. Промена на лозинка Променете ја Вашата лозинка + Вклучи заклучување на екран со лозинка Заклучи ги екранот и известувањата со лозинка Безбедност на екранот @@ -3505,6 +3521,7 @@ LED шема за трепкање Прилагоди Промена на звук и вибрација + Звук Тивко Стандардно @@ -5378,6 +5395,7 @@ Додајте како приказна Додај порака + Додај одговор Испрати на Еднократно видлива медиумска датотека @@ -5982,6 +6000,8 @@ %1$d одговор %1$d одговори + + Story no longer hidden Додај @@ -7267,7 +7287,23 @@ - Ослободете простор од %1$s за да ги преземете вашите медиумски датотеки. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Во ред - - - Историја на плаќања - - Резервна копија на сите текстуални пораки и медиумски датотеки - - Детали за плаќањето - - Вид на резервна копија - - Датум на плаќање - - Сподели + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Зачестеност на правење резервни копии Правење резервна копија користејќи мобилен интернет + + View backup key + + Unlock to view backup key Исклучи и избриши ги резервните копии @@ -7370,13 +7406,27 @@ Резервната копија ќе се прави во текот на ноќта. - Вид на резервна копија + Backup plan Резервните копии се оневозможени - - %1$s · %2$s/месечно - - Вклучи резервна копија + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Вашиот клуч за резервни копии - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Вашиот клуч за резервни копии е код од 64 цифри којшто ви помага да ги вратите податоците од резервната копија кога ќе ја реинсталирате Signal апликацијата. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ако го заборавите вашиот клуч, нема да можете да ги вратите податоците од резервната копија. Signal нема да може да ви помогне да ги вратите податоците од резервната копија. - Next + Следно - Record your backup key + Направете запис од вашиот клуч за резервни копии - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Овој клуч е потребен за враќање на вашата корисничка сметка и податоците. Чувајте го овој клуч на безбедно место. Ако го изгубите, нема да може да ја вратите вашата корисничка сметка. - Copy to clipboard + Копирај на таблата со исечоци - Next + Следно - Keep your key safe + Чувајте го клучот на безбедно место - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal нема да може да ви помогне да ги вратите податоците од резервната копија ако го заборавите вашиот клуч. Чувајте го на безбедно место и не го споделувајте со други лица. - I\'ve recorded my key + Имам запис од мојот клуч - Continue + Продолжи - See key again + Повторно видете го клучот diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index bfc792de32..a877cee0b5 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + അതെ/ഉവ്വ് അല്ല/ഇല്ല ഇല്ലാതാക്കൂ @@ -260,6 +263,7 @@ ഫോട്ടോയ്‌ക്കായി അമർത്തുക, വീഡിയോയ്‌ക്കായി പിടിക്കുക + ക്യാപ്‌ചർ ചെയ്യുക ക്യാമറ മാറ്റുക ഗാലറി തുറക്കുക @@ -346,7 +350,7 @@ ഇത് സാധുതയുള്ളൊരു കോൾ ലിങ്കല്ല. ചേരാൻ ശ്രമിക്കുന്നതിന് മുമ്പ് മുഴുവൻ ലിങ്കും ശരിയാണെന്ന് ഉറപ്പ് വരുത്തുക. - You are already in a call + നിങ്ങൾ ഇതിനകം ഒരു കോളിൽ ഉണ്ട് @@ -403,6 +407,7 @@ ഈ മീഡിയ തുറക്കാൻ കഴിയുന്ന ഒരു ആപ്പ് കണ്ടെത്താനായില്ല. %1$s പകർത്തി %1$s മുതൽ + %1$s വരെ   കൂടുതൽ വായിക്കുക  കൂടുതൽ ഡൗൺലോഡു ചെയ്യുക @@ -436,6 +441,7 @@ എഡിറ്റ് ചെയ്തത് അയയ്ക്കുക സന്ദേശം രചിക്കുക ക്ഷമിക്കണം, നിങ്ങളുടെ അറ്റാച്ചുമെന്റ് ക്രമീകരിക്കുന്നതിൽ ഒരു പിശക് ഉണ്ടായിരുന്നു. + സ്വീകർത്താവ് സാധുവായ ഒരു SMS അല്ലെങ്കിൽ ഇമെയിൽ വിലാസമല്ല! സന്ദേശം ശൂന്യമാണ്! ഗ്രൂപ്പിലെ അംഗങ്ങൾ @@ -576,6 +582,7 @@ %1$d അറ്റാച്ച്‌മെന്റുകൾ സ്റ്റോറേജിലേക്ക് സംരക്ഷിക്കുന്നു… ശേഷിക്കുന്നു… + ഡാറ്റ (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \'%1$s\' വിച്ഛേദിക്കണോ? ഈ ഉപകരണം അൺലിങ്കുചെയ്യുന്നതിലൂടെ, ഇതിന് മേലിൽ സന്ദേശങ്ങൾ അയയ്‌ക്കാനോ സ്വീകരിക്കാനോ കഴിയില്ല. നെറ്റ്‌വർക്ക് കണക്ഷൻ പരാജയപ്പെട്ടു + വീണ്ടും ശ്രമിക്കുക ഉപകരണം വിച്ഛേദിക്കുന്നൂ… ഉപകരണം വിച്ഛേദിക്കുന്നൂ… @@ -1408,7 +1416,7 @@ ഗ്രൂപ്പിന്റെ ലിങ്ക് പങ്കിടുക ലിങ്ക് പുനഃസജ്ജമാക്കുക്ക - Require admin approval + അഡ്മിൻ അംഗീകാരം ആവശ്യമാണ് ഗ്രൂപ്പ് ലിങ്ക് വഴി ചേരുന്ന പുതിയ അംഗങ്ങളെ അംഗീകരിക്കുന്നതിന് ഒരു അഡ്മിൻ ആവശ്യമാണ്. ഗ്രൂപ്പ് ലിങ്ക് പുനസജ്ജമാക്കണമെന്ന് നിങ്ങള്‍ക്ക് ഉറപ്പാണ‍ോ? നിലവിലെ ലിങ്ക് ഉപയോഗിച്ച് ആളുകൾക്ക് മേലിൽ ഗ്രൂപ്പിൽ ചേരാനാവില്ല. @@ -1507,6 +1515,7 @@ %1$d SMS ക്ഷണങ്ങൾ അയയ്‌ക്കണോ? Signal ലേക്ക് മാറാം: %1$s + നിങ്ങൾക്ക് പങ്കിടാൻ അപ്ലിക്കേഷനുകളൊന്നുമില്ലെന്ന് തോന്നുന്നു. @@ -1931,6 +1940,7 @@ നിങ്ങൾക്ക് മെസേജ് അയയ്ക്കാൻ %1$s എന്നയാളെ അനുവദിക്കണോ? നിങ്ങൾ അവരെ അൺബ്ലോക്ക് ചെയ്യുന്നത് വരെ നിങ്ങൾക്ക് മെസേജ് ഒന്നും ലഭിക്കില്ല. %1$s എന്നയാളെ നിങ്ങൾക്ക് സന്ദേശം അയയ്ക്കാൻ അനുവദിക്കണോ? നിങ്ങൾ അൺബ്ലോക്ക് ചെയ്യുന്നത് വരെ നിങ്ങൾക്ക് സന്ദേശങ്ങളൊന്നും ലഭിക്കില്ല. + %1$s എന്നതിൽ നിന്ന് സമകാലികവിവരങ്ങളും വാര്‍ത്തകളും ലഭിക്കണോ? നിങ്ങള്‍ അവരെ അണ്‍ബ്ലോക്ക് ചെയുന്നതു വരെ സമകാലിക വിവരങ്ങളൊന്നും ലഭിക്കില്ല. ഈ ഗ്രൂപ്പുമായുള്ള സംഭാഷണം തുടരുകയും നിങ്ങളുടെ പേരും ഫോട്ടോയും അതിന്റെ അംഗങ്ങളുമായി പങ്കിടുകയും ചെയ്യണോ? ഈ ലെഗസി ഗ്രൂപ്പ് ഇനി ഉപയോഗിക്കാനാകില്ല. @പരാമർശങ്ങളും അഡ്‌മിനുകളും പോലുള്ള പുതിയ സവിശേഷതകൾ സജീവമാക്കാൻ ഒരു പുതിയ ഗ്രൂപ്പ് സൃഷ്‌ടിക്കുക. @@ -2245,6 +2255,7 @@ മറ്റാരുമില്ല %1$s ഈ കോളിലുണ്ട് + %1$s പേർ ഈ കോളിലുണ്ട് %1$s - ഉം %2$s - ഉം ഈ കോളിലുണ്ട്  @@ -2647,7 +2658,7 @@ ഞങ്ങൾ പിന്നീട് വീണ്ടും ശ്രമിക്കും. Signal അപ്ഡേറ്റ് ആയി %1$s പതിപ്പിലേക്ക് ഓട്ടോമാറ്റിക്കായി അപ്ഡേറ്റ് ആയി. - You updated to version %1$s. + നിങ്ങൾ പതിപ്പ് %1$s -ലേക്ക് അപ്‌ഡേറ്റ് ചെയ്‌തു. സന്ദേശം അയക്കണോ? @@ -3067,6 +3078,7 @@ അറ്റാച്ചുമെന്റ് ലഘുചിത്രം ദ്രുത ക്യാമറ അറ്റാച്ചുമെന്റ് ഡ്രോയറിലേക്ക് മാറുക ഓഡിയോ അറ്റാച്ചുമെന്റ് റെക്കോർഡുചെയ്‌ത് അയയ്‌ക്കുക + ഓഡിയോ അറ്റാച്ചുമെന്റിന്റെ റെക്കോർഡിംഗ് ലോക്ക് ചെയ്യുക സന്ദേശം അയയ്ക്കാൻ കഴിഞ്ഞില്ല. നിങ്ങളുടെ കണക്ഷൻ പരിശോധിച്ച് വീണ്ടും ശ്രമിക്കുക. @@ -3085,6 +3097,7 @@ സന്ദേശം വായിച്ചു + കോൺ‌ടാക്റ്റിന്റെ ഫോട്ടോ @@ -3169,6 +3182,7 @@ എല്ലാം ഇപ്പോൾ നന്നായി തോന്നുന്നു! കോൾ അറിയിപ്പുകൾ സ്വീകരിക്കുന്നതിന്, ഇവിടെ തൊടുക കൂടാതെ \"അറിയിപ്പുകൾ കാണിക്കുക\" ഓണാക്കുക. കോൾ അറിയിപ്പുകൾ സ്വീകരിക്കുന്നതിന്, ഇവിടെ തൊടുക, അറിയിപ്പുകൾ ഓണാക്കുക കൂടാതെ സൗണ്ട്, പോപ്പ്-അപ്പ് എന്നിവ പ്രാപ്തമാണെന്ന് ഉറപ്പാക്കുക. + കോൾ അറിയിപ്പുകൾ സ്വീകരിക്കുന്നതിന്, ഇവിടെ തൊടുക കൂടാതെ \"ബാറ്ററി\" ക്രമീകരണങ്ങളിൽ പശ്ചാത്തല പ്രവർത്തനം പ്രാപ്തമാക്കുക. ക്രമീകരണങ്ങൾ കോൾ അറിയിപ്പുകൾ സ്വീകരിക്കുന്നതിന്, ക്രമീകരണങ്ങൾ തൊടുക കൂടാതെ \"അറിയിപ്പുകൾ കാണിക്കുക\" ഓണാക്കുക. @@ -3306,7 +3320,7 @@ ഒരു ഇടവേളയിലാണ് പുതിയ ഒരു കാര്യം ചെയ്യുന്നു - One or more characters is invalid. + ഒന്നോ അതിലധികമോ പ്രതീകങ്ങൾ അസാധുവാണ്. ഗ്രൂപ്പ് എഡിറ്റുചെയ്യുക @@ -3405,6 +3419,7 @@ + പിന്തുണ വിവരം Signal Android പിന്തുണ അഭ്യർത്ഥന @@ -3493,6 +3508,7 @@ നിങ്ങൾ അയയ്‌ക്കുന്ന സന്ദേശങ്ങൾക്കായി വെബ്‌സൈറ്റുകളിൽ നിന്ന് ലിങ്ക് പ്രിവ്യൂകൾ നേരിട്ട് വീണ്ടെടുക്കുക. രഹസ്യവാചകം മാറ്റുക നിങ്ങളുടെ രഹസ്യവാചകം മാറ്റുക + പാസ്‌ഫ്രെയ്‌സ് സ്‌ക്രീൻ ലോക്ക് പ്രവർത്തനക്ഷമമാക്കുക ഒരു പാസ്‌ഫ്രെയ്‌സ് ഉപയോഗിച്ച് സ്‌ക്രീനും അറിയിപ്പുകളും ലോക്കുചെയ്യുക സ്‌ക്രീൻ സുരക്ഷ @@ -3505,6 +3521,7 @@ LED ബ്ലിങ്ക് പാറ്റേൺ ഇഷ്ടാനുസൃതമാക്കുക ശബ്ദവും വൈബ്രേഷനും മാറ്റുക + ശബ്ദം നിശബ്ദത സ്ഥിരസ്ഥിതി @@ -5378,6 +5395,7 @@ സ്റ്റോറിയിലേക്ക് ചേർക്കുക ഒരു സന്ദേശം ചേര്‍ക്കുക + ഒരു മറുപടി ചേർക്കുക ഇനിപ്പറയുന്നയാൾക്ക് അയയ്ക്കുക ഒറ്റത്തവണ വീഡിയോ കാണുക @@ -5982,6 +6000,8 @@ %1$d മറുപടി %1$d മറുപടികൾ + + Story no longer hidden ചേർക്കുക @@ -7267,7 +7287,23 @@ - നിങ്ങളുടെ മീഡിയ ഡൗൺലോഡ് ചെയ്യാൻ %1$s ഇടം സൃഷ്‌ടിക്കുക. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ശരി - - - പേയ്മെൻ്റ് ചരിത്രം - - വാചകവും എല്ലാ മീഡിയ ബാക്കപ്പും - - പേയ്മെന്റ് വിശദാംശങ്ങൾ - - ബാക്കപ്പ് തരം - - പണം നൽകിയ തീയതി - - പങ്കിടുക + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ ബാക്കപ്പ് ആവൃത്തി മൊബൈൽ ഡാറ്റ ഉപയോഗിച്ച് ബാക്കപ്പ് ചെയ്യുക + + View backup key + + Unlock to view backup key ബാക്കപ്പ് ഓഫാക്കി ഇല്ലാതാക്കുക @@ -7370,13 +7406,27 @@ ഒറ്റരാത്രികൊണ്ട് ബാക്കപ്പ് സൃഷ്ടിക്കപ്പെടും. - ബാക്കപ്പ് തരം + Backup plan ബാക്കപ്പുകൾ പ്രവർത്തനരഹിതമാക്കി - - %1$s · %2$s/മാസം - - ബാക്കപ്പുകൾ പ്രവർത്തനക്ഷമമാക്കുക + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + നിങ്ങളുടെ ബാക്കപ്പ് കീ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + നിങ്ങൾ Signal വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുമ്പോൾ ബാക്കപ്പ് പുനഃസ്ഥാപിക്കാൻ നിങ്ങളെ അനുവദിക്കുന്ന 64 അക്ക കോഡാണ് നിങ്ങളുടെ ബാക്കപ്പ് കീ. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + നിങ്ങളുടെ കീ മറന്നുപോയാല്‍, നിങ്ങളുടെ ബാക്കപ്പ് പുനഃസ്ഥാപിക്കാൻ നിങ്ങൾക്ക് കഴിയില്ല. നിങ്ങളുടെ ബാക്കപ്പ് വീണ്ടെടുക്കാൻ Signal-ന് നിങ്ങളെ സഹായിക്കാനാകില്ല. - Next + അടുത്തത് - Record your backup key + നിങ്ങളുടെ ബാക്കപ്പ് കീ രേഖപ്പെടുത്തുക - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + നിങ്ങളുടെ അക്കൗണ്ടും ഡാറ്റയും വീണ്ടെടുക്കാൻ ഈ കീ ആവശ്യമാണ്. ഈ കീ സുരക്ഷിതമായ ഒരിടത്ത് സൂക്ഷിക്കുക. നിങ്ങൾക്കത് നഷ്‌ടപ്പെടുകയാണെങ്കിൽ, നിങ്ങളുടെ അക്കൗണ്ട് വീണ്ടെടുക്കാൻ നിങ്ങൾക്ക് കഴിയില്ല. - Copy to clipboard + ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തൂ - Next + അടുത്തത് - Keep your key safe + നിങ്ങളുടെ കീ സുരക്ഷിതമായി സൂക്ഷിക്കുക - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + നിങ്ങളുടെ കീ നഷ്‌ടപ്പെടുകയാണെങ്കിൽ, നിങ്ങളുടെ ബാക്കപ്പ് പുനഃസ്ഥാപിക്കാൻ നിങ്ങളെ സഹായിക്കാൻ Signal-ന് കഴിയില്ല. സുരക്ഷിതമായും ഭദ്രമായും എവിടെയെങ്കിലും സൂക്ഷിക്കുക, മറ്റുള്ളവരുമായി പങ്കിടരുത്. - I\'ve recorded my key + ഞാൻ എൻ്റെ കീ രേഖപ്പെടുത്തി - Continue + തുടരുക - See key again + കീ വീണ്ടും കാണുക diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index bd3ec191ef..0afd89ea22 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + हो नाही हटवा @@ -260,6 +263,7 @@ फोटो करिता टॅप करा, व्हिडिओ करिता धरून ठेवा + कॅप्चर करा कॅमेरा बदला गॅलरी उघडा @@ -346,7 +350,7 @@ ही एक वैध कॉल लिंक नाही. संपूर्ण लिंक एकत्रित असल्याची खात्री करा आणि जॉईन होण्याच प्रयत्न करण्यापूर्वी दुरूस्त करा. - You are already in a call + आपण आधीच एक कॉलमध्ये आहात @@ -403,6 +407,7 @@ हे मिडिया उघडण्यास सक्षम असे अॅप सापडू शकले नाही. प्रत बनवली %1$s %1$s पासून + %1$s पर्यंत  अधिक जाणा   अधिक डाऊनलोड करा @@ -436,6 +441,7 @@ संपादन पाठवा संदेश लिहा क्षमस्व, आपल्या संलग्न मध्ये एक त्रुटी होती. + प्राप्तकर्ता एक वैध SMS किंवा ईमेल ऍड्रेस नाही! संदेश रिक्त आहे! गट सदस्य @@ -576,6 +582,7 @@ %1$d संलग्नके संग्रहणामध्ये जतन करत आहे… प्रलंबित… + डेटा (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \'%1$s\' अनलिंक करायचे? हा डिव्हाईस अनलिंक केल्याने, तो त्यापुढे संदेश पाठविण्यास किंवा प्राप्त करण्यास सक्षम नसेल, नेटवर्क कनेक्शन अयशस्वी + पुन्हा प्रयत्न करा डिव्हाईस अनलिंक करत आहे… डिव्हाईस अनलिंक करत आहे @@ -1408,7 +1416,7 @@ गट लिंक सामायिक करा लिंक रीसेट करा - Require admin approval + प्रशासकाची मंजुरी आवश्यक आहे नवीन सदस्यांना गट लिंक द्वारा सामील होण्यासाठी ऍडमिन स्वीकार करणे आवश्यक आहे. गट लिंक रीसेट करण्याची आपणास खात्री आहे का? वर्तमान लिंक वापरून लोक यापुढे गटात सामील होऊ शकणार नाहीत. @@ -1507,6 +1515,7 @@ %1$d SMS आमंत्रणे पाठवायची? Signal वर स्विच करूया: %1$s + असे दिसते की आपल्याकडे सामायिक करण्यासाठी कुठलेही अॅप नाहीत. @@ -1931,6 +1940,7 @@ %1$s ला आपल्याला संदेश पाठवू आणि आपले नाव आणि फोटो त्यांच्यासह सामायिक करू द्यायचे? आपण त्यांना अनावरोधित करेपर्यंत आपल्याला कोणतेही संदेश प्राप्त होणार नाहीत. %1$s ला आपल्याला संदेश पाठवू द्यायचे? जोपर्यंत आपण त्यांना अनावरोधित करत नाही तोपर्यंत आपल्याला कोणतेही संदेश प्राप्त होणार नाहीत. + %1$s कडून अद्यतने आणि बातम्या मिळवायच्या? आपण त्यांना अनावरोधित करेपर्यंत आपल्याला कोणतीही अद्यतने मिळणार नाहीत. आपले या गटासोबतचे चॅट सुरू ठेवायचे आणि त्याच्या सदस्यांसह आपले नाव आणि फोटो सामायिक करायचे? हा वारसा गट यापुढे वापरला जाऊ शकणार नाही. @mentions आणि प्रशासक यांसारखी नवीन वैशिष्ट्ये सक्रिय करण्यासाठी एक नवीन गट तयार करा. @@ -2245,6 +2255,7 @@ येथे अजून कुणीही नाही %1$s या कॉलमध्ये आहे + %1$s या काॅल मध्ये आहे %1$s आणि %2$s या कॉलमध्ये आहेत @@ -2647,7 +2658,7 @@ आपण नंतर पुन्हा प्रयत्न करू. Signal यशस्वीरित्या अद्ययावत झाले आपण %1$s आवृत्तीवर आपोआप अद्ययावत झाला आहात. - You updated to version %1$s. + आपण आवृत्ती %1$s वर अद्यतनित केले आहे. संदेश पाठवायचा? @@ -3067,6 +3078,7 @@ संलग्न थंबनेल जलद कॅमेरा संलग्न ड्रॉवर टॉगल करा ऑडिओ संलग्न रेकॉर्ड करा आणि पाठवा + ऑडिओ संलग्नचे रेकॉर्डिंग लॉक करा संदेश पाठवला जाऊ शकला नाही. आपले कनेक्शन तपासा आणि पुन्हा प्रयत्न करा. @@ -3085,6 +3097,7 @@ संदेश वाचला + संपर्क फोटो @@ -3169,6 +3182,7 @@ आता सगळे छान दिसते! कॉल सूचना प्राप्त करण्यासाठी, येथे टॅप करा आणि \"सूचना दाखवा\" चालू करा. कॉल सूचना प्राप्त करण्यासाठी, येथे टॅप करा आणि सूचना चालू करा आणि ध्वनी आणि पॉप-अप सक्षम केले असल्याची खात्री करा. + कॉल सूचना प्राप्त करण्यासाठी, येथे टॅप करा आणि \"बॅटरी\" सेटिंगमध्ये पार्श्वभूमी अॅक्टिव्हिटी सक्षम करा. सेटिंग कॉल सूचना प्राप्त करण्यासाठी, सेटिंगवर टॅप करा आणि \"सूचना दाखवा\" चालू करा. @@ -3306,7 +3320,7 @@ आराम करत आहे काहीतरी नवीन काम करत आहे - One or more characters is invalid. + एक किंवा अधिक वर्ण अवैध आहेत. गट संपादन करा @@ -3405,6 +3419,7 @@ + समर्थन माहिती Signal Android सपोर्ट विनंती @@ -3493,6 +3508,7 @@ तुम्ही पाठवलेल्या संदेशांसाठी थेट कोणत्याही वेबसाइटवरून लिंक पूर्वावलोकने प्राप्त करू शकता. पासफ्रेझ बदला आपला पासफ्रेझ बदला + पासफ्रेझ स्क्रीन लॉक सक्षम करा स्क्रीन आणि सूचना पासफ्रेजने लॉक करा स्क्रीन सुरक्षा @@ -3505,6 +3521,7 @@ LED ब्लिंक पद्धत सानुकूलित करा आवाज आणि कंपन बदला + ध्वनी शांत पूर्वनिर्धारित @@ -5378,6 +5395,7 @@ स्टोरीत जोडा संदेश जोडा + प्रत्युत्तर जोडा यांना पाठवा एकदा मिडिया पहा @@ -5982,6 +6000,8 @@ %1$d प्रत्त्युत्तर %1$d प्रत्त्युत्तरे + + Story no longer hidden जोडा @@ -7267,7 +7287,23 @@ - आपली मिडीया डाउनलोड करण्यसाठी %1$s जागा रिकामी करा. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ठीक - - - पेमेंट्स इतिहास - - मजकूर आणि सर्व मिडीया बॅकअप करा - - पेमेंट तपशील - - बॅकअप प्रकार - - तारीख भरली - - सामायिक करा + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ बॅकअप वारंवारता मोबाइल वापरून बॅकअप करा + + View backup key + + Unlock to view backup key बॅकअप बंद करा आणि हटवा @@ -7370,13 +7406,27 @@ रात्रभर बॅकअप तयार केला जाईल. - बॅकअप प्रकार + Backup plan बॅकअप अक्षम केले - - %1$s · %2$s/महिना - - बॅकअप सक्षम करा + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + आपली बॅकअप की - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + आपली बॅकअप की हा एक 64-अंकी कोड असून तो आपण जेव्हा Signal पुन्हा-इन्स्टॉल करता तेव्हा आपणाला आपला बॅकअप पुर्नस्थापित करू देते. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + आपण आपली पिन विसरल्यास आपण आपला बॅकअप पुर्नस्थापित करू शकणार नाही. Signal आपणाला आपला बॅकअप पुर्नस्थापित करण्यास मदत करू शकणार नाही. - Next + पुढे - Record your backup key + आपली बॅकअप कि नोंद करा - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + ही की आपले अकाऊंट आणि डेटा पुर्नप्राप्त करण्यासाठी आवश्यक आहे. ही की कोठेतरी सुरक्षित ठेवा. आपण जी ती गहाळ केल्यास, आपण आपले अकाऊंट पुर्नप्राप्त करू शकणार नाही. - Copy to clipboard + क्लिपबोर्ड वर कॉपी करा - Next + पुढे - Keep your key safe + आपली की सुरक्षित ठेवा - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + आपण जर आपली की गमावल्यास आपणाला आपला बॅकअप पुनर्संचयित करण्यात Signal आपणाला मदत करू शकणार नाही. त्याला कोठेतरी सुरक्षित आणि संरक्षित संग्रहित करून ठेवा, आणि ती इतरांसोबत शेअर करू नका. - I\'ve recorded my key + मी माझी की रेकॉर्ड करून ठेवला आहे - Continue + सुरू ठेवा - See key again + की पुन्हा पहा diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index a863478985..a4d8b6047d 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ya Tidak Padam @@ -258,6 +261,7 @@ Ketik untuk foto, tekan untuk video + Tangkap Tukar kamera Buka geleri @@ -343,7 +347,7 @@ Ini bukan pautan panggilan yang sah. Pastikan keseluruhan pautan tidak terjejas dan betul sebelum cuba menyertai. - You are already in a call + Anda sudah berada dalam panggilan @@ -400,6 +404,7 @@ Tidak dapat mencari aplikasi untuk membuka media ini. Menyalin %1$s daripada %1$s + kepada %1$s  Baca selanjutnya  Muat turun selanjutnya @@ -433,6 +438,7 @@ Edit hantar Karang mesej Maaf, terdapat ralat menetapkan lampiran anda. + Penerima bukan SMS atau alamat emel yang sah! Mesej kosong! Ahli kumpulan @@ -565,6 +571,7 @@ Menyimpan %1$d lampiran ke storan… Menunggu… + Data (Signal) MMS SMS @@ -986,6 +993,7 @@ Nyahpaut \'%1$s Dengan menyahpautkan peranti ini, ia tidak akan dapat menghantar atau menerima mesej lagi. Gagal menyambung rangkaian + Cuba lagi Menyahpaut peranti… Menyahpaut peranti @@ -1368,7 +1376,7 @@ Pautan kumpulan Kongsi Pautan tetapan semula - Require admin approval + Memerlukan Kelulusan Pentadbir Memerlukan pentadbir untuk meluluskan ahli baharu yang menyertai kumpulan melalui pautan kumpulan. Adakah anda pasti mahu menetapkan semula pautan kumpulan? Orang tidak dapat lagi menyertai kumpulan menggunakan pautan semasa. @@ -1464,6 +1472,7 @@ Hantar %1$d jemputan SMS? Mari tukar ke Signal: %1$s + Nampaknya anda tidak mempunyai sebarang aplikasi untuk dikongsi. @@ -1873,6 +1882,7 @@ Benarkan %1$s menghantar mesej kepada anda dan kongsikan nama dan foto anda dengan mereka? Anda tidak akan menerima sebarang mesej sehingga anda menyahsekatnya. Benarkan %1$s mesej anda? Anda tidak akan menerima sebarang mesej sehingga anda menyahsekat mereka. + Dapatkan kemas kini dan berita daripada %1$s? Anda tidak akan menerima sebarang kemas kini sehingga anda menyahsekat mereka. Teruskan sembang anda dengan kumpulan ini dan kongsikan nama dan foto anda dengan ahli-ahlinya? Kumpulan Legasi ini tidak boleh digunakan lagi. Buat kumpulan baharu untuk mengaktifkan ciri baharu seperti @sebutan dan pentadbir. @@ -2171,6 +2181,7 @@ Tiada seorang pun di sini %1$s berada dalam panggilan ini + %1$s ada dalam panggilan ini %1$s dan %2$s berada dalam panggilan ini @@ -2562,7 +2573,7 @@ Kita akan cuba lagi nanti. Signal berjaya dikemas kini Anda telah dikemas kini secara automatik kepada versi %1$s. - You updated to version %1$s. + Anda mengemas kini kepada versi %1$s. Hantar mesej? @@ -2975,6 +2986,7 @@ Imej kecil lampiran Togol laci lampiran kamera pantas Rekod dan hantar lampiran audio + Kunci lampiran audio merekod Mesej tidak dapat dihantar. Periksa sambungan anda dan cuba lagi. @@ -2993,6 +3005,7 @@ Mesej dibaca + Foto kenalan @@ -3077,6 +3090,7 @@ Semuanya kelihatan baik sekarang! Untuk menerima pemberitahuan panggilan, ketik sini dan hidupkan \"Tunjukkan pemberitahuan\". Untuk menerima pemberitahuan panggilan, ketik sini dan hidupkan pemberitahuan dan pastikan Bunyi dan Tetingkap Timbul didayakan. + Untuk menerima pemberitahuan panggilan, ketik sini dan dayakan aktiviti latar belakang dalam tetapan \"Bateri\". Tetapan Untuk menerima pemberitahuan panggilan, ketik Tetapan dan hidupkan \"Tunjukkan pemberitahuan\". @@ -3207,7 +3221,7 @@ Berehat sebentar Mengusahakan sesuatu yang baharu - One or more characters is invalid. + Satu atau lebih aksara tidak sah. Edit kumpulan @@ -3306,6 +3320,7 @@ + Maklumat Sokongan Permintaan Sokongan Signal Android @@ -3393,6 +3408,7 @@ Dapatkan pratonton pautan terus daripada laman web untuk mesej yang anda hantar. Tukar frasa laluan Tukar frasa laluan anda + Dayakan frasa laluan kunci skrin Kunci skrin dan pemberitahuan dengan frasa laluan Keselamatan skrin @@ -3405,6 +3421,7 @@ Corak kelip LED Sesuaikan Tukar bunyi dan getaran + Bunyi Senyap Lalai @@ -5251,6 +5268,7 @@ Tambah ke cerita Tambah mesej + Tambah balasan Hantar kepada Media lihat sekali @@ -5850,6 +5868,8 @@ %1$d balasan + + Story no longer hidden Tambah @@ -7109,7 +7129,23 @@ - Kosongkan %1$s ruang untuk muat turun media anda. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ OK - - - Sejarah pembayaran - - Teks dan semua sandaran media - - Butiran pembayaran - - Jenis sandaran - - Tarikh bayar - - Kongsi + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ Kekerapan sandaran Sandarkan menggunakan selular + + View backup key + + Unlock to view backup key Matikan dan padamkan sandaran @@ -7212,13 +7248,27 @@ Sandaran akan dibuat semalaman. - Jenis sandaran + Backup plan Sandaran dilumpuhkan - - %1$s · %2$s/bulan - - Mendayakan sandaran + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + Kunci sandaran anda - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Kunci sandaran anda ialah kod 64 digit yang boleh memulihkan sandaran apabila anda memasang semula Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Jika terlupa PIN, anda tidak akan dapat memulihkan sandaran. Signal tidak dapat membantu memulihkan sandaran anda. - Next + Seterusnya - Record your backup key + Rekodkan kunci sandaran anda - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Kunci ini diperlukan untuk memulihkan akaun dan data anda. Simpan kunci ini di tempat yang selamat. Jika anda kehilangannya, anda tidak akan dapat memulihkan akaun anda. - Copy to clipboard + Salin ke papan klip - Next + Seterusnya - Keep your key safe + Pastikan kunci anda selamat - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal tidak akan dapat membantu memulihkan sandaran jika anda kehilangan kunci. Simpannya di tempat yang selamat dan jangan kongsikannya dengan orang lain. - I\'ve recorded my key + Saya telah merekodkan kunci saya - Continue + Teruskan - See key again + Lihat kunci lagi diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 0201228262..5726e1b962 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + ဟုတ်ကဲ့ မလုပ်ပါ ဖျက်ရန် @@ -258,6 +261,7 @@ ဓါတ်ပုံအတွက် နှိပ်ပါ။ ဗီဒီယိုအတွက် ဖိထားပါ + ဓာတ်ပုံရိုက်သည် ကင်မရာပြောင်းရန် ပုံပြခန်းကို ဖွင့်ပါ @@ -343,7 +347,7 @@ ဤသည်မှာ မှန်ကန်သော ကောလ်လင့်ခ်တစ်ခု မဟုတ်ပါ။ ပါဝင်ရန် မကြိုးစားမီ လင့်ခ်တစ်ခုလုံးမှာ နဂိုအတိုင်းဖြစ်ကြောင်းနှင့် မှန်ကန်ကြောင်း သေချာအောင်လုပ်ပါ။ - You are already in a call + သင်သည် ကောလ်ထဲတွင် ရှိပြီးဖြစ်သည် @@ -400,6 +404,7 @@ ရုပ်သံ ကိုဖွင့်နိုင်သည့် app မရှိပါ။ ကူးပြီးပါပြီ %1$s %1$s မှ + %1$s သို့ ဆက်ဖတ်ရန် ဒေါင်းလုတ် ဆွဲရန် @@ -433,6 +438,7 @@ တည်းဖြတ်မှု ပေးပို့ရန် စာရေးသားမည် ပူးတွဲဖိုင်ကို ထည့်ရာတွင် အမှားအယွင်းရှိသည်။ + လက်ခံသူထံသို့ပို့၍မရနိုင်ပါ SMS သို့မဟုတ် အီးမေးလိပ်စာ မဟုတ်ပါ! သတင်းစကား မရှိပါ! အဖွဲ့ဝင်များ @@ -565,6 +571,7 @@ ပူးတွဲဖိုင် %1$d ကို ဖုန်းထဲတွင် သိမ်းနေသည် ဆိုင်းငံ့ထားသည်… + ဒေတာ (Signal) MMS SMS @@ -986,6 +993,7 @@ \'%1$s\' ကိုမချိတ်တော့ဘူးလား? မချိတ်ဆက်တော့သည့်အတွက် စာတိုများကို ပေးပို့ခြင်း သို့မဟုတ် လက်ခံခြင်း မပြုနိုင်တော့ပါ။ ကွန်ယက်ချိတ်ဆက်ခြင်းမအောင်မြင်ပါ + ထပ်မံကြိုးစားမည် ကရိယာကို မချိတ်ဆက်တော့ ဖုန်းကို မချိတ်ဆက်တော့ @@ -1368,7 +1376,7 @@ Group link ဝေမျှရန် link ကိုပြန်လည်သတ်မှတ် - Require admin approval + အက်ဒ်မင်၏ ခွင့်ပြုချက်လိုအပ်သည် အဖွဲ့ဝင်အသစ်များကို အဖွဲ့လင့်ခ်မှ ဝင်ခြင်းကို စီမံသူမှ အတည်ပြုရန် လိုအပ်စေပါ။ အဖွဲ့လင့်ခ်ကို ပြန်သတ်မှတ်မည်ဆိုတာ သင်သေချာပါသလား? လက်ရှိလင့်ခ်ဖြင့် အခြားလူများ အဖွဲ့သို့ မဝင်ရောက်နိုင်တော့ပါမည်။ @@ -1464,6 +1472,7 @@ %1$dစာ SMS ဖြင့် ဖိတ်ခေါ်မလား။ Signal ကိုပြောင်းသုံးကြစို့ : %1$s + သင့်မှာမျှဝေစရာ app များရှိပုံမရပါ။ @@ -1873,6 +1882,7 @@ %1$s အား သင့်ထံ မက်ဆေ့ချ်ပို့ခွင့်ပြုပြီး သင်၏ အမည်နှင့် ပုံကို ၎င်းထံ မျှဝေမည်လား။ ၎င်းတို့အား ဘလော့ခ် မဖြေမချင်း မက်ဆေ့ချ်များ ရရှိမည် မဟုတ်ပါ။ %1$s အား သင့်ထံ မက်ဆေ့ချ် ပို့ခွင့်ပြုမည်လား။ ၎င်းတို့အား ဘလော့ခ် မဖြေမချင်း မက်ဆေ့ချ်များ ရရှိမည် မဟုတ်ပါ။ + %1$s ထံမှ အပ်ဒိတ်များနှင့် သတင်းများ ရယူမည်လား။ ၎င်းတို့အား ဘလော့ခ် မဖြေမချင်း အပ်ဒိတ်များ ရရှိမည် မဟုတ်ပါ။ ထိုအဖွဲ့နှင့် ဆက်လက်၍ ချက်(တ်)ပြီး သင်၏ အမည်နှင့် ရုပ်ပုံကို အဖွဲ့ဝင်များနှင့် ဝေမျှမည်လား။ ဤပုံစံဟောင်းအဖွဲ့ကို အသုံးပြု၍မရတော့ပါ။ @mentions နှင့် အက်ဒ်မင်များကဲ့သို့ အင်္ဂါရပ်အသစ်များကို သက်ဝင်လုပ်ဆောင်ရန် အဖွဲ့သစ်တစ်ဖွဲ့ ဖန်တီးပါ။ @@ -2171,6 +2181,7 @@ အခြား မည်သူမျှ မရှိပါ %1$s သည် ၎င်းခေါ်ဆိုမှုထဲတွင် ရှိပါသည် + %1$s တို့သည် ဤကောလ်တွင် ရှိနေပါသည် %1$s နှင့် %2$s သည် ၎င်းခေါ်ဆိုမှုထဲတွင် ရှိပါသည် @@ -2562,7 +2573,7 @@ နောက်မှ ထပ်ကြိုးစားပါမည်။ Signal ကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်လိုက်ပါပြီ သင်သည် ဗားရှင်း %1$s သို့ အလိုအလျောက် အပ်ဒိတ်လုပ်လိုက်ပါပြီ။ - You updated to version %1$s. + သင်သည် ဗားရှင်း %1$s သို့ အပ်ဒိတ်လုပ်ထားသည်။ စာပို့မလား? @@ -2975,6 +2986,7 @@ ပူးတွဲဖိုင် စီခြင်း ကင်မရာပူးတွဲဖိုင်စီစဉ်ခြင်း နှိပ်ရန် အသံဖမ်းပြီး ပေးပို့လိုက်ပါ + အသံဖိုင်တွဲ၏ အသံသွင်းချက်ကို လော့ခ်ချပါ မက်ဆေ့ချ်ပို့ လို့မရပါ။ သင်၏ အင်တာနက်လိုင်းကို စစ်၍ ပြန်စမ်းကြည့်ပါ။ @@ -2993,6 +3005,7 @@ စာဖတ်၍ ပြီးစီး။ + လိပ်စာပါ ဓာတ်ပုံ @@ -3077,6 +3090,7 @@ အခု အားလုံးကြည့်ကောင်းနေပြီ! ခေါ်ဆိုမှု သတိပေးချက်များ လက်ခံရန် ဤနေရာနှိပ်၍ \"သတိပေးချက်များ ပြရန်\" အား ဖွင့်ထားပေးပါ။ ခေါ်ဆိုမှု သတိပေးချက်များ လက်ခံရန် ဤနေရာတွင်နှိပ်၍ သတိပေးချက်များ ဖွင့်ကာ အသံ နှင့် စာပေါ်ခြင်းများ (pop-ups) ကို ဖွင့်ထားရန် သေချာစေပါ။ + ခေါ်ဆိုမှု သတိပေးချက်များ လက်ခံရန် ဤနေရာတွင်နှိပ်၍ \"ဘက်ထရီ\" အပြင်အဆင်များထဲတွင် နောက်ခံလှုပ်ရှားမှု ဖွင့်ထားပေးပါ။ အပြင်အဆင်များ ခေါ်ဆိုမှု သတိပေးချက်များ လက်ခံရန် အပြင်အဆင်များ နှိပ်၍ \"သတိပေးချက်များ ပြရန်\" ကိုဖွင့်ထားပေးပါ။ @@ -3207,7 +3221,7 @@ အနားယူခြင်း အရာအသစ်တစ်ခု ကိုလုပ်ဆောင်နေသည် - One or more characters is invalid. + တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော စာလုံးများသည် အကျုံးမဝင်ပါ။ အဖွဲ့ကို ပြင်မည် @@ -3306,6 +3320,7 @@ + ပံ့ပိုးမှု အချက်အလက်များ Signal Android ပံ့ပိုးရန် တောင်းဆိုမှု @@ -3393,6 +3408,7 @@ သင်ပေးပို့သော မက်ဆေ့ချ်များ အတွက် ဝဘ်ဆိုဒ်များမှ ကြိုတင်ကြည့်ရှုနိုင်သည့် လင့်ခ်များကို တိုက်ရိုက်ရယူပါ။ စကားဝှက်ကို ပြောင်းပါ သင့်ရဲ့ စကားဝှက်ကို ပြောင်းပါ + ဖုန်းမျက်နှာပြင်ကို စကားဝှက်ဖြင့် ပိတ်ထား၍ ရပါသည် ဖုန်းမျက်နှာပြင်နှင့် အသိပေးချက်များကို စကားဝှက်နှင့် ပိတ်ပါ အဖွင့်စာမျက်နှာ လုံခြုံရေး @@ -3405,6 +3421,7 @@ LED လင်းမည့်ပုံစံ စိတ်ကြိုက်လုပ်ရန် အသံနှင့် တုန်ခါမှုကို ပြောင်းရန် + အသံ အသံတိတ် မူလ @@ -5251,6 +5268,7 @@ စတိုရီတွင် ပေါင်းထည့်ရန် မက်ဆေ့ချ် ပေါင်းထည့်ရန် + ပြန်စာ ပေါင်းထည့်ရန် ဖော်ပြပါသို့ ပို့ရန် တစ်ခါကြည့် မီဒီယာ @@ -5850,6 +5868,8 @@ ပြန်စာ %1$d ခု + + Story no longer hidden ပေါင်းထည့်မယ် @@ -7109,7 +7129,23 @@ - သင့်မီဒီယာကို ဒေါင်းလုဒ်လုပ်ရန် နေရာလွတ် %1$s ထွက်လာအောင် ရှင်းထုတ်ပါ။ + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ အိုကေ - - - ငွေပေးချေမှုမှတ်တမ်း - - စာတိုနှင့် မီဒီယာအားလုံးဘက်ခ်အပ် - - ငွေပေးချေမှု အသေးစိတ် - - ဘက်ခ်အပ် အမျိုးအစား - - ပေးချေသည့် ရက်စွဲ - - ဝေမျှမည် + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ ဘက်ခ်အပ် အကြိမ်ရေ ဆယ်လူလာသုံး၍ ဘက်ခ်အပ်လုပ်ပါ + + View backup key + + Unlock to view backup key ပိတ်ပြီး ဘက်ခ်အပ်ကို ဖျက်ပါ @@ -7212,13 +7248,27 @@ ဘက်ခ်အပ်ကို ညတွင်းချင်း ဖန်တီးပါမည်။ - ဘက်ခ်အပ် အမျိုးအစား + Backup plan ဘက်ခ်အပ် ပိတ်ထားသည် - - %1$s အတွက် တစ်လလျှင် · %2$s - - ဘက်ခ်အပ် လုပ်ဆောင်မည် + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + သင့် ဘက်ခ်အပ်ကီး - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + သင်၏ ဘက်ခ်အပ်ကီးသည် Signal ကို ပြန်လည်ထည့်သွင်းသောအခါတွင် သင့် ဘက်ခ်အပ်ကို ပြန်လည်ရယူနိုင်သည့် ဂဏန်း 64 လုံးကုဒ်ဖြစ်သည်။ - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + သင့် ကီးကိုမေ့သွားပါက သင်၏ဘက်ခ်အပ်ကို ပြန်လည်ရယူနိုင်တော့မည် မဟုတ်ပါ။ Signal သည် သင်၏ဘက်ခ်အပ်ကို ပြန်လည်ရယူရန် မကူညီနိုင်ပါ။ - Next + ဆက်သွားမည် - Record your backup key + သင်၏ အက်ခ်အပ်ကီးကို မှတ်တမ်းတင်ပါ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + သင့်အကောင့်နှင့် ဒေတာကို ပြန်လည်ရယူရန် ဤကီးလိုအပ်ပါသည်။ ဤကီးကို လုံခြုံသောနေရာတွင် သိမ်းဆည်းပါ။ ၎င်းကိုဆုံးရှုံးသွားပါက သင့်အကောင့်ကို ပြန်လည်ရယူနိုင်မည်မဟုတ်ပါ။ - Copy to clipboard + ကလစ်ဘုတ်သို့ ကူးယူမည် - Next + ဆက်သွားမည် - Keep your key safe + သင့်ကီးကို လုံခြုံအောင်ထားပါ - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + ကီးဆုံးရှုံးသွားပါက Signal သည် သင့် ဘက်ခ်အပ်ကို ပြန်လည်ရယူရန် ကူညီနိုင်မည်မဟုတ်ပါ။ ၎င်းကို ဘေးကင်းလုံခြုံသောနေရာတွင် သိမ်းဆည်းထားပြီး အခြားသူများနှင့် မမျှဝေပါနှင့်။ - I\'ve recorded my key + ကျွန်ုပ်ကီးကို မှတ်တမ်းတင်ပြီးပါပြီ - Continue + ဆက်လုပ်ရန် - See key again + ကီး ကိုပြန်ကြည့်ရန် diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 4ebdd94d7e..3df7c592d1 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ja Nei Slett @@ -260,6 +263,7 @@ Trykk for foto, hold for video + Ta bilde Bytt kamera Åpne galleri @@ -346,7 +350,7 @@ Dette er ikke en gyldig samtalelenke. Pass på at hele lenken er skrevet riktig, og prøv på nytt. - You are already in a call + Du er allerede i en samtale @@ -403,6 +407,7 @@ Fant ingen programmer som kan åpne dette innholdet. Kopiert %1$s fra %1$s + til %1$s   Les Mer   Last ned mer @@ -436,6 +441,7 @@ Send redigering Skriv melding Beklager, det oppstod en feil under vedlegging av filen. + Mottaker er ikke et gyldig nummer eller e-post-adresse. Meldingen er tom! Gruppemedlemmer @@ -576,6 +582,7 @@ Lagrer %1$d vedlegg på enheten … Venter… + Data (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Vil du koble fra \'%1$s\'? Hvis du kobler denne enheten fra tjenesten, kan den ikke lenger sende eller motta meldinger. Nettverkstilkoblingen mislyktes + Prøv igjen Kobler fra enhet … Kobler fra enhet @@ -1408,7 +1416,7 @@ Gruppelenke Del Reset link - Require admin approval + Krev godkjenning fra administrator Krev administratorgodkjenning for nye medlemmer som blir med via gruppelenken. Er du sikker på at du vil tilbakestille gruppelenken? Folk vil ikke lenger kunne bli med i gruppen ved hjelp av den nåværende lenken. @@ -1507,6 +1515,7 @@ Vil du sende %1$d SMS-invitasjoner? La oss bytte til Signal: %1$s + Det ser ut som at du ikke har noen programmer å dele denne informasjonen med. @@ -1931,6 +1940,7 @@ Vil du at %1$s skal kunne sende deg meldinger, og vil du dele navn og bilde med vedkommende? Du mottar ikke noen meldinger før du fjerner blokkeringen av personen. Vil du at %1$s skal kunne sende deg meldinger? Du mottar ikke noen meldinger før du fjerner blokkeringen av personen. + Ønsker du å motta oppdateringer og nyheter fra %1$s? Du mottar ikke noen oppdateringer før du fjerner blokkeringen av personen. Fortsett denne samtalen med denne gruppen og del ditt navn og bilde med gruppens medlemmer? Denne Legacy-gruppen kan ikke brukes lenger. Du må opprette en ny gruppe for å aktivere nye funksjoner som @-omtaler og administratorer. @@ -2245,6 +2255,7 @@ Ingen andre er her %1$ser i denne samtalen + %1$s er med i denne samtalen %1$s og %2$s er i denne samtalen. @@ -2647,7 +2658,7 @@ Vi gir det et nytt forsøk senere. Signal er oppdatert Appen ble automatisk oppdatert til versjon %1$s. - You updated to version %1$s. + Du har oppdatert appen til versjon %1$s. Send melding? @@ -3067,6 +3078,7 @@ Miniatyrbilde av vedlegg Slå av/på hurtig kamera-vedleggsfunksjon Ta opp og sende lyd vedlegg + Lås opptak av lydmelding Meldingen kunne ikke sendes. Kontroller nettverksforbindelsen din og prøv igjen. @@ -3085,6 +3097,7 @@ Melding lest + Kontaktfoto @@ -3169,6 +3182,7 @@ Alt ser bra ut nå! For å motta samtalevarsler, trykk her og skru på \"Vis varsler.\" For å motta samtalevarsler, trykk her og skru på varsler og sørg for at Lyd og Forgrunnsvinduer er aktivert. + For å motta samtalevarsler, trykk her og tillat bakgrunnsaktivitet i batteri-innstillinger. Innstillinger For å motta samtalevarsler, trykk Innstillinger og skru på \"Vis varsler.\" @@ -3306,7 +3320,7 @@ Tar en pause Jobber med noe nytt - One or more characters is invalid. + Teksten inneholder ugyldige tegn. Rediger gruppe @@ -3405,6 +3419,7 @@ + Brukterstøtteinformasjon Kontakt Signal Android support @@ -3493,6 +3508,7 @@ Hent forhåndsvisninger av lenker direkte fra nettsteder for meldinger du sender. Endre passordfrase Endre passordet ditt + Aktiver passordfrase-skjermlås Lås skjerm og varsler med passordfrase Skjermsikkerhet @@ -3505,6 +3521,7 @@ LED-blinkemønster Tilpass Endre lyd og vibrering + Lyd Stille Forvalgt @@ -5378,6 +5395,7 @@ Legg til i story Legg til en melding + Legg til et svar Send til Mediefiler som vises én gang @@ -5982,6 +6000,8 @@ %1$d svar %1$d svar + + Story no longer hidden Legg til @@ -7267,7 +7287,23 @@ - Du trenger %1$s mer lagringsplass for å laste ned mediefilene. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Betalingshistorikk - - Sikkerhetskopiering av alle mediefiler og tekst - - Betalingsinformasjon - - Sikkerhetskopieringstype - - Betalingsdato - - Del + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Hyppighet for sikkerhetskopiering Sikkerhetskopiering via mobilnettverket + + View backup key + + Unlock to view backup key Slå av og slett sikkerhetskopi @@ -7370,13 +7406,27 @@ Sikkerhetskopiene opprettes på nattestid. - Sikkerhetskopieringstype + Backup plan Sikkerhetskopiering slått av - - %1$s · %2$s per måned - - Slå på sikkerhetskopiering + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Sikkerhetskode - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Sikkerhetskoden din består av 64 tegn. Du trenger den for å gjenopprette sikkerhetskopien din når du installerer Signal på nytt. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Hvis du glemmer koden, vil du ikke kunne gjenopprette sikkerhetskopien. Signal kan ikke hjelpe deg med dette. - Next + Neste - Record your backup key + Noter ned sikkerhetskoden din - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Du trenger denne koden for å gjenopprette kontoen din og dataene dine. Oppbevar koden på en trygg plass. Uten koden kan du ikke gjenopprette kontoen din. - Copy to clipboard + Kopiere til utklippstavle - Next + Neste - Keep your key safe + Ta godt vare på koden - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal kan ikke hjelpe deg med å gjenopprette sikkerhetskopien din dersom du mister koden. Oppbevar den på en trygg plass, og ikke del den med andre. - I\'ve recorded my key + Jeg har notert ned koden - Continue + Fortsett - See key again + Se koden igjen diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index b8682680e5..24df9700f6 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ja Nee Verwijderen @@ -105,9 +108,9 @@ Geen app gevonden waarmee je media kunt selecteren. - Signal heeft toegang tot de opslag nodig om afbeeldingen, video\'s of audio te kunnen verzenden, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. - Signal heeft toegang tot de contacten nodig om contactinformatie te kunnen verzenden, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Contacten’ in. - Signal heeft toegang tot de locatie nodig om locaties te kunnen verzenden, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Locatie’ in. + Signal heeft toegang tot de opslag nodig om afbeeldingen, video\'s of audio te kunnen verzenden, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. + Signal heeft toegang tot de contacten nodig om contactinformatie te kunnen verzenden, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Contacten’ in. + Signal heeft toegang tot de locatie nodig om locaties te kunnen verzenden, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Locatie’ in. Toegang tot je locatie toestaan @@ -260,6 +263,7 @@ Tik voor foto, houd vast voor video + Opnemen Camera wisselen Galerij openen @@ -346,7 +350,7 @@ Dit is geen geldige oproeplink. Zorg dat je de volledige link gebruikt met de juiste karakters voordat je probeert deel te nemen. - You are already in a call + Je neemt op dit moment al aan een oproep deel @@ -403,7 +407,8 @@ Geen app gevonden waarmee dit bestand geopend kan openen. %1$s naar klembord gekopieerd van %1$s - via %1$s + + aan %1$s  Meer lezen  Meer downloaden   In afwachting @@ -436,6 +441,7 @@ Bewerkt bericht versturen Bericht opstellen Sorry, er is een fout opgetreden bij het toevoegen van je bijlage. + Ontvanger is geen geldig sms- of e-mailadres! Bericht is leeg! Groepsleden @@ -484,14 +490,14 @@ Om spraakberichten te sturen, moet je Signal toegang geven tot je microfoon. Signal heeft toegang tot de microfoon nodig om een spraakbericht te kunnen opnemen. - Signal heeft toegang tot de microfoon nodig om spraakberichten te kunnen opnemen, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Microfoon’ in. + Signal heeft toegang tot de microfoon nodig om spraakberichten te kunnen opnemen, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Microfoon’ in. - Signal heeft toegang tot de microfoon en de camera nodig om %1$s te kunnen bellen, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Microfoon’ en ‘Camera’ in. + Signal heeft toegang tot de microfoon en de camera nodig om %1$s te kunnen bellen, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Microfoon’ en ‘Camera’ in. Geef Signal toegang tot de camera om foto\'s en video\'s te maken. - Signal heeft toegang tot de camera nodig om foto’s en video’s te kunnen maken, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Camera’ in. + Signal heeft toegang tot de camera nodig om foto’s en video’s te kunnen maken, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Camera’ in. Signal heeft toegang tot de camera nodig om foto’s en video’s te kunnen maken Signal heeft toegang tot de microfoon nodig om video te kunnen opnemen met geluid. - Signal heeft toegang tot de microfoon en de camera nodig om video te kunnen opnemen, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Microfoon’ en ‘Camera’ in. + Signal heeft toegang tot de microfoon en de camera nodig om video te kunnen opnemen, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Microfoon’ en ‘Camera’ in. Signal heeft toegang tot de microfoon nodig om video te kunnen opnemen. %1$s %2$s @@ -576,6 +582,7 @@ %1$d bijlagen opslaan in lokale opslag… In afwachting… + Internet (versleuteld met Signal-protocol) Mms Sms @@ -589,7 +596,7 @@ Verwijderen Verwijderen van alle apparaten - Dit bericht wordt verwijderd voor iedereen in de chat als ze een recente versie van Signal gebruiken. Ze kunnen zien dat je een bericht hebt verwijderd. + Dit bericht wordt voor iedereen in de chat verwijderd als ze een recente versie van Signal gebruiken. Ze kunnen zien dat je een bericht hebt verwijderd. Oorspronkelijk bericht niet gevonden Oorspronkelijk bericht niet langer beschikbaar. Openen van bericht is mislukt @@ -655,7 +662,7 @@ Wees voorzichtig met het accepteren van gespreksverzoeken van mensen die je niet kent. Kijk uit voor: - Bekijk dit verzoek zorgvuldig. Geen van je contacten of mensen met wie je chat, maakt deel uit van deze groep. Hier zijn een paar dingen om op te letten: + Beoordeel dit verzoek zorgvuldig. Geen van je contacten of mensen met wie je chat, maakt deel uit van deze groep. Hier zijn een paar dingen om op te letten: Vorige tip @@ -769,7 +776,7 @@ Profiel Fout bij instellen van profielfoto Probleem bij instellen van profiel - Stel je profiel op + Stel je profiel in Je profiel en wijzigingen die je maakt zijn zichtbaar voor mensen die je een bericht stuurt, contacten en groepen. Profielfoto instellen @@ -828,7 +835,7 @@ Herstel overslaan - Back-upfoutpictogram + Icoon voor back-upfout Maak ruimte vrij om je media te herstellen. @@ -872,7 +879,7 @@ Tot nu toe %1$d… %1$s%% tot nu toe… - Signal heeft toegang tot de opslagruimte nodig om back-ups te kunnen maken, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. + Signal heeft toegang tot de opslagruimte nodig om back-ups te kunnen maken, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. Stel back-uptijd in @@ -939,7 +946,7 @@ Chatsessie vernieuwd - Het kan soms nodig zijn om je chatsessie te vernieuwen. Dit heeft geen invloed op de beveiliging van je chat, maar het is mogelijk dat je een bericht van je contact hebt gemist. Je kunt je contact vragen om hun meest recente berichten opnieuw te sturen. + Signal maakt gebruik van end-to-end-versleuteling en het kan soms nodig zijn om je chatsessie te vernieuwen. Dit heeft geen invloed op de beveiliging van je chat, maar het is mogelijk dat je een bericht van je contact hebt gemist. Je kunt je contact vragen om hun meest recente berichten opnieuw te sturen. @@ -1006,6 +1013,7 @@ ‘%1$s’ ontkoppelen? Door dit apparaat te ontkoppelen kan het niet langer berichten versturen en ontvangen. Netwerkfout + Opnieuw proberen Apparaat aan het ontkoppelen… Apparaat aan het ontkoppelen @@ -1408,7 +1416,7 @@ Groepslink Delen Link vernieuwen - Require admin approval + Nieuwe deelnemers goedkeuren Vereis voor iedere persoon die via de groepslink lid wil worden eerst goedkeuring van een beheerder. Weet je zeker dat je de groepslink wilt vernieuwen? Als je de link vernieuwt kunnen mensen niet langer lid worden via de huidige link. @@ -1507,6 +1515,7 @@ %1$d sms-uitnodigingen verzenden? Laten we Signal gebruiken om een gesprek te voeren: %1$s + Het lijkt erop dat je geen apps hebt waarmee je een uitnodiging kan verzenden. @@ -1931,11 +1940,12 @@ Wil je berichten van %1$s ontvangen en je profielnaam, -foto en -omschrijving met diegene delen? Totdat je deze persoon hebt gedeblokkeerd, zul je geen berichten ontvangen. Wil je berichten van %1$s ontvangen? Totdat je deze persoon hebt gedeblokkeerd, zul je geen berichten ontvangen. - Wil je updates en nieuws van %1$s ontvangen? Je zult geen berichten ontvangen totdat je diegene hebt gedeblokkeerd. + + Wil je updates en nieuws van %1$s ontvangen? Je zult geen berichten ontvangen totdat je dit contact hebt gedeblokkeerd. Wil je je profielnaam, -foto en -omschrijving met de andere leden delen om je chat met de groep voort te kunnen zetten? Dit is een verouderde groep en deze kan niet meer gebruikt worden. Maak een nieuwe groep om nieuwe functies zoals @vermeldingen en beheerders te activeren. Deze verouderde groep kan niet langer gebruikt worden omdat die te veel leden heeft. Het maximum ledenaantal voor een groep is %1$d. - Doorgaan met deze chat met %1$s en je naam en profielfoto met hen delen? + Doorgaan met deze chat met %1$s en je profielnaam, -foto en -omschrijving met hen delen? Wil je lid worden van deze groep en je profielnaam, -foto en -omschrijving met de andere leden delen? Leden van de groep weten niet dat je hun berichten hebt gezien totdat je het gespreksverzoek hebt geaccepteerd. Wil je lid worden van deze groep en je profielnaam, -foto en -omschrijving met de andere leden delen? Je kunt hun berichten nog niet zien totdat je de uitnodiging hebt geaccepteerd. Wil je lid worden van deze groep? Leden van de groep weten niet dat je hun berichten hebt gezien totdat je het gespreksverzoek hebt geaccepteerd. @@ -1985,12 +1995,12 @@ Geen apparaat gevonden. Netwerkfout. Ongeldige QR-code. - Sorry, je hebt al te veel apparaten gekoppeld. Probeer er enkele te verwijderen. + Sorry, je hebt het maximale aantal apparaten dat je aan je account kunt koppelen al bereikt. Verwijder een apparaat en probeer het opnieuw. Sorry, dit is geen geldige QR-code om een apparaat te koppelen. Een Signal-apparaat koppelen? Het lijkt erop dat je een Signal-apparaat wilt koppelen met behulp van een externe scanner. Scan voor de veiligheid de code opnieuw vanuit Signal. - Signal heeft toegang tot de camera nodig om een QR-code te scannen, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Camera’ in. + Signal heeft toegang tot de camera nodig om een QR-code te scannen, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Camera’ in. Kan geen QR-code scannen zonder toegang tot camera @@ -2245,6 +2255,7 @@ Er is niemand hier %1$s is in deze oproep aanwezig + %1$s bent in deze oproep aanwezig %1$s en %2$s zijn in deze oproep aanwezig @@ -2277,9 +2288,9 @@ Een icoon dat een bedrade headset voorstelt. - Een icoon dat een speakerphone voorstelt. + Een icoon dat een luidspreker voorstelt. - Een icoon dat de oortelefoon van een apparaat voorstelt. + Een icoon dat het standaardgeluid uit een telefoon voorstelt. Hand opsteken @@ -2342,7 +2353,7 @@ Geen gemeenschappelijke groepen - Overweeg dit gespreksverzoek zorgvuldig: + Beoordeel dit gespreksverzoek zorgvuldig: %1$d gemeenschappelijke groep @@ -2417,7 +2428,7 @@ Ik begrijp het Google Play Services-fout Google Play Services wordt momenteel bijgewerkt of is tijdelijk niet beschikbaar. Probeer het nog eens. - Gebruiksvoorwaarden & privacybeleid + Voorwaarden en privacybeleid Signal heeft toegang tot je contacten en de opslag nodig om je te kunnen verbinden met je vrienden en berichten te sturen. Je contacten worden end-to-end versleuteld geüpload en blijven altijd onleesbaar voor de Signal-dienst. Signal heeft toegang tot je contacten om je te kunnen verbinden met je vrienden. Je contacten worden end-to-end versleuteld geüpload en blijven altijd onleesbaar voor de Signal-dienst. Je hebt te vaak geprobeerd dit telefoonnummer te registreren. Probeer het later opnieuw. @@ -2470,7 +2481,7 @@ deze stappen voor probleemoplossing - Neem contact op met ons supportteam + Neem contact op met ondersteuning Registratievergrendeling inschakelen? @@ -2626,7 +2637,7 @@ Je hebt gedoneerd namens %1$s - Je hebt een badge ingewisseld + Je hebt een badge in ontvangst genomen Reageerde met %1$s op je verhaal @@ -2647,7 +2658,7 @@ We proberen het later opnieuw. Signal succesvol bijgewerkt Je bent automatisch bijgewerkt naar versie %1$s. - You updated to version %1$s. + Je hebt bijgewerkt naar versie %1$s. Bericht verzenden? @@ -2717,7 +2728,7 @@ Ons Signal-veiligheidsnummer: Het lijkt erop dat je geen apps hebt waarmee je je veiligheidsnummer kunt delen. Geen veiligheidsnummer gevonden op het klembord - Signal heeft toegang tot de camera nodig om een QR-code te scannen, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Camera’ in. + Signal heeft toegang tot de camera nodig om een QR-code te scannen, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Camera’ in. Kan QR-codes niet scannen zonder toegang tot de camera Om het veiligheidsnummer van %1$s te kunnen weergeven moeten jullie eerst berichten uitwisselen. @@ -2754,7 +2765,7 @@ Door jou verzonden Niet-ondersteund mediatype Concept - Signal heeft toegang tot de opslagruimte nodig om iets op te slaan, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. + Signal heeft toegang tot de opslagruimte nodig om iets op te slaan, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. Kan niet opslaan naar externe opslag zonder machtiging Bericht verwijderen? Dit bericht zal permanent worden verwijderd. @@ -2868,7 +2879,7 @@ Zoeken - Zoek ongelezen chats + Ongelezen chats zoeken Zoeken naar chats, contacten en berichten @@ -2941,7 +2952,7 @@ Om de oproep te beantwoorden, moet je Signal toegang geven tot je microfoon. Geef Signal toegang tot je microfoon en camera om de video-oproep te beantwoorden. - Signal heeft toegang tot de microfoon en de camera nodig om oproepen te maken of te ontvangen, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Microfoon’ en ‘Camera’ in. + Signal heeft toegang tot de microfoon en de camera nodig om oproepen te maken of te ontvangen, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Microfoon’ en ‘Camera’ in. Beantwoord vanaf een gekoppeld apparaat. Geweigerd vanaf een gekoppeld apparaat. Bezet op een gekoppeld apparaat. @@ -2959,7 +2970,7 @@ Beantwoorden - Beantwoorden zonder eigen camera aan te zetten + Beantwoorden zonder video @@ -3012,7 +3023,7 @@ Foto van contact - Signal heeft toegang tot de contacten nodig om je contacten te kunnen weergeven, maar je hebt dit geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Contacten’ in. + Signal heeft toegang tot de contacten nodig om je contacten te kunnen weergeven, maar je hebt dit geweigerd. Ga naar de app-instellingen in je telefooninstellingen, tik op ‘Machtigingen’ en schakel ‘Contacten’ in. Fout bij het ophalen van contacten. Controleer je internetverbinding en probeer het opnieuw. Gebruikersnaam niet gevonden "‘%1$s’ is geen Signal-gebruiker. Controleer de gebruikersnaam en probeer het opnieuw." @@ -3062,11 +3073,12 @@ SIM %1$d Verzenden - Berichtsamenstelling + Bericht opstellen Emojitoetsenbord wisselen Bijlagevoorbeeld - Camera overzicht weergeven/verbergen + Camera overzicht weergeven of verbergen Audio opnemen en verzenden + Vergrendel opnemen van audio Het bericht kon niet worden verzonden. Controleer je internetverbinding en probeer het opnieuw. @@ -3085,6 +3097,7 @@ Bericht gelezen + Foto van contact @@ -3169,6 +3182,7 @@ Alles ziet er nu goed uit! Tik hier en schakel ‘Meldingen weergeven’ in om oproepmeldingen te ontvangen. Tik hier en schakel meldingen in en zorg dat geluiden en pop-ups zijn ingeschakeld om oproepmeldingen te ontvangen. + Tik hier en schakel in de batterij -instellingen achtergrondactiviteit in om oproepmeldingen te ontvangen. Instellingen Tik op ‘Instellingen’ en schakel vervolgens ‘Meldingen weergeven’ in om oproepmeldingen te ontvangen. @@ -3306,7 +3320,7 @@ Ik wil even offline zijn Ik werk aan iets nieuws - One or more characters is invalid. + Een of meer karakters zijn ongeldig. Groep bewerken @@ -3405,6 +3419,7 @@ + Ondersteuningsinformatie Signal-Android ondersteuningsverzoek @@ -3431,10 +3446,10 @@ Dit bericht Recentelijk gebruikt Smileys & mensen - Natuur - Voedsel + Dieren & natuur + Eten & drinken Activiteiten - Plaatsen + Reizen & plaatsen Objecten Symbolen Vlaggen @@ -3493,6 +3508,7 @@ Genereer voor elke link die je verzendt een voorbeeld van de website en stuur dit mee met je bericht Wachtwoord wijzigen Wijzig je wachtwoord + Appvergrendeling met wachtwoord inschakelen Vergrendel de Signal-app en -meldingen met een wachtwoord Meekijkpreventie @@ -3505,6 +3521,7 @@ LED-knipperpatroon Aanpassen Geluid en trillen aanpassen + Berichtgeluid Stil Standaardmeldingen @@ -3852,7 +3869,7 @@ Naar Van - Transactiegegevens, waaronder het bedrag en het tijdstip van de transactie, zijn onderdeel van het kasboek van MobileCoin. + Transactiegegevens, waaronder het bedrag en het tijdstip van de transactie, zijn onderdeel van het grootboek van MobileCoin. Samenvoegingskosten Er worden ‘samenvoegingskosten’ in rekening gebracht wanneer de munten die je bezit niet kunnen worden samengevoegd tot de nieuwe gewenste betaling. Door deze samenvoeging kun je toch betalingen blijven verzenden. Er is geen verdere informatie beschikbaar over deze transactie @@ -3861,7 +3878,7 @@ Verzonden betaling Ontvangen betaling Betaling voltooid %1$s - Nummer blokkeren + Bloknummer Overschrijven @@ -4568,7 +4585,7 @@ Beheerder - Toestaan + Goedkeuren Afwijzen @@ -4684,7 +4701,7 @@ Het telefoonnummer dat je hebt ingevuld komt niet overeen met het telefoonnummer van je Signal-account. Weet je zeker dat je je account wilt verwijderen? Hiermee verwijder je je Signal-account en reset je de app. De app wordt gesloten nadat dit proces is voltooid. - Het wissen van lokale gegevens is mislukt. Je kunt de lokale gegevens zelf wissen via het ‘apps’-menu in de instellingenapp van je besturingssysteem. + Het wissen van lokale gegevens is mislukt. Je kunt de lokale gegevens zelf wissen via het ‘apps’-menu in de Android-instellingenapp. App-instellingen openen Groepen aan het verlaten… @@ -4712,7 +4729,7 @@ - Delen via andere app + Delen Verzend , %1$s @@ -4727,7 +4744,7 @@ Chatkleur Chatkleuren resetten - Chatkleur verwijderen + Chatkleur resetten De kleur voor deze chat verwijderen? Chatachtergrond kiezen Achtergrond dimmen bij donkere modus @@ -4795,7 +4812,7 @@ Uitschakelen zonder overschrijven Uitschakelen Uitschakelen zonder overschrijven? - Je krediet blijft beschikbaar in de portemonnee die aan Signal is gekoppeld, zodra je betalingen weer inschakelt. + Je krediet is weer beschikbaar in de portemonnee die aan Signal is gekoppeld zodra je betalingen weer inschakelt. Fout bij het uitschakelen van je portemonnee @@ -5094,7 +5111,7 @@ Foutopsporingslog Licenties - Gebruiksvoorwaarden & privacybeleid + Voorwaarden en privacybeleid Auteursrecht Signal Messenger Uitgegeven onder een GNU AGPLv3-licentie @@ -5142,7 +5159,7 @@ Aan Signal doneren - Signal wordt gefinancierd door donaties van gebruikers. Overweeg om maandelijks te doneren. + Signal wordt mogelijk gemaakt door donaties van mensen zoals jij. Als je maandelijks doneert krijg je een badge. Doneren @@ -5163,9 +5180,9 @@ Voeg een foto toe - Help Signal mogelijk maken + Word donateur van Signal - Signal wordt onder andere gefinancierd door donaties van mensen zoals jij. Als je ons steunt krijg je een badge. + Signal wordt mogelijk gemaakt door donaties van mensen zoals jij. Als je ons steunt krijg je een badge. Niet nu Doneren @@ -5347,9 +5364,9 @@ Berichttekst toevoegen Sneller doorsturen - Video\'s worden bijgesneden tot clips van 30 seconden en als meerdere Verhalen verstuurd. + Video\'s worden in clips van 30 seconden geknipt en als meerdere verhalen verstuurd. - Video\'s die naar Verhalen gestuurd worden kunnen niet langer zijn dan 30 seconden. + Video\'s die naar verhalen worden gestuurd kunnen niet langer zijn dan 30 seconden. Doorgestuurde berichten worden vanaf nu onmiddellijk verzonden. %1$d bericht verzenden @@ -5378,6 +5395,7 @@ Aan verhaal toevoegen Berichttekst toevoegen + Een reactie toevoegen Versturen naar Eenmaligeweergave-media @@ -5472,10 +5490,10 @@ Er zal niet opnieuw geld worden afgeschreven. Je badge zal worden verwijderd van je profiel wanneer je laatste betalingsperiode is afgelopen. Niet nu Bevestigen - Maandelijkse donatie wijzigen + Maandelijkse donatie aanpassen Je maandelijkse donatie is stopgezet. - Maandelijkse donatie wijzigen? - Bijwerken + Maandelijkse donatie aanpassen? + Nu aanpassen Het nieuw ingestelde bedrag (%1$s) zal vandaag worden afgeschreven. Je donatie wordt maandelijks automatisch verlengd en opnieuw afgeschreven. %1$s/maand @@ -5493,7 +5511,7 @@ Als je iets kunt missen, doneer dan vandaag nog om Signal leuk, betrouwbaar en beschikbaar te houden voor iedereen. - Bedankt voor je ondersteuning! + Bedankt voor je steun! Je hebt een donateursbadge van Signal verdiend! Toon hem op je profiel en laat zien dat je ons steunt. Je kunt ook @@ -5573,7 +5591,7 @@ Je kunt je ‘Boost’-badge opnieuw voor 30 dagen activeren met een nieuwe eenmalige donatie. Je kunt Signal gewoon blijven gebruiken, maar als je technologie die écht voor jou is gemaakt wilt blijven ondersteunen, overweeg dan om een maandelijkse donateur te worden. - Help Signal mogelijk maken + Word donateur van Signal Nogmaals eenmalig doneren Niet nu @@ -5582,7 +5600,7 @@ Je maandelijkse donatie is stopgezet omdat Signal je betaling niet kon verwerken. Je badge is niet langer op je profiel zichtbaar. Je maandelijkse donatie is stopgezet. %1$s Je ‘%2$s’-badge is niet langer zichtbaar op je profiel. - Je kunt Signal gewoon blijven gebruiken, maar als je Signal wilt ondersteunen en je badge weer wilt weergeven dan moet je je donatie vernieuwen. + Je kunt Signal gewoon blijven gebruiken, maar als je Signal wilt blijven ondersteunen en je badge weer wilt weergeven dan moet je je donatie vernieuwen. Maandelijkse donatie vernieuwen Google Pay openen @@ -5591,7 +5609,7 @@ Het lukt Signal niet om je maandelijkse donatie af te schrijven. Controleer of je betalingsmethode nog steeds juist is. Werk je betalingsinformatie bij in Google Pay als dat nodig is. Signal zal je donatie binnen een paar dagen opnieuw proberen af te schrijven. Dit niet opnieuw weergeven - Neem contact op + Neem contact op met ondersteuning Ontvang een ‘%1$s’-badge @@ -5606,7 +5624,7 @@ Je betaling wordt nog verwerkt. Dit kan een paar minuten duren afhankelijk van je internetverbinding. - Probeer de betaling opnieuw te doen of neem contact op met je bank om meer informatie te vragen. + Probeer de betaling opnieuw te voltooien of neem contact op met je bank voor meer informatie. Deze betaling is door de rekeninghouder ingetrokken en kon niet worden verwerkt. Er is geen geld afgeschreven. @@ -5620,11 +5638,11 @@ Meer lezen Fout bij het verwerken van donatie. %1$s - Je donatie kon niet worden verwerkt, er is geen geld afgeschreven. Probeer het opnieuw. + Je donatie kon niet worden verwerkt en er is geen geld afgeschreven. Probeer het opnieuw. Wordt nog verwerkt Kan badge niet toevoegen - Kon badge-echtheid niet nagaan + Kon badge niet valideren Kon antwoord van de server niet valideren. Neem contact op met ondersteuning. @@ -5635,13 +5653,13 @@ Je donatie wordt nog verwerkt. Dit kan een paar minuten duren afhankelijk van je internetverbinding. Het stopzetten van je maandelijkse donatie is mislukt Om je maandelijkse donatie stop te zetten moet je toegang hebben tot internet. - Je apparaat biedt geen ondersteuning voor Google Pay, daarom kun je via de app geen maandelijkse donatie maken en kun je geen badge verdienen. Je kunt nog wel doneren via Signals website. + Je apparaat biedt geen ondersteuning voor Google Pay. Hierdoor kun je via de app geen maandelijkse donatie doen en kun je geen badge verdienen. Je kunt nog wel doneren via Signals website. Netwerkfout. Controleer je internetverbinding en probeer het opnieuw. Opnieuw proberen Kan donatie niet versturen - Deze gebruiker kan geen donaties ontvangen totdat Signal is geüpgraded. + Deze gebruiker kan geen donaties ontvangen totdat deze Signal heeft bijgewerkt. Je donatie kon niet worden verstuurd vanwege een netwerkfout. Controleer je internetverbinding en probeer het opnieuw. @@ -5670,7 +5688,7 @@ Meer informatie - Controleer of je betalingsmethode nog steeds juist is. Werk je betalingsinformatie bij in Google Pay als dat nodig is. Probeer het daarna opnieuw. Neem contact op met je bank om hen om meer informatie te vragen als dit probleem aanhoud. + Controleer of je betalingsmethode nog steeds juist is. Werk je betalingsinformatie bij in Google Pay als dat nodig is. Probeer het daarna opnieuw. Neem contact op met je bank als dit probleem aanhoudt. Je betaalkaart ondersteunt dit type betaling niet. Probeer het via een andere betalingsmethode opnieuw. @@ -5690,9 +5708,9 @@ Het jaar waarop je betalingsmethode verloopt is onjuist. Werk eerst je betalingsinformatie bij in Google Pay en probeer het daarna opnieuw. - Probeer opnieuw de betaling te voltooien of neem contact op met je bank voor meer informatie. + Probeer de betaling opnieuw te voltooien of neem contact op met je bank voor meer informatie. - Probeer het opnieuw of neem contact op met je bank om hen om meer informatie te vragen. + Probeer het opnieuw of neem contact op met je bank voor meer informatie. @@ -5924,11 +5942,11 @@ Tik om toe te voegen - Er zijn op dit moment geen nieuwe verhaaldelen om weer te geven. + Er zijn op dit moment geen recente verhalen om weer te geven. Verhaal verbergen - Verhaal niet langer verbergen + Verhaal niet verbergen Doorsturen @@ -5944,13 +5962,13 @@ Verzenden is mislukt - Gedeeltelijk verstuurd + Gedeeltelijk verzonden Tik om opnieuw te proberen Verhaal verbergen? - Nieuwe verhaaldelen van %1$s zullen niet langer bovenaan je lijst met verhalen worden weergegeven. + Nieuwe verhaalupdates van %1$s zullen niet langer bovenaan je lijst met verhalen worden weergegeven. Verbergen @@ -5982,6 +6000,8 @@ %1$d reactie %1$d reacties + + Story no longer hidden Toevoegen @@ -6011,7 +6031,7 @@ Kijker verwijderen? - %1$s kan deze post nog steeds zien, maar zal toekomstige posts die je deelt via %2$s niet meer kunnen zien. + %1$s kan deze verhaalupdate nog steeds zien, maar zal toekomstige updates die je deelt via %2$s niet meer kunnen zien. Kijker verwijderen @@ -6069,7 +6089,7 @@ %1$d personen - Kies wie je verhaal kan zien. Wijzigingen hebben geen invloed op verhalen die je al verstuurd hebt. + Kies wie je verhaal kan zien. Wijzigingen hebben geen invloed op verhalen die je al hebt gedeeld. Reacties @@ -6150,11 +6170,11 @@ - Je verhaal kan niet worden geüpload. Controleer je internetverbinding en probeer het opnieuw. + Je verhaal kan niet worden verzonden. Controleer je internetverbinding en probeer het opnieuw. Versturen - Uitschakelen en verwijderen + Uitzetten en verwijderen @@ -6197,7 +6217,7 @@ Gekopieerd naar klembord - Bekijk meer + Meer lezen Privéreactie aan het verzenden… @@ -6209,7 +6229,7 @@ Kon de inhoud niet laden - Verhaal is geüpload + Verhaal is verzonden Verhaal verzenden is mislukt @@ -6257,7 +6277,7 @@ De ontvanger wordt in een persoonlijk bericht op de hoogte gebracht van de donatie. Voeg hieronder je eigen bericht toe. - Een eenmalige donatie + Eenmalige donatie Berichttekst toevoegen @@ -6275,7 +6295,7 @@ Niet nu - Badge inwisselen… + In ontvangst aan het nemen… Je hebt namens %1$s een donatie gedaan aan Signal. Ze krijgen de mogelijkheid om hun steun op hun profiel te tonen. @@ -6411,7 +6431,7 @@ Mijn verhaalprivacy - Kies wie je verhaal kan zien. Je kunt altijd wijzigingen aanbrengen in je instellingen. + Kies wie je verhaal kan zien. Je kunt dit altijd wijzigingen in de instellingen. Alle Signal-contacten @@ -6432,7 +6452,7 @@ - Verhaaldelen verdwijnen automatisch na 24 uur. Kies wie jouw verhalen kan zien of maak nieuwe verhalen voor specifieke personen of groepen. + Verhalen verdwijnen automatisch na 24 uur. Kies wie jouw verhalen kan zien of maak nieuwe verhalen voor specifieke personen of groepen. Verhalen uitzetten @@ -6724,9 +6744,9 @@ Nieuw: groepsverhalen - Deel verhaaldelen in een groepschat waar je al in zit. + Deel verhalen in een groepschat waar je al in zit. - Iedereen in de groepschat kan een verhaal toevoegen. + Iedereen in de groepschat kan aan het verhaal toevoegen. Alle leden van de groepschat kunnen reacties op verhalen bekijken. @@ -6747,7 +6767,7 @@ - Je Signal-gebruikersnaam instellen + Stel je Signal-gebruikersnaam in Ontdek telefoonnummerprivacy, optionele gebruikersnamen en links. @@ -6792,7 +6812,7 @@ Instellen - Je scherm wordt na 1 minuut vergrendeld. + Minimale tijd voordat scherm wordt vergrendeld is 1 minuut. @@ -6903,7 +6923,7 @@ Luidspreker uit - Knop om foto te maken + Opnameknop Doorgaan-knop @@ -7101,7 +7121,7 @@ Weigeren - Toestaan + Goedkeuren Meldingen op volledig scherm inschakelen? @@ -7267,7 +7287,23 @@ - Maak %1$s ruimte vrij om je media te kunnen downloaden. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Begrepen - - - Betalingsgeschiedenis - - Back-up van tekst en alle media - - Betalingsdetails - - Back-uptype - - Datum van betaling - - Delen + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Back-upfrequentie Back-up maken via mobiel netwerk + + View backup key + + Unlock to view backup key Back-ups uitschakelen en verwijderen @@ -7370,13 +7406,27 @@ De back-up wordt vannacht gemaakt. - Back-uptype + Backup plan Back-ups uitgeschakeld - - %1$s · %2$s/maand - - Back-ups inschakelen + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Je back-upsleutel - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Je back-upsleutel is een 64-cijferige code waarmee je je back-up kunt herstellen wanneer je Signal opnieuw installeert. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Als je je sleutel vergeet, kun je je back-up niet herstellen. Signal kan je niet helpen je back-up te herstellen. - Next + Volgende - Record your backup key + Je back-upsleutel vastleggen - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Deze sleutel is nodig om je account en gegevens te herstellen. Bewaar deze sleutel op een veilige manier. Als je hem kwijtraakt, kun je je account niet meer herstellen. - Copy to clipboard + Kopiëren naar klembord - Next + Volgende - Keep your key safe + Bewaar je sleutel op een veilige manier - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal kan je niet helpen met het herstellen van je back-up als je je sleutel kwijtraakt. Bewaar de sleutel op een veilige manier en deel deze niet met anderen. - I\'ve recorded my key + Ik heb mijn sleutel vastgelegd - Continue + Doorgaan - See key again + Sleutel opnieuw weergeven diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 809af0d24e..6a091b412b 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + ਹਾਂ ਨਹੀਂ ਮਿਟਾਓ @@ -260,6 +263,7 @@ ਫੋਟੋ ਲਈ ਛੂਹੋ, ਵੀਡੀਓ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ + ਤਸਵੀਰ ਖਿੱਚੋ ਕੈਮਰਾ ਬਦਲੋ ਗੈਲਰੀ ਖੋਲ੍ਹੋ @@ -346,7 +350,7 @@ ਕਾਲ ਦਾ ਇਹ ਲਿੰਕ ਵੈਧ ਨਹੀਂ ਹੈ। ਕਾਲ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਲਿੰਕ ਪੂਰਾ ਅਤੇ ਸਹੀ ਹੈ। - You are already in a call + ਤੁਸੀਂ ਪਹਿਲਾਂ ਹੀ ਕਾਲ ਵਿੱਚ ਹੋ @@ -403,6 +407,7 @@ ਇਸ ਮੀਡੀਆ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਕੋਈ ਐਪ ਨਹੀਂ ਮਿਲ ਸਕੀ। %1$s ਕਾਪੀ ਕੀਤਾ %1$s ਵੱਲੋਂ + %1$s ਨੂੰ  ਹੋਰ ਪੜ੍ਹੋ  ਹੋਰ ਡਾਊਨਲੋਡ ਕਰੋ @@ -436,6 +441,7 @@ ਸੋਧਿਆ ਗਿਆ ਸੁਨੇਹਾ ਭੇਜੋ ਸੁਨੇਹਾ ਲਿਖੋ ਮਾਫ਼ ਕਰਨਾ, ਤੁਹਾਡੀ ਅਟੈਚਮੈਂਟ ਨੂੰ ਜੋੜਨ ਵਿੱਚ ਕੋਈ ਤਰੁੱਟੀ ਹੋਈ ਸੀ। + ਪ੍ਰਾਪਤਕਰਤਾ ਇੱਕ ਪ੍ਰਮਾਣਿਤ SMS ਜਾਂ ਈਮੇਲ ਪਤਾ ਨਹੀਂ ਹੈ! ਸੁਨੇਹਾ ਖਾਲੀ ਹੈ! ਗਰੁੱਪ ਦੇ ਮੈਂਬਰ @@ -576,6 +582,7 @@ %1$d ਅਟੈਚਮੈਂਟਾਂ ਨੂੰ ਸਟੋਰੇਜ ਵਿੱਚ ਸੇਵ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ… ਬਾਕੀ… + ਡੇਟਾ (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \'%1$s\' ਨਾਲ ਲਿੰਕ ਤੋੜਨਾ ਹੈ? ਇਸ ਡਿਵਾਈਸ ਦਾ ਲਿੰਕ ਤੋੜ ਕੇ, ਇਹ ਹੁਣ ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਯੋਗ ਨਹੀਂ ਰਹੇਗੀ। ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਅਸਫ਼ਲ + ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਡਿਵਾਈਸ ਦਾ ਲਿੰਕ ਤੋੜਿਆ ਜਾ ਰਿਹਾ ਹੈ… ਡਿਵਾਈਸ ਦਾ ਲਿੰਕ ਤੋੜਿਆ ਜਾ ਰਿਹਾ ਹੈ @@ -1408,7 +1416,7 @@ ਗਰੁੱਪ ਲਿੰਕ ਸਾਂਝਾ ਕਰੋ ਲਿੰਕ ਰੀਸੈੱਟ ਕਰੋ - Require admin approval + ਐਡਮਿਨ ਦੀ ਮਨਜ਼ੂਰੀ ਲੋੜੀਂਦੀ ਹੈ ਗਰੁੱਪ ਲਿੰਕ ਰਾਹੀਂ ਸ਼ਾਮਲ ਹੋਣ ਵਾਲੇ ਨਵੇਂ ਮੈਂਬਰਾਂ ਨੂੰ ਮਨਜ਼ੂਰੀ ਦੇਣ ਲਈ ਕਿਸੇ ਐਡਮਿਨ ਦੀ ਲੋੜ ਹੈ। ਕੀ ਤੁਸੀਂ ਯਕੀਨਨ ਗਰੁੱਪ ਲਿੰਕ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਲੋਕ ਹੁਣ ਮੌਜੂਦਾ ਲਿੰਕ ਦੀ ਵਰਤੋਂ ਨਾਲ ਗਰੁੱਪ ਵਿੱਚ ਸ਼ਾਮਲ ਨਹੀਂ ਹੋ ਸਕਣਗੇੇ। @@ -1507,6 +1515,7 @@ ਕੀ %1$d ਨੂੰ SMS ਸੱਦੇ ਭੇਜਣੇ ਹਨ? ਆਓ Signal ਨੂੰ ਅਪਣਾਈਏ: %1$s + ਇੰਜ ਜਾਪਦਾ ਹੈ ਕਿ ਤੁਹਾਡੇ ਕੋਲ ਸਾਂਝੀਆਂ ਕਰਨ ਲਈ ਕੋਈ ਐਪਸ ਨਹੀਂ ਹਨ| @@ -1931,6 +1940,7 @@ ਕੀ ਤੁਸੀਂ %1$s ਨੂੰ ਤੁਹਾਨੂੰ ਸੁਨੇਹਾ ਭੇਜਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣਾ ਚਾਹੁੰਦੇ ਹੋ ਅਤੇ ਉਹਨਾਂ ਨਾਲ ਆਪਣਾ ਨਾਂ ਅਤੇ ਫ਼ੋਟੋ ਸਾਂਝੀ ਕਰਨੀ ਚਾਹੁੰਦੇ ਹੋ? ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ ਉੱਤੋਂ ਪਾਬੰਦੀ ਨਹੀਂ ਹਟਾਉਂਦੇ, ਉਦੋਂ ਤੱਕ ਤੁਹਾਨੂੰ ਕੋਈ ਵੀ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਣਗੇ। ਕੀ ਤੁਸੀਂ %1$s ਨੂੰ ਤੁਹਾਨੂੰ ਸੁਨੇਹਾ ਭੇਜਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣਾ ਚਾਹੁੰਦੇ ਹੋ? ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ ਉੱਤੋਂ ਪਾਬੰਦੀ ਨਹੀਂ ਹਟਾਉਂਦੇ, ਉਦੋਂ ਤੱਕ ਤੁਹਾਨੂੰ ਕੋਈ ਵੀ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਣਗੇ। + ਕੀ ਤੁਸੀਂ %1$s ਤੋਂ ਅੱਪਡੇਟਾਂ ਅਤੇ ਖ਼ਬਰਾਂ ਪ੍ਰਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ ਉੱਤੋਂ ਪਾਬੰਦੀ ਨਹੀਂ ਹਟਾਉਂਦੇ, ਉਦੋਂ ਤੱਕ ਤੁਹਾਨੂੰ ਕੋਈ ਵੀ ਅੱਪਡੇਟ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਵੇਗੀ। ਇਸ ਗਰੁੱਪ ਵਿੱਚ ਆਪਣੀ ਚੈਟ ਅਤੇ ਇਸਦੇ ਮੈਂਬਰਾਂ ਨਾਲ ਆਪਣਾ ਨਾਂ ਅਤੇ ਫ਼ੋਟੋ ਸਾਂਝੀ ਕਰਨੀ ਜਾਰੀ ਰੱਖਣੀ ਹੈ? ਇਸ ਲੈਗਸੀ ਗਰੁੱਪ ਨੂੰ ਹੁਣ ਹੋਰ ਨਹੀਂ ਵਰਤਿਆ ਜਾ ਸਕਦਾ। @mentions ਅਤੇ ਐਡਮਿਨ ਵਰਗੇ ਨਵੇਂ ਫੀਚਰਾਂ ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਕਰਨ ਲਈ ਇੱਕ ਨਵਾਂ ਗਰੁੱਪ ਬਣਾਓ। @@ -2245,6 +2255,7 @@ ਇੱਥੇ ਹੋਰ ਕੋਈ ਨਹੀਂ ਹੈ %1$s ਇਸ ਕਾਲ ਵਿੱਚ ਹੈ + %1$s ਇਸ ਕਾਲ ਵਿੱਚ ਹਨ %1$s ਅਤੇ %2$s ਇਸ ਕਾਲ ਵਿੱਚ ਹਨ @@ -2647,7 +2658,7 @@ ਅਸੀਂ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਾਂਗੇ। Signal ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ ਤੁਹਾਨੂੰ ਆਪਣੇ-ਆਪ ਵਰਜ਼ਨ %1$s ਉੱਤੇ ਅੱਪਡੇਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ। - You updated to version %1$s. + ਤੁਸੀਂ %1$s ਵਰਜ਼ਨ ਵਿੱਚ ਅੱਪਡੇਟ ਕਰ ਲਿਆ ਹੈ। ਸੁਨੇਹਾ ਭੇਜਣਾ ਹੈ? @@ -3067,6 +3078,7 @@ ਅਟੈਚਮੈਂਟ ਥੰਬਨੇਲ ਤੁਰੰਤ ਕੈਮਰਾ ਅਟੈਚਮੈਂਟ ਦਰਾਜ਼ ਨੂੰ ਟੌਗਲ ਕਰੋ ਰਿਕਾਰਡ ਅਤੇ ਆਡੀਓ ਅਟੈਚਮੈਂਟ ਭੇਜੋ + ਆਡੀਓ ਅਟੈਚਮੈਂਟ ਦੀ ਰਿਕਾਰਡਿੰਗ ਲੌਕ ਕਰੋ ਸੁਨੇਹਾ ਭੇਜਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ। ਆਪਣੇ ਕਨੈਕਸ਼ਨ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। @@ -3085,6 +3097,7 @@ ਸੁਨੇਹਾ ਪੜ੍ਹਿਆ ਗਿਆ + ਸੰਪਰਕ ਫੋਟੋ @@ -3169,6 +3182,7 @@ ਹੁਣ ਹਰ ਚੀਜ਼ ਠੀਕ ਜਾਪਦੀ ਹੈ! ਕਾਲ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ, ਇੱਥੇ ਟੈਪ ਕਰੋ ਅਤੇ \"ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ\" ਨੂੰ ਚਾਲੂ ਕਰੋ। ਕਾਲ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ, ਇੱਥੇ ਟੈਪ ਕਰੋ ਅਤੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰੋ ਤੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਅਵਾਜ਼ ਅਤੇ ਪੌਪ-ਅੱਪ ਸਮਰੱਥ ਕੀਤੇ ਹੋਣ। + ਕਾਲ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ, ਇੱਥੇ ਟੈਪ ਕਰੋ ਅਤੇ \"ਬੈਟਰੀ\" ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਪਿਛੋਕੜ ਗਤੀਵਿਧੀ ਨੂੰ ਸਮਰੱਥ ਕਰੋ। ਸੈਟਿੰਗਾਂ ਕਾਲ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ, ਸੈਟਿੰਗਾਂ ’ਤੇ ਟੈਪ ਕਰੋ ਅਤੇ \"ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ\" ਨੂੰ ਚਾਲੂ ਕਰੋ। @@ -3306,7 +3320,7 @@ ਸਾਹ ਲਵੋ ਕੁਝ ਨਵਾਂ ਬਣਾ ਰਹੇ ਹਾਂ - One or more characters is invalid. + ਇੱਕ ਜਾਂ ਵੱਧ ਅੱਖਰ ਅਵੈਧ ਹਨ। ਗਰੁੱਪ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰੋ @@ -3405,6 +3419,7 @@ + ਸਹਿਯੋਗ ਜਾਣਕਾਰੀ Signal ਐਂਡਰਾਈਡ ਸਹਿਯੋਗ ਬੇਨਤੀ @@ -3493,6 +3508,7 @@ ਹੁਣ ਤੁਹਾਡੇ ਦੁਆਰਾ ਭੇਜੇ ਗਏ ਸੁਨੇਹਿਆਂ ਲਈ ਸਿੱਧੇ ਕਿਸੇ ਵੀ ਵੈੱਬਸਾਈਟ ਤੋਂ ਲਿੰਕ ਝਲਕ ਪ੍ਰਾਪਤ ਕਰੋ। ਪਾਸਫ਼੍ਰੇਜ਼ ਬਦਲੋ ਆਪਣਾ ਪਾਸਫ਼੍ਰੇਜ਼ ਬਦਲੋ + ਪਾਸਫ਼੍ਰੇਜ਼ ਸਕਰੀਨ ਲੌਕ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ ਪਾਸਫ਼੍ਰੇਜ਼ ਦੇ ਨਾਲ ਸਕਰੀਨ ਅਤੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਲੌਕ ਕਰੋ ਸਕ੍ਰੀਨ ਸੁਰੱਖਿਆ @@ -3505,6 +3521,7 @@ LED ਝਪਕੋ ਪੈਟਰਨ ਕਸਟਮਾਈਜ਼ ਕਰੋ ਧੁਨ ਅਤੇ ਵਾਈਬ੍ਰੇਸ਼ਨ ਬਦਲੋ + ਆਵਾਜ਼ ਚੁੱਪ ਡਿਫੌਲਟ @@ -5378,6 +5395,7 @@ ਸਟੋਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ ਸੁਨੇਹਾ ਜੋੜੋ + ਜਵਾਬ ਜੋੜੋ ਇਹਨਾਂ ਨੂੰ ਭੇਜੋ ਮੀਡੀਆ ਜੋ ਇੱਕ ਵਾਰ ਦੇਖਿਆ ਜਾ ਸਕਦਾ ਹੈ @@ -5982,6 +6000,8 @@ %1$d ਜਵਾਬ %1$d ਜਵਾਬ + + Story no longer hidden ਜੋੜੋ @@ -7267,7 +7287,23 @@ - ਆਪਣਾ ਮੀਡੀਆ ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ %1$s ਥਾਂ ਖਾਲੀ ਕਰੋ। + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ਠੀਕ ਹੈ - - - ਭੁਗਤਾਨ ਦਾ ਇਤਿਹਾਸ - - ਟੈਕਸਟ ਅਤੇ ਸਾਰੇ ਮੀਡੀਏ ਦਾ ਬੈਕਅੱਪ - - ਭੁਗਤਾਨ ਦੇ ਵੇਰਵੇ - - ਬੈਕਅੱਪ ਦੀ ਕਿਸਮ - - ਭੁਗਤਾਨ ਕਰਨ ਦੀ ਮਿਤੀ - - ਸਾਂਝਾ ਕਰੋ + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ ਬੈਕਅੱਪ ਦੀ ਬਾਰੰਬਾਰਤਾ ਸੈਲੂਲਰ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਬੈਕਅੱਪ ਲਓ + + View backup key + + Unlock to view backup key ਬੈਕਅੱਪ ਬੰਦ ਕਰੋ ਅਤੇ ਮਿਟਾਓ @@ -7370,13 +7406,27 @@ ਬੈਕਅੱਪ ਰਾਤੋ-ਰਾਤ ਬਣਾਇਆ ਜਾਵੇਗਾ। - ਬੈਕਅੱਪ ਦੀ ਕਿਸਮ + Backup plan ਬੈਕਅੱਪ ਨੂੰ ਅਸਮਰੱਥ ਕੀਤਾ ਗਿਆ - - %1$s · %2$s/ਮਹੀਨਾ - - ਬੈਕਅੱਪ ਨੂੰ ਸਮਰੱਥ ਕਰੋ + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + ਤੁਹਾਡੀ ਬੈਕਅੱਪ ਕੁੰਜੀ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + ਤੁਹਾਡੀ ਬੈਕਅੱਪ ਕੁੰਜੀ ਇੱਕ 64-ਅੰਕਾਂ ਵਾਲਾ ਕੋਡ ਹੈ ਜਿਸ ਦੀ ਮਦਦ ਨਾਲ ਤੁਸੀਂ Signal ਨੂੰ ਮੁੜ-ਇੰਸਟਾਲ ਕਰਨ ਤੋਂ ਬਾਅਦ ਆਪਣਾ ਬੈਕਅੱਪ ਰੀਸਟੋਰ ਕਰ ਸਕਦੇ ਹੋ। - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + ਜੇਕਰ ਤੁਸੀਂ ਆਪਣੀ ਕੁੰਜੀ ਭੁੱਲ ਜਾਂਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਆਪਣਾ ਬੈਕਅੱਪ ਰੀਸਟੋਰ ਨਹੀਂ ਕਰ ਸਕੋਗੇ। Signal ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਨੂੰ ਰਿਕਵਰ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਨਹੀਂ ਕਰ ਸਕੇਗਾ। - Next + ਅੱਗੇ - Record your backup key + ਆਪਣੀ ਬੈਕਅੱਪ ਕੁੰਜੀ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + ਇਹ ਕੁੰਜੀ ਤੁਹਾਡੇ ਖਾਤੇ ਅਤੇ ਡੇਟਾ ਨੂੰ ਰਿਕਵਰ ਕਰਨ ਲਈ ਲੋੜੀਂਦੀ ਹੈ। ਇਸ ਕੁੰਜੀ ਨੂੰ ਕਿਤੇ ਸੁਰੱਖਿਅਤ ਰੱਖੋ। ਜੇਕਰ ਤੁਸੀਂ ਇਸਨੂੰ ਗੁਆ ਦਿੰਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਆਪਣਾ ਖਾਤਾ ਰਿਕਵਰ ਨਹੀਂ ਕਰ ਸਕੋਗੇ। - Copy to clipboard + ਕਲਿਪਬੋਰਡ \'ਤੇ ਕਾਪੀ ਕਰੋ - Next + ਅੱਗੇ - Keep your key safe + ਆਪਣੀ ਕੁੰਜੀ ਸੁਰੱਖਿਅਤ ਰੱਖੋ - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + ਜੇਕਰ ਤੁਸੀਂ ਆਪਣੀ ਕੁੰਜੀ ਗੁਆ ਦਿੰਦੇ ਹੋ ਤਾਂ Signal ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਨੂੰ ਰੀਸਟੋਰ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਨਹੀਂ ਕਰ ਸਕੇਗਾ। ਇਸਨੂੰ ਕਿਸੇ ਸੁਰੱਖਿਅਤ ਥਾਂ \'ਤੇ ਸਟੋਰ ਕਰੋ ਅਤੇ ਇਸਨੂੰ ਦੂਜਿਆਂ ਨਾਲ ਸਾਂਝਾ ਨਾ ਕਰੋ। - I\'ve recorded my key + ਮੈਂ ਆਪਣੀ ਕੁੰਜੀ ਰਿਕਾਰਡ ਕਰ ਲਈ ਹੈ - Continue + ਜਾਰੀ ਰੱਖੋ - See key again + ਕੁੰਜੀ ਨੂੰ ਦੁਬਾਰਾ ਦੇਖੋ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e8cf25a207..a6509f41dc 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Tak Nie Usuń @@ -264,6 +267,7 @@ Dotknij, aby zrobić zdjęcie. Przytrzymaj, aby nagrać wideo. + Zrób zdjęcie Zmień aparat Otwórz galerię @@ -352,7 +356,7 @@ Ten link jest nieprawidłowy. Upewnij się, że link nie został ucięty i jest poprawny, zanim spróbujesz dołączyć. - You are already in a call + Uczestniczysz już w rozmowie @@ -409,6 +413,7 @@ Nie można znaleźć aplikacji, aby otworzyć te multimedia. Skopiowano %1$s od %1$s + do %1$s   Czytaj dalej   Pobierz więcej @@ -442,6 +447,7 @@ Wyślij edycję Nowa wiadomość Przepraszamy, wystąpił błąd podczas dodawania załącznika. + Podany numer lub adres e-mail jest niepoprawny! Wiadomość jest pusta! Członkowie grupy @@ -598,6 +604,7 @@ Zapisywanie %1$d załączników na dysku… Oczekiwanie… + Dane (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Odłączyć \"%1$s\"? Wyrejestrowanie tego urządzenia spowoduje, że nie będziesz miał(a) możliwości wysyłania i odbierania wiadomości. Błąd połączenia sieciowego + Spróbuj ponownie Odłączanie urządzenia… Wyrejestrowywanie urządzenia @@ -1488,7 +1496,7 @@ Link do grupy Udostępnij Zresetuj link - Require admin approval + Wymagaj potwierdzenia administratora Wymagaj akceptacji nowych członków, dołączających do grupy przez link, przez administratora. Na pewno chcesz zresetować link do grupy? Ludzie nie będą mogli dołączyć do grupy, używając obecnego linku. @@ -1593,6 +1601,7 @@ Wysłać %1$d zaproszeń SMS? Zacznij używać Signal: %1$s + Wygląda na to, że nie masz żadnych aplikacji z którymi możesz się podzielić. @@ -2047,6 +2056,7 @@ Pozwolić %1$s wysyłać do Ciebie wiadomości i udostępnić Twoje imię i zdjęcie? Nie otrzymasz żadnych wiadomości, dopóki nie odblokujesz tego kontaktu. Pozwolić %1$s wysyłać Ci wiadomości? Nie otrzymasz wiadomości od tego użytkownika, dopóki go nie odblokujesz. + Czy chcesz otrzymywać informacje i nowości od %1$s? Nie otrzymasz żadnych informacji, dopóki nie odblokujesz tego kontaktu. Kontynuować czat z tą grupą i udostępnić Twoje imię i zdjęcie jej członkom? To Stara grupa, z której nie możesz już korzystać. Utwórz nową grupę, aby móc korzystać z takich nowych funkcji jak @wzmianki i administratorzy. @@ -2393,6 +2403,7 @@ Brak innych uczestników rozmowy %1$s uczestniczy w rozmowie + %1$s uczestniczysz w rozmowie %1$s i %2$s uczestniczą w rozmowie @@ -2817,7 +2828,7 @@ Spróbujemy ponownie później. Signal został pomyślnie zaktualizowany Aplikacja została automatycznie zaktualizowana do wersji %1$s. - You updated to version %1$s. + Zaktualizowano Signal do wersji %1$s. Wysłać wiadomość? @@ -3251,6 +3262,7 @@ Minaturka załącznika Bezpośrednie przechwytywanie Nagraj i wyślij załącznik dźwiękowy + Zablokuj nagrywanie załącznika audio Nie udało się wysłać wiadomości. Sprawdź połączenie z internetem i spróbuj ponownie. @@ -3269,6 +3281,7 @@ Wiadomość odczytana + Zdjęcie kontaktu @@ -3353,6 +3366,7 @@ Teraz wszystko wygląda dobrze! Aby otrzymywać powiadomienia o połączeniach, stuknij tutaj i włącz \"Pokazuj powiadomienia\". Aby otrzymywać powiadomienia o połączeniach, stuknij tutaj, włącz powiadomienia i upewnij się, że dźwięk i komunikaty są włączone. + Aby otrzymywać powiadomienia o połączeniach, stuknij tutaj i włącz aktywność w tle w sekcji ustawień \"Bateria\". Ustawienia Aby otrzymywać powiadomienia o połączeniach, stuknij Ustawienia i włącz \"Pokazuj powiadomienia\". @@ -3504,7 +3518,7 @@ Robię przerwę Pracuję nad czymś nowym - One or more characters is invalid. + Co najmniej jeden znak jest nieprawidłowy. Edytuj grupę @@ -3603,6 +3617,7 @@ + Informacje dodatkowe Zgłoszenie pomocy technicznej Signal Android @@ -3693,6 +3708,7 @@ Pobieraj podglądy linków we wiadomościach bezpośrednio ze stron internetowych. Zmień hasło Zmień swoje hasło + Włącz blokowanie ekranu hasłem Zablokuj ekran i powiadomienia hasłem Ochrona ekranu @@ -3705,6 +3721,7 @@ Wzór powiadomień LED Dostosuj Zmień dźwięk i wibracje + Dźwięk Wycisz Domyślny @@ -5632,6 +5649,7 @@ Dodaj do relacji Dodaj wiadomość + Dodaj odpowiedź Wyślij do Multimedia jednorazowe @@ -6246,6 +6264,8 @@ %1$d odpowiedzi %1$d odpowiedzi + + Story no longer hidden Dodaj @@ -7583,7 +7603,23 @@ - Aby pobrać multimedia, zwolnij %1$s miejsca. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ OK - - - Historia płatności - - Kopia zapasowa wiadomości tekstowych i wszystkich multimediów - - Szczegóły płatności - - Typ kopii zapasowej - - Data zapłaty - - Udostępnij + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Częstotliwość tworzenia kopii zapasowych Tworzenie kopii zapasowej przy użyciu sieci komórkowej + + View backup key + + Unlock to view backup key Usuń kopie zapasowe i wyłącz tworzenie kolejnych @@ -7686,13 +7722,27 @@ Kopia zapasowa będzie tworzona codziennie w nocy. - Typ kopii zapasowej + Backup plan Kopie zapasowe wyłączone - - %1$s · %2$s/miesiąc - - Włącz kopie zapasowe + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Twój kod kopii zapasowej - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Twój 64-cyfrowy kod kopii zapasowej umożliwi Ci przywrócenie kopii zapasowej po ponownej instalacji Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Jeśli go zapomnisz, stracisz możliwość przywrócenia kopii zapasowej. Signal nie będzie mógł pomóc w odzyskaniu kopii zapasowej. - Next + Dalej - Record your backup key + Zapisz kod kopii zapasowej - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Ten kod będzie potrzebny do odzyskania konta i danych. Przechowuj go w bezpiecznym miejscu. Jeśli go zgubisz, stracisz możliwość przywrócenia konta. - Copy to clipboard + Skopiuj do schowka - Next + Dalej - Keep your key safe + Zapisz kod w bezpiecznym miejscu - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Jeśli zgubisz kod, Signal nie będzie mógł Ci pomóc w przywróceniu kopii zapasowej. Przechowuj kod w bezpiecznym miejscu i nie udostępniaj innym osobom. - I\'ve recorded my key + Kod zapisany - Continue + Kontynuuj - See key again + Wyświetl kod ponownie diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 9b56c0fa3c..1eb91aa1ce 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Sim Não Excluir @@ -260,6 +263,7 @@ Toque para foto, segure para vídeo + Capturar Mudar de câmera Abrir galeria @@ -346,7 +350,7 @@ Este link de chamada não é válido. Confira se o link foi alterado. Se for esse o caso, corrija e tente entrar novamente. - You are already in a call + Você já está em uma chamada @@ -403,6 +407,7 @@ Não foi possível encontrar um aplicativo para abrir este arquivo. Copiado %1$s de %1$s + para %1$s   Leia mais   Baixe mais @@ -436,6 +441,7 @@ Mandar versão editada Escrever mensagem Desculpe, ocorreu um erro ao anexar o documento. + O destinatário não é um endereço de SMS ou de email válido! A mensagem está vazia! Membros do grupo @@ -576,6 +582,7 @@ Salvando %1$d anexos no armazenamento… Pendente… + Dados (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Desvincular \'%1$s\'? Ao desvincular este dispositivo, ele não será mais capaz de enviar ou receber mensagens. Falha na conexão de rede + Tentar de novo Desvinculando dispositivo… Desvinculando dispositivo @@ -1408,7 +1416,7 @@ Link do grupo Compartilhar Redefinir o link - Require admin approval + Requer aprovação do admin Exigir que um administrador aprove pedidos de novos membros que entrarem através do link do grupo. Tem certeza que quer redefinir o link do grupo? As pessoas não poderão mais se conectar ao grupo usando o link atual. @@ -1507,6 +1515,7 @@ Enviar %1$d convites por SMS? Vamos mudar para o Signal: %1$s + Parece que você não tem aplicativos para onde compartilhar. @@ -1931,6 +1940,7 @@ Permitir que %1$s envie mensagens para você e veja o seu nome e foto? Você não receberá mensagens dessa pessoa até que a desbloqueie. Permitir que %1$s envie mensagens para você? Você não receberá mensagens dessa pessoa até que a desbloqueie. + Receber atualizações e novidades de %1$s? Você não receberá nenhuma atualização dessa pessoa até que a desbloqueie. Continuar seu chat com este grupo e exibir seu nome e foto para os membros? Este Grupo Legado não pode ser mais usado. Crie um novo grupo para ativar novos recursos como como @menções e admins. @@ -2245,6 +2255,7 @@ Ninguém mais está aqui %1$s está nesta chamada + %1$s estão nesta chamada %1$s e %2$s estão nesta chamada @@ -2647,7 +2658,7 @@ Tentaremos novamente mais tarde. Signal atualizado Seu app foi atualizado automaticamente para a versão %1$s. - You updated to version %1$s. + Você fez uma atualização para a versão %1$s. Enviar mensagem? @@ -3067,6 +3078,7 @@ Miniatura do anexo Alternar gaveta rápida de anexos de câmera Gravar e enviar anexo de áudio + Trancar gravação de anexo de áudio A mensagem não pôde ser enviada. Verifique sua conexão e tente novamente. @@ -3085,6 +3097,7 @@ Mensagem lida + Foto do contato @@ -3169,6 +3182,7 @@ Está tudo ótimo, agora! Para receber notificações de chamadas, toque aqui e ative \"Mostrar notificações\". Para receber notificações de chamadas, toque aqui e ative as notificações. Certifique-se de que o Som e o Pop-up da notificação estejam ativados. + Para receber notificações de chamadas, toque aqui e habilite a atividade em segundo plano nas configurações da \"Bateria\". Configurações Para receber notificações de chamadas, toque em Configurações e ative \"Mostrar notificações\". @@ -3306,7 +3320,7 @@ Relaxando Trabalhando em algo novo - One or more characters is invalid. + Um ou mais caracteres são inválidos. Editar grupo @@ -3405,6 +3419,7 @@ + Info de Suporte Solicitação de suporte Signal Android @@ -3493,6 +3508,7 @@ Carregar pré-visualizações de links diretamente dos sites em mensagens que você envia. Alterar frase-chave Alterar sua frase-chave + Habilitar o bloqueio da tela com frase-chave Bloquear tela e notificações com frase-chave Segurança da tela @@ -3505,6 +3521,7 @@ Padrão de piscagem do LED Personalizar Mudar som e vibração + Som Silencioso Padrão @@ -5378,6 +5395,7 @@ Adicionar ao story Adicionar uma mensagem + Responder Enviar para Mídia temporária @@ -5982,6 +6000,8 @@ %1$d resposta %1$d respostas + + Story no longer hidden Adicionar @@ -7267,7 +7287,23 @@ - Libere %1$s de espaço para baixar seu arquivo de mídia. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Histórico de pagamentos - - Backup de mensagens de texto e arquivos de mídia - - Detalhes de pagamento - - Tipo de backup - - Data de pagamento - - Compartilhar + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Frequência de backup Fazer backup usando dados móveis + + View backup key + + Unlock to view backup key Desativar e excluir backups? @@ -7370,13 +7406,27 @@ O backup será criado durante a noite. - Tipo de backup + Backup plan Backups desativados - - %1$s · %2$s/mês - - Habilitar backups + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Sua chave de backup - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Sua chave de backup é um código de 64 dígitos que permite restaurar seu backup ao reinstalar o Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Se você esquecer sua chave, não será possível restaurar o backup. O Signal não tem como ajudar na recuperação do seu backup. - Next + Próximo - Record your backup key + Grave sua chave de backup - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Essa chave é necessária para restaurar sua conta e seus dados. Certifique-se de guardá-la em um local seguro. Sem essa chave, não será possível recuperar sua conta. - Copy to clipboard + Copiar para a área de transferência - Next + Próximo - Keep your key safe + Mantenha sua chave segura - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + O Signal não poderá te ajudar a restaurar seu backup se você perder sua chave. Guarde a chave em algum lugar seguro e não a compartilhe com ninguém. - I\'ve recorded my key + Eu gravei minha chave - Continue + Continuar - See key again + Ver chave novamente diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 2c0800f0ca..aa9be4bfe0 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Sim Não Eliminar @@ -260,6 +263,7 @@ Toque para fotografia, mantenha premido para vídeo + Capturar Alterar câmara Abrir galeria @@ -346,7 +350,7 @@ Este não é um link de chamada válido. Confirme se o link inteiro está intacto e correto antes de tentar entrar. - You are already in a call + Já se encontra numa chamada @@ -403,6 +407,7 @@ Não foi possível encontrar uma aplicação capaz de abrir este ficheiro. Copiado %1$s de %1$s + para %1$s   Ler mais   Descarregar mais @@ -436,6 +441,7 @@ Enviar edição Compor mensagem Lamentamos mas ocorreu um erro com o envio do seu anexo. + O destinatário não é um número de SMS ou endereço de e-mail válido! A mensagem encontra-se vazia! Membros do grupo @@ -576,6 +582,7 @@ A guardar %1$d anexos para o armazenamento local… Pendente… + Dados (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Desassociar \'%1$s\'? Ao desassociar este dispositivo, ele irá deixar de poder enviar ou receber mensagens. Falha na ligação à rede + Tentar novamente A desassociar dispositivo… A desassociar dispositivo @@ -1408,7 +1416,7 @@ Link do grupo Partilhar Redefinir link - Require admin approval + Requerer aprovação do administrador Solicite a um administrador que aprove a entrada de novos membros através do link para o grupo. Tem a certeza que deseja redefinir o link para o grupo? As pessoas deixarão de poder entrar para o grupo através do link atual. @@ -1507,6 +1515,7 @@ Enviar %1$d convites por SMS? Vamos mudar para o Signal: %1$s + Parece que não tem aplicações para partilhar. @@ -1931,6 +1940,7 @@ Quer partilhar o seu nome e foto com o utilizador %1$s e permitir que este lhe envie mensagens? Não receberá nenhuma mensagem deste utilizador até o desbloquear. Quer deixar que %1$s lhe envie mensagens? Não receberá nenhuma mensagem deste utilizador até o desbloquear. + Obter atualizações e novidades de %1$s? Não receberá nenhuma atualização desse utilizador até o desbloquear. Deseja continuar este chat com este grupo e partilhar o seu nome e fotografia com eles? Este \"grupo legado\" já não pode ser usado. Crie um novo grupo para ativar novas funcionalidades como @menções e admins. @@ -2245,6 +2255,7 @@ Não se encontra mais ninguém aqui %1$s está nesta chamada + %1$s estão nesta chamada %1$s e %2$s estão nesta chamada @@ -2647,7 +2658,7 @@ Tentaremos mais tarde. Signal atualizado com sucesso Foi atualizado automaticamente para a versão %1$s. - You updated to version %1$s. + Atualizou para a versão %1$s. Enviar mensagem? @@ -3067,6 +3078,7 @@ Miniatura de anexo Comutar a câmara rápida da gaveta de anexos Gravar e enviar anexo de áudio + Bloquear a gravação de um anexo de áudio A mensagem não pode ser enviada. Verifique a sua ligação e tente novamente. @@ -3085,6 +3097,7 @@ Mensagem lida + Fotografia de contacto @@ -3169,6 +3182,7 @@ Agora tudo parece estar bem! Para receber notificações de chamadas, toque aqui e ative \"Mostrar notificações\". Para receber notificações de chamadas, toque aqui e ative as notificações e assegure-se que o \'Som\' e os \'Pop-up\' se encontram ativos. + Para receber notificações de chamadas, toque aqui e ative ativar atividade em segundo plano nas definições de \"Bateria\". Definições Para receber notificações de chamadas, toque em \'Definições\' e ative \"Mostrar notificações\". @@ -3306,7 +3320,7 @@ A fazer uma pausa A trabalhar em algo novo - One or more characters is invalid. + Um ou mais caracteres são inválidos. Editar o grupo @@ -3405,6 +3419,7 @@ + Informação de suporte Pedido de Suporte do Signal para Android @@ -3493,6 +3508,7 @@ Recupere pré-visualizações de links diretamente a partir de websites para as mensagens que enviar. Alterar frase-chave Alterar a sua frase-chave + Ativar o bloqueio de ecrã com frase-chave Bloquear ecrã e notificações com uma frase-chave Segurança de ecrã @@ -3505,6 +3521,7 @@ Padrão do piscar do LED Personalizar Alterar som e vibração + Som Silencioso Predefinição @@ -5378,6 +5395,7 @@ Adicionar à história Adicionar uma mensagem + Adicionar uma resposta Enviar para Ver multimédia de visualização única @@ -5982,6 +6000,8 @@ %1$d resposta %1$d respostas + + Story no longer hidden Adicionar @@ -7267,7 +7287,23 @@ - Liberte %1$s de espaço para transferir o ficheiro. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Histórico de pagamentos - - Cópia de segurança de texto e todos os ficheiros - - Detalhes do pagamento - - Tipo de cópia de segurança - - Data de pagamento - - Partilhar + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Frequência da cópia de segurança Fazer cópia de segurança utilizando ligação móvel + + View backup key + + Unlock to view backup key Desative e elimine a cópia de segurança @@ -7370,13 +7406,27 @@ A cópia de segurança será criada durante a noite. - Tipo de cópia de segurança + Backup plan Cópias de segurança desativadas - - %1$s · %2$s/mês - - Ativar cópias de segurança + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + A sua chave da cópia de segurança - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + A sua chave da cópia de segurança é um código de 64 dígitos que lhe permite restaurar a sua cópia de segurança ao reinstalar o Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Se esquecer a sua chave, não poderá restaurar a cópia de segurança. O Signal não o pode ajudar a restaurar a sua cópia de segurança. - Next + Seguinte - Record your backup key + Anote a sua chave da cópia de segurança - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Esta chave é necessária para recuperar a sua conta e dados. Guarde esta chave num local seguro. Se a perder, não poderá recuperar a sua conta. - Copy to clipboard + Copiar para a área de transferência - Next + Seguinte - Keep your key safe + Mantenha a sua chave segura - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Caso perca a sua chave, o Signal não o poderá ajudar a restaurar a sua cópia de segurança. Guarde-a num local seguro e não a partilhe com outras pessoas. - I\'ve recorded my key + Anotei a minha chave - Continue + Continuar - See key again + Ver chave outra vez diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index bab9d5fea3..15256bfef2 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Da Nu Șterge @@ -262,6 +265,7 @@ Atinge pentru poză, ține apăsat pentru videoclip + Capturează Schimbă camera Deschide galerie @@ -349,7 +353,7 @@ Link-ul de apel nu este valid. Asigură-te că întregul link este intact și corect înainte de a te alătura grupului. - You are already in a call + Ești deja într-un apel @@ -406,6 +410,7 @@ Nu pot găsi o aplicație pentru a deschide acest tip media. Copiat %1$s de la %1$s + către %1$s   Află mai Multe   Descarcă mai Multe @@ -439,6 +444,7 @@ Trimite editare Compune mesaj Ne pare rău, a apărut o eroare în setarea atașamentului tău. + Destinatarul nu este o adresă validă de SMS sau email! Mesajul este gol! Membri grup @@ -587,6 +593,7 @@ Se salvează %1$d de atașamente pe spațiul de stocare… În curs… + Date (Signal) MMS SMS @@ -1026,6 +1033,7 @@ Disociez \'%1$s\'? Prin disocierea acestui dispozitiv, nu va mai putea trimite și primi mesaje. Conexiunea la rețea a eșuat + Încearcă din nou Se disociază dispozitivul… Se disociază dispozitivul @@ -1448,7 +1456,7 @@ Link grup Distribuie Resetare link - Require admin approval + Necesită aprobarea administratorului Solicită unui administrator să aprobe membri noi care se alătură prin linkul grupului. Sigur dorești să resetezi linkul de grup? Oamenii nu vor mai putea să se alăture grupului folosind linkul curent. @@ -1550,6 +1558,7 @@ Trimit %1$d de invitații prin SMS? Hai să folosim Signal: %1$s + Se pare că nu ai aplicații cu care să poți distribui. @@ -1989,6 +1998,7 @@ Permiți lui %1$s să îți trimită mesaje și îți împărtășești numele și fotografia cu ei? Nu vei primi niciun mesaj până când nu îi deblochezi. Vrei să primești mesaje de la %1$s? Nu vei primi niciun mesaj până când contactul este deblocat. + Vrei să primești știri și actualizări de la %1$s? Nu vei primi nicio actualizare până când nu deblochezi. Continui conversația cu acest grup și partajezi numele și fotografia ta cu membrii săi? Acest grup vechi nu mai poate fi utilizat. Creează un grup nou pentru a activa funcții noi, cum ar fi @mențiuni și administratori. @@ -2319,6 +2329,7 @@ Nu este nimeni aici %1$s este în acest apel + %1$s ești în acest apel %1$s și %2$s sunt în acest apel @@ -2732,7 +2743,7 @@ Vom încerca din nou mai târziu. Signal a fost actualizat cu succes Ai primit o actualizare automată la versiunea %1$s. - You updated to version %1$s. + Ai actualizat la versiunea %1$s. Trimit mesajul? @@ -3159,6 +3170,7 @@ Miniatură atașament Comutare panou cu atașamente de la camera rapidă Înregistrează și trimite un atașament audio + Blochează înregistrarea atașamentului audio Mesajul nu a putut fi trimis. Verifică-ți conexiunea și încearcă din nou. @@ -3177,6 +3189,7 @@ Mesaj citit + Poză contact @@ -3261,6 +3274,7 @@ Acum totul arată bine! Pentru a primi notificări de apel, atinge aici și activează \"Afișare notificări\". Pentru a primi notificări de apel, atinge aici și activează notificările și asigură-te că sunetul și fereastra pop-up sunt activate. + Pentru a primi notificări de apel, atinge aici și activează activitatea de fundal în setările de \"Baterie\". Setări Pentru a primi notificări de apel, atinge Setări și activează „Afișare notificări”. @@ -3405,7 +3419,7 @@ Într-o pauză Lucrez la ceva nou - One or more characters is invalid. + Unul sau mai multe caractere sunt nevalide. Editează grupul @@ -3504,6 +3518,7 @@ + Informații suport Cerere suport Signal Android @@ -3593,6 +3608,7 @@ Obține previzualizările link-urilor direct de pe site-urile web pentru mesajele pe care le trimiți. Schimbă parola Schimbă-ți parola + Activează parola pentru ecranul de blocare Blochează ecranul și notificările cu o parolă Securitate ecran @@ -3605,6 +3621,7 @@ Tipar de clipire LED Personalizează Modifică sunetul și vibrațiile + Sunet Silențios Implicit @@ -5505,6 +5522,7 @@ Adaugă la poveste Adaugă un mesaj + Adaugă un răspuns Trimite către Mijloc media vizibil o singură dată @@ -6114,6 +6132,8 @@ %1$d răspunsuri %1$d răspunsuri + + Story no longer hidden Adaugă @@ -7425,7 +7445,23 @@ - Eliberează %1$s spațiu pentru a-ți descărca fișierele media. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7488,19 +7524,15 @@ OK - - - Istoricul plăților - - Text și tot backup-ul media - - Detalii de plată - - Tipul de backup - - Data plății - - Distribuie + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7515,6 +7547,10 @@ Frecvența backup-ului Efectuează backup folosind date mobile + + View backup key + + Unlock to view backup key Dezactivează și elimină backup-ul @@ -7528,13 +7564,27 @@ Backup-ul va fi creat peste noapte. - Tipul de backup + Backup plan Backup-uri dezactivate - - %1$s - %2$s/lună - - Activează backup-urile + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7584,33 +7634,33 @@ - Your backup key + Codul tău de rezervă - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Codul de rezervă este un cod din 64 de cifre care îți permite să îți restaurezi backup-ul atunci când reinstalezi Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Dacă uiți codul, nu vei putea să îți restaurezi copia de rezervă. Signal nu te poate ajuta să îți recuperezi backup-ul. - Next + Următorul - Record your backup key + Înregistrează-ți codul de rezervă - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Codul e necesar ca să-ți recuperezi contul și datele. Păstrează codul într-un loc sigur. Dacă îl pierzi, nu îți vei putea recupera contul. - Copy to clipboard + Copiază în clipboard - Next + Următorul - Keep your key safe + Păstrează-ți codul în siguranță - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal nu te va putea ajuta să îți restaurezi backupul dacă îți pierzi codul. Păstrează-l într-un loc sigur și nu îl împărtăși cu alții. - I\'ve recorded my key + Mi-am înregistrat codul - Continue + Continuă - See key again + Vezi din nou codul diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f0d58163e7..0bf3f612b2 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Да Нет Удалить @@ -264,6 +267,7 @@ Нажмите для фото, удерживайте для видео + Снять Сменить камеру Открыть галерею @@ -352,7 +356,7 @@ Это недействительная ссылка на звонок. Убедитесь, что ссылка не повреждена и корректна, перед тем как попробовать присоединиться. - You are already in a call + Вы уже участвуете в другом звонке @@ -409,6 +413,7 @@ Не найдено приложение, которое может открыть этот медиафайл. Скопировано %1$s от %1$s + для %1$s   Читать далее   Скачать больше @@ -442,6 +447,7 @@ Отправить с изменениями Написать сообщение Извините, произошла ошибка при обработке вложения. + Адрес получателя не является ни номером телефона, ни адресом электронной почты! Пустое сообщение! Участники группы @@ -598,6 +604,7 @@ Сохраняем %1$d вложения в хранилище… Ожидание… + Интернет (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Отвязать «%1$s»? После отвязки это устройство больше не сможет отправлять или получать сообщения. Сбой сетевого подключения + Попробовать ещё раз Отвязываем устройство… Отвязка устройства @@ -1488,7 +1496,7 @@ Ссылка на группу Поделиться Сбросить ссылку - Require admin approval + Требовать одобрение администратора Администратор должен будет одобрять новых участников, присоединяющихся по ссылке на группу. Вы уверены, что хотите сбросить ссылку на группу? Больше никто не сможет присоединиться к группе по текущей ссылке. @@ -1593,6 +1601,7 @@ Отправить %1$d SMS-приглашений? Давайте перейдем на Signal: %1$s + Похоже, что у вас нет приложений, через которые можно поделиться. @@ -2047,6 +2056,7 @@ Разрешить %1$s отправлять вам сообщения и открыть ваше имя и фото этому человеку? Вы не получите никаких сообщений, пока не разблокируете его. Разрешить %1$s отправлять вам сообщения? Вы не получите никаких сообщений, пока не разблокируете этого человека. + Получать обновления и новости от %1$s? Вы не будете получать никаких сообщений, пока не разблокируете этого человека. Продолжить ваш чат с этой группой и открыть ваше имя и фото её участникам? Эта Старая группа больше не может использоваться. Создайте новую группу, чтобы активировать такие новые функции, как @упоминания и администраторы групп. @@ -2393,6 +2403,7 @@ Здесь нет никого, кроме вас %1$s в этом звонке + %1$s в этом звонке %1$s и %2$s в этом звонке @@ -2817,7 +2828,7 @@ Мы попробуем ещё раз позже. Signal успешно обновлён Вы были автоматически обновлены до версии %1$s. - You updated to version %1$s. + Вы обновились до версии %1$s. Отправить сообщение? @@ -3251,6 +3262,7 @@ Миниатюра вложения Быстрая камера Записать и отправить аудио-вложение + Зафиксировать запись аудиосообщения Не удалось отправить сообщение. Проверьте ваше подключение к интернету и попробуйте ещё раз. @@ -3269,6 +3281,7 @@ Сообщение прочитано + Фотография контакта @@ -3353,6 +3366,7 @@ Теперь всё в порядке! Чтобы получать уведомления о звонках, нажмите здесь и включите «Включить уведомления». Чтобы получать уведомления о звонках, нажмите здесь, включите уведомления и убедитесь, что «Оповещения» и «Всплывающее окно» включены. + Чтобы получать уведомления о звонках, нажмите здесь и включите фоновую активность в настройках батареи. Настройки Чтобы получать уведомления о звонках, нажмите «Настройки» и включите «Включить уведомления». @@ -3504,7 +3518,7 @@ Делаю перерыв Работаю над кое-чем новым - One or more characters is invalid. + Один или несколько символов недействительны. Редактировать группу @@ -3603,6 +3617,7 @@ + Информация для поддержки Запрос поддержки Signal Android @@ -3693,6 +3708,7 @@ Получать предпросмотры ссылок напрямую от сайтов для сообщений, которые вы отправляете. Изменить парольную фразу Изменить свою парольную фразу + Включить блокировку экрана парольной фразой Блокировка экрана и уведомлений с помощью парольной фразы Защита экрана @@ -3705,6 +3721,7 @@ Мигание светодиода Настроить Изменить звук и вибрацию + Мелодия Без звука По умолчанию @@ -5632,6 +5649,7 @@ Добавить к истории Добавьте сообщение + Добавьте ответ Отправить Просмотреть одноразовые медиа @@ -6246,6 +6264,8 @@ %1$d ответов %1$d ответов + + Story no longer hidden Добавить @@ -7583,7 +7603,23 @@ - Освободите %1$s места для загрузки медиафайлов. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ ОК - - - История платежей - - Резервное копирование сообщений и всех медиафайлов - - Подробности платежа - - Тип резервного копирования - - Дата оплаты - - Поделиться + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Частота резервного копирования Резервное копирование с помощью сотовых данных + + View backup key + + Unlock to view backup key Отключить и удалить резервную копию @@ -7686,13 +7722,27 @@ Резервная копия будет создана за одну ночь. - Тип резервного копирования + Backup plan Резервное копирование отключено - - %1$s · %2$s/месяц - - Включить резервные копии + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Ваш резервный ключ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Резервный ключ — это 64-значный код, который позволяет восстановить резервную копию при повторной установке Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Если вы забудете свой ключ, вы не сможете восстановить резервную копию. Signal не может помочь восстановить резервную копию. - Next + Далее - Record your backup key + Запишите резервный ключ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Этот ключ необходим для восстановления вашей учётной записи и данных. Храните этот ключ в безопасном месте. Если вы потеряете его, то не сможете восстановить свою учётную запись. - Copy to clipboard + Копировать в буфер обмена - Next + Далее - Keep your key safe + Храните ключ в безопасности - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal не сможет помочь восстановить резервную копию, если вы потеряете ключ. Храните его в безопасном и надежном месте и ни с кем им не делитесь. - I\'ve recorded my key + Я записал(а) свой ключ - Continue + Продолжить - See key again + Просмотреть ключ ещё раз diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 82b884516c..ddfc45ace4 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Áno Nie Vymazať @@ -264,6 +267,7 @@ Pre fotografiu ťuknite, pre video podržte + Zachytiť Zmeniť fotoaparát Otvoriť galériu @@ -352,7 +356,7 @@ Toto nie je platný odkaz na hovor. Uistite sa, že je celý odkaz správny a v poriadku pred tým, ako sa pokúsite pripojiť. - You are already in a call + Už sa zúčastňujete hovoru @@ -409,6 +413,7 @@ Nepodarilo sa nájst aplikáciu schopnú otvoriť tento typ súboru. Skopírovaných %1$s od %1$s + pre %1$s   Čítať ďalej   Stiahnuť viac @@ -442,6 +447,7 @@ Odoslať úpravu Vytvoriť správu Nastala chyba pri vytváraní prílohy. + Príjemca nie je platná SMS alebo emailová adresa! Správa je prázdna! Členovia skupiny @@ -598,6 +604,7 @@ Ukladám %1$d príloh do úložiska… Čaká na spracovanie… + Dáta (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Odpojiť \"%1$s\"? Po odpojení zariadenia nebudete môcť prijímať, ani odosielať správy. Pripojenie k sieti zlyhalo + Skúsiť znova Odpojuje sa zariadenie… Odpojenie zariadenia @@ -1488,7 +1496,7 @@ Odkaz do skupiny Zdieľať Zresetovať odkaz - Require admin approval + Vyžadovať schválenie administrátorom Vyžadovať schválenie adminom pri nových členoch, ktorí sa pridajú prostredníctvom odkazu do skupiny. Ste si istý/á, že chcete zresetovať odkaz do skupiny? Ľudia sa už viac nebudú môcť do skupiny pridať prostredníctvom aktuálneho odkazu. @@ -1593,6 +1601,7 @@ Odoslať %1$d SMS pozvánok? Prejdime na Signal: %1$s + Vyzerá to, že nemáte žiadne aplikácie kam by ste mohli zdieľať. @@ -2047,6 +2056,7 @@ Povoliť používateľovi %1$s, aby vám posielal správy a zdieľať s ním vaše meno a fotku? Kým ho neodblokujete, nepríjmete od neho žiadne správy. Chcete povoliť používateľovi %1$s, aby vám poslal správu? Kým ho neodblokujete, nepríjmete od neho žiadne správy. + Chcete dostávať aktualizácie a novinky od používateľa %1$s? Kým ich neodblokujete, nebudete dostávať žiadne aktualizácie. Pokračovať v čete v tejto skupine a zdieľať vaše meno a fotku s jej členmi? Túto zastaranú skupinu už nie je možné používať. Vytvorte novú skupinu na aktiváciu nových funkcií, ako sú @zmienky a správcovia. @@ -2393,6 +2403,7 @@ Nikto iný tu nie je %1$s je v tomto hovore + %1$s sú v tomto hovore %1$s a %2$s sú v tomto hovore @@ -2817,7 +2828,7 @@ Skúsime to znova neskôr. Signal bol úspešne aktualizovaný Aplikácia bola automaticky aktualizovaná na verziu %1$s. - You updated to version %1$s. + Aktualizovali ste Signal na verziu %1$s. Odoslať správu? @@ -3251,6 +3262,7 @@ Ikona prílohy Prepnúť na rýchlu prílohu fotoaparátom Nahrať a odoslať zvukovú prílohu + Zamknúť nahrávanie zvukovej prílohy Nepodarilo sa odoslať správu. Skontrolujte svoje pripojenie na internet a skúste znovu. @@ -3269,6 +3281,7 @@ Správa prečítaná + Fotka kontaktu @@ -3353,6 +3366,7 @@ Všetko teraz vyzerá byť v poriadku! Ak chcete dostávať upozornenia na hovory, ťuknite tu a zapnite možnosť „Zobraziť upozornenia“. Ak chcete dostávať upozornenia na hovory, ťuknite tu, zapnite upozornenia a uistite sa, že sú povolené zvuky a kontextové okná. + Ak chcete dostávať oznámenia o hovoroch, ťuknite tu a povoľte aktivitu na pozadí v nastaveniach batérie. Nastavenia Ak chcete dostávať upozornenia na hovory, ťuknite na Nastavenia a zapnite možnosť „Zobraziť upozornenia“. @@ -3504,7 +3518,7 @@ Dávam si prestávku Pracujem na niečom novom - One or more characters is invalid. + Jeden alebo viac znakov je neplatných. Upraviť skupinu @@ -3603,6 +3617,7 @@ + Podporné informácie Žiadosť o podporu pre Signal Android @@ -3693,6 +3708,7 @@ Získať ukážky stránok priamo z webových prepojení pre správy, ktoré odošlete. Zmeniť heslo Zmeniť heslo + Chrániť obrazovku heslom Uzamknúť obrazovku a upozornenia pomocou hesla Zabezpečenie obrazovky @@ -3705,6 +3721,7 @@ Sekvencia blikania LED Prispôsobiť Zmeniť zvuk a vibrovanie + Zvuk Ticho Predvolená @@ -5632,6 +5649,7 @@ Pridať do príbehu Pridajte správu + Pridať odpoveď Odoslať Médiá na jedno pozretie @@ -6246,6 +6264,8 @@ %1$d odpovedí %1$d odpovedí + + Story no longer hidden Pridať @@ -7583,7 +7603,23 @@ - Uvoľnite %1$s miesta na stiahnutie médií. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ OK - - - História platieb - - Zálohovanie správ a všetkých médií - - Platobné údaje - - Typ zálohy - - Dátum zaplatenia - - Zdieľať + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Frekvencia zálohovania Zálohovať pomocou mobilnej siete + + View backup key + + Unlock to view backup key Vypnúť a vymazať zálohu @@ -7686,13 +7722,27 @@ Zálohovanie prebehne počas noci. - Typ zálohy + Backup plan Zálohy sú vypnuté - - %1$s · %2$s/mesačne - - Povoliť zálohovanie + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Váš záložný kľúč - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Váš záložný kľúč je 64-ciferný kód, ktorý vám umožňuje obnoviť zálohu, keď znovu nainštalujete Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ak zabudnete svoj kľúč, nebudete môcť obnoviť zálohu. Signal vám nemôže pomôcť obnoviť zálohu. - Next + Ďalej - Record your backup key + Uložte si záložný kľúč - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Tento kľúč je potrebný na obnovenie vášho účtu a údajov. Uložte ho na bezpečné miesto. Ak ho stratíte, nebudete môcť svoj účet obnoviť. - Copy to clipboard + Kopírovať - Next + Ďalej - Keep your key safe + Uchovajte svoj kľúč v bezpečí - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal vám nebude môcť pomôcť obnoviť zálohu, ak stratíte kľúč. Uschovajte ho na bezpečnom mieste a s nikým ho nezdieľajte. - I\'ve recorded my key + Uložil/a som svoj kľúč - Continue + Pokračovať - See key again + Pozrieť si kľúč znova diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 7632a77423..0ca118399e 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Da Ne Izbriši @@ -264,6 +267,7 @@ Tapnite za fotografijo, zadržite za video + Zajemi Zamenjaj kamero Odpri galerijo @@ -352,7 +356,7 @@ Neveljavna klicna povezava. Pred poskusom pridružitve poskrbi, da bo povezava ostala nespremenjena. - You are already in a call + Ste že v tem klicu @@ -409,6 +413,7 @@ Ne najdem aplikacije za odpiranje te vrste datotek. Kopirano %1$s od %1$s + za %1$s   Več … Naloži več @@ -442,6 +447,7 @@ Pošlji urejeno Sestavi sporočilo Oprostite, pri nastavljanju priponke je prišlo do napake. + Naslov prejemnika ni veljaven SMS oz. email naslov! Sporočilo je prazno! Člani_ce skupine @@ -598,6 +604,7 @@ Shranjevanje %1$d prilog v shrambo … V teku … + Podatkovno (Signal) MMS SMS @@ -1046,6 +1053,7 @@ Odstranim \'%1$s\'? Z odstranitvijo naprave ta ne bo več mogla pošiljati ali prejemati sporočil. Povezovanje z omrežjem neuspešno + Poskusite znova Odstranjevanje naprave … Odstranjujem napravo @@ -1488,7 +1496,7 @@ Povezava do skupine Deli Ponastavi povezavo - Require admin approval + Zahtevajte odobritev administratorja Za pridružitev tej skupini preko deljene povezave je zahtevana odobritev skrbnika_ce. Ste prepričani, da želite ponastaviti povezavo do skupine? Uporabiki_ce se več ne bodo mogli_le pridružiti skupini preko sedanje povezave. @@ -1593,6 +1601,7 @@ Pošljem %1$d vabil SMS? Preklopi na Signal: %1$s + Kaže, da nimate nameščene nobene primerne aplikacije. @@ -2047,6 +2056,7 @@ Želite omogočiti uporabniku_ci %1$s, da vam pošilja sporočila in z njim_njo deliti svoje ime in fotografijo? Dokler ga_je ne odblokirate, od njega_nje ne boste prejemali sporočil. Bi želeli omogočiti uporabniku_ci %1$s, da vam pošilja sporočila? Dokler ga/je ne odblokirate, ne boste prejemali njegovih/njenih sporočil. + Prejemate novice in novosti od %1$s? Dokler jih ponovno ne dovolite, ne boste prejemali nobenih novosti. Želite nadaljevati klepet s to skupino in deliti svoje ime ter fotografijo s člani? Te stare skupine ni več mogoče uporabljati. Ustvarite novo skupino, da aktivirate nove funkcije, kot so @omembe in skrbniki. @@ -2393,6 +2403,7 @@ Tu ni nikogar drugega %1$s je v tem klicu + %1$s so na tem klicu %1$s in %2$s sta v tem klicu @@ -2817,7 +2828,7 @@ Znova bomo poskusili pozneje. Signal je bil uspešno posodobljen Samodejna posodobitev na različico %1$s. - You updated to version %1$s. + Signal ste posodobili na različico %1$s. Pošljem sporočilo? @@ -3251,6 +3262,7 @@ Predogledna sličica priponke Vklopi hitri poteznik za video priponke Posnemi in pošlji glasovno priponko + Zakleni snemanje zvočne priponke Sporočilo ni moglo biti poslano. Preverite internetno povezavo in poskusite znova. @@ -3269,6 +3281,7 @@ Sporočilo prebrano + Fotografija stika @@ -3353,6 +3366,7 @@ Vse izgleda OK! Za prejemanje obvestil o klicih tapnite tu in vklopite \"Prikazuj obvestila.\" Za prejemanje obvestil o klicih tapnite tu in vklopite obvetila. Prepričajte se, da so zvok in pojavna okna vklopljeni. + Za prejemanje obvestil o klicih tapnite tu in vklopite delovanje v ozadju v nastavitvah baterije. Nastavitve Za prejemanje obvestil o klicih tapnite Nastavitve in vklopite \"Prikazuj obvestila.\" @@ -3504,7 +3518,7 @@ Možgani na off Delam na nečem … - One or more characters is invalid. + Eden ali več znakov je neveljavnih. Uredi skupino @@ -3603,6 +3617,7 @@ + Podporne informacije Zahteva za podporo Signal Android @@ -3693,6 +3708,7 @@ Za vse spletne strani, ki jih delite, bodo v sporočilu ustvarjene predogledne sličice. Zamenjaj geslo Zamenjava gesla + Vklop zaklepanja zaslona z geslom Zaklep zaslona in obvestil z geslom Zaščita zaslona @@ -3705,6 +3721,7 @@ Utripanje LED Prilagodi Spremenite zvok in vibriranje + Zvok Tiho Privzeto @@ -5632,6 +5649,7 @@ Dodaj k zgodbi Dodaj sporočilo + Dodaj odgovor Poslano uporabniku_ci Datoteka za enkratni ogled @@ -6246,6 +6264,8 @@ %1$d odgovori %1$d odgovorov + + Story no longer hidden Dodaj @@ -7583,7 +7603,23 @@ - Sprostite %1$s prostora za prenos medijev. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ OK - - - Zgodovina plačil - - Varnostno kopiranje sporočil in vseh medijev - - Podatki o plačilu - - Vrsta varnostne kopije - - Datum plačila - - Deli + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Pogostost varnostnega kopiranja Varnostno kopiranje z uporabo mobilnega omrežja + + View backup key + + Unlock to view backup key Izklopi in izbriši varnostno kopiranje @@ -7686,13 +7722,27 @@ Varnostna kopija bo ustvarjena čez noč. - Vrsta varnostne kopije + Backup plan Varnostno kopiranje izklopljeno - - %1$s · %2$s/mesec - - Dovoli varnostno kopiranje + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Vaš varnostni ključ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Ključ varnostne kopije je 64-mestna koda, s katero lahko obnovite varnostno kopijo, ko znova namestite program Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Če pozabite svoj ključ, ne boste mogli obnoviti varnostne kopije. Signal vam ne more pomagati obnoviti varnostne kopije. - Next + Naprej - Record your backup key + Zapišite svoj ključ za varnostno kopiranje - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Ta ključ je potreben za obnovitev računa in podatkov. Ta ključ shranite na varno. Če ga izgubite, računa ne boste mogli obnoviti. - Copy to clipboard + Kopiraj v odložišče - Next + Naprej - Keep your key safe + Hranite svoj ključ na varnem - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Če izgubite ključ, vam Signal ne bo mogel pomagati obnoviti varnostne kopije. Shranite ga na varnem mestu in ga ne delite z drugimi. - I\'ve recorded my key + Zapisal_a sem svoj ključ - Continue + Nadaljuj - See key again + Ponoven ogled ključa diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 1efdd3c5e5..4d7ec7fed4 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Po Jo Fshije @@ -260,6 +263,7 @@ Prekni për foto, mbajeni shtypur për video + Shkrep Ndërroni kameran Hap albumet @@ -346,7 +350,7 @@ Kjo nuk është një lidhje e vlefshme telefonate. Sigurohu që e gjithë lidhja të jetë e paprekur dhe e saktë para se të përpiqesh t\'i bashkohesh. - You are already in a call + Jeni tashmë në një thirrje @@ -403,6 +407,7 @@ Nuk gjendet një aplikacion që mund ta hapë këtë media. U kopjua %1$s nga%1$s + për %1$s   Lexoni më shumë   Shkarkoni më shumë @@ -436,6 +441,7 @@ Dërgo përpunimin Shkruaj mesazhin Na vjen keq, ka ndodhur një gabim në vendosjen e bashkëngjitjes tuaj. + Marrësi nuk është një adresë e vlefshme mesazhi ose emaili! Mesazhi është bosh! Anëtarët e grupit @@ -576,6 +582,7 @@ Duke ruajtur %1$d bashkëngjitjet në magazinë… Në pritje… + Të dhëna (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Të shkëputet \'%1$s\'? Duke e shkëputur këtë pajisje, s\\’do të jetë më në gjendje të dërgojë ose marrë mesazhe. Dështoi lidhja me rrjetin + Riprovo Po shkëputet pajisja… Po shkëputet pajisja @@ -1408,7 +1416,7 @@ Lidhje grupi Ndajeni Me të Tjerë Ricaktoni lidhje - Require admin approval + Kërkohet miratimi i administratorit Kërko doemos që një përgjegjës të miratojë anëtarë të rinj që vijnë prej një lidhjeje grupi. Jeni i sigurt se doni të ricaktohet lidhja e grupit? Njerëzit s\\’do të jenë më në gjendje të bëhen pjesë e grupit duke përdorur lidhjen e tanishme. @@ -1507,6 +1515,7 @@ Të dërgohen %1$d ftesa SMS? Le të kalojmë në Signal: %1$s + Duket sikur s\\’keni aplikacione me të cilat të ndani. @@ -1931,6 +1940,7 @@ Dëshiron që %1$s të të dërgojnë mesazh dhe të ndash emrin dhe foton tënde me ta? Nuk do të marrësh asnjë mesazh derisa t\'i zhbllokosh. Dëshiron që %1$s të të dërgojnë mesazh? Nuk do të marrësh asnjë mesazh derisa t\'i zhbllokosh. + Dëshiron të marrësh përditësime dhe lajme nga %1$s? Nuk do të marrësh asnjë përditësim derisa t\'i zhbllokosh. Dëshiron të vazhdosh bisedën me këtë grup dhe të ndash me anëtarët e tij emri dhe foton tënde? Grupi i trashëguar nuk mund të përdoret më. Krijo grup të ri për të aktivizuar veçori të reja si @përmendjet dhe administratorët. @@ -2245,6 +2255,7 @@ S\\’ka tjetër këtu %1$s është në këtë thirrje + Në këtë thirrje gjenden %1$s %1$s dhe %2$s janë në këtë thirrje @@ -2647,7 +2658,7 @@ Do të provojmë përsëri më vonë. Signal u përditësua me sukses Je përditësuar automatikisht në versionin %1$s. - You updated to version %1$s. + U përditësove në versionin %1$s. Të dërgohet mesazhi? @@ -3067,6 +3078,7 @@ Miniaturë Bashkëngjitjeje Hap/mbyll sirtar të shpejtë bashkëngjitjesh nga kamera Incizoni dhe dërgoni bashkëngjitje zanore + Blloko incizim bashkëngjitjeje audio Mesazhi s\\’u dërgua dot. Kontrolloni lidhjen tuaj dhe riprovoni. @@ -3085,6 +3097,7 @@ Mesazhi u lexua + Foto kontakti @@ -3169,6 +3182,7 @@ Tani gjithçka duket në rregull! Për të marrë njoftime thirrjesh, prekeni këtu dhe aktivizoni “Shfaq njoftime”. Për të marrë njoftime thirrjes, prekeni këtu dhe aktivizoni njoftimet dhe sigurohuni se Tinguj dhe Flluska janë të aktivizuara. + Për të marrë njoftime thirrjesh, prekeni këtu dhe aktivizoni veprimtari në prapaskenë, te parametrat e “Baterisë”. Parametrat Për të marrë njoftime thirrjesh, prekni parametrat dhe aktivizoni “Shfaq njoftime”. @@ -3306,7 +3320,7 @@ Pushoni ca Po merrem me diçka të re - One or more characters is invalid. + Një ose më shumë karaktere janë të pavlefshme. Përpunoni grup @@ -3405,6 +3419,7 @@ + Të dhëna Asistence Kërkesë Për Asistencë për Signal Android @@ -3493,6 +3508,7 @@ Merrni, për mesazhet që dërgoni, paraparje lidhjesh drejt e nga sajtet. Ndryshoje frazëkalimin Ndryshoni frazëkalimin tuaj + Aktivizoni kyçje ekrani me frazëkalim Kyçje ekrani dhe njoftime me frazëkalim Siguri ekrani @@ -3505,6 +3521,7 @@ Rregullsi xixëllimi LED-i Përshtateni Ndryshoni tingull dhe dridhje + Tingull Heshtazi Parazgjedhje @@ -5378,6 +5395,7 @@ Shto në postimin e përkohshëm Shtoni një mesazh + Shtoni një përgjigje Dërguar Media që mund të shihet vetëm një herë @@ -5982,6 +6000,8 @@ %1$d përgjigje %1$d përgjigje + + Story no longer hidden Shtoje @@ -7267,7 +7287,23 @@ - Liro %1$s hapësirë për të shkarkuar media. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Historia e pagesave - - Kopjeruajtje për tekst dhe për të gjitha mediat - - Detajet e pagesës - - Lloji i kopjeruajtjeve - - Datë pagese - - Ndajeni Me të Tjerë + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Frekuenca e kopjeruajtjes Kopjeruaj duke perdorur internetin celular + + View backup key + + Unlock to view backup key Të çaktivizohen dhe fshihen kopjeruajtjet @@ -7370,13 +7406,27 @@ Kopjeruajtja do të krijohet brenda natës. - Lloji i kopjeruajtjeve + Backup plan Kopjeruajtjet u çaktivizuan - - %1$s · %2$s/muaj - - Aktivizoji kopjeruajtjet + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Kodi yt i kopjeruajtjes - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Kodi i kopjeruajtjes është 64-shifror dhe të lejon të rikthesh kopjeruajtjen kur riinstalon Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Nëse harron kodin, nuk do të jesh në gjendje të rikthesh kopjeruajtjen. Signal nuk mund të të ndihmojë të rikuperosh kopjeruajtjen. - Next + Tjetër - Record your backup key + Regjistro kodin e kopjeruajtjes - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Ky kod kërkohet për të rikuperuar llogarinë dhe të dhënat e tua. Ruaje kodin në një vend të sigurt. Nëse e humb, nuk do të mund të rikuperosh llogarinë. - Copy to clipboard + Kopjoje në të papastër - Next + Tjetër - Keep your key safe + Ruaj kodin në mënyrë të sigurt - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal nuk do të jetë në gjendje të të ndihmojë në rikthimin e kopjeruajtjes nëse humb kodin. Ruaje në një vend të sigurt dhe mos e ndaj me të tjerët. - I\'ve recorded my key + E kam regjistruar kodin tim - Continue + Vazhdo - See key again + Shiko sërish kodin diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 5c3b2fa361..2dc491d596 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Да Не Избриши @@ -260,6 +263,7 @@ Додирните да снимите фотографију, држите да снимите видео + Сними Промени камеру Отвори галерију @@ -346,7 +350,7 @@ Овај линк није важећи линк за позив. Проверите да ли је цео линк комплетан и исправан пре него што покушате да се придружите. - You are already in a call + Већ сте у позиву @@ -403,6 +407,7 @@ Није пронађена апликација која може да отвори овај медиј. Копирано: %1$s пошиљалац: %1$s + прималац: %1$s   Прочитајте више   Преузмите више @@ -436,6 +441,7 @@ Пошаљи измену Напишите поруку Дошло је до грешке приликом постављања прилога. + Прималац није важећи број за SMS или имејл! Порука је празна! Чланови групе @@ -576,6 +582,7 @@ Прилози се чувају на меморијском простору (%1$d)… На чекању… + Мобилни интернет (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Желите ли да прекинете везу са уређајем „%1$s“? Овај уређај више неће моћи да шаље или прима поруке ако прекинете везу с њим. Повезивање на мрежу није успело + Пробајте поново Прекида се веза са уређајем… Прекидање везе са уређајем @@ -1408,7 +1416,7 @@ Линк за групу Поделите Ресетуj линк - Require admin approval + Обавезно одобрење администратора Обавезно је да администратор групе одобри нове чланове који се придружују групи преко линка за групу. Желите ли сигурно да ресетујете линк за групу? Људи више неће моћи да се придруже групи преко тренутног линка. @@ -1507,6 +1515,7 @@ Желите ли да пошаљете позивнице (%1$d) SMS-ом? Хајде да пређемо на Signal: %1$s + Изгледа да немате ниједну апликацију на коју бисте могли да поделите. @@ -1931,6 +1940,7 @@ Желите ли да дозволите да вам %1$s шаље поруке и види ваше име и слику? Нећете примати поруке док не одблокирате овог корисника. Желите ли да дозволите да вам %1$s пише? Нећете примати поруке док не одблокирате овог корисника. + Желите ли да вам %1$s шаље новости? Нећете примати новости док не одблокирате овог корисника. Желите ли да наставите са ћаскањем у овој групи и да са њеним члановима поделите своје име и слику? Ова стара група се више не може користити. Креирајте нову групу да бисте активирали нове функционалности као што су @помињања и администратори. @@ -2245,6 +2255,7 @@ Нема никога више овде %1$s је у овом позиву + %1$s су у овом позиву %1$s и %2$s су у овом позиву @@ -2647,7 +2658,7 @@ Пробаћемо поново касније. Signal је ажуриран Апликација је аутоматски ажурирана на верзију %1$s. - You updated to version %1$s. + Апликација је ажурирана на верзију %1$s. Желите ли да пошаљете поруку? @@ -3067,6 +3078,7 @@ Сличица прилога Активирајте фиоку камере за брзе прилоге Сними и пошаљи аудио прилог + Закључај снимање аудио прилога Порука није послата. Проверите да ли сте повезани на интернет и пробајте поново. @@ -3085,6 +3097,7 @@ Порука је прочитана + Слика контакта @@ -3169,6 +3182,7 @@ Сада све изгледа добро! Да бисте примали обавештења о позивима, додирните овде и укључите „Прикажи обавештења“. Да бисте примали обавештења о позивима, додирните овде и укључите обавештења и проверите да ли су упаљени звук и искачући прозор. + Да бисте примали обавештења о позивима, додирните овде и омогућите активност у позадини у подешавањима за батерију. Подешавања Да бисте примали обавештења о позивима, додирните „Подешавања“ и укључите „Прикажи обавештења“. @@ -3306,7 +3320,7 @@ Правим паузу Радим на нечему новом - One or more characters is invalid. + Један или више знакова су неважећи. Уређивање групе @@ -3405,6 +3419,7 @@ + Информације о подршци Signal Android – захтев за подршку @@ -3493,6 +3508,7 @@ Директно са веб-сајтова преузимајте приказе линкова за поруке које шаљете. Промените приступну фразу Промените приступну фразу + Укључи приступну фразу за закључавање екрана Закључај екран и обавештења приступном фразом Безбедност екрана @@ -3505,6 +3521,7 @@ Шаблон трептања LED светла Прилагоди Промените звук и вибрацију + Звук Без звука Подразумевано @@ -5378,6 +5395,7 @@ Додај садржај у причу Напишите поруку + Напишите одговор Пошаљи кориснику Једнократни медиј @@ -5982,6 +6000,8 @@ Одговора: %1$d Одговора: %1$d + + Story no longer hidden Додај @@ -7267,7 +7287,23 @@ - За преузимање медија ослободите %1$s простора. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ У реду - - - Историја плаћања - - Резервна копија текста и осталих медија - - Подаци о плаћању - - Врста резервне копије - - Датум плаћања - - Поделите + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Учесталост резервних копија Резервне копије преко мобилне мреже + + View backup key + + Unlock to view backup key Искључи и избриши резервне копије @@ -7370,13 +7406,27 @@ Резервна копија ће бити направљена преко ноћи. - Врста резервне копије + Backup plan Резервне копије су онемогућене - - %1$s · %2$s/месечно - - Укључи резервне копије + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Ваш кључ за резервне копије - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Кључ за резервне копије је 64-цифрени код који вам омогућава да вратите резервну копију када поново инсталирате Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ако заборавите кључ, нећете моћи да вратите резервну копију. Signal вам не може помоћи да повратите резервну копију. - Next + Следеће - Record your backup key + Сачувајте кључ за резервне копије - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Овај кључ је потребан за враћање вашег налога и података. Чувајте овај кључ негде на сигурном. Ако га изгубите, нећете моћи да повратите свој налог. - Copy to clipboard + Копирај - Next + Следеће - Keep your key safe + Чувајте кључ на сигурном - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal неће моћи да вам помогне да вратите резервну копију ако изгубите кључ. Чувајте га негде на сигурном и безбедном и немојте га давати другима. - I\'ve recorded my key + Сачувао/-ла сам свој кључ - Continue + Настави - See key again + Види кључ поново diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 8f88779a51..87c8ca5bb7 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ja Nej Ta bort @@ -260,6 +263,7 @@ Tryck för foto, håll för video + Ta foto Byt kamera Öppna galleri @@ -346,7 +350,7 @@ Detta är inte en giltig samtalslänk. Kontrollera att hela länken är intakt och korrekt innan du försöker gå med. - You are already in a call + Du är redan i ett samtal @@ -403,6 +407,7 @@ Det går inte att hitta en app som kan öppna detta medium. Kopierade %1$s från %1$s + till %1$s   Läs mer   Hämta mer @@ -436,6 +441,7 @@ Skicka redigerad Skriv meddelande Tyvärr uppstod ett fel vid inställningen av din bilaga. + Mottagaren är inte en giltig SMS eller e-postadress! Meddelandet är tomt! Gruppmedlemmar @@ -576,6 +582,7 @@ Sparar %1$d bilagor i lagring … Väntar… + Data (Signal) MMS SMS @@ -1006,6 +1013,7 @@ Sluta länka till \"%1$s\"? Genom att ta bort länken till denna enhet kommer den inte längre att kunna skicka eller ta emot meddelanden. Nätverksanslutning misslyckades + Försök igen Sluta länka enhet… Sluta länka enhet @@ -1408,7 +1416,7 @@ Grupplänk Dela Återställ länk - Require admin approval + Kräv godkännande av administratör Kräv en administratör för att godkänna nya medlemmar som går med via grupplänken. Är du säker på att du vill återställa grupplänken? Personer kommer inte längre att kunna gå med i gruppen med den aktuella länken. @@ -1507,6 +1515,7 @@ Skicka %1$d SMS-inbjudningar? Låt oss använda Signal: %1$s + Verkar som att du inte har några appar att dela till. @@ -1931,6 +1940,7 @@ Tillåt att %1$s skickar meddelanden till dig och dela ditt namn och foto? Du får inte några meddelanden förrän du har avblockerat personen. Låta %1$s skicka meddelanden till dig? Du kommer inte att få några meddelanden förrän du avblockerar dem. + Få uppdateringar och nyheter från %1$s? Du kommer inte att få några uppdateringar förrän du avblockerar dem. Fortsätt din chatt med denna grupp och dela ditt namn och foto med dess medlemmar? Den här äldre gruppen kan inte längre användas. Skapa en ny grupp för att aktivera nya funktioner som @omnämnanden och administratörer. @@ -2245,6 +2255,7 @@ Ingen annan är här %1$s är i detta samtal + %1$s är i detta samtal %1$s och %2$s är i detta samtal @@ -2647,7 +2658,7 @@ Vi försöker igen senare. Signal har uppdaterats Du uppdaterades automatiskt till version %1$s. - You updated to version %1$s. + Du har uppdaterat till version %1$s. Skicka meddelande? @@ -3067,6 +3078,7 @@ Bifogad miniatyrbild Dölj/Visa snabbpanel för kamerabilaga Spela in och skicka ljudbilaga + Lås inspelning av ljudbilaga Meddelandet kunde inte skickas. Kontrollera din anslutning och försök igen. @@ -3085,6 +3097,7 @@ Meddelande läst + Kontaktfoto @@ -3169,6 +3182,7 @@ Allt ser bra ut nu! För att ta emot samtalsaviseringar, tryck här och aktivera \"Visa aviseringar.\" För att ta emot samtalsaviseringar, tryck här och aktivera aviseringar och se till att ljud och popup är aktiverat. + För att ta emot samtalsaviseringar, tryck här och aktivera bakgrundsaktivitet i \"Batteri\"-inställningarna. Inställningar För att ta emot samtalsaviseringar, tryck på Inställningar och aktivera \"Visa aviseringar.\" @@ -3306,7 +3320,7 @@ Tar en rast Arbetar på något nytt - One or more characters is invalid. + Ett eller flera tecken är ogiltiga. Redigera grupp @@ -3405,6 +3419,7 @@ + Supportinformation Signal Android-supportförfrågan @@ -3493,6 +3508,7 @@ Hämta länkförhandsgranskningar direkt från webbplatser för meddelanden du skickar. Ändra lösenord Ändra ditt lösenord + Aktivera låsskärmens lösenord Lås skärm och aviseringar med ett lösenord Skärmsäkerhet @@ -3505,6 +3521,7 @@ Blinkmönster på ljusindikator Anpassa Ändra ljud och vibrering + Ljud Tyst Standard @@ -5378,6 +5395,7 @@ Lägg till i story Lägg till ett meddelande + Lägg till ett svar Skicka till Tillfällig mediefil @@ -5982,6 +6000,8 @@ %1$d svar %1$d svar + + Story no longer hidden Lägg till @@ -7267,7 +7287,23 @@ - Frigör %1$s utrymme för att ladda ner dina media. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Betalningshistorik - - Säkerhetskopia av text och all media - - Betalningsuppgifter - - Typ av säkerhetskopia - - Betalningsdatum - - Dela + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Frekvens för säkerhetskopiering Säkerhetskopiera med mobilnät + + View backup key + + Unlock to view backup key Stänga av och ta bort säkerhetskopia @@ -7370,13 +7406,27 @@ Säkerhetskopia kommer att skapas över natten. - Typ av säkerhetskopia + Backup plan Säkerhetskopior inaktiverade - - %1$s · %2$s/månad - - Aktivera säkerhetskopior + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Din säkerhetskopieringsnyckel - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Din säkerhetskopieringsnyckel är en kod på 64 tecken som låter dig återställa din säkerhetskopia när du installerar om Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Om du glömmer din nyckel kommer du inte att kunna återställa din säkerhetskopia. Signal kan inte hjälpa dig att återställa din säkerhetskopia. - Next + Nästa - Record your backup key + Spara din säkerhetskopieringsnyckel - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Denna nyckel krävs för att återställa ditt konto och dina data. Förvara den här nyckeln på ett säkert ställe. Om du förlorar den kommer du inte att kunna återställa ditt konto. - Copy to clipboard + Kopiera till urklipp - Next + Nästa - Keep your key safe + Förvara din nyckel säkert - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal kommer inte att kunna hjälpa dig att återställa din säkerhetskopia om du förlorar din nyckel. Spara den någonstans säkert och dela den inte med andra. - I\'ve recorded my key + Jag har sparat min nyckel - Continue + Fortsätt - See key again + Visa nyckel igen diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 431aa5a5eb..f0ffb31af9 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Ndio Hapana Futa @@ -260,6 +263,7 @@ Gusa ili upate picha, shikilia ili upate video + Piga Geuza kamera Fungua kitunzio @@ -346,7 +350,7 @@ Hiki si kiungo sahihi cha simu. Hakikisha kiungo chote kimekamilika na ni sahihi kabla hujajaribu kujiunga. - You are already in a call + Tayari uko kwenye mazungumzo ya simu @@ -403,6 +407,7 @@ Haiwezi kupata programu inayoweza kufungua media hii. nakala 1%1$s kutoka %1$s + kwenda %1$s   Soma Zaidi   Pakua Zaidi @@ -436,6 +441,7 @@ Tuma uhariri Tunga ujumbe Samahani, hitilafi ilitokea kwenye kuweka kiambatanisho chako. + Mpokeaji sio anwani sahihi ya Ujumbe au barua pepe! Ujumbe ni mtupu! Wanachama wa kikundi @@ -576,6 +582,7 @@ Inahifadhi viambatisho %1$d kwenye hifadhi Subiriwa… + Data (Signal) Ujumbe wa picha Ujumbe wa maneno @@ -1006,6 +1013,7 @@ Tenganisha \' %1$s\'? Kwa kutengua uunganishwaji wa kifaa hiki, hakitaweza tena kutuma au kupokea ujumbe. Kuunganisha mtandao kumefeli + Jaribu tena Inatengua uunganishwaji kifaa… Inatengua uunganishwaji kifaa… @@ -1408,7 +1416,7 @@ Kiungo cha kikundi Shiriki Weka upya kiungo - Require admin approval + Inahitaji idhini ya admin Itisha ruhusa ya msimamizi kuidhinisha wanachama wapya wanaojiunga kupitia kiungo cha kikundi. Je, una hakika unataka kuweka upya kiungo cha kikundi? Watu hawataweza tena kujiunga na kikundi kwa kutumia kiungo cha sasa. @@ -1507,6 +1515,7 @@ Tuma %1$d mialiko ya SMS? Wacha tubadili kwa Signal: %1$s + Inaonekana huna programu zozote za kushiriki @@ -1931,6 +1940,7 @@ Je ungependa kumruhusu %1$s akutumie ujumbe na ushiriki naye jina na picha yako? Hutapokea ujumbe wowote mpaka utakapoondoa kizuizi kwake. Je ungependa kumruhusu %1$s akutumie ujumbe? Hutapata jumbe zozote mpaka utakapoondoa kizuizi kwake. + Pata masasisho na habari kutoka kwa %1$s? Hutapata sasisho zozote mpaka utakapoondoa kizuizi kwao. Ungependa kuendelea kuzungumza na kikundi hiki na ushiriki jina na picha yako na wanachama wake? Kikundi hiki cha Legacy hakiwezi kutumiwa tena. Unda kikundi kipya ili kutumia vipengele vipya kama @watajwa na admins. @@ -2245,6 +2255,7 @@ Hakuna mtu mwingine hapa %1$s yuko katika mazungumzo haya ya simu + %1$s wanashiriki kwenye simu hii %1$s na %2$s wako kwenye mazungumzo haya ya simu @@ -2647,7 +2658,7 @@ Tutajaribu tena baadae. Signal imesasishwa kwa mafanikio Umesasishwa kiotomatiki kwenda toleo la %1$s. - You updated to version %1$s. + Umesasisha kwenda toleo la %1$s. Tuma ujumbe? @@ -3067,6 +3078,7 @@ Maelezo ya kiambatanisho Badili haraka droo ya kiambatanisha kamera Rekodi na tuma kiambatisho cha sauti + Funga kurekodi kwa kiambatisho cha sauti Ujumbe haujatumwa. Kagua muunganisho wako kisha ujaribu tena. @@ -3085,6 +3097,7 @@ Ujumbe umesomwa + Picha ya mwasiliani @@ -3169,6 +3182,7 @@ Kila kitu shwari sasa! Ili kupokea arifa za simu, gusa hapa na uwashe \"Onyesha arifa.\" Ili kupokea arifa za simu, gusa hapa na uwashe arifa na uhakikishe kuwa mipangilio ya Sauti na Madirisha Ibukizi imewezeshwa. + Ili upokee arifa za simu, gusa hapa na uwashe shughuli za chinichini katika mipangilio ya \"Betri\". Mipangilio Ili kupokea arifa za simu, gusa Mipangilio na uwashe \"Onyesha arifa.\" @@ -3306,7 +3320,7 @@ Nimeenda mapumziko kidogo Napangia mambo mapya - One or more characters is invalid. + Herufi moja au zaidi ni batili. Hariri kikundi @@ -3405,6 +3419,7 @@ + Maelezo ya Usaidizi Ombi la Usaidizi wa Signal Android @@ -3493,6 +3508,7 @@ Chukua hakiki za viungo moja kwa moja kutoka kwenye tovuti kwa ujumbe unaotuma. Badili Nenosiri Badili Nenosiri lako + Wezesha Nenosiri la kufunga skriini Funga skriini na arifa pamoja na Nenosiri usalama wa skrini @@ -3505,6 +3521,7 @@ Mfano wa LED blink Maalum Badili sauti na mtetemo + Sauti Kimya Chaguo msingi @@ -5378,6 +5395,7 @@ Ongeza kwenye stori Ongeza ujumbe + Ongeza jibu Tuma kwa Tazama mara moja @@ -5982,6 +6000,8 @@ Jibu%1$d Majibu %1$d + + Story no longer hidden Ongeza @@ -7267,7 +7287,23 @@ - Achilia %1$s ya nafasi ili uweze kupakua video na picha zako. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ Sawa - - - Historia ya malipo - - Nakala za maandishi na picha na video zote - - Taarifa za malipo - - Aina ya nakala - - Tarehe Ilipolipwa - - Shiriki + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Masafa ya nakala Weka nakala kwa kutumia mtandao + + View backup key + + Unlock to view backup key Zima na ufute nakala @@ -7370,13 +7406,27 @@ Hifadhi nakala itaundwa usiku. - Aina ya chelezo + Backup plan Chelezo zimezimwa - - %1$s · %2$s/kwa mwezi - - Wezesha nakalahifadhi + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Funguo wako wa ziada - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Ufunguo wako wa ziada ni msimbo wa tarakimu 64 unaokuwezesha kurejesha hifadhi nakala yako unaposakinisha tena Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Ukisahau ufunguo wako, hutaweza kurejesha nakala yako. Signal haiwezi kukusaidia kurejesha hifadhi nakala yako. - Next + Inayofuata - Record your backup key + Rekodi ufunguo wangu wa ziada - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Ufunguo huu unahitajika kurejesha akaunti na data yako. Hifadhi ufunguo huu mahali salama. Ukiipoteza, hutaweza kurejesha akaunti yako. - Copy to clipboard + Nakili kwenye clipbodi - Next + Inayofuata - Keep your key safe + Ulinde salama ufunguo wako - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal haitaweza kukusaidia kurejesha nakala yako ya hifadhi ukipoteza ufunguo wako. Ihifadhi mahali salama, na usiwapatie wengine. - I\'ve recorded my key + Nimerekodi ufunguo wangu - Continue + Endelea - See key again + Angalia ufunguo tena diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index dd9d2b1911..7672371a5f 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + ஆம் இல்லை நீக்கு @@ -260,6 +263,7 @@ புகைப்படத்திற்கு தட்டவும், காணொளிக்கு தொடர்ந்து அழுத்தவும் + பிடிப்பு கேமராவை மாற்றவும் கேலரியை திற @@ -346,7 +350,7 @@ இது சரியான அழைப்பு இணைப்பு அல்ல. சேர முயலும் முன் முழு இணைப்பும் அப்படியே உள்ளதா என்றும் சரியானதா என்றும் உறுதி செய்து கொள்ளவும். - You are already in a call + நீங்கள் ஏற்கனவே அழைப்பில் உள்ளீர்கள் @@ -403,6 +407,7 @@ இந்த ஊடகத்தை திறக்க பயன்படும் ஒரு பயன்பாட்டை கண்டுபிடிக்க இயலவில்லை நகலெடுக்கப்பட்டன %1$s அனுப்பினர் %1$s + பெறுனர் %1$s மேலும் படிக்க மேலும் பதிவிறக்க @@ -436,6 +441,7 @@ அனுப்பியதை திருத்து செய்தியை இயற்று மன்னிக்கவும், உங்கள் இணைப்பை அமைப்பதில் பிழை ஏற்ப்பட்டது. + பெறுநர் ஒரு செல்லுபடியாகும் SMS அல்லது மின்னஞ்சல் முகவரி அல்ல! செய்தி காலியாக உள்ளது! குழு உறுப்பினர்கள் @@ -576,6 +582,7 @@ சேமிப்பகத்தில் %1$d இணைப்புகளை சேமிக்கிறது… நிலுவையில் உள்ளது… + தரவு (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \'%1$s\' துண்டி? இந்த சாதனத்தை துண்டிப்பதின் மூலம், இது இனி செய்திகளை அனுப்பவோ பெறவோ முடியாது. பிணைய இணைப்பு தோல்வியுற்றது + மீண்டும் முயற்சிக்கவும் கருவி துண்டிக்கப்படுகிறது … கருவி துண்டிக்கப்படுகிறது @@ -1408,7 +1416,7 @@ குழு இணைப்பு பகிர் இணைப்பை மீட்டமைக்கவும் - Require admin approval + நிர்வாகி அனுமதி தேவை குழு இணைப்பு வழியாக குழுவில் சேர விரும்பும் புதிய உறுப்பினர்களுக்கு நிர்வாகி ஒப்புதல் தேவை. குழு இணைப்பை மீட்டமைக்க விரும்புகிறீர்களா? தற்போதைய இணைப்பைப் பயன்படுத்தி மக்கள் இனி குழுவில் சேர முடியாது. @@ -1507,6 +1515,7 @@ %1$d SMS அழைப்புகளை அனுப்பவா? %1$s: நாம் Signalக்கு மாறுவோம் + உங்களிடம் பகிர எந்த பயன்பாடுகளும் இல்லை என்று தெரிகிறது. @@ -1931,6 +1940,7 @@ %1$s உங்களுக்கு மெசேஜ் அனுப்ப அனுமதித்து, உங்கள் பெயரையும் புகைப்படத்தையும் அவர்களுடன் பகிர்ந்து கொள்ள விரும்புகிறீர்களா? நீங்கள் அவரை தடை நீக்கம் செய்யும் வரை எந்த மெசேஜ்களையும் பெற மாட்டீர்கள். உங்களுக்கு %1$s மெசேஜ் அனுப்ப அனுமதிக்கிறீர்களா? நீங்கள் தடை நீக்கம் செய்யும் வரை நீங்கள் எந்த மெசேஜையும் பெறமாட்டீர்கள். + %1$s -இடமிருந்து அறிவிப்புகள் மற்றும் செய்திகளைப் பெற வேண்டுமா? நீங்கள் தடை நீக்கம் செய்யும் வரை எந்த அறிவிப்புகளையும் பெறமாட்டீர்கள். இந்த குழுவுடன் உங்கள் சாட்டைத் தொடர, உங்கள் பெயரையும் புகைப்படத்தையும் அதன் உறுப்பினர்களுடன் பகிர்ந்து கொள்ளவா? இந்த லெகசி குழுவை இனி பயன்படுத்த முடியாது. @குறிப்புகள் மற்றும் நிர்வாகிகள் போன்ற புதிய அம்சங்களைச் செயல்படுத்த புதிய குழுவை உருவாக்கவும். @@ -2245,6 +2255,7 @@ வேறு யாரும் இங்கு இல்லை %1$s இந்த அழைப்பில் இருக்கிறார் + %1$s இந்த அழைப்பில் உள்ளார் %1$s மற்றும் %2$sஇந்த அழைப்பில் உள்ளனர் @@ -2647,7 +2658,7 @@ பின்னர் மீண்டும் முயல்வோம். Signal வெற்றிகரமாக புதுப்பிக்கப்பட்டது நீங்கள் தானாகவே பதிப்பு %1$sக்குப் புதுப்பிக்கப்பட்டீர்கள். - You updated to version %1$s. + நீங்கள் பதிப்பு %1$s-க்குப் புதுப்பிக்கப்பட்டுள்ளீர்கள். செய்தி அனுப்ப? @@ -3067,6 +3078,7 @@ இணைப்பின் சிறுபடம் விரைவான கேமரா இணைப்பு டிராயரை நிலைமாற்று ஆடியோ இணைப்பு பதிவு செய்து அனுப்பவும் + ஆடியோ இணைப்பு பதிவை பூட்டு செய்தி அனுப்ப முடியவில்லை. உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும். @@ -3085,6 +3097,7 @@ செய்தி வாசிக்கப்பட்டது + தொடர்பின் புகைப்படம் @@ -3169,6 +3182,7 @@ இப்போது எல்லாம் நன்றாக இருக்கிறது! அழைப்பு பெற அறிவிப்புகள், தட்டவும் இங்கே மற்றும் இயக்கவும் \"காட்டு அறிவிப்புகள். \" அழைப்பு பெற அறிவிப்புகள், தட்டவும் இங்கே மற்றும் இயக்கவும் அறிவிப்புகள் ஒலி மற்றும் பாப்-அப் இயக்கப்பட்டிருப்பதை உறுதிசெய்க. + அழைப்பு பெற அறிவிப்புகள், தட்டவும் இங்கே மற்றும் இயக்கவும் பின்னணி செயல்பாடு \"பேட்டரி\" இல் அமைப்புகள். அமைப்புகள் அழைப்பு பெற அறிவிப்புகள், தட்டவும் அமைப்புகள் மற்றும் இயக்கவும் \"காட்டு அறிவிப்புகள். \" @@ -3306,7 +3320,7 @@ ஓய்வெடுத்து கொண்டிருக்கிறேன் ஒரு புதிய விஷயத்தில் ஈடுபட்டுள்ளேன் - One or more characters is invalid. + ஒன்று அல்லது அதற்கு மேற்பட்ட எழுத்துக்கள் தவறாக உள்ளன. குழுவைத் திருத்து @@ -3405,6 +3419,7 @@ + ஆதரவு தகவல் Signal Android ஆதரவு கோரிக்கை @@ -3493,6 +3508,7 @@ நீங்கள் அனுப்பும் செய்திகளுக்கு வலைத்தளங்களிலிருந்து இணைப்பு முன்னோட்டங்களை நேரடியாக மீட்டெடுக்கவும். கடவுச்சொல்லை மாற்று உன்னுடய கடவுச்சொல்லை மாற்று + கடவுச்சொல் திரை பூட்டை இயக்கவும் கடவுச்சொல் மூலம் திரை மற்றும் அறிவிப்புகளைப் பூட்டு திரை பாதுகாப்பு @@ -3505,6 +3521,7 @@ LED மிளிரும் முறை தனிப்பயனாக்கவும் ஒலி மற்றும் அதிர்வை மாற்றவும் + ஒலி அமைதி இயல்புநிலை @@ -5378,6 +5395,7 @@ ஸ்டோரியில் சேர்க்கவும் கூட்டு ஒரு செய்தி + ஒரு பதிலைச் சேர்க்கவும் இவருக்கு அனுப்பு ஒரு முறை பார்க்கக்கூடிய மீடியா @@ -5982,6 +6000,8 @@ %1$d பதில் %1$d பதில்கள் + + Story no longer hidden சேர்க்கவும் @@ -7267,11 +7287,27 @@ - உங்கள் ஊடகத்தைப் பதிவிறக்க %1$s இடத்தைக் காலியாக்கவும். + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + - Google Play + கூகுள் பிளே கிரெடிட் அல்லது டெபிட் கார்டு @@ -7330,19 +7366,15 @@ சரி - - - கட்டண வரலாறு - - உரை மற்றும் அனைத்து ஊடகக் காப்புப்பிரதி - - பேமெண்ட் விவரங்கள் - - காப்புப்பிரதி வகை - - செலுத்திய தேதி - - பகிர் + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ காப்புப்பிரதி கால இடைவெளி செல்லுலாரைப் பயன்படுத்தி காப்புப்பிரதி எடுத்தல் + + View backup key + + Unlock to view backup key அணைத்துவிட்டு, காப்புப்பிரதியை அழித்தல் @@ -7370,13 +7406,27 @@ ஒரு இரவிற்குள் காப்புப்பிரதி உருவாக்கப்படும். - காப்புப்பிரதி வகை + Backup plan காப்புப்பிரதிகள் முடிக்கப்பட்டுள்ளன - - %1$s · %2$s/மாதம் - - காப்புப்பிரதிகளை இயக்கு + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + உங்கள் காப்புப்பிரதி குறியீடு - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + உங்கள் காப்புப்பிரதி குறியீடானது 64 இலக்கக் குறியீடாகும், இது சிக்னலை மீண்டும் நிறுவும் போது உங்கள் காப்புப்பிரதியை மீட்டமைக்க உதவுகிறது. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + உங்கள் குறியீட்டை மறந்துவிட்டால், உங்கள் காப்புப்பிரதியை மீட்டெடுக்க முடியாது. உங்கள் காப்புப்பிரதியை மீட்டெடுக்க சிக்னல் உங்களுக்கு உதவாது. - Next + அடுத்து - Record your backup key + உங்கள் காப்புபிரதி குறியீட்டைப் பதிவு செய்யவும் - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + உங்கள் கணக்கு மற்றும் தரவை மீட்டெடுக்க இந்தக் குறியீடு தேவை. இந்தக் குறியீட்டைப் பாதுகாப்பான இடத்தில் சேமித்து வைக்கவும். நீங்கள் அதை இழந்துவிட்டால், உங்கள் கணக்கை மீட்டெடுக்க முடியாது. - Copy to clipboard + கிளிப்போர்டுக்கு நகலெடுக்கவும் - Next + அடுத்து - Keep your key safe + உங்கள் குறியீட்டைப் பாதுகாப்பாக வைத்திருக்கவும் - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + நீங்கள் குறியீட்டைப் இழந்தால், உங்கள் காப்புப்பிரதியை மீட்டெடுக்க சிக்னலால் உங்களுக்கு உதவ முடியாது. அதை பாதுகாப்பாகவும் பத்திரமாகவும் ஏதாவது ஒரு இடத்தில் சேமித்து வைக்கவும், மற்றவர்களுடன் பகிர்ந்து கொள்ள வேண்டாம். - I\'ve recorded my key + நான் எனது குறியீட்டைப் பதிவு செய்துவிட்டேன் - Continue + தொடர்க - See key again + குறியீட்டைப் மீண்டும் காண்க diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 8051a3cfa6..2519304f7b 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + అవును కాదు తొలగించండి @@ -260,6 +263,7 @@ ఫోటో కోసం నొక్కండి, వీడియో కోసం పట్టుకోండి + క్యాప్చర్ కెమెరాను మార్చండి చిత్రశాల ను తెరువు @@ -346,7 +350,7 @@ ఇది చెల్లుబాటు అయ్యే కాల్ లింక్ కాదు. చేరడానికి ప్రయత్నించే ముందు మొత్తం లింక్ సంపూర్ణంగా మరియు సరిగ్గా ఉందని నిర్ధారించుకోండి. - You are already in a call + మీరు ఇప్పటికే కాల్‌లో ఉన్నారు @@ -403,6 +407,7 @@ మీడియా ఎంచుకోవడానికి అనువర్తనం దొరకదు. ప్రతి తీసుకోబడింది %1$s %1$s నుండి + %1$s వరకు   ఇంకా చదవండి  మరిన్ని డౌన్‌లోడ్ చేయండి @@ -436,6 +441,7 @@ సవరణను పంపండి సందేశాన్ని కంపోజ్చెయ్ క్షమించండి, మీ అటాచ్మెంట్ అమర్చడంలో లోపం ఉంది. + స్వీకర్త చెల్లుబాటుకాని ఎస్ఎంఎస్ లేదా ఇమెయిలు చిరునామా ! సందేశం ఖాళీగా ఉంది! సమూహ సభ్యులు @@ -576,6 +582,7 @@ %1$d అటాచ్‌మెంట్‌లను స్టోరేజీకి సేవ్ చేస్తోంది…   పెండింగ్ + సమాచారం (Signal) ఎమ్మెమ్మెస్ ఎస్సెమ్మెస్ @@ -1006,6 +1013,7 @@ తొలగించరాదనుకుంటే \'%1$s\'? ఈ పరికరం లింక్ను తీసివేసిన ద్వారా, అది ఇకపై పంపండి లేదా సందేశాలను అందుకుంటారు చెయ్యగలరు. నెట్వర్క్ సంబంధం విఫలమైంది + మళ్ళీ ప్రయత్నించండి అపరమిత పరికరం అపరమిత పరికరం @@ -1408,7 +1416,7 @@ సమూహ లింక్ పంచుకోండి లింక్‌ను రీసెట్ చేయండి - Require admin approval + అడ్మిన్ ఆమోదం అవసరం సమూహ లింక్ ద్వారా చేరిన కొత్త సభ్యులను ఆమోదించడానికి నిర్వాహకుడిని అవసరం. మీరు సమూహ లింక్‌ను రీసెట్ చేయాలనుకుంటున్నారా? ప్రస్తుత లింక్‌ను ఉపయోగించి ప్రజలు ఇకపై సమూహంలో చేరలేరు. @@ -1507,6 +1515,7 @@ %1$d SMS ఆహ్వానాలను పంపాలా? మనం Signal కు మారుదాం: %1$s + మీరు పంచుకొనడానికి మీ దగ్గర వేరే యాప్స్ లేనట్టు కనబడుతుంది. @@ -1931,6 +1940,7 @@ %1$s మీకు సందేశం పంపనివ్వండి మరియు మీ పేరు మరియు ఫోటోను వారితో పంచుకునేదా? మీరు వారిని అన్‌బ్లాక్ చేసేంతవరకు మీరు ఎటువంటి సందేశాలు అందుకోరు. %1$s మీకు సందేశం పంపనివ్వమంటారా? మీరు వారిని అన్‌బ్లాక్ చేసేంతవరకు మీరు ఎలాంటి సందేశాలను అందుకోరు. + %1$s నుంచి అప్‌డేట్స్ మరియు వార్తలను పొందేదా? మీరు వారిని అన్‌బ్లాక్ చేసేంతవరకు మీరు ఎలాంటి అప్‌డేట్‌లను అందుకోలేరు. ఈ గ్రూప్‌తో మీ చాట్‌ను కొనసాగించండి మరియు మీ పేరు మరియు ఫోటోను దాని సభ్యులతో పంచుకునేదా? ఈ లెగసీ గ్రూపు ఇక ఏమాత్రం ఉపయోగించబడదు. @మెన్షన్‌లు మరియు అడ్మిన్‌లు వంటి కొత్త విలక్షణతలను సక్రియం చేయడానికి కొత్త గ్రూపును సృష్టించండి. @@ -2245,6 +2255,7 @@ మరెవరూ ఇక్కడ లేరు %1$s ఈ కాల్‌లో ఉన్నారు + %1$sలు ఈ కాల్‌లో ఉన్నారు %1$s మరియు %2$s ఈ కాల్‌లో ఉన్నారు @@ -2647,7 +2658,7 @@ మేము తర్వాత మళ్ళీ ప్రయత్నిస్తాము. Signal విజయవంతంగా అప్‌డేట్ చేయబడింది మీరు ఆటోమేటిక్‌గా %1$s వెర్షన్‌కు అప్‌డేట్ చేయబడ్డారు. - You updated to version %1$s. + మీరు %1$s వెర్షన్‌కు అప్‌డేట్‌ చేశారు. సందేశం పంపాల? @@ -3067,6 +3078,7 @@ జోడింపు సూక్ష్మచిత్రం టోగుల్ శీఘ్ర కెమెరా అటాచ్మెంట్ సొరుగు రికార్డు మరియు ఆడియో అటాచ్మెంట్ పంపడానికి + ఆడియో అటాచ్మెంట్ యొక్క రికార్డింగ్ ని లాక్ చేయండి సందేశం పంపబడలేదు. మీ కనెక్షన్‌ను తనిఖీ చేసి, మళ్లీ ప్రయత్నించండి. @@ -3085,6 +3097,7 @@ సందేశం చదవబడింది + పరిచయ ఫొటో @@ -3169,6 +3182,7 @@ ఇప్పుడు అంతా మంచిగా కనిపిస్తోంది! కాల్ నోటిఫికేషన్‌లను పొందడానికి, ఇక్కడ తట్టి, ‘‘నోటిఫికేషన్‌లను చూపించు’’ ఆన్ చేయండి. కాల్ నోటిఫికేషన్‌లను పొందడానికి, ఇక్కడ తట్టి, నోటిఫికేషన్‌లను ఆన్ చేయండి, సౌండ్ మరియు పాప్-అప్‌లు ప్రారంభించినట్లుగా ధృవీకరించుకోండి. + కాల్ నోటిఫికేషన్‌లను పొందడానికి, ఇక్కడ తట్టి, ‘‘బ్యాటరీ’’ సెట్టింగ్‌ల్లో బ్యాక్‌గ్రౌండ్ యాక్టివిటీని ప్రారంభించండి. అమరికలు కాల్ నోటిఫికేషన్‌లను పొందడానికి, సెట్టింగ్‌లపై తట్టి, ‘‘నోటిఫికేషన్‌లను చూపించు’’ ఆన్ చేయండి. @@ -3306,7 +3320,7 @@ విశ్రాంతి తీసుకొంటున్నా క్రొత్తదానిపై పని చేస్తోంది - One or more characters is invalid. + ఒకటి లేదా అంతకంటే ఎక్కువ అక్షరాలు చెల్లవు. సమూహాన్ని మార్చు @@ -3405,6 +3419,7 @@ + మద్దతు సమాచారం Signal Android మద్దతు అభ్యర్థన @@ -3493,6 +3508,7 @@ మీరు పంపే సందేశాల కోసం వెబ్‌సైట్ల నుండి నేరుగా లింక్ పూర్వప్రదర్శనలు తిరిగి పొందండి. సంకేతపదమును మార్చు మీ సంకేతపదమును మార్చండి + పాస్ఫ్రేజ్ స్క్రీన్ లాక్ను ప్రారంభించండి పాస్ఫ్రేజ్తో లాక్ స్క్రీన్ మరియు నోటిఫికేషన్లు స్క్రీన్ భద్రత @@ -3505,6 +3521,7 @@ ఎల్ఇడి రెప్పపాటు నమూనా కస్టమైజ్ చేయడం సౌండ్ మరియు వైబ్రేషన్ మార్చడం + శబ్దము మౌనం అప్రమేయం @@ -5378,6 +5395,7 @@ స్టోరీకు జోడించండి ఒక సందేశాన్ని జోడించండి + ప్రత్యుత్తరమును జోడించండి వీరికి పంపండి ఒక్కసారి వీక్షించదగిన మీడియా @@ -5982,6 +6000,8 @@ %1$d రిప్లై %1$d రిప్లైలు + + Story no longer hidden చేర్చు @@ -7267,7 +7287,23 @@ - మీ మీడియాను డౌన్‌లోడ్ చేయడానికి మొత్తం స్థలంలో %1$s స్థలాన్ని ఖాళీ చేయండి. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ సరే - - - చెల్లింపుల చరిత్ర - - టెక్స్ట్ మరియు మొత్తం మీడియా బ్యాకప్ - - చెల్లింపు వివరాలు - - బ్యాకప్ రకం - - చెల్లించిన తేదీ - - పంచుకోండి + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ బ్యాకప్ తరచుదనం సెల్యులార్ ఉపయోగించి బ్యాకప్ చేయండి + + View backup key + + Unlock to view backup key బ్యాకప్‌ను ఆఫ్ చేసి మరియు తొలగించండి @@ -7370,13 +7406,27 @@ బ్యాకప్ రాత్రికి రాత్రి సృష్టించబడుతుంది. - బ్యాకప్ రకం + Backup plan బ్యాకప్‌లు నిలిపివేయబడ్డాయి - - %1$s · %2$s/నెలకు - - ప్రత్యామ్నాయ ప్రారంభించు + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + మీ బ్యాకప్ కీ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + మీ బ్యాకప్ కీ అనేది 64-అంకెల కోడ్, ఇది మీరు Signal ను మళ్ళీ ఇన్‌స్టాల్ చేసినప్పుడు మీ బ్యాకప్‌ను పునరుద్ధరించడానికి మిమ్మల్ని అనుమతిస్తుంది. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + ఒకవేళ మీరు మీ కీని మర్చిపోతే, మీరు మీ బ్యాకప్‌ను పునరుద్ధరించలేరు. మీ బ్యాకప్‌ను తిరిగి పొందడంలో Signal మీకు సహాయం చేయలేదు. - Next + తరువాత - Record your backup key + మీ బ్యాకప్ కీని రికార్డ్ చేయండి - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + మీ ఖాతా మరియు డేటాను తిరిగి పొందడానికి ఈ కీ అవసరం. ఈ కీని ఎక్కడైనా సురక్షితంగా నిల్వ చేయండి. ఒకవేళ మీరు దీన్ని పోగొట్టుకుంటే, మీరు మీ ఖాతాను తిరిగి పొందలేరు. - Copy to clipboard + తాత్కాలికంగా భద్రపరుచు ప్రదేశముకు నకలు చెయ్యి - Next + తరువాత - Keep your key safe + మీ కీని సురక్షితంగా ఉంచండి - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + ఒకవేళ మీరు మీ కీని పోగొట్టుకుంటే మీ బ్యాకప్‌ను పునరుద్ధరించడంలో Signal మీకు సహాయం చేయలేకపోతుంది. దాన్ని ఎక్కడైనా భద్రంగా మరియు సురక్షితంగా నిల్వచేసుకోండి మరియు దాన్ని ఇతరులతో పంచుకోకండి. - I\'ve recorded my key + నేను నా కీని రికార్డ్ చేశాను - Continue + కొనసాగండి - See key again + కీని మళ్ళీ చూడండి diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 75b22c5ae8..60de010eee 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + ใช่ ไม่ ลบ @@ -258,6 +261,7 @@ แตะสำหรับรูปภาพ กดค้างสำหรับวิดีโอ + ถ่ายรูป เปลี่ยนกล้อง เปิดอัลบั้มภาพ @@ -343,7 +347,7 @@ นี่ไม่ใช่ลิงก์การโทรที่ถูกต้อง ตรวจสอบให้แน่ใจว่าลิงก์ทั้งหมดครบถ้วนและถูกต้องก่อนที่จะพยายามเข้าร่วม - You are already in a call + คุณอยู่ในสายแล้ว @@ -400,6 +404,7 @@ ไม่พบแอปที่สามารถเปิดสื่อนี้ คัดลอกแล้ว %1$s จาก %1$s + ถึง %1$s อ่านเพิ่มเติม ดาวน์โหลดเพิ่มเติม @@ -433,6 +438,7 @@ ส่งที่แก้ไขแล้ว เขียนข้อความ ขออภัย พบปัญหาในการตั้งค่าแฟ้มแนบของคุณ + SMS หรืออีเมลของผู้รับไม่ถูกต้อง ข้อความว่างเปล่า! สมาชิกกลุ่ม @@ -565,6 +571,7 @@ กำลังบันทึกแฟ้มแนบ %1$d ไปยังที่เก็บข้อมูล… รอดำเนินการ… + ข้อมูล (Signal) MMS SMS @@ -986,6 +993,7 @@ เลิกเชื่อมโยง \'%1$s\' หรือไม่? อุปกรณ์นี้จะไม่สามารถส่งหรือรับข้อความได้อีกต่อไปเมื่อทำการเลิกเชื่อมโยง การเชื่อมต่อเครือข่ายล้มเหลว + ลองอีกครั้ง กำลังเลิกเชื่อมโยงอุปกรณ์… กำลังเลิกเชื่อมโยงอุปกรณ์ @@ -1368,7 +1376,7 @@ ลิงก์กลุ่ม แบ่งปัน ตั้งค่าลิงก์ใหม่ - Require admin approval + ต้องได้รับการอนุมัติจากผู้ดูแล ผู้ดูแลต้องอนุมัติสมาชิกใหม่ที่เข้าร่วมผ่านลิงก์กลุ่ม คุณแน่ใจหรือไม่ว่าต้องการตั้งค่าลิงก์กลุ่มนี้ใหม่? คนจะไม่สามารถเข้าร่วมกลุ่มด้วยลิงก์ปัจจุบันได้อีกต่อไป @@ -1464,6 +1472,7 @@ ส่งคำเชิญทาง %1$dSMS หรือไม่? เปลี่ยนมาใช้ Signal กัน: %1$s + ดูเหมือนว่าคุณไม่มีแอปอะไรที่จะแบ่งปันได้ @@ -1873,6 +1882,7 @@ อนุญาตให้ %1$s ส่งข้อความหารวมถึงมองเห็นชื่อและรูปของคุณหรือไม่ คุณจะไม่ได้รับข้อความจนกว่าจะเลิกบล็อกผู้ใช้รายนี้ อนุญาตให้ %1$s ส่งข้อความหาคุณหรือไม่ คุณจะไม่ได้รับข้อความจนกว่าจะเลิกบล็อกผู้ใช้รายนี้ + รับข้อมูลอัปเดตและข่าวสารจาก %1$s หรือไม่ คุณจะไม่ได้รับข้อมูลใดๆ จนกว่าจะเลิกบล็อกผู้ใช้คนนี้ ต้องการแชทกับกลุ่มนี้ต่อไปและแชร์ชื่อกับรูปภาพให้สมาชิกในกลุ่มเห็นหรือไม่ กลุ่มแบบเก่ากลุ่มนี้ไม่สามารถใช้งานได้แล้ว คุณสามารถสร้างกลุ่มใหม่เพื่อเข้าถึงหลากหลายฟีเจอร์ใหม่ ไม่ว่าจะเป็น @การกล่าวถึง และผู้ดูแล @@ -2171,6 +2181,7 @@ ไม่มีใครที่นี่ %1$s อยู่ในการโทรนี้ + %1$s อยู่ในสายนี้ %1$s และ %2$s อยู่ในการโทรนี้ @@ -2562,7 +2573,7 @@ เราจะลองใหม่อีกครั้งในภายหลัง อัปเดต Signal เรียบร้อยแล้ว ได้รับการอัปเดตอัตโนมัติเป็นเวอร์ชัน %1$s เรียบร้อยแล้ว - You updated to version %1$s. + อัปเดตเป็นเวอร์ชัน %1$s เรียบร้อยแล้ว ส่งข้อความหรือไม่? @@ -2975,6 +2986,7 @@ รูปย่อของแฟ้มแนบ สลับเมนูถ่ายภาพและแนบรูปแบบเร็ว อัดและส่งแฟ้มแนบแบบเสียง + ล็อกการอัดแฟ้มแนบแบบเสียง ข้อความไม่สามารถส่งได้ โปรดตรวจสอบการเชื่อมต่อของคุณและลองอีกครั้ง @@ -2993,6 +3005,7 @@ ข้อความถูกอ่านแล้ว + ภาพของผู้ติดต่อ @@ -3077,6 +3090,7 @@ ทุกอย่างดูดีแล้ว! เพื่อรับการแจ้งเตือนการโทร แตะที่นี่และเปิด \"แสดงการแจ้งเตือน\" เพื่อรับการแจ้งเตือนการโทร แตะที่นี่และเปิดการแจ้งเตือน เพื่อดูให้แน่ใจว่าเสียงและพ็อปอัปเปิดใช้งานอยู่ + เพื่อรับการแจ้งเตือนการโทร แตะที่นี่และเปิดใช้งานกิจกรรมพื้นหลังในการตั้งค่า \"แบตเตอรี่\" การตั้งค่า เพื่อรับการแจ้งเตือนการโทร แตะการตั้งค่าและเปิด \"แสดงการแจ้งเตือน\" @@ -3207,7 +3221,7 @@ อยู่ระหว่างพัก อยู่ระหว่างการทำอะไรใหม่ๆ - One or more characters is invalid. + มีอักขระที่ไม่ถูกต้องอย่างน้อยหนึ่งตัว แก้ไขกลุ่ม @@ -3306,6 +3320,7 @@ + ข้อมูลความช่วยเหลือ การขอความช่วยเหลือ Signal บนแอนดรอยด์ @@ -3393,6 +3408,7 @@ เรียกภาพตัวอย่างลิงก์โดยตรงจากเว็บไซต์สำหรับข้อความที่คุณส่ง เปลี่ยนวลีรหัสผ่าน เปลี่ยนวลีรหัสผ่านของคุณ + เปิดใช้งานการล็อกหน้าจอด้วยวลีรหัสผ่าน ล็อกหน้าจอและการแจ้งเตือนด้วยวลีรหัสผ่าน ความปลอดภัยหน้าจอ @@ -3405,6 +3421,7 @@ รูปแบบการกระพริบไฟ LED ปรับแต่ง เปลี่ยนเสียงและการสั่น + เสียง ไม่ส่งเสียง ค่าเริ่มต้น @@ -5251,6 +5268,7 @@ เพิ่มไปยังสตอรี่ เพิ่มข้อความ + เพิ่มการตอบกลับ ส่งไปให้ ไฟล์สื่อแบบที่ดูได้ครั้งเดียว @@ -5850,6 +5868,8 @@ ตอบกลับ %1$d + + Story no longer hidden เพิ่ม @@ -7109,7 +7129,23 @@ - เพิ่มพื้นที่จัดเก็บ %1$s เพื่อดาวน์โหลดไฟล์สื่อของคุณ + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ ตกลง - - - ประวัติการชำระเงิน - - สำรองข้อความและสื่อทั้งหมด - - รายละเอียดการชำระเงิน - - ประเภทของการสำรองข้อมูล - - วันที่ชำระ - - แชร์ + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ ความถี่ในการสำรองข้อมูล สำรองข้อมูลขณะใช้อินเทอร์เน็ตมือถือ + + View backup key + + Unlock to view backup key ปิดใช้งานและลบข้อมูลสำรอง @@ -7212,13 +7248,27 @@ ระบบจะสำรองข้อมูลในช่วงกลางคืน - ประเภทของการสำรองข้อมูล + Backup plan การสำรองข้อมูลถูกปิดใช้งาน - - %1$s · %2$s/เดือน - - เปิดใช้การสำรองข้อมูล + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + กุญแจสำรองของคุณ - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + กุญแจสำรองคือรหัส 64 หลักที่จะทำให้คุณสามารถกู้คืนข้อมูลสำรองในกรณีที่ติดตั้ง Signal ใหม่อีกครั้ง - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + หากลืมกุญแจของตัวเอง คุณจะไม่สามารถกู้คืนข้อมูลสำรอง และ Signal จะไม่สามารถช่วยกู้ข้อมูลสำรองให้คุณได้ - Next + ดำเนินการต่อ - Record your backup key + บันทึกกุญแจสำรองของคุณ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + คุณจำเป็นต้องใช้กุญแจนี้ในการกู้คืนบัญชีและข้อมูล โปรดเก็บกุญแจนี้ไว้ในที่ปลอดภัย คุณจะไม่สามารถกู้คืนบัญชีได้หากทำกุญแจหาย - Copy to clipboard + คัดลอกไปยังคลิปบอร์ด - Next + ดำเนินการต่อ - Keep your key safe + เก็บกุญแจของคุณให้ปลอดภัย - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + หากทำกุญแจหาย Signal จะไม่สามารถช่วยกู้คืนข้อมูลสำรองของคุณได้ โปรดเก็บรักษากุญแจนี้ไว้ในที่ปลอดภัยและไม่บอกรหัสของคุณกับใคร - I\'ve recorded my key + ฉันบันทึกกุญแจนี้ไว้เรียบร้อยแล้ว - Continue + ดำเนินการต่อ - See key again + ดูกุญแจอีกครั้ง diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 758c5cb760..1943647726 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Oo Hindi Burahin @@ -260,6 +263,7 @@ I-tap para sa photo, i-hold para sa video + Kunan Magpalit ng Camera Buksan ang gallery @@ -346,7 +350,7 @@ Hindi valid ang call link na ito. Siguraduhing buo at tama ang link bago sumali sa tawag. - You are already in a call + Nasa call ka na @@ -403,6 +407,7 @@ Walang mahanap na app na makakapagbukas sa media na ito. Kumopya ng %1$s mula kay %1$s + para kay %1$s   Magbasa pa   Mag-download pa @@ -436,6 +441,7 @@ I-send ang edit Magsulat ng mensahe Paumanhin, nagka-error sa pagsasaayos ng iyong kalakip. + Ang tatanggap ay hindi valid na SMS o email address! Walang laman ang mensahe! Mga kasapi ng grupo @@ -576,6 +582,7 @@ Sine-save ang %1$d attachments sa storage… Nakabinbin… + Data (Signal) MMS SMS @@ -1006,6 +1013,7 @@ I-unlik ang \'%1$s\'? Sa pamamagitan ng pag-unlink sa device na ito, hindi na ito makakapagpadala o makakatanggap ng mga mensahe. Nabigo ang koneksyon sa network + Subukang muli Ina-unlik ang device… Ina-unlink ang device @@ -1408,7 +1416,7 @@ Link ng group Ibahagi I-reset ang link - Require admin approval + Mag-require ng admin approval I-require ang admin na mag-approve ng bagong members na nag-join gamit ang group link. Sigurado ka bang gusto mong i-reset ang group link? Hindi na makaka-join ang users sa group na ito gamit ang current link. @@ -1507,6 +1515,7 @@ Mapadala ng %1$d SMS na imbitasyon? Lumipat tayo sa Signal: %1$s + Mukhang wala kang anumang app na maaaring bahaginan. @@ -1931,6 +1940,7 @@ Hayaang mag-message si %1$s sa \'yo at i-share sa kanya ang pangalan at photo mo? Hindi ka makakatanggap ng anumang message hanggang sa i-unblock mo siya. Hayaang mag-message si %1$s sa\'yo? Hindi ka makakatanggap ng anumang message hanggang sa i-unblock mo siya. + Gusto mo bang makakuha ng updates at news mula kay %1$s? Hindi ka makakatanggap ng anumang update hanggang sa i-unblock mo siya. Gusto mo bang ipagpatuloy ang iyong chat sa group na ito at i-share ang pangalan at photo mo sa members nito? Hindi na magagamit ang Legacy Group na ito. Gumawa ng bagong group para ma-activate ang bagong features gaya ng @mentions at admins. @@ -2245,6 +2255,7 @@ Walang ibang tao rito %1$s ang nasa call na ito + %1$s ang nasa call na ito Sila %1$s at %2$s ay nasa call na ito @@ -2647,7 +2658,7 @@ Susubukan ulit namin ito mamaya. Successful na na-update ang Signal Automatic na na-update ang iyong device sa version %1$s. - You updated to version %1$s. + Nag-update ka sa version %1$s. Ipadala ang mensahe? @@ -3067,6 +3078,7 @@ Thumbnail ng Kalakip I-toggle ang quick camera attachment drawer Mag-record at magpadala ng audio na kalakip + I-lock ang pag-record ng audio na kalakip Message could not be sent. I-check ang iyong connection at subukan ulit. @@ -3085,6 +3097,7 @@ Nabasa na ang mensahe + Larawan ng kontak @@ -3169,6 +3182,7 @@ Everything looks good now! Para maka-receive ng call notifications, i-tap ito at i-turn on ang \"Show notifications.\" Para maka-receive ng call notifications, i-tap ito, i-turn on ang notifications, at siguraduhing naka-enable rin ang Sound at Pop-up. + Para maka-receive ng call notifications, i-tap ito at i-enable ang background activity sa \"Battery\" settings. Settings Para maka-receive ng call notifications, i-tap ang Settings at i-turn on ang \"Show notifications.\" @@ -3306,7 +3320,7 @@ Taking a break Working on something new - One or more characters is invalid. + Invalid ang isa o mahigit pang characters. I-edit ang grupo @@ -3405,6 +3419,7 @@ + Pangsuportang impormasyon Signal Android na pangsuportang hiling @@ -3493,6 +3508,7 @@ I-retrieve ang link previews directly from websites for messages you send. Baguhin ang passphrase Baguhin ang iyong passphrase + I-enable ang passphrase screen lock Lock screen at mga notipikasyon na may passphrase Seguridad ng screen @@ -3505,6 +3521,7 @@ Pattern ng pagpatay-sindi ng LED I-customize Palitan ang sound at vibration + Tunog Tahimik Default @@ -5378,6 +5395,7 @@ I-add sa story Mag-add ng message + Mag-add ng reply I-send kay View-once media @@ -5982,6 +6000,8 @@ %1$d reply %1$d replies + + Story no longer hidden Mag-add @@ -7267,7 +7287,23 @@ - Mag-free up ng %1$s na space para ma-download ang iyong media. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ OK - - - Payment history - - Backup ng text at lahat ng media - - Payment details - - Uri ng backup - - Date paid - - Ibahagi + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Dalas ng backup Mag-back up gamit ang mobile data + + View backup key + + Unlock to view backup key I-off at burahin ang backup @@ -7370,13 +7406,27 @@ Gagawin ang backup overnight. - Uri ng backup + Backup plan Naka-disable ang backups - - %1$s · %2$s/buwan - - I-enable ang mga backup? + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Ang backup key mo - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Ang backup key mo ay isang 64-digit code na magagamit mo sa pag-restore ng iyong backup kapag nag-install ka ulit ng Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Kapag nakalimutan mo ang iyong PIN, hindi mo maaaring ma-restore ang backup mo. Hindi ka matutulungan ng Signal sa pag-recover ng backup mo. - Next + Susunod - Record your backup key + I-record ang backup key mo - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Kailangan ang key para ma-recover mo ang iyong account at data. Itago ang key na ito sa ligtas na lugar. Kapag nawala mo ito, hindi mo mare-recover ang iyong account. - Copy to clipboard + Kopyahin sa clipboard - Next + Susunod - Keep your key safe + Panatilihing ligtas ang iyong key - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Hindi ka matutulungan ng Signal na i-restore ang backup mo kapag nawala mo ang iyong key. Ilagay ito sa ligtas na lugar, at huwag itong i-share sa iba. - I\'ve recorded my key + Na-record ko na ang aking key - Continue + Magpatuloy - See key again + Tingnan ulit ang key diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 429fbf75ce..3c8a9890f0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Evet Hayır Sil @@ -260,6 +263,7 @@ Fotoğraf için dokunun, video için basılı tutun + Yakala Kamerayı değiştir Galeriyi aç @@ -346,7 +350,7 @@ Bu, geçerli bir arama bağlantısı değil. Katılmayı denemeden önce tüm bağlantının tam ve doğru olduğundan emin ol. - You are already in a call + Zaten bir aramadasın @@ -403,6 +407,7 @@ Bu içeriği açabilen bir uygulama bulunamadı. %1$s kopyalandı gönderen %1$s + alıcı %1$s   Dahasını Okuyun   Dahasını İndirin @@ -436,6 +441,7 @@ Düzenlemeyi gönder İleti oluştur Üzgünüz, eklentiniz ayarlanırken hata oluştu. + Alıcı geçerli bir SMS veya e-posta adresi değil! İleti boş! Grup üyeleri @@ -576,6 +582,7 @@ %1$d eklenti depolamaya kaydediliyor… Bekleniyor… + Veri (Signal) MMS SMS @@ -1006,6 +1013,7 @@ \'%1$s\' bağlantısını kaldır? Bu cihazın bağlantısını kaldırarak, artık ileti alamayacak ya da gönderemeyeceksiniz. Ağ bağlantısı başarısız + Tekrar dene Cihaz bağlantısı kesiliyor… Cihaz bağlantısı kaldırılıyor @@ -1408,7 +1416,7 @@ Grup bağlantısı Paylaş Bağlantıyı sıfırla - Require admin approval + Yönetici onayı iste Grup bağlantısı üzerinden katılan yeni üyelerin yönetici tarafından onaylanması gereksin. Grup bağlantısını sıfırlamak istediğinizden emin misiniz? İnsanlar artık geçerli bağlantıyı kullanarak gruba katılamazlar. @@ -1507,6 +1515,7 @@ %1$d SMS daveti gönderilsin mi? Hadi Signal\'e geçelim: %1$s + Paylaşmak için herhangi bir uygulamanız yok gibi görünüyor. @@ -1931,6 +1940,7 @@ %1$s kişisinin seninle yazışmasına ve adınla fotoğrafını paylaşmasına izin vermek istiyor musun? Engelini kaldırana kadar hiçbir ileti almayacaksın. %1$s sana mesaj gönderebilsin mi? Engelini kaldırana kadar hiçbir ileti almayacaksın. + %1$s ile ilgili güncelleme ve haber alınsın mı? Engelini kaldırana kadar herhangi bir güncelleme almayacaksın. Bu grupla sohbete devam etmek ve adını ve fotoğrafını üyeleriyle paylaşmak ister misin? Bu Eski Grup artık kullanılamıyor. @bahsetmeler ve yöneticiler gibi yeni özellikleri etkinleştirmek için yeni bir grup oluştur. @@ -2245,6 +2255,7 @@ Burada kimse yok %1$s bu aramada + %1$s bu aramada %1$s ve %2$s bu aramada @@ -2647,7 +2658,7 @@ Daha sonra tekrar deneyeceğiz. Signal başarıyla güncellendi Otomatik olarak %1$s sürümüne güncelleme yapıldı. - You updated to version %1$s. + %1$s sürümüne güncelledin. İleti gönderilsin mi? @@ -3067,6 +3078,7 @@ Eklenti Önizlemesi Hızlı kamera eki çekmecesini göster/gizle Ses eki kaydet ve gönder + Ses eklentisi kaydını kilitle İleti gönderilemedi. Bağlantınızı kontrol edip tekrar deneyiniz. @@ -3085,6 +3097,7 @@ İleti okundu + Kişi fotoğrafı @@ -3169,6 +3182,7 @@ Şimdi her şey iyi gözüküyor! Arama bildirimlerini almak için, buraya dokunun ve \"Bildirimleri göster\" seçeneğini açın. Arama bildirimlerini almak için buraya dokunun ve bildirimleri açın ve Ses ve Açılır Pencere\'nin etkin olduğundan emin olun. + Arama bildirimlerini almak için, buraya dokunun ve \"Batarya\" ayarlarında arka plan faaliyetini etkinleştirin. Ayarlar Arama bildirimlerini almak için Ayarlar seçeneğine dokunun ve \"Bildirimleri göster\" seçeneğini açın. @@ -3306,7 +3320,7 @@ Dinleniyor Yeni bir şeyler üzerine çalışıyor - One or more characters is invalid. + Bir veya daha fazla karakter geçersiz. Grubu düzenle @@ -3405,6 +3419,7 @@ + Destek Bilgisi Signal Android Destek İsteği @@ -3493,6 +3508,7 @@ Gönderdiğiniz iletiler için doğrudan web sitelerinden bağlantı önizlemelerini al. Parola değiştir Parolanı değiştir + Ekran kilidi parolasını etkinleştirin Ekranı ve bildirimleri bir parola ile kilitleyin. Ekran güvenliği @@ -3505,6 +3521,7 @@ LED yanıp sönme şekli Özelleştir Ses ve titreşimi değiştir + Zil Sesi Sessiz Varsayılan @@ -5378,6 +5395,7 @@ Hikayeye ekle İleti ekle + Yanıt ekle Gönder: Bir kez görünür medya @@ -5982,6 +6000,8 @@ %1$d yanıt %1$d yanıt + + Story no longer hidden Ekle @@ -7267,7 +7287,23 @@ - Medyanı indirmek için %1$s alan boşalt. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ TAMAM - - - Ödeme geçmişi - - Metin ve tüm medya yedeklemesi - - Ödeme detayları - - Yedekleme türü - - Ödeme tarihi - - Paylaş + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ Yedekleme sıklığı Hücresel bağlantı kullanarak yedekleme + + View backup key + + Unlock to view backup key Yedeklemeyi kapat ve sil @@ -7370,13 +7406,27 @@ Yedekleme gece boyunca oluşturulur. - Yedekleme türü + Backup plan Yedeklemeler devre dışı - - %1$s · %2$s/ay - - Yedekleri etkinleştir + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + Yedekleme anahtarın - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Yedekleme anahtarın, Signal\'i yeniden yüklediğinde yedeklemeni geri yüklemeni sağlayan 64 haneli bir koddur. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Anahtarını unutursan yedeklemeni geri yükleyemezsin. Signal, yedeklemeni kurtarmana yardımcı olamaz. - Next + İleri - Record your backup key + Yedekleme anahtarını kaydet - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Hesabını ve verilerini kurtarmak için bu anahtar gereklidir. Bu anahtarı güvenli bir yerde sakla. Kaybedersen, hesabını kurtarmanız mümkün olmaz. - Copy to clipboard + Panoya kopyala - Next + İleri - Keep your key safe + Anahtarını güvende tut - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Anahtarını kaybedersen Signal yedeklemeni geri yüklemene yardımcı olamaz. Güvenli ve emniyetli bir yerde sakla ve başkalarıyla paylaşma. - I\'ve recorded my key + Anahtarımı kaydettim - Continue + Devam et - See key again + Anahtarı tekrar gör diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index a2cc008970..452c7d1914 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + ھەئە ياق ئۆچۈر @@ -258,6 +261,7 @@ چېكىلسە رەسىم، بېسىپ تۇرۇلسا سىن + ئېكران تۇتۇش كامېرا ئالماشتۇر سۈرەتداننى ئاچ @@ -343,7 +347,7 @@ بۇ ئىناۋەتلىك چاقىرىق ئۇلىنىشى ئەمەس. توپقا قوشۇلۇشتىن بۇرۇن پۈتۈن ئۇلانمىنىڭ بىخەتەر ۋە توغرا ئىكەنلىكىنى جەزملەشتۈرۈڭ. - You are already in a call + سىز ئاللىبۇرۇن چاقىرىقتا @@ -400,6 +404,7 @@ بۇ ۋاسىتەنى ئاچالايدىغان ئەپ تاپالمىدى. كۆچۈرۈلگەن %1$s دىن باشلاپ%1$s + تاپشۇرۇۋالغۇچى %1$s تېخىمۇ كۆپ تېخىمۇ كۆپ چۈشۈرۈش @@ -433,6 +438,7 @@ تەھرىرلەنگەننى يوللاش ئۇچۇر ياز كەچۈرۈڭ، قوشۇمچە ھۆججىتىڭىزنى تەڭشەشتە خاتالىق كۆرۈلدى. + تاپشۇرۇۋالغۇچى ئىناۋەتلىك قىسقا ئۇچۇر ياكى ئېلخەت ئادرېسى ئەمەس! ئۇچۇر بوش! گۇرۇپپا ئەزالىرى @@ -565,6 +571,7 @@ %1$d يۈكلەنمىنى بوشلۇققا ساقلاۋاتىدۇ… كۈتۈۋاتىدۇ… + مەلۇمات(Signal) كۆپ ۋاسىتە ئۇچۇرى قىسقا ئۇچۇر @@ -986,6 +993,7 @@ «%1$s» نى ئاجرىتامسىز؟ بۇ ئۈسكۈنىنى ئاجراتقاندىن كېيىن، ئۇ داۋاملىق ئۇچۇر ئەۋەتەلمەيدۇ ياكى قوبۇل قىلالمايدۇ. تور ئۇلىنىشى مەغلۇپ بولدى + قايتا سىناڭ ئۈسكۈنە ئاجراۋاتىدۇ… ئۈسكۈنە ئاجراۋاتىدۇ… @@ -1368,7 +1376,7 @@ گۇرۇپپا ئۇلانمىسى ھەمبەھىرلە ئۇلانمىنى ئەسلىگە قايتۇر - Require admin approval + باشقۇرغۇچىنىڭ تەستىقىنى تەلەپ قىلىش گۇرۇپپا ئۇلانمىسى ئارقىلىق قوشۇلماقچى بولغان يېڭى ئەزالارنى باشقۇرغۇچى دەلىللەيدۇ. گۇرۇپپا ئۇلانمىسىنى راستلا ئەسلىگە قايتۇرامسىز؟ ئەسلىگە قايتۇرۇلغاندىن كېيىن كىشىلەر ئىلگىرىكى ئۇلانما ئارقىلىق گۇرۇپپىغا قوشۇلالمايدۇ. @@ -1464,6 +1472,7 @@ %1$d SMS تەكلىپى يوللامسىز؟ Signal دا پاراڭلىشايلى:%1$s + قارىغاندا ھەمبەھىرلەيدىغان ئەپ يوقتەك تۇرىدۇ. @@ -1873,6 +1882,7 @@ %1$s نىڭ سىزگە ئۇچۇر يوللىشىغا ۋە ئىسمىڭىز ۋە رەسىمىڭىزنى ئورتاقلىشىشىغا رۇخسەتمۇ؟ ئۇلارنى چەكلەشتىن بىكار قىلمىغۇچە ئۇلارنىڭ ئۇچۇرىنى تاپشۇرۇۋالالمايسىز. %1$s سىزگە ئۇچۇر قىلسا بولامدۇ؟ ئۇلارنى توسۇشنى ئەمەلدىن قالدۇرمىغىچە ئۇلاردىن ئۇچۇر تاپشۇرۇۋالمايسىز. + %1$s توغرىسىدىكى يېڭىلىقلاردىن خەۋەردار بولماقچىمۇ؟ ئۇلارنى توسۇشنى ئەمەلدىن قالدۇرمىغىچە ئۇلاردىن ئۇچۇر تاپشۇرۇۋالمايسىز. بۇ گۇرۇپپىدا پاراڭنى داۋاملاشتۇرامسىز ھەمدە ئىسمىڭىز ۋە سۈرىتىڭىزنى ئۇنىڭ ئەزالىرى بىلەن ھەمبەھىرلەمسىز؟ بۇ كونا گۇرۇپپىنى ئەمدى ئىشلەتكىلى بولمايدۇ. يېڭى گۇرۇپپا قۇرۇپ @تىلغا ئېلىش ۋە باشقۇرغۇچى قاتارلىق ئىقتىدارلارنى سىناپ كۆرۈڭ. @@ -2171,6 +2181,7 @@ بۇ يەردە ھېچكىم يوق %1$s بۇ چاقىرىقتا + %1$s مۇ بۇ چاقىرىقتا %1$s ۋە %2$s بۇ چاقىرىقتا @@ -2562,7 +2573,7 @@ كېيىنرەك قايتا سىناپ باقىمىز. Signal ئوڭۇشلۇق يېڭىلاندى سىز ئاپتوماتىك ھالدا %1$s نەشرىگە يېڭىلاندىڭىز. - You updated to version %1$s. + سىز%1$s نەشرىگە يېڭىلىدىڭىز. ئۇچۇر ئەۋەتەمسىز؟ @@ -2975,6 +2986,7 @@ قوشۇمچىنىڭ كىچىك سۈرىتى تېز كامېرا قوشۇمچە تارتمىسىغا ئالماشتۇر ئۈن قوشۇمچىسىنى ئۇنگە ئال ۋە ئەۋەت + ئۈن قوشۇمچىسىنى ئۈنگەئېلىشنى قۇلۇپلا ئۇچۇر ئەۋەتىلەلمىدى. ئۇلىنىشڭىزنى تەكشۈرۈپ قايتا سىناڭ. @@ -2993,6 +3005,7 @@ ئۇچۇر ئوقۇلدى + ئالاقەداش سۈرەت @@ -3077,6 +3090,7 @@ ھازىر ھەممە ئىش جايىدا كۆرۈنۈۋاتىدۇ! چاقىرىش ئۇقتۇرۇشىنى تاپشۇرۇۋېلىش ئۈچۈن، بۇ جاينى چېكىپ، «ئۇقتۇرۇشلارنى كۆرسەت»نى ئېچىڭ. چاقىرىش ئۇقتۇرۇشىنى تاپشۇرۇۋېلىش ئۈچۈن، بۇ جاينى چېكىپ، ئۇقتۇرۇشلارنى ئېچىڭ ھەمدە ئۈن ۋە قاڭقىش تىزىملىكى قوزغىتىلغانلىقىنى جەزملەڭ. + چاقىرىش ئۇقتۇرۇشىنى تاپشۇرۇۋېلىش ئۈچۈن، بۇ جاينى چېكىپ، «توكدان» تەڭشەكلىرىدىن ئارقا سۇپا پائالىيىتىنى ئېچىڭ. تەڭشەكلەر چاقىرىش ئۇقتۇرۇشىنى تاپشۇرۇۋېلىش ئۈچۈن، تەڭشەكلەرنى چېكىپ، «ئۇقتۇرۇشلارنى كۆرسەت»نى ئېچىڭ. @@ -3207,7 +3221,7 @@ ئارام ئارايلى يېڭىلىقنى سىنايلى - One or more characters is invalid. + بىر ياكى بىر نەچچە بەلگىلەر ئىناۋەتسىز. گۇرۇپپا تەھرىر @@ -3306,6 +3320,7 @@ + قوللاش ئۇچۇرى Signal Android قوللاش ئىلتىماسى @@ -3393,6 +3408,7 @@ سىز يوللىغان تور بېكەت ئۇلانمىسىدىن بىۋاسىتە ئالدىن كۆرسىتىش ھاسىللايدۇ. ئىم ئىبارىسى ئۆزگەرت ئىم ئىبارىسى ئۆزگەرتىڭ + ئېكران قۇلۇپ ئىم ئىبارىسىنى قوزغىتىدۇ ئېكران ۋە ئۇقتۇرۇشلارنى ئىم ئىبارىسى بىلەن قۇلۇپلايدۇ ئېكران بىخەتەرلىكى @@ -3405,6 +3421,7 @@ LED چاقناش شەكلى خاسلاشتۇرۇش ئاۋاز ۋە تىتىرەشنى ئۆزگەرتىش + ئاۋاز تىنچ كۆڭۈلدىكى @@ -5251,6 +5268,7 @@ ھېكايىگە قوشۇش بىز ئۇچۇر قوش + جاۋاپ قوش تاپشۇرۇۋالغۇچى بىرلا قېتىم كۆرۈلىدىغان مېدىيا @@ -5850,6 +5868,8 @@ %1$d جاۋاب + + Story no longer hidden قوشۇش @@ -7109,11 +7129,27 @@ - مېدىيارىڭىزنى چۈشۈرۈش ئۈچۈن %1$s بوشلۇق ھازىرلاڭ. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + - Google Play + گۇگۇل ئويۇنى ئىناۋەتلىك كارتا ياكى بانكا كارتىسى @@ -7172,19 +7208,15 @@ بولىدۇ - - - پۇل تۆلەش تارىخى - - تېكىست ۋە بارلىق مېدىيانى زاپاسلاش - - پۇل تۆلەش تەپسىلاتلىرى - - زاپاسلاش تىپى - - پۇل تۆلەنگەن ۋاقىت - - ھەمبەھىرلە + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ زاپاسلاش چاستوتىسى كۆچمە ئېقىم ئىشلىتىپ زاپاسلاش + + View backup key + + Unlock to view backup key ئېتىپ زاپاسلانمىنى ئۆچۈرۈش @@ -7212,13 +7248,27 @@ زاپاسلاش بىر كېچىدىلا قۇرۇلىدۇ. - زاپاسلاش تىپى + Backup plan زاپاسلاش چەكلەنگەن - - %1$s·%2$s/ئايلىق - - زاپاسلارنى قوزغات + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + زاپاسلاش ئاچقۇچىڭىز - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + سىزنىڭ زاپاسلاش ئاچقۇچىڭىز بولسا سىگنالنى قايتا قاچىلىغاندا ياردەم قىلىپ زاپاسلاشنى ئەسلىگە كەلتۈرىدىغان 64 خانىلىق كود. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + ئەگەر ئاچقۇچنى ئۇنتۇپ قالسىڭىز ، زاپاسلاشنى ئەسلىگە كەلتۈرەلمەيسىز. سىگنال زاپاسلاشنى ئەسلىگە كەلتۈرۈشىڭىزگە ياردەم قىلالمىدى. - Next + كېيىنكى - Record your backup key + زاپاسلاش ئاچقۇچىڭىز خاتىرلەڭ - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + بۇ ئاچقۇچ ھېساباتىڭىز ۋە سانلىق مەلۇماتلىرىڭىزنى ئەسلىگە كەلتۈرۈش ئۈچۈن تەلەپ قىلىنىدۇ. بۇ ئاچقۇچنى بىخەتەر جايدا ساقلاڭ. ئەگەر ئۇنى يوقىتىپ قويسىڭىز ، ھېساباتىڭىزنى ئەسلىگە كەلتۈرەلمەيسىز. - Copy to clipboard + چاپلاش تاختىسىغا كۆچۈر - Next + كېيىنكى - Keep your key safe + ئاچقۇچىڭىزنى بىخەتەر ساقلاڭ - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + ئەگەر ئاچقۇچڭىزنى يوقىتىپ قويسىڭىز ، سىگنال ياردەملىشىپ زاپاسلاشنى ئەسلىگە كەلتۈرەلمەيدۇ. ئۇنى بىخەتەر جايدا ساقلاڭ ، ھەمدە باشقىلار بىلەن ئورتاقلاشماڭ. - I\'ve recorded my key + ئاچقۇچنى خاتىرىلىدىم - Continue + داۋاملاشتۇرۇش - See key again + ئاچقۇچنى قايتا كۆرۈڭ diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e6214e7791..6f88fb19dc 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Так Ні Видалити @@ -264,6 +267,7 @@ Торкніться для фото, утримуйте для відео + Зняти Змінити камеру Відкрити галерею @@ -352,7 +356,7 @@ Це посилання на виклик недійсне. Перевірте, чи посилання повне й правильне, і повторіть спробу приєднатися до виклику. - You are already in a call + Ви вже берете участь у виклику @@ -409,6 +413,7 @@ Не знайдено застосунок, який міг би відкрити цей медіафайл. Скопійовано: %1$s від користувача %1$s + у %1$s   Читати далі   Завантажити ще @@ -442,6 +447,7 @@ Надіслати редагування Написати повідомлення На жаль, під час додавання вкладення сталася помилка. + Цьому одержувачу неможливо надіслати SMS-повідомлення / імейл! Повідомлення порожнє! Учасники групи @@ -598,6 +604,7 @@ Триває збереження %1$d вкладення в пам\'яті… Надсилання триває… + інтернет (Signal) MMS-повідомлення SMS-повідомлення @@ -1046,6 +1053,7 @@ Відв\'язати «%1$s»? Якщо відв\'язати цей пристрій, він більше не зможе надсилати та отримувати повідомлення. Зв\'язок з мережею відсутній + Повторіть спробу Триває відв\'язування пристрою… Триває відв\'язування пристрою @@ -1488,7 +1496,7 @@ Посилання на групу Поділитися Скинути посилання - Require admin approval + Адміністратор затверджує учасників Нових учасників, які приєднуються до групи за посиланням, повинен затверджувати адміністратор. Ви точно хочете скинути посилання на цю групу? Долучатися до групи за поточним посиланням стане неможливо. @@ -1593,6 +1601,7 @@ Надіслати %1$d SMS-запрошення? Пора переходити в Signal: %1$s + У вас немає застосунків, через які можна надіслати запрошення. @@ -2047,6 +2056,7 @@ Дозволити користувачу %1$s надсилати вам повідомлення та ділитись ім\'ям і фото з цим користувачем? Ви не отримаєте повідомлень, поки не розблокуєте цього користувача. Дозволити користувачу %1$s надсилати вам повідомлення? Ви не отримаєте повідомлень, поки не розблокуєте цього користувача. + Отримувати оновлення та новини від %1$s? Ви не отримуватимете оновлень, поки не розблокуєте користувача. Продовжувати спілкуватися з цією групою та ділитися своїм ім’ям та фото з її учасниками? Цю застарілу групу більше не можна використовувати. Створіть нову групу, щоб активувати такі функції, як @згадки та адміністрування. @@ -2393,6 +2403,7 @@ Тут більше нікого немає %1$s бере участь у цьому виклику + %1$s беруть участь у цьому виклику %1$s і %2$s беруть участь у цьому виклику @@ -2587,19 +2598,19 @@ Триває оновлення сервісів Google Play або вони тимчасово недоступні. Спробуйте пізніше. Умови надання послуг і Політика конфіденційності Signal потребує доступу до ваших контактів і медіафайлів, щоб ви могли зв\'язуватися з друзями й надсилати повідомлення. Ваші контакти буде вивантажено з використанням функції конфіденційного виявлення контактів Signal, тобто їх буде зашифровано наскрізним методом і вони будуть невидимі для служби Signal. - Signal потребує дозволу для перегляду контактів, щоб допомогти вам зв’язатися з друзями. Ваші контакти завантажуються за допомогою функції виявлення приватних контактів Signal, що означає, що вони наскрізне зашифровані і ніколи не відображаються службою Signal. - Ви зробили надто багато спроб зареєструвати цей номер. Будь ласка, спробуйте ще раз пізніше. + Signal потребує доступу до ваших контактів, щоб ви могли зв\'язуватися з друзями. Ваші контакти буде вивантажено з використанням функції конфіденційного виявлення контактів Signal, тобто їх буде зашифровано наскрізним методом і вони будуть невидимі для служби Signal. + Ви зробили забагато спроб зареєструвати цей номер. Будь ласка, спробуйте пізніше. Ви зробили забагато спроб зареєструвати цей номер. Будь ласка, повторіть через %1$s. - Немає з\'єднання із сервером. Перевірте чи наявність доступу до Інтернет та спробуйте знову. + Неможливо з\'єднатися з сервісом. Перевірте з\'єднання з мережею і спробуйте знову. Ми не змогли надіслати вам код підтвердження в SMS. Код можна також отримати, прийнявши аудіовиклик. Не вдається надіслати запит на код підтвердження. Перевірте підключення до мережі та спробуйте знову. Нестандартний формат номеру - Введене число (%1$s) виглядає нестандартним.\n\nВи мали на увазі %2$s? - Signal Android — Формат телефонних номерів + Здається, ви ввели номер у нестандартному форматі: %1$s.\n\nВи мали на увазі %2$s? + Signal для Android — Формат телефонних номерів Запит на виклик надіслано @@ -2607,19 +2618,19 @@ Код підтвердження запитано - Ви в %1$d кроці від відправки журналу налагодження - Ви в %1$d кроках від відправки журналу налагодження - Ви в %1$d кроках від відправки журналу налагодження - Ви в %1$d кроках від відправки журналу налагодження + Ще %1$d крок, і журнал налагодження буде надіслано + Ще %1$d кроки, і журнал налагодження буде надіслано + Ще %1$d кроків, і журнал налагодження буде надіслано + Ще %1$d кроку, і журнал налагодження буде надіслано - Нам потрібно підтвердження що Ви людина. + Нам потрібно переконатися, що ви людина. Аудіовиклик Скасувати Далі Продовжити - Візьміть конфіденційність із собою.\nБудьте собою в кожному повідомленні. + Не відмовляйте собі в конфіденційності.\nБудьте собою в кожному повідомленні. Номер телефону @@ -2627,7 +2638,7 @@ Введіть код, який ми відправили на %1$s Номер телефону - Код Країни + Код країни Виклик Код підтвердження Надішліть код ще раз @@ -2662,7 +2673,7 @@ Детальніше - Не знайдено результатів для \'%1$s\' + Результатів для «%1$s» не знайдено @@ -2702,8 +2713,8 @@ Ви скинули безпечний сеанс. - %1$s скидає безпечний сеанс. - Дублікат повідомлення. + Користувач %1$s скинув безпечний сеанс. + Продубльоване повідомлення. Стікери @@ -2727,7 +2738,7 @@ Не вдалося завантажити набір стікерів - Редагувати + Змінити Готово Зберегти @@ -2737,11 +2748,11 @@ Збережено Торкніться рядка, щоб видалити його Надіслати - Не вдалось передати логи - Успішно! - Скопіюйте цей URL-адресу та додайте його до звіту про проблему або до листа в службу підтримки:\\ n\n %1$s + Не вдалося надіслати журнал + Готово! + Скопіюйте цю URL-адресу й додайте її до свого повідомлення про проблему чи листа в службу підтримки:\n\n%1$s Поділитися - Цей журнал буде доступний онлайн для перегляду розробниками. Ви можете перевірити його перед завантаженням. + Цей журнал буде загальнодоступний онлайн для перегляду відповідними фахівцями. Ви можете ознайомитися з ним перед вивантаженням. @@ -2756,13 +2767,13 @@ Групу оновлено Покинути групу - Скидання безпечного сеансу. + Безпечний сеанс скинуто. Чернетка: - Медіаповідомлення + Медіафайл Стікер - Фото вже переглянуто - Відео вже переглянуто - Медіа вже переглянуто + Одноразове фото + Одноразове відео + Одноразовий медіафайл Це повідомлення видалено. Ви видалили це повідомлення. @@ -2781,11 +2792,11 @@ Ви позначили як перевірений Ви позначили як неперевірений Не вдалось опрацювати повідомлення - Проблема з доставленням - Запит спілкування + Проблема з доставкою + Запит на повідомлення Ви приховали цей контакт. Щоб повернути контакт у свій список, надішліть йому повідомлення. - Світлини + Фото GIF Голосове повідомлення Файл @@ -2817,7 +2828,7 @@ Ми спробуємо знову пізніше. Signal успішно оновлено Ваш застосунок автоматично оновлено до версії %1$s. - You updated to version %1$s. + Версію оновлено до %1$s. Надіслати повідомлення? @@ -2837,14 +2848,14 @@ Ім\'я користувача Видалити Ім\'я користувача видалено. - Виникла помилка мережі. + Сталася мережева помилка. Забагато спроб. Краще спробувати знову пізніше. - Це ім\'я користувача вже використовується. - Імена користувачів можуть мати лише такі символи: a–Z, 0–9 та нижнє підкреслювання. + Це ім\'я користувача недоступне. + Імена користувачів можуть мати лише такі символи: a–Z, 0–9 і знак підкреслення. Ім\'я користувача не може починатися з цифри. - Невірне ім\'я користувача. - Ім\'я користувача складається з символів кількістю від %1$d до %2$d. + Неправильне ім\'я користувача. + В імені користувача повинно бути від %1$d до %2$d символів. Імена користувачів завжди поєднуються з кількома цифрами. @@ -2872,22 +2883,22 @@ Продовжити - %1$d контакт у Signal! - %1$d контактів у Signal! - %1$d контактів у Signal! - %1$d контактів у Signal! + %1$d контакт є в Signal! + %1$d контакти є в Signal! + %1$d контактів є в Signal! + %1$d контакту є в Signal! - Копіювати чи поділитися посиланням на ім\'я користувача + Скопіюйте посилання на ім\'я користувача або поділіться ним - Ваш контакт працює на новішій версії Signal із несумісним форматом QR-коду. Будь ласка, оновіться, щоб порівняти. + Ваш контакт використовує новішу версію Signal з несумісним форматом QR-кодів. Вам необхідно оновити свій застосунок, щоб порівняти QR-коди. Форматування QR-коду, що ви зісканували, не підходить для підтвердження коду безпеки. Зіскануйте QR-код ще раз. Поділитися кодом безпеки через… Наш код безпеки в Signal: - Схоже, що у вас немає програм, щоб поділитися. + У вас немає застосунків, через які можна надіслати код безпеки. У буфері обміну не знайдено коду безпеки для порівняння Для сканування QR-кодів Signal потребує доступу до камери, але його не надано. Перейдіть у налаштування застосунку, відкрийте «Дозволи» та виберіть камеру. Для сканування QR-коду необхідний доступ до камери @@ -2908,11 +2919,11 @@ - Повідомлення зашифроване для неіснуючої сесії + Повідомлення зашифровано для неіснуючої сесії Погано зашифроване MMS повідомлення - MMS повідомлення зашифроване для неіснуючої сесії + MMS повідомлення зашифровано для неіснуючої сесії Вимкнути сповіщення @@ -2924,22 +2935,22 @@ Ви - Непідтримуваний тип медіа + Непідтримуваний тип медіафайлу Чернетка - Signal потребує доступу до зовнішньої пам\'яті, щоб зберігати файли, але його не надано. Перейдіть у налаштування застосунку, відкрийте «Дозволи» та виберіть «Пам\'ять». + Signal потребує доступу до пам\'яті, щоб зберігати файли, але його не надано. Перейдіть у налаштування застосунку, відкрийте «Дозволи» та виберіть «Пам\'ять». Не надано відповідні дозволи, тому неможливо зберегти файли в зовнішній пам\'яті Видалити повідомлення? Це повідомлення буде остаточно виалено. %1$s у «%2$s» - Ви надіслали %1$s + Ви надіслали користувачеві %1$s - %1$s надіслав вам - Мультимедійний більше не доступний. + Користувач %1$s надіслав вам + Медіафайл більше не доступний. Не вдається відтворити медіа. Під час пошуку повідомлення сталася помилка. - Неможливо знайти програму для того щоб поділитись цим медіафайлом. + Немає застосунку, через який можна було б поділитися цим медіафайлом. Закрити Помилка медіа @@ -2974,12 +2985,12 @@ Не вдалося доставити повідомлення. Під час доставлення повідомлення сталася помилка. Доставлення повідомлення зупинено. - Підтвердьте, щоб продовжувати обмін повідомленнями на Signal. + Пройдіть перевірку, щоб продовжувати спілкуватись у Signal. Позначити все як прочитане Прочитано - Вимкніть ці сповіщення - Фото вже переглянуто - Відео вже переглянуто + Вимкнути ці сповіщення + Одноразове фото + Одноразове відео Відповісти Повідомлення Signal %1$s %2$s @@ -3251,6 +3262,7 @@ Мініатюра вкладення Увімк/вимк скриньку вкладень камери Записати та надіслати аудіо вкладення + Заблокувати запис аудіо вкладення Не вдалося надіслати повідомлення. Перевірте з\'єднання з інтернетом і надішліть повідомлення ще раз. @@ -3269,6 +3281,7 @@ Повідомлення прочитано + Фото контакту @@ -3353,6 +3366,7 @@ Тепер все добре! Щоб отримувати сповіщення про виклики, натисніть тут і ввімкніть «Показувати сповіщення». Щоб отримувати сповіщення про виклики, натисніть тут і ввімкніть сповіщення, а також перевірте, чи ввімкнено звук і спливні сповіщення. + Щоб отримувати сповіщення про виклики, натисніть тут і дозвольте фонову активність в налаштуваннях акумулятора. Налаштування Щоб отримувати сповіщення про виклики, перейдіть у налаштування та ввімкніть «Показувати сповіщення». @@ -3504,7 +3518,7 @@ Відпочиваю Працюю над чимось новим - One or more characters is invalid. + Ви використали принаймні один недозволений символ. Редагувати групу @@ -3603,6 +3617,7 @@ + Допоміжна інформація Запит на підтримку Signal на Android @@ -3693,6 +3708,7 @@ Показувати попередній вигляд вебсторінок за посиланнями, які ви надсилаєте в повідомленнях. Змінити фразу-пароль Змінить вашу фразу-пароль + Увімкнути фразу-пароль Захистити Сигнал та сповіщення повідомлень фразою-паролем Безпека екрана @@ -3705,6 +3721,7 @@ Шаблон мерехтіння LED Налаштувати Змінити звук та вібрацію + Звук Беззвучно Системна @@ -5632,6 +5649,7 @@ Додати до історії Додати повідомлення + Додати відповідь Надіслати Одноразовий медіафайл @@ -6246,6 +6264,8 @@ %1$d відповідей %1$d відповіді + + Story no longer hidden Додати @@ -7583,7 +7603,23 @@ - Звільніть %1$s, щоб завантажити свої медіафайли. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7646,19 +7682,15 @@ Зрозуміло - - - Історія платежів - - Текстові повідомлення та всі медіафайли - - Платіжні дані - - Тип резервного копіювання - - Дата оплати - - Поділитися + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7673,6 +7705,10 @@ Періодичність створення Резервне копіювання через мобільний інтернет + + View backup key + + Unlock to view backup key Вимкнути й видалити резервну копію @@ -7686,13 +7722,27 @@ Резервну копію буде створено вночі. - Тип резервного копіювання + Backup plan Резервне копіювання вимкнено - - %1$s · %2$s/місяць - - Увімкнути резервне копіювання + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7742,33 +7792,33 @@ - Your backup key + Ключ від резервної копії - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Ключ від резервної копії — це 64-значний код, який дозволяє відновити дані з резервної копії в разі перевстановлення Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Якщо ви забудете цей ключ, то не зможете відновити дані з резервної копії. Signal не зможе допомогти вам з відновленням даних. - Next + Далі - Record your backup key + Збережіть ключ від своєї резервної копії - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Цей ключ потрібен для відновлення акаунту й даних. Зберігайте його в надійному місці. Якщо ви втратите ключ, то не зможете відновити акаунт. - Copy to clipboard + Скопіювати в буфер обміну - Next + Далі - Keep your key safe + Бережіть цей ключ - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal не зможе допомогти відновити дані з резервної копії, якщо ви втратите ключ від неї. Зберігайте ключ у надійному місці й нікому його не повідомляйте. - I\'ve recorded my key + Я підтверджую, що ключ записано - Continue + Продовжити - See key again + Подивитися ключ ще раз diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 9518096929..bec5f762a5 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + جی ہاں جی نہیں حذف کریں @@ -260,6 +263,7 @@ تصویر کے لیے ٹیپ کریں، ویڈیو کے لیے پکڑ کر رکھیں + کھینچیں کیمرہ تبدیل کریں گیلری کھولیں @@ -346,7 +350,7 @@ یہ کال لنک مؤثر نہیں ہے۔ شامل ہونے کی کوشش کرنے سے قبل یہ یقینی بنائیں کہ مکمل لنک محفوظ اور درست ہے۔ - You are already in a call + آپ پہلے ہی کال میں ہیں @@ -403,6 +407,7 @@ اس میڈیا کو کھولنے کے قابل ایپ نہیں مل سکا۔ کاپی ہوگئی%1$s %1$sسے + %1$s کو مزید پڑھیں مزید ڈاؤن لوڈ کریں @@ -436,6 +441,7 @@ ترمیم بھیجیں پیغام تحریر کریں معذرت، آپ کی اٹیچمنٹ ترتیب میں کوئی خرابی تھی۔ + وصول کنندہ ایس ایم ایس یا ای میل ایڈریس درست نہیں ہے! پیغام خالی ہے! گروپ کے اراکین @@ -576,6 +582,7 @@ سٹوریج میں %1$d اٹیچمنٹس محفوظ ہو رہی ہیں… زیر غور ہو رہا ہے۔۔۔ + ڈیٹا (Signal) ایم ایم ایس ایس ایم ایس @@ -1006,6 +1013,7 @@ غیر منسلک\'%1$s؟ ڈیوائس کو غیر منسلک کرنے کے ذریعے، یہ مزید پیغامات بھیجنے اور وصول کرنے کے قابل نہیں ہو گا۔ نیٹ ورک رابطے میں ناکامی + دوبارہ کوشش کریں غیر منسلک ڈیوائس۔۔۔ غیر منسلک ڈیوائس @@ -1408,7 +1416,7 @@ گروپ لنک اشتراک کریں لنک کو دوبارہ ترتیب دیں - Require admin approval + ایڈمن کی منظوری درکار ہے کسی گروپ کے ذریعے نئے ممبروں کی شمولیت کی منظوری کے لیئے کسی منتظم کی ضرورت ہے۔ کیا آپ واقعی گروپ لنک کو دوبارہ ترتیب دینا چاہتے ہیں؟ موجودہ لنک کا استعمال کرکے لوگ اب گروپ میں شامل نہیں ہوسکیں گے۔ @@ -1507,6 +1515,7 @@ %1$d ایس ایم ایس دعوت نامے بھیجے؟ Signal میں سو ئچ کریں:%1$s + ایسا لگتا ہے جیسے آپ کے پاس اشتراک کرنے کیلئے کوئی ایپ نہیں ہے۔ @@ -1931,6 +1940,7 @@ %1$s کو خود کو میسج بھیجنے اور ان کے ساتھ اپنا نام اور تصویر شیئر کرنے کی اجازت دیں؟ جب تک آپ ان کو اَن بلاک نہیں کرتے تب تک آپ کو کوئی میسجز موصول نہیں ہوں گے۔ %1$s کو خود کو میسج بھیجنے کی اجازت دیں؟ جب تک آپ ان کو اَن بلاک نہیں کرتے تب تک آپ کو کوئی میسجز موصول نہیں ہوں گے۔ + %1$s سے اپ ڈیٹس اور خبریں حاصل کریں؟ جب تک آپ ان کو اَن بلاک نہیں کرتے تب تک آپ کو کوئی اپ ڈیٹس موصول نہیں ہوں گی۔ اس گروپ کے ساتھ اپنی چیٹ جاری رکھیں اور اس کے ممبرز کے ساتھ اپنا نام اور تصویر شیئر کریں؟ یہ لیگیسی گروپ مزید استعمال نہیں کیا جا سکتا۔ @مینشنز اور ایڈمنز جیسی نئے فیچرز کو فعال بنانے کے لیے نیا گروپ بنائیں۔ @@ -2245,6 +2255,7 @@ یہاں کوئی اور نہیں ہے %1$s اس کال میں ہے + %1$s اس کال میں ہیں %1$s اور %2$s اس کال میں ہیں @@ -2647,7 +2658,7 @@ ہم کچھ دیر بعد دوبارہ کوشش کریں گے۔ Signal کامیابی کے ساتھ اپ ڈیٹ کر دیا گیا آپ خودکار طور پر %1$s ورژن پر اپ ڈیٹ ہو گئے تھے۔ - You updated to version %1$s. + آپ نے ورژن%1$s پر اَپ ڈیٹ کر لیا ہے۔ پیغام بھیجیں؟ @@ -3067,6 +3078,7 @@ منسلکہ تھمب نیل کیمرہ منسلک دراج جلدی سے ٹوگل کریں آڈیو سے منسلک شدہ مواد ریکارڈ کریں اور بھیجیں + آڈیو سے منسلک شدہ مواد کی لاک ریکارڈ ہو رہی ہے پیغام نہیں بھیجا جاسکتا۔ اپنا کنکشن چیک کریں اور دوبارہ کوشش کریں۔ @@ -3085,6 +3097,7 @@ پیغام پڑھیں + رابطہ تصویر @@ -3169,6 +3182,7 @@ اب ہر چیز اچھی لگ رہی ہے! کال کی اطلاعات موصول کرنے کے لئے ، یہاں پر ٹیپ کریں اور \"اطلاعات دکھائیں\" کو آن کریں۔ کال کی اطلاعات موصول کرنے کے لئے ، یہاں پر ٹیپ کریں اور اطلاعات کو آن کریں اور یقینی بنائیں کہ آواز اور pop-up قابل ہیں۔ + کال کی اطلاعات موصول کرنے کے لئے ، یہاں پر ٹیپ کریں اور \"بیٹری\" کی ترتیبات میں پس منظر کی سرگرمی کو فعال کریں۔ ترتیبات کال کی اطلاعات موصول کرنے کیلئے ، ترتیبات کو ٹیپ کریں اور \"اطلاعات دکھائیں\" کو آن کریں۔ @@ -3306,7 +3320,7 @@ ایک وقفے پر کسی نئی چیز پر کام کرنا - One or more characters is invalid. + ایک یا زیادہ حروف غلط ہیں۔ گروپ میں ترمیم کریں @@ -3405,6 +3419,7 @@ + مدد کی معلومات Signalا Android کی مدد کی درخواست @@ -3493,6 +3508,7 @@ اپنے بھیجے ہوئے پیغامات کیلئے براہ راست ویب سائٹس سے لنک پیش نظارہ بازیافت کریں۔ پاسفریز تبدیل کریں اپنا پاسفریز تبدیل کریں + پاسفریز سکرین لاک فعال کریں لاک سکرین اور اطلاعات ایک پاسفریز کے ساتھ سکرین سیکیورٹی @@ -3505,6 +3521,7 @@ LED بلنک پیٹرن حسب ضرورت بنائیں آواز اور وائبریشن تبدیل کریں + آواز خاموش پہلے سے طے شدہ @@ -5378,6 +5395,7 @@ سٹوری میں شامل کریں پیغام شامل کریں + جواب شامل کریں بھیجیں بنام ایک مرتبہ دیکھا جانے والا میڈیا @@ -5982,6 +6000,8 @@ %1$d جواب %1$d جوابات + + Story no longer hidden شامل کریں @@ -7267,7 +7287,23 @@ - اپنا میڈیا ڈاؤن لوڈ کرنے کے لیے %1$s کی جگہ خالی کریں۔ + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7330,19 +7366,15 @@ ٹھیک ہے - - - پیمنٹ ہسٹری - - ٹیکسٹ اور تمام میڈیا بیک اپ - - پیمنٹ کی تفصیلات - - بیک اپ کی قسم - - ادا کیے جانے کی تاریخ - - اشتراک کریں + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7357,6 +7389,10 @@ بیک اپ کی فریکوئنسی سیلولر کا استعمال کرتے ہوئے بیک اپ کریں + + View backup key + + Unlock to view backup key بند کریں اور بیک اپ حذف کریں @@ -7370,13 +7406,27 @@ بیک اپ رات تیار کیا جائے گا۔ - بیک اپ کی قسم + Backup plan بیک اپس غیر فعال کر دیے گئے - - %1$s ·%2$s/ماہانہ - - بیک اپ فعال کریں + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7426,33 +7476,33 @@ - Your backup key + آپ کی بیک اپ کیی - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + آپ کی بیک اپ کی کیی 64-ہندسوں پر مشتمل ایک کوڈ ہے جو آپ کو Signal دوبارہ انسٹال کرنے کے بعد اپنے بیک اپ کو بحال کرنے کی اجازت دیتا ہے۔ - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + اگر آپ اپنی کیی بھول جاتے ہیں، تو آپ اپنے بیک اپ کو بحال نہیں کر سکیں گے۔ Signal آپ کے بیک اپ کو بحال کرنے میں مدد نہیں کر سکتا۔ - Next + اگلا - Record your backup key + اپنی بیک اپ کیی محفوظ کریں - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + یہ کیی آپ کے اکاؤنٹ اور ڈیٹا کو بحال کرنے کے لیے درکار ہوتی ہے۔ اس کیی کو کسی محفوظ جگہ پر اسٹور کریں۔ اگر آپ اسے کھو دیتے ہیں، تو آپ اپنا اکاؤنٹ بحال نہیں کر سکیں گے۔ - Copy to clipboard + کلپ بورڈ کو کاپی کریں - Next + اگلا - Keep your key safe + اپنی کیی کو محفوظ رکھیں - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + اگر آپ اپنی کیی کو کھو دیتے ہیں تو Signal آپ کے بیک اپ کو بحال کرنے میں مدد نہیں کر سکے گا۔ اسے کسی انتہائی محفوظ جگہ پر اسٹور کریں، اور دوسروں کے ساتھ شئیر نہ کریں۔ - I\'ve recorded my key + میں نے اپنی کیی کو بحال کر لیا ہے - Continue + جاری رکھیں - See key again + کیی کو دوبارہ دیکھیں diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 428ce196d7..a2d4e499ac 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + Không Xóa @@ -258,6 +261,7 @@ Nhấn để chụp ảnh, giữ để quay video + Chụp Chuyển camera Mở bộ sưu tập @@ -343,7 +347,7 @@ Đường dẫn cuộc gọi này không hợp lệ. Đảm bảo rằng đường dẫn đầy đủ và chuẩn xác trước khi ấn để tham gia. - You are already in a call + Bạn đã đang ở trong một cuộc gọi @@ -400,6 +404,7 @@ Không tìm thấy ứng dụng để mở tệp đa phương tiện này. Đã sao chép %1$s từ %1$s + tới %1$s Xem thêm Tải thêm @@ -433,6 +438,7 @@ Gửi chỉnh sửa Soạn tin nhắn Xin lỗi, có lỗi thiết đặt tập tin đính kèm của bạn. + Nơi nhận không phải là địa chỉ SMS hay email hợp lệ! Tin nhắn trống! Thành viên trong nhóm @@ -565,6 +571,7 @@ Đang lưu %1$d tệp đính kèm vào bộ nhớ… Đang chờ… + Dữ liệu (Signal) MMS SMS @@ -986,6 +993,7 @@ Gỡ liên kết \'%1$s\'? Khi gỡ liên kết thiết bị này, nó sẽ không thể gửi hoặc nhận tin nhắn được nữa. Lỗi kết nối mạng + Thử lại Đang gỡ liên kết thiết bị… Đang gỡ liên kết thiết bị @@ -1368,7 +1376,7 @@ Đường dẫn nhóm Chia sẻ Đặt lại đường dẫn - Require admin approval + Cần chấp thuận từ quản trị viên Cần quản trị viên chấp nhận thành viên tham gia qua đường dẫn nhóm. Bạn có muốn đặt lại đường dẫn nhóm? Người dùng khác sẽ không thể tham gia nhóm bằng đường dẫn hiện tại. @@ -1464,6 +1472,7 @@ GỬI %1$d LỜI MỜI QUA SMS? Hãy chuyển sang dùng Signal: %1$s + Có vẻ như bạn không có bất kỳ ứng dụng nào để chia sẻ. @@ -1873,6 +1882,7 @@ Cho phép %1$s nhắn tin cho bạn và chia sẻ tên và ảnh của bạn với họ? Bạn sẽ không nhận được tin nhắn nào cho tới khi bạn ngưng chặn họ. Cho phép %1$s nhắn tin cho bạn? Bạn sẽ không nhận thêm tin nhắn mới cho tới khi bạn bỏ chặn họ. + Nhận cập nhật và tin tức từ %1$s? Bạn sẽ không nhận được cập nhật nào cho tới khi bạn bỏ chặn họ. Tiếp tục cuộc trò chuyện của bạn với nhóm này và chia sẻ tên và ảnh của bạn với các thành viên nhóm? Không thể tiếp tục sử dụng Nhóm Cũ này. Tạo một nhóm mới để kích hoạt các tính năng mới như @nhắc tên và quản trị. @@ -2171,6 +2181,7 @@ Không có ai khác đang ở đây %1$s đang trong cuộc gọi này + %1$s đang trong cuộc gọi này %1$s và %2$s đang trong cuộc gọi này @@ -2562,7 +2573,7 @@ Chúng tôi sẽ thử lại sau. Cập nhật Signal thành công Bạn đã được tự động cập nhật lên phiên bản %1$s. - You updated to version %1$s. + Bạn đã cập nhật lên phiên bản %1$s. Gửi tin nhắn? @@ -2975,6 +2986,7 @@ Ảnh nhỏ tập tin đính kèm Mở ngăn kéo chụp ảnh nhanh Ghi âm và gửi tệp âm thanh + Khoá ghi âm Không thể gửi tin nhắn. Vui lòng kiểm tra kết nối mạng và thừ lại. @@ -2993,6 +3005,7 @@ Đã xem tin nhắn + Ảnh liên hệ @@ -3077,6 +3090,7 @@ Mọi thứ có vẻ đã ổn! Để bật thông báo cuộc gọi, hãy nhấn vào đây và bật \"Hiện thông báo.\" Để nhận thông báo cuộc gọi, nhấn vào đây và bật thông báo và đảm bảo Âm thanh và Pop-up được bật lên. + Để nhận thông báo cuộc gọi, nhấn vào đây và bật hoạt động nền trong cài đặt \"Pin\". Cài đặt Để nhận thông báo cuộc gọi, hãy vào Cài đặt và bật \"Hiện thông báo.\" @@ -3207,7 +3221,7 @@ Đang nghỉ ngơi Đang làm một dự án mới - One or more characters is invalid. + Có một hoặc nhiều ký tự không hợp lệ. Sửa nhóm @@ -3306,6 +3320,7 @@ + Thông tin hỗ trợ Yêu cầu hỗ trợ về Signal Android @@ -3393,6 +3408,7 @@ Thêm xem trước liên kết trang web vào những tin nhắn bạn gửi. Đổi mật khẩu Đổi mật khẩu + Bật khoá màn hình bằng mật khẩu Khoá màn hình và thông báo bằng mật khẩu An ninh màn hình @@ -3405,6 +3421,7 @@ Tần suất nháy LED Tùy chỉnh Thay đổi âm thanh và rung + Âm thanh Im lặng Mặc định @@ -5251,6 +5268,7 @@ Thêm vào story Thêm vào một tin nhắn + Thêm trả lời Gửi đến Đa phương tiện xem một lần @@ -5850,6 +5868,8 @@ %1$d câu trả lời + + Story no longer hidden Thêm @@ -7109,7 +7129,23 @@ - Giải phóng đến %1$s dung lượng để tải xuống tập tin đa phương tiện của bạn. + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ OK - - - Lịch sử thanh toán - - Bản sao lưu tin nhắn và tất cả tập tin đa phương tiện - - Chi tiết Thanh toán - - Loại bản sao lưu - - Ngày chi trả - - Chia sẻ + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ Tần suất sao lưu Sao lưu bằng dữ liệu di động + + View backup key + + Unlock to view backup key Tắt và xóa bản sao lưu @@ -7212,13 +7248,27 @@ Bản sao lưu sẽ được tạo trong đêm. - Loại bản sao lưu + Backup plan Đã tắt sao lưu - - %1$s · %2$s/tháng - - Bật sao lưu + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + Mã khóa sao lưu - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + Mã khóa sao lưu của bạn là mã có 64 chữ số có tác dụng giúp bạn khôi phục bản sao lưu khi cài lại Signal. - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + Nếu quên mã khóa, bạn sẽ không thể khôi phục bản sao lưu. Signal không thể giúp bạn khôi phục bản sao lưu. - Next + Tiếp - Record your backup key + Lưu mã khóa sao lưu - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + Cần có mã khóa này để khôi phục tài khoản và dữ liệu của bạn. Lưu mã này ở nơi an toàn. Nếu để mất, bạn sẽ không thể khôi phục tài khoản của mình. - Copy to clipboard + Sao chép vào clipboard - Next + Tiếp - Keep your key safe + Cất giữ an toàn mã khóa - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + Signal sẽ không thể giúp bạn khôi phục bản sao lưu nếu bạn để mất mã khóa. Lưu mã khóa ở nơi an toàn và bảo mật, và không chia sẻ với ai khác. - I\'ve recorded my key + Tôi đã lưu mã của mình - Continue + Tiếp tục - See key again + Xem lại mã khóa diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index 1ef8ee0933..f7dea6075c 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + 唔係 刪除 @@ -258,6 +261,7 @@ 撳一下影相,撳住就拍片 + 撳掣 轉相機 打開圖片庫 @@ -343,7 +347,7 @@ 通話連結無效。請檢查連結係咪完整同正確,然後再嘗試加入。 - You are already in a call + 你已經參與緊另一個通話 @@ -400,6 +404,7 @@ 搵唔到個 app 可以開到呢個多媒體。 複製咗 %1$s 由 %1$s + 到 %1$s   睇埋佢   下載埋佢 @@ -433,6 +438,7 @@ 傳送編輯 寫訊息 唔好意思,您份附件搞唔掂。 + 收件人唔係有效嘅短訊或電郵地址! 乜嘢都無寫噃! 個谷嘅成員 @@ -565,6 +571,7 @@ 儲存緊 %1$d 個附件去儲存空間… 仲要等等… + 數據 (Signal) 多媒體短訊 短訊 @@ -986,6 +993,7 @@ 係咪要解除連結「%1$s」? 解除咗連結嘅話,呢部機就無辦法再傳送同接收訊息㗎喇。 網絡連線唔掂 + 再試一次 解除緊部機嘅連結… 解除緊部機嘅連結 @@ -1368,7 +1376,7 @@ 谷拎 分享 重設條拎 - Require admin approval + 需要管理員核准 以谷拎加入嘅新成員送交話事人審批。 您係咪確定要重設條谷拎?人哋將唔再能夠用而家條拎加入呢個谷。 @@ -1464,6 +1472,7 @@ 係咪要傳送 %1$d 個邀請短訊? 不如我哋齊齊轉會 Signal: %1$s + 似乎您無 app 用嚟去分享噃。 @@ -1873,6 +1882,7 @@ 你係咪想俾 %1$s 傳送訊息俾你,同埋同佢分享你嘅名同相?喺解除封鎖之前,你都唔會收到對方嘅任何訊息。 你係咪想俾 %1$s 傳送訊息俾你?喺解除封鎖之前,你都唔會收到對方嘅任何訊息。 + 你係咪想接收 %1$s 嘅更新資訊同最新消息?喺解除封鎖之前,你都唔會收到對方嘅任何更新資訊。 係咪要繼續同呢個谷傾偈,並向個谷嘅所有成員分享您嘅名同相? 呢個舊版群組已經唔用得喇。如果想啟用新功能,例如 @提及同埋管理員功能,就開個新群組啦。 @@ -2171,6 +2181,7 @@ 仲未有人喺度 %1$s 嚟到呢次通話 + %1$s嚟到呢次通話 %1$s 同 %2$s 嚟到呢次通話 @@ -2562,7 +2573,7 @@ 我哋遲啲再試過啦。 Signal 已經成功更新 已經自動更新到 %1$s 版本。 - You updated to version %1$s. + 你已經更新至 %1$s 版本。 係咪要傳送訊息? @@ -2975,6 +2986,7 @@ 附件縮圖 切換快速相機附件選單 錄音並傳送音訊附件 + 音訊附件鎖定錄音 訊息傳送唔到。請檢查您嘅連線,然後再試下啦。 @@ -2993,6 +3005,7 @@ 個訊息睇咗 + 聯絡人相片 @@ -3077,6 +3090,7 @@ 似乎已經萬事俱備! 要接收通話通知嘅話,撳一下呢度並開啟「顯示通知」。 要接收通話通知嘅話,撳一下呢度並開啟通知,同時確保已經啟用聲音同彈出式視窗。 + 要接收通話通知嘅話,撳一下呢度並喺「電量」設定啟用背景活動。 設定 要接收通話通知嘅話,撳一下 [設定] 並開啟「顯示通知」。 @@ -3207,7 +3221,7 @@ 不必搜索物證 想一想 神話的要點 顛覆角度 哪懼無路 - One or more characters is invalid. + 一個或多個字元無效。 編輯個谷 @@ -3306,6 +3320,7 @@ + 支援資訊 Signal Android 支援請求 @@ -3393,6 +3408,7 @@ 您傳送嘅訊息如果有拎嘅話,跟住條拎去網站度攞返個預覽。 更改密碼 更改您個密碼 + 啟用密碼畫面鎖 用密碼將畫面同通知上鎖 畫面要遮掩 @@ -3405,6 +3421,7 @@ LED 眨燈規律 自訂 更改響聲同震動 + 響聲 靜音 預設 @@ -5251,6 +5268,7 @@ 加料落是日花生 寫返句訊息 + 寫返句回覆 傳送至 閱後即焚媒體 @@ -5850,6 +5868,8 @@ %1$d 個回覆 + + Story no longer hidden 加入去 @@ -7109,7 +7129,23 @@ - 請你搵多 %1$s 空間嚟下載媒體。 + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ 確定 - - - 付款紀錄 - - 文字同埋全部媒體備份 - - 付款詳情 - - 備份類型 - - 已付日期 - - 分享 + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ 備份頻率 用流動數據備份 + + View backup key + + Unlock to view backup key 閂咗佢同埋刪除備份 @@ -7212,13 +7248,27 @@ 備份將會通宵建立。 - 備份類型 + Backup plan 停用咗備份 - - %1$s · %2$s /月 - - 啟用備份 + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + 你嘅備份金鑰 - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + 你嘅備份金鑰係一個 64 位數字嘅代碼,方便你喺重新安裝 Signal 之後還原備份。 - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + 如果你唔記得咗個金鑰,就冇辦法還原備份喇。Signal 冇辦法幫你還原備份。 - Next + 下一步 - Record your backup key + 記低你嘅備份金鑰 - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + 系統需要用呢個金鑰嚟恢復你嘅帳戶同資料。請將金鑰儲存喺安全嘅地方。如果你整唔見咗佢,就冇辦法恢復帳戶㗎喇。 - Copy to clipboard + 複製到剪貼簿 - Next + 下一步 - Keep your key safe + 安全儲存你嘅金鑰 - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + 如果你整唔見咗個金鑰,Signal 就冇辦法幫你還原備份喇。請將佢儲存喺安全嘅地方,千祈唔好同人分享。 - I\'ve recorded my key + 記低咗我嘅金鑰喇 - Continue + 繼續 - See key again + 再睇多次金鑰 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index cdb5d6e8a6..51b5ba0cd0 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + 删除 @@ -258,6 +261,7 @@ 点击拍照,长按录像 + 拍照 切换相机 打开相册 @@ -343,7 +347,7 @@ 这不是一个有效的通话链接,加入前请确保链接完整正确。 - You are already in a call + 您已在通话中 @@ -400,6 +404,7 @@ 未找到可打开该媒体的应用。 已复制 %1$s 来自 %1$s + 发至 %1$s   查看更多   下载更多 @@ -433,6 +438,7 @@ 发送已编辑消息 撰写消息 抱歉,设置附件时出现错误。 + 收件人不是有效的短信或电子邮件地址! 消息为空! 群组成员 @@ -565,6 +571,7 @@ 正在保存 %1$d 附件到存储… 待处理… + 数据 (Signal) 彩信 短信 @@ -986,6 +993,7 @@ 取消关联“%1$s”? 取消关联之后,将不能再通过该设备发送或接收消息。 网络连接失败 + 重试 取消关联设备中… 正在取消关联设备 @@ -1368,7 +1376,7 @@ 群组链接 分享 重置链接 - Require admin approval + 需要管理员批准 通过群组链接的新成员需要经过管理员同意才能入群。 确定要重置群组链接吗?重置后他人将无法通过原来的链接加入群组。 @@ -1464,6 +1472,7 @@ 发送 %1$d 个短信邀请? 用 Signal 来聊天吧:%1$s + 看起来设备上没有可用于分享的应用。 @@ -1873,6 +1882,7 @@ 是否允许%1$s给您发送消息并向其显示您的名称和照片?在取消屏蔽之前,您不会收到对方的任何消息。 是否允许%1$s给您发送消息?在取消屏蔽之前,您不会收到对方的任何消息。 + 是否要接收%1$s的更新和新闻?在取消屏蔽之前,您不会收到对方的任何更新消息。 要参与此群组对话并向其成员分享您的昵称和头像吗? 此旧版群组已无法使用。请创建新群组以激活“@提及”和“管理员”等新功能。 @@ -2171,6 +2181,7 @@ 没有人在这里 %1$s 在此通话中 + %1$s正在此通话中 %1$s 和 %2$s 在此通话中 @@ -2562,7 +2573,7 @@ 我们稍后会再试一次。 Signal 已成功更新 您已自动更新到 %1$s 版本。 - You updated to version %1$s. + 您已更新到 %1$s 版本。 发送消息? @@ -2975,6 +2986,7 @@ 附件缩略图 切换快速相机附件抽屉 录音并分享附件 + 锁定音频附件录音状态 无法发送消息,请检查您的网络后再试。 @@ -2993,6 +3005,7 @@ 消息已读 + 联系人图片 @@ -3077,6 +3090,7 @@ 目前看起来一切正常! 为了接收通话通知,请点击此处并开启“显示通知”。 为了接收通话通知,请点击此处并开启通知,以及确保已启用“声音”和“弹出”。 + 为了接收通话通知,请点击此处并启用“电池”设置下的背景活动。 设置 为了接收通话通知,请点击设置并开启“显示通知”。 @@ -3207,7 +3221,7 @@ 休息一下下 打磨新玩意儿 - One or more characters is invalid. + 一个或多个字符无效。 编辑群组 @@ -3306,6 +3320,7 @@ + 支持信息 Signal Android支持请求 @@ -3393,6 +3408,7 @@ 直接从您发送的链接网站中生成预览。 修改密码 修改您的密码 + 启用屏幕锁定密码 使用密码锁定屏幕和通知 屏幕安全 @@ -3405,6 +3421,7 @@ LED 闪烁模式 自定义 更改音效和振动模式 + 声音 无提示 默认 @@ -5251,6 +5268,7 @@ 添加到动态 添加消息 + 添加回复 发送给 阅后即焚媒体 @@ -5850,6 +5868,8 @@ %1$d 个回复 + + Story no longer hidden 添加 @@ -7109,7 +7129,23 @@ - 至少释放 %1$s 的空间以下载媒体。 + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ - - - 付款历史 - - 文本和所有媒体备份 - - 付款详情 - - 备份类型 - - 付款日期 - - 分享 + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ 备份频率 使用蜂窝网络备份 + + View backup key + + Unlock to view backup key 关闭并删除备份 @@ -7212,13 +7248,27 @@ 备份将在夜间创建。 - 备份类型 + Backup plan 已停用备份 - - %1$s · %2$s/月 - - 启用备份 + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + 您的备份密钥 - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + 您的备份密钥是一个 64 位代码,可让您在重新安装 Signal 时恢复备份。 - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + 如果您忘记了自己的密钥,您将无法恢复备份。Signal 无法帮助您恢复备份。 - Next + 下一步 - Record your backup key + 记录您的备份密钥 - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + 这是恢复您的账户和数据时所需的密钥,请将其保存在安全的地方。如果密钥丢失,您将无法恢复您的账户。 - Copy to clipboard + 复制到剪切板 - Next + 下一步 - Keep your key safe + 安全保存您的密钥 - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + 如果密钥丢失,Signal 将无法帮助您恢复备份。请将其保存在安全的地方,并避免与他人共享该密钥。 - I\'ve recorded my key + 我已记录我的密钥 - Continue + 继续 - See key again + 再次查看密钥 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 97d23c83a7..bdcd6ad725 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + 刪除 @@ -258,6 +261,7 @@ 輕觸拍攝照片,長按錄製影片 + 擷取 切換鏡頭 開啟圖片庫 @@ -343,7 +347,7 @@ 這不是有效的通話連結。在嘗試加入之前,請確保整個連結是完整且正確的。 - You are already in a call + 你已經在通話中 @@ -400,6 +404,7 @@ 找不到能夠開啟此媒體的應用程式。 已複製 %1$s 自 %1$s + 至 %1$s   閱讀更多   下載更多 @@ -433,6 +438,7 @@ 發送已編輯訊息 撰寫訊息 抱歉,設定您的附件時發生錯誤。 + 收件人並非一個有效的短訊或電子郵件地址! 訊息空白! 群組成員 @@ -565,6 +571,7 @@ 正在儲存 %1$d 個附件到儲存空間… 待處理… + 數據 (Signal) 多媒體短訊 短訊 @@ -986,6 +993,7 @@ 要取消連結「%1$s」嗎? 一經取消連結,此裝置將再也無法傳送或接收訊息。 網絡連線失敗 + 再試一次 正在取消連結裝置… 正在取消連結裝置 @@ -1368,7 +1376,7 @@ 群組連結 分享 重設連結 - Require admin approval + 需要管理員核准 要求透過群組連結加入的新成員須經管理員核准。 您確定要重設群組連結嗎?別人將無法再以目前的連結加入群組。 @@ -1464,6 +1472,7 @@ 要傳送 %1$d 個短訊邀請嗎? 一齊轉用 Signal 咯:%1$s + 您似乎未有任何應用程式可作分享。 @@ -1873,6 +1882,7 @@ 你是否要允許 %1$s 與你通訊,並向對方分享你的名字和相片?在解除封鎖之前,你都不會收到對方的任何訊息。 你是否要允許 %1$s 與你通訊?在解除封鎖之前,你都不會收到對方的任何訊息。 + 你是否要獲取 %1$s 的更新資訊及最新消息?在解除封鎖之前,你都不會收到任何更新資訊。 要繼續與此群組的對話,並與其成員分享您的名稱和照片嗎? 此舊版群組無法再使用。建立新群組以啟用「@提及」和管理員等新功能。 @@ -2171,6 +2181,7 @@ 目前惟有您一人之境 %1$s 現已在此通話中 + %1$s現已在此通話中 %1$s 和 %2$s 現已在此通話中 @@ -2562,7 +2573,7 @@ 我們將稍後再試。 Signal 已成功更新 已自動更新到版本 %1$s。 - You updated to version %1$s. + 已更新到版本 %1$s。 要傳送訊息嗎? @@ -2975,6 +2986,7 @@ 附件縮圖 切換快速相機附件隱藏式選單 錄製並傳送音訊附件 + 鎖定錄製音訊附件 訊息無法傳送。請檢查您的連線,然後再試一次。 @@ -2993,6 +3005,7 @@ 訊息已讀 + 聯絡人照片 @@ -3077,6 +3090,7 @@ 看來一切就緒! 若要接收通話通知,輕觸此處並開啟「顯示通知」。 若要接收通話通知,輕觸此處並開啟通知,同時確定已啟用聲音和彈出式視窗。 + 若要接收通話通知,輕觸此處並在「電量」設定中啟用背景活動。 設定 若要接收通話通知,輕觸「設定」並開啟「顯示通知」。 @@ -3207,7 +3221,7 @@ 人間有味是清歡 新世紀 我請你要有準備 - One or more characters is invalid. + 部分字元無效。 編輯群組 @@ -3306,6 +3320,7 @@ + 支援資訊 Signal Android 支援請求 @@ -3393,6 +3408,7 @@ 為您傳送的訊息直接從網站提取連結預覽。 更改密碼 變更您的密碼 + 啟用密碼畫面鎖定 使用密碼鎖定畫面與通知 畫面安全性 @@ -3405,6 +3421,7 @@ LED 閃爍規律 自訂 變更聲效與震動 + 聲效 靜音 預設值 @@ -5251,6 +5268,7 @@ 新增至限時動態 新增訊息 + 新增回覆 傳送給 閱後即焚媒體檔案 @@ -5850,6 +5868,8 @@ %1$d 則回覆 + + Story no longer hidden 新增 @@ -7109,7 +7129,23 @@ - 釋放 %1$s 的空間來下載你的多媒體。 + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ 確定 - - - 付款紀錄 - - 文字和所有多媒體備份 - - 付款詳情 - - 備份類型 - - 付款日期 - - 分享 + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ 備份頻率 使用行動數據備份 + + View backup key + + Unlock to view backup key 關閉並刪除備份 @@ -7212,13 +7248,27 @@ 備份將會通宵建立。 - 備份類型 + Backup plan 備份已停用 - - %1$s · %2$s /月 - - 啟用備份 + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + 你的備份金鑰 - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + 你的備份金鑰是一組 64 位數的代碼,可讓你在重新安裝 Signal 時還原備份。 - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + 如果你忘記了金鑰,你將會無法還原備份。Signal 無法幫助你還原備份。 - Next + 下一步 - Record your backup key + 記錄你的備份金鑰 - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + 需要此金鑰才能恢復你的帳戶和資料。將此金鑰存放在安全的地方。如果你將其遺失,你將會無法恢復帳戶。 - Copy to clipboard + 複製至剪貼簿 - Next + 下一步 - Keep your key safe + 妥善保管你的金鑰 - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + 如果你遺失金鑰,Signal 將無法幫助你還原備份。將其存放在安全可靠的地方,不要與他人分享。 - I\'ve recorded my key + 我已記錄我的金鑰 - Continue + 繼續 - See key again + 再看一次金鑰 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 7893a28d67..2320076116 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -22,6 +22,9 @@ + + https://play.google.com/store/account/subscriptions?sku=%1$s&package=%2$s + 刪除 @@ -258,6 +261,7 @@ 輕觸一下拍照,長按錄製影片 + 擷取 切換相機 開啟相簿 @@ -343,7 +347,7 @@ 這不是有效的通話連結。在嘗試加入之前,請確保整個連結是完整且正確的。 - You are already in a call + 你已經在通話中 @@ -400,6 +404,7 @@ 找不到合適的應用程式來開啟媒體檔案。 已複製 %1$s 來自 %1$s + 至 %1$s 讀取更多 下載更多 @@ -433,6 +438,7 @@ 發送已編輯訊息 撰寫訊息 抱歉,無法成功設定你指定的附檔。 + 收件人並不是一個有效的簡訊或電子郵件地址! 訊息內容是空的! 群組成員 @@ -565,6 +571,7 @@ 儲存%1$d附檔到儲存空間中… 暫待等候中… + 數據 (Signal) 多媒體訊息 簡訊 @@ -986,6 +993,7 @@ 取消連結 \'%1$s\'? 如果取消連結此裝置,它將不能再接受或傳送訊息。 網路連線失敗 + 再試一次 取消連結裝置中… 取消連結裝置 @@ -1368,7 +1376,7 @@ 群組連結 分享 重設連結 - Require admin approval + 需要管理員核准 需要管理員批准通過群組連結加入新成員。 你確定要重置群組連結嗎? 人們將無法再使用當前連結加入該群組。 @@ -1464,6 +1472,7 @@ 傳送 %1$d 則簡訊邀請? 讓我們切換到 Signal:%1$s + 看起來您沒有任何可以分享的應用。 @@ -1873,6 +1882,7 @@ 讓%1$s傳訊息給你並與他們分享你的名字和照片嗎? 除非你解鎖他們,否則你將不會收到任何訊息。 要讓 %1$s 傳訊息給你嗎?在解封鎖他們之前,你不會收到任何訊息。 + 要從 %1$s取得更新和新聞的通知嗎? 在你取消封鎖它們之前,你不會收到任何更新通知。 繼續與該群群組聊天,並與其成員分享你的名字和照片嗎? 此舊版群組無法再使用。建立新群組以啟用「@提及」和管理員等新功能。 @@ -2171,6 +2181,7 @@ 沒有人在這裡 %1$s正在此通話中 + %1$s 正在通話中 %1$s 及 %2$s 正在此通話中 @@ -2562,7 +2573,7 @@ 我們將稍後再試。 Signal 已成功更新 已自動更新到版本 %1$s。 - You updated to version %1$s. + 已更新到版本 %1$s。 傳送訊息? @@ -2975,6 +2986,7 @@ 附檔縮圖 切換快速相機附件隱藏式選單 錄製並傳送語音附檔 + 鎖定語音錄製附檔 無法傳送訊息。 檢查你的連線,然後重試。 @@ -2993,6 +3005,7 @@ 訊息已讀 + 聯絡人的相片 @@ -3077,6 +3090,7 @@ 似乎一切就緒! 若要接收通話通知,請輕觸此處並開啟「顯示通知」。 若要接收通話通知,請輕觸此處並開啟通知,並確保已啟用「聲音與彈出式視窗」。 + 若要接收通話通知,請輕觸此處並在「電量」設定中啟用背景活動。 設定 若要接收通話通知,請輕觸一下「設定」並啟用「顯示通知」。 @@ -3207,7 +3221,7 @@ 正在休息中 從事新事物 - One or more characters is invalid. + 部分字元無效。 編輯群組 @@ -3306,6 +3320,7 @@ + 支援訊息 Signal Android 支援請求 @@ -3393,6 +3408,7 @@ 為你傳送的訊息中所包含的連結直接從網站檢索預覽。 變更自訂密碼 變更您的自訂密碼 + 啟用螢幕鎖定的自訂密碼 使用鎖定螢幕及通知密碼 螢幕安全設定 @@ -3405,6 +3421,7 @@ LED 閃爍模式 客製化 變更音效及震動 + 提示音效 靜音 預設 @@ -5251,6 +5268,7 @@ 新增至限時動態 新增訊息 + 新增回覆 傳送給 一次性多媒體內容 @@ -5850,6 +5868,8 @@ %1$d 則回覆 + + Story no longer hidden 新增 @@ -7109,7 +7129,23 @@ - 釋放 %1$s 的空間來下載你的多媒體。 + Free up %1$s of space to restore your media. + + Restoring media + + Restore paused + + Restore complete + + + Waiting for Wi-Fi… + + No internet… + + Device has low battery + + %1$s of %2$s + @@ -7172,19 +7208,15 @@ 確定 - - - 付款紀錄 - - 文字和所有多媒體備份 - - 付款詳情 - - 備份類型 - - 付款日期 - - 分享 + + + %1$s/month, renews %2$s + + Last backup %1$s + + Automatic backups with Signal\'s secure end-to-end encrypted storage service. + + Set up @@ -7199,6 +7231,10 @@ 備份頻率 使用行動數據備份 + + View backup key + + Unlock to view backup key 關閉並刪除備份 @@ -7212,13 +7248,27 @@ 備份將會通宵建立。 - 備份類型 + Backup plan 備份已停用 - - %1$s · %2$s /月 - - 啟用備份 + + %1$s/month + + Your backup plan is free + + Renews %1$s + + Back up your message history so you never lose data when you get a new phone or reinstall Signal. + + Other ways to backup + + On-device backups + + Save your backups to a folder on this device + + Manage or cancel + + Upgrade %1$d/%2$d @@ -7268,33 +7318,33 @@ - Your backup key + 你的備份金鑰 - Your backup key is a 64-digit code that lets you restore your backup when you re-install Signal. + 你的備份金鑰是一組 64 位數的代碼,可讓你在重新安裝 Signal 時還原備份。 - If you forget your key, you will not be able to restore your backup. Signal cannot help you recover your backup. + 如果你忘記了金鑰,你將會無法還原備份。Signal 無法幫助你還原備份。 - Next + 下一步 - Record your backup key + 記錄你的備份金鑰 - This key is required to recover your account and data. Store this key somewhere safe. If you lose it, you won’t be able to recover your account. + 需要此金鑰才能恢復你的帳戶和資料。將此金鑰存放在安全的地方。如果你將其遺失,你將會無法恢復帳戶。 - Copy to clipboard + 複製到剪貼簿 - Next + 下一步 - Keep your key safe + 妥善保管你的金鑰 - Signal will not be able to help you restore your backup if you lose your key. Store it somewhere safe and secure, and do not share it with others. + 如果你遺失金鑰,Signal 將無法幫助你還原備份。將其存放在安全可靠的地方,不要與他人分享。 - I\'ve recorded my key + 我已記錄我的金鑰 - Continue + 繼續 - See key again + 再看一次金鑰 diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index 94023ea13b..a199b21b23 100644 --- a/app/static-ips.gradle.kts +++ b/app/static-ips.gradle.kts @@ -1,9 +1,9 @@ rootProject.extra["service_ips"] = """new String[]{"13.248.212.111","76.223.92.165"}""" -rootProject.extra["storage_ips"] = """new String[]{"142.251.40.147"}""" -rootProject.extra["cdn_ips"] = """new String[]{"18.161.21.122","18.161.21.4","18.161.21.66","18.161.21.70"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.251.40.211"}""" +rootProject.extra["cdn_ips"] = """new String[]{"52.85.247.123","52.85.247.56","52.85.247.61","52.85.247.8"}""" rootProject.extra["cdn2_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" rootProject.extra["cdn3_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" -rootProject.extra["sfu_ips"] = """new String[]{"34.160.196.25"}""" +rootProject.extra["sfu_ips"] = """new String[]{"35.244.250.191"}""" rootProject.extra["content_proxy_ips"] = """new String[]{"107.178.250.75"}""" rootProject.extra["svr2_ips"] = """new String[]{"20.119.62.85"}""" rootProject.extra["cdsi_ips"] = """new String[]{"40.122.45.194"}""" From b0a1e69109ebb56ae6334f05996bea9952ae3651 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 3 Oct 2024 10:45:29 -0400 Subject: [PATCH 46/53] Bump version to 7.19.0 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bd1ef30412..c7957e8e5a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1465 -val canonicalVersionName = "7.18.2" +val canonicalVersionCode = 1466 +val canonicalVersionName = "7.19.0" val currentHotfixVersion = 0 val maxHotfixVersions = 100 From 957f17836a25ea131f79db351dbf4970ec9367c3 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 4 Oct 2024 12:48:46 -0400 Subject: [PATCH 47/53] Fix potentially slow migration query. --- .../securesms/database/AttachmentTable.kt | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index da99a9e687..dc74ae4631 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -284,6 +284,8 @@ class AttachmentTable( ) """ + private const val DATA_FILE_INDEX = "attachment_data_index" + @JvmField val CREATE_INDEXS = arrayOf( "CREATE INDEX IF NOT EXISTS attachment_message_id_index ON $TABLE_NAME ($MESSAGE_ID);", @@ -291,7 +293,7 @@ class AttachmentTable( "CREATE INDEX IF NOT EXISTS attachment_sticker_pack_id_index ON $TABLE_NAME ($STICKER_PACK_ID);", "CREATE INDEX IF NOT EXISTS attachment_data_hash_start_index ON $TABLE_NAME ($DATA_HASH_START);", "CREATE INDEX IF NOT EXISTS attachment_data_hash_end_index ON $TABLE_NAME ($DATA_HASH_END);", - "CREATE INDEX IF NOT EXISTS attachment_data_index ON $TABLE_NAME ($DATA_FILE);", + "CREATE INDEX IF NOT EXISTS $DATA_FILE_INDEX ON $TABLE_NAME ($DATA_FILE);", "CREATE INDEX IF NOT EXISTS attachment_archive_media_id_index ON $TABLE_NAME ($ARCHIVE_MEDIA_ID);", "CREATE INDEX IF NOT EXISTS attachment_archive_transfer_state ON $TABLE_NAME ($ARCHIVE_TRANSFER_STATE);" ) @@ -1420,24 +1422,24 @@ class AttachmentTable( * This is basically all attachments that have data and are finished downloading. */ fun getDataFilesWithMultipleValidAttachments(): List { - val targetDataFile = "target_data_file" return readableDatabase - .select("DISTINCT($DATA_FILE) AS $targetDataFile") - .from(TABLE_NAME) + .select("DISTINCT(a1.$DATA_FILE)") + .from("$TABLE_NAME a1 INDEXED BY $DATA_FILE_INDEX") .where( """ - $targetDataFile NOT NULL AND - $TRANSFER_STATE = $TRANSFER_PROGRESS_DONE AND ( - SELECT COUNT(*) - FROM $TABLE_NAME + a1.$DATA_FILE NOT NULL AND + a1.$TRANSFER_STATE = $TRANSFER_PROGRESS_DONE AND EXISTS ( + SELECT 1 + FROM $TABLE_NAME a2 INDEXED BY $DATA_FILE_INDEX WHERE - $DATA_FILE = $targetDataFile AND - $TRANSFER_STATE = $TRANSFER_PROGRESS_DONE - ) > 1 + a1.$DATA_FILE = a2.$DATA_FILE AND + a2.$TRANSFER_STATE = $TRANSFER_PROGRESS_DONE AND + a2.$ID != a1.$ID + ) """ ) .run() - .readToList { it.requireNonNullString(targetDataFile) } + .readToList { it.requireNonNullString(DATA_FILE) } } /** From f64ade1ce90479ccb90c446d2b3e8457973cd6eb Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 4 Oct 2024 13:08:45 -0400 Subject: [PATCH 48/53] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 48 +++++------ app/src/main/res/values-ar/strings.xml | 98 +++++++++++----------- app/src/main/res/values-az/strings.xml | 16 ++-- app/src/main/res/values-bg/strings.xml | 16 ++-- app/src/main/res/values-bn/strings.xml | 18 ++-- app/src/main/res/values-bs/strings.xml | 14 ++-- app/src/main/res/values-ca/strings.xml | 18 ++-- app/src/main/res/values-cs/strings.xml | 18 ++-- app/src/main/res/values-da/strings.xml | 18 ++-- app/src/main/res/values-de/strings.xml | 18 ++-- app/src/main/res/values-el/strings.xml | 18 ++-- app/src/main/res/values-es/strings.xml | 90 ++++++++++---------- app/src/main/res/values-et/strings.xml | 16 ++-- app/src/main/res/values-eu/strings.xml | 50 +++++------ app/src/main/res/values-fa/strings.xml | 18 ++-- app/src/main/res/values-fi/strings.xml | 18 ++-- app/src/main/res/values-fr/strings.xml | 20 ++--- app/src/main/res/values-ga/strings.xml | 18 ++-- app/src/main/res/values-gl/strings.xml | 16 ++-- app/src/main/res/values-gu/strings.xml | 18 ++-- app/src/main/res/values-hi/strings.xml | 18 ++-- app/src/main/res/values-hr/strings.xml | 18 ++-- app/src/main/res/values-hu/strings.xml | 18 ++-- app/src/main/res/values-in/strings.xml | 18 ++-- app/src/main/res/values-it/strings.xml | 18 ++-- app/src/main/res/values-iw/strings.xml | 18 ++-- app/src/main/res/values-ja/strings.xml | 18 ++-- app/src/main/res/values-ka/strings.xml | 14 ++-- app/src/main/res/values-kk/strings.xml | 16 ++-- app/src/main/res/values-km/strings.xml | 16 ++-- app/src/main/res/values-kn/strings.xml | 16 ++-- app/src/main/res/values-ko/strings.xml | 18 ++-- app/src/main/res/values-ky/strings.xml | 16 ++-- app/src/main/res/values-lt/strings.xml | 16 ++-- app/src/main/res/values-lv/strings.xml | 16 ++-- app/src/main/res/values-mk/strings.xml | 16 ++-- app/src/main/res/values-ml/strings.xml | 16 ++-- app/src/main/res/values-mr/strings.xml | 18 ++-- app/src/main/res/values-ms/strings.xml | 18 ++-- app/src/main/res/values-my/strings.xml | 14 ++-- app/src/main/res/values-nb/strings.xml | 18 ++-- app/src/main/res/values-nl/strings.xml | 22 ++--- app/src/main/res/values-pa/strings.xml | 16 ++-- app/src/main/res/values-pl/strings.xml | 18 ++-- app/src/main/res/values-pt-rBR/strings.xml | 18 ++-- app/src/main/res/values-pt/strings.xml | 16 ++-- app/src/main/res/values-ro/strings.xml | 18 ++-- app/src/main/res/values-ru/strings.xml | 18 ++-- app/src/main/res/values-sk/strings.xml | 18 ++-- app/src/main/res/values-sl/strings.xml | 16 ++-- app/src/main/res/values-sq/strings.xml | 16 ++-- app/src/main/res/values-sr/strings.xml | 50 +++++------ app/src/main/res/values-sv/strings.xml | 18 ++-- app/src/main/res/values-sw/strings.xml | 16 ++-- app/src/main/res/values-ta/strings.xml | 16 ++-- app/src/main/res/values-te/strings.xml | 16 ++-- app/src/main/res/values-th/strings.xml | 18 ++-- app/src/main/res/values-tl/strings.xml | 12 +-- app/src/main/res/values-tr/strings.xml | 18 ++-- app/src/main/res/values-ug/strings.xml | 18 ++-- app/src/main/res/values-uk/strings.xml | 18 ++-- app/src/main/res/values-ur/strings.xml | 18 ++-- app/src/main/res/values-vi/strings.xml | 18 ++-- app/src/main/res/values-yue/strings.xml | 18 ++-- app/src/main/res/values-zh-rCN/strings.xml | 18 ++-- app/src/main/res/values-zh-rHK/strings.xml | 18 ++-- app/src/main/res/values-zh-rTW/strings.xml | 18 ++-- app/static-ips.gradle.kts | 4 +- 68 files changed, 703 insertions(+), 703 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 7e06804ec1..e304f2d16c 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -6001,7 +6001,7 @@ %1$d antwoorde - Story no longer hidden + Storie nie meer versteek nie Voeg toe @@ -7287,20 +7287,20 @@ - Free up %1$s of space to restore your media. + Stel %1$s stoorruimte beskikbaar om jou media af te laai. - Restoring media + Besig om media te herstel - Restore paused + Herstel is onderbreek - Restore complete + Herstel vanaf rugsteun voltooi - Waiting for Wi-Fi… + Wag tans vir WiFi… - No internet… + Geen internet… - Device has low battery + Toestel se battery is laag %1$s of %2$s @@ -7368,13 +7368,13 @@ - %1$s/month, renews %2$s + %1$s/maand, hernu %2$s - Last backup %1$s + Laaste rugsteun %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Outomatiese rugsteun met Signal se veilige end-tot-end-geënkripteerde stoorruimtediens. - Set up + Stel op @@ -7390,9 +7390,9 @@ Rugsteun met behulp van mobiele data - View backup key + Sien rugsteunsleutel - Unlock to view backup key + Ontsluit om rugsteunsleutel te sien Skakel rugsteun af en skrap dit @@ -7406,27 +7406,27 @@ Rugsteun sal oornag geskep word. - Backup plan + Rugsteunplan Rugsteun gedeaktiveer - %1$s/month + %1$s/maand - Your backup plan is free + Jou rugsteunplan is gratis - Renews %1$s + Hernu %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Rugsteun jou boodskapgeskiedenis sodat jy nooit data verloor wanneer jy \'n nuwe foon kry of Signal herinstalleer nie. - Other ways to backup + Ander maniere om te rugsteun - On-device backups + Rugsteun op toestel - Save your backups to a folder on this device + Stoor jou rugsteun in \'n vouer op hierdie toestel - Manage or cancel + Bestuur of kanselleer - Upgrade + Gradeer op %1$d/%2$d diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index f6818ca3fc..fa8dfe5423 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -2116,9 +2116,9 @@ مكالمة فيديو صادرة · %1$s - بدأت مكالمة فيديو + بدأتَ مكالمة فيديو. - بدأت مكالمة فيديو · %1$s + بدأتَ مكالمة فيديو · %1$s %1$s بدأ مكالمة فيديو @@ -2130,7 +2130,7 @@ الأعضاء %1$s و %2$s و%3$d آخر موجودون في المكالمة الجماعية · %4$s الأعضاء %1$s و %2$s و%3$d آخر موجودون في المكالمة الجماعية · %4$s - الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية · %4$s + الأعضاء %1$s و %2$s و%3$d اثنان آخران موجودان في المكالمة الجماعية · %4$s الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية · %4$s الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية · %4$s الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية · %4$s @@ -2140,7 +2140,7 @@ الأعضاء %1$s و %2$s و%3$d آخر موجودون في المكالمة الجماعية الأعضاء %1$s و %2$s و%3$d آخر موجود في المكالمة الجماعية - الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية + الأعضاء %1$s و %2$s و%3$d آخران موجودان في المكالمة الجماعية الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية الأعضاء %1$s و %2$s و%3$d آخرون موجودون في المكالمة الجماعية @@ -2151,13 +2151,13 @@ لن تتمكن بعد الآن من إرسال رسائل SMS من سيجنال. قُم بدعوة %1$s إلى سيجنال لإبقاء المحادثة هنا. - عمليات الدفع: %1$s + المدفوعات: %1$s عمليات الدفع - تم الإبلاغ عنها كرِسالة غير مرغوب فيها + تمَّ الإبلاغ عنها كرِسالة غير مرغوب فيها - لقد قبِلت طلب المراسلة + قبِلتَ طلب المراسلة موافقة @@ -2231,7 +2231,7 @@ هل ترغبُ بربط هذا الجهاز؟ - مواصلة + متابعة سوف يتمكن من @@ -2248,8 +2248,8 @@ هل ترغبُ بربط جهاز يستخدم سيجنال؟ يبدو أنك تحاول ربط جهاز يستخدم سيجنال باستخدام ماسح ضوئي من طرف ثالث. للحماية، امسح الكود مرّة أخرى باستخدام سيجنال. - يحتاج سيجنال إلى إذن الوصول إلى الكاميرا من أجل مسح كود الـ QR ضوئيًا، ولكن رُفِض الإذن على نحو دائم. يُرجى زيارة \"الإعدادات\" في جهازك، وتحديد \"التطبيقات\"، ثم اختيار \"الأذونات\"، ثم تفعيل \"الكاميرا\". - لا يمكن مسح كود الـ QR بدون منح إذن الوصول إلى الكاميرا + يحتاج سيجنال إلى إذن الوصول إلى الكاميرا من أجل مسح كود الـ QR ضوئيًا، ولكن رُفِض الإذن بشكلٍ دائم. يُرجى زيارة \"الإعدادات\" في جهازك، وتحديد \"التطبيقات\"، ثم اختيار \"الأذونات\"، ثم تفعيل \"الكاميرا\". + لا يمكن مسح كود الـ QR بدون منح إذن الوصول إلى الكاميرا. حدِّث الآن @@ -2264,45 +2264,45 @@ - أدخل العبارة السرية + أدخِل العبارة السرية أيقونة سيجنال - أدخل العبارة السرية + أدخِل العبارة السرية عبارة سرية غير صحيحة! - فك قفل سيجنال - سيجنال Android - قفل الشاشة + فتح قفل سيجنال + سيجنال أندرويد - قفل الشاشة الخريطة - إدخال إشارة - قبول العنوان + ضع علامة + اقبل العنوان - نسخة خدمات Google Play التي قمت بتثبيتها لا تعمل بشكل صحيح. الرجاء إعادة تثبيت خدمات Google Play والمحاولة مرة أخرى. + نسخة خدمات Google Play التي قمت بتثبيتها لا تعمل بشكل صحيح. يُرجى إعادة تثبيت خدمات Google Play والمحاولة مرّة أُخرى. - الرقم التعريفي الشخصي غير صحيح - تخطي إدخال الرمز الشخصي ؟ - بحاجة إلى مساعدة ؟ - رمزك التعريفي الشخصي هو سلسلة مكونة من %1$d حروف قد تكون أرقاما فقط أو حروفا أبجَدرَقمية.\n\nإذا نسيت رمزك التعريفي الشخصي، تستطيع إنشاء رمز جديد، ثم التسجيل واستخدام حسابك لكنك ستفقد الإعدادات المحفوظة مثل معلومات حسابك الشخصي. - إذا كنت لا تتذكر رقمك التعريفي الشخصي، يمكنك إنشاء واحد جديد. يمكنك التسجيل واستخدام حسابك لكن سوف تفقد بعض الإعدادات المحفوظة، مثل معلومات حسابك. - إنشاء رقم تعريفي شخصي جديد + رقم التعريف الشخصي (PIN) غير صحيح + أترغبُ بتخطي إدخال رقم التعريف الشخصي (PIN)؟ + هل تحتاج إلى مساعدة؟ + رقم التعريف الشخصي الخاص بك هو سلسلة مكونة من %1$d حروف قد تكون أرقامًا فقط أو مزيج من الحروف والأرقام.\n\nإذا كنتَ لا تتذكر رمز التعريف الشخصي الخاص بك، يمكنك إنشاء رمز جديد. يمكنك بعدها التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + إذا كنتَ لا تتذكر رقم التعريف الشخصي الخاص بك، يمكنك إنشاء واحد جديد. يمكنك بعدها التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + إنشاء رقم تعريفي شخصي (PIN) جديد الاتصال بالدعم إلغاء تخطّي - بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريفي شخصي جديد. والتسجيل واستخدام حسابك لكنك ستفقد الإعدادات المحفوظة مثل معلومات حسابك الشخصي. - بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريفي شخصي جديد. والتسجيل واستخدام حسابك لكنك ستفقد الإعدادات المحفوظة مثل معلومات حسابك الشخصي. - بقيت لديك%1$d محاولات. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريفي شخصي جديد. والتسجيل واستخدام حسابك لكنك ستفقد الإعدادات المحفوظة مثل معلومات حسابك الشخصي. - بقيت لديك %1$d محاولات. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريفي شخصي جديد. والتسجيل واستخدام حسابك لكنك ستفقد الإعدادات المحفوظة مثل معلومات حسابك الشخصي. - بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريفي شخصي جديد. والتسجيل واستخدام حسابك لكنك ستفقد الإعدادات المحفوظة مثل معلومات حسابك الشخصي. - بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريفي شخصي جديد. والتسجيل واستخدام حسابك لكنك ستفقد الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريف شخصي جديد. يمكنك أيضًا التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريف شخصي جديد. يمكنك أيضًا التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + بقيت لديك %1$d محاولتان. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريف شخصي جديد. يمكنك أيضًا التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + بقيت لديك %1$d محاولاتٍ. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريف شخصي جديد. يمكنك أيضًا التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريف شخصي جديد. يمكنك أيضًا التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. + بقيت لديك %1$d محاولة. إذا انتهت محاولاتك، تستطيع إنشاء رقم تعريف شخصي جديد. يمكنك أيضًا التسجيل واستخدام حسابك لكنك ستفقد بعض الإعدادات المحفوظة مثل معلومات حسابك الشخصي. - التسجيل في سيجنال - تحتاج مساعدة بالرقم التعريفي الشخصي في أندرويد + التسجيل في سيجنال - تحتاج مساعدة لرقم التعريف الشخصي في أندرويد - إنشاء رقمك التعريفي الشخصي - لقد نفذت تخميناتك للرقم التعريفي الشخصي، لكن مازال بوسعك الوصول إلى حسابك على سيجنال من خلال إنشاء رقم تعريفي شخصي جديد. للحفاظ على خصوصيتك وأمن معلوماتك، سيتم استرجاع حسابك لكن بدون أية معلومات شخصية عن الحساب أو الإعدادات. + إنشاء رقم التعريف الشخصي + نفدَت تخميناتك لرقم التعريف الشخصي، لكن مازال بوسعك الوصول إلى حسابك على سيجنال من خلال إنشاء رقم تعريف شخصي جديد. للحفاظ على خصوصيتك وأمن معلوماتك، سيتم استرجاع حسابك لكن بدون أية معلومات أو إعدادات محفوظة عن الحساب. إنشاء رقم تعريف شخصي جديد @@ -2319,15 +2319,15 @@ تحذير - إذا قمت بتعطيل الرمز التعريفي الشخصي، ستضيع جميع بياناتك عند قيامك بإعادة التسجيل إلى سيجنال إلا إذا قمت باسترجاعها بواسطة النسخ الاحتياطي اليدوي. لا يمكنك تفعيل قفل التسجيل أثناء تعطيل الرمز التعريفي الشخصي. - إلغاء الرقم التعريفي الشخصي + إذا قمتَ بتعطيل رمز التعريف الشخصي، ستفقد جميع بياناتك عند قيامك بإعادة التسجيل في سيجنال، إلّا إذا قمتَ باسترجاعها بواسطة النسخ الاحتياطي اليدوي. لا يمكنك تفعيل قفل التسجيل أثناء تعطيل رمز التعريف الشخصي. + إلغاء رقم التعريف الشخصي تقييم التطبيق - إذا كنت مستمتع باستخدام التطبيق رجاء مساعدتنا بتقييمه - قَيّم الآن - لا، شكرا - لاحقا + إذا كنتَ تستمتع باستخدام التطبيق، يُرجى مساعدتنا من خلال تقييمه. + قَيِّم الآن + لا، شكرًا + لاحقًا الكل · %1$d @@ -2339,8 +2339,8 @@ أنت - التأكد لمواصلة التراسُل - للتمكّن من الوقاية من الإزعاج في سيجنال، يُرجى إتمام التحقق. + قُم بالتحقُّق لمواصلة المراسلة + لمنع وصول الرسائل غير المرغوب فيها في سيجنال، يُرجى إتمام التحقُّق. بعد التحقق، يمكنك مواصلة التراسل. ستُرسَل تلقائيًا لك كل رسالة متوقفة مؤقتًا. @@ -7919,13 +7919,13 @@ - Free up %1$s of space to restore your media. + حرِّر %1$s من المساحة لاسترجاع وسائطك. - Restoring media + جارٍ استرجاع الوسائط - Restore paused + تمَّ إيقاف الاسترجاع - Restore complete + اكتملَت عملية الاستعادة. Waiting for Wi-Fi… @@ -7934,7 +7934,7 @@ Device has low battery - %1$s of %2$s + %1$s من %2$s @@ -8006,7 +8006,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + إعداد @@ -8042,11 +8042,11 @@ تعطيل النُسخ الاحتياطية - %1$s/month + %1$s/شهر Your backup plan is free - Renews %1$s + تجديد %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -8058,7 +8058,7 @@ Manage or cancel - Upgrade + الترقية %1$d/%2$d diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 4b92c60a01..a276548527 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Media faylını bərpa etmək üçün %1$s həcmində yer boşaldın. - Restoring media + Media faylı bərpa olunur - Restore paused + Bərpaetmə dayandırıldı - Restore complete + Geri yükləmə tamamlandı Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s /%2$s @@ -7410,11 +7410,11 @@ Ehtiyat nüsxələr qeyri-aktivdir - %1$s/month + %1$s/ay Your backup plan is free - Renews %1$s + %1$s yenilənir Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Yüksəlt %1$d/%2$d diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index ab7c62d3f5..ce4e66ddb4 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Освободете %1$s място, за да възстановите мултимедията. - Restoring media + Възстановяване на мултимедия - Restore paused + Възстановяването е на пауза - Restore complete + Възстановяването е завършено Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s от %2$s @@ -7410,11 +7410,11 @@ Резервните копия са деактивирани - %1$s/month + %1$s/месец Your backup plan is free - Renews %1$s + Подновява се на %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Обновяване %1$d/%2$d diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 5bd48d501c..572c7d9a84 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + আপনার মিডিয়া পুনরুদ্ধার করতে %1$s জায়গা খালি করুন। - Restoring media + মিডিয়া পুনরুদ্ধার করা হচ্ছে - Restore paused + পুনর্বহালে সাময়িক বিরতি দেওয়া হয়েছে - Restore complete + পুনরুদ্ধার সম্পূর্ণ Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s এর %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + সেট আপ করুন @@ -7410,11 +7410,11 @@ ব্যাকআপ নিষ্ক্রিয় করা হয়েছে - %1$s/month + %1$s/মাস Your backup plan is free - Renews %1$s + %1$s নবায়ন করছে Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + আপগ্রেড করুন %1$d/%2$d diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index d0185584ed..5fb60c83e7 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Oslobodite %1$s prostora za obnovu medijskih sadržaja. - Restoring media + Obnova medijskih sadržaja - Restore paused + Vraćanje je pauzirano - Restore complete + Vraćanje podataka je završeno Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s od %2$s @@ -7726,11 +7726,11 @@ Rezervne kopije su onemogućene. - %1$s/month + %1$s/mjesečno Your backup plan is free - Renews %1$s + Obnavlja se %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 15233522b0..3027c89e19 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Allibera %1$s d\'espai per restaurar els teus arxius multimèdia. - Restoring media + Restaurant els arxius - Restore paused + Restauració interrompuda - Restore complete + Restauració completa Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s de %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Configurar @@ -7410,11 +7410,11 @@ Còpies de seguretat desactivades - %1$s/month + %1$s/ mes Your backup plan is free - Renews %1$s + Renovacions %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Actualitza %1$d/%2$d diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 9ee855228c..df02dce1a6 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Uvolněte %1$s místa pro obnovení médií. - Restoring media + Obnovování médií - Restore paused + Obnovení pozastaveno - Restore complete + Obnova dokončena Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s z %2$s @@ -7690,7 +7690,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Nastavit @@ -7726,11 +7726,11 @@ Zálohování zakázáno - %1$s/month + %1$s / měsíčně Your backup plan is free - Renews %1$s + Obnovení %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Aktualizovat %1$d/%2$d diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 3d5abac0bc..2d7f22f971 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Frigør %1$s lagerplads for at gendanne dine medier. - Restoring media + Gendanner medier - Restore paused + Gendannelse sat på pause - Restore complete + Gendannelse afsluttet Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s af %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Opsæt @@ -7410,11 +7410,11 @@ Sikkerhedskopiering deaktiveret - %1$s/month + %1$s/måned Your backup plan is free - Renews %1$s + Fornyes %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Opgradér %1$d/%2$d diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a25038b90a..5140a03861 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Du musst %1$s freigeben, um deine Medien wiederherzustellen. - Restoring media + Medien werden wiederhergestellt - Restore paused + Wiederherstellung angehalten - Restore complete + Wiederherstellung abgeschlossen Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s von %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Einrichten @@ -7410,11 +7410,11 @@ Datensicherung deaktiviert - %1$s/month + %1$s/Monat Your backup plan is free - Renews %1$s + Verlängert sich am %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Aktualisieren %1$d/%2$d diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 98285f1885..1f90b298a2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Απελευθέρωσε %1$s χώρου για επαναφορά των πολυμέσων σου. - Restoring media + Επαναφορά πολυμέσων - Restore paused + Επαναφορά σε παύση - Restore complete + Η ανάκτηση ολοκληρώθηκε Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s από %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Ρύθμιση @@ -7410,11 +7410,11 @@ Τα αντίγραφα ασφαλείας απενεργοποιήθηκαν - %1$s/month + %1$s/μήνα Your backup plan is free - Renews %1$s + Ανανεώνεται %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Αναβάθμιση %1$d/%2$d diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index fbb9950b6f..8c23c04990 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -48,8 +48,8 @@ Signal se está actualizando… - Todavía no has creado una clave de acceso. - ¿Desactivar clave de acceso? + Todavía no has creado una frase de contraseña. + ¿Desactivar frase de contraseña? Se desbloqueará Signal y las notificaciones de mensajes de forma permanente. Desactivar Error de conexión con el servidor @@ -861,14 +861,14 @@ Copias de seguridad de los chats - Las copias de seguridad se cifran con una clave de acceso y se guardan en tu dispositivo. + Las copias de seguridad se cifran con una frase de contraseña y se guardan en tu dispositivo. Crear copia de seguridad Última copia: %1$s Carpeta de copias de seguridad Hora de la copia de seguridad - Verificar clave de copia de seguridad - Prueba tu clave de acceso a la copia de seguridad y comprueba que coincida + Verificar contraseña de copia de seguridad + Prueba tu frase de contraseña de la copia de seguridad y comprueba que coincida Activar Desactivar "Para restaurar una copia de seguridad, reinstala Signal. Abre la aplicación, toca \"Restaurar copia\" y busca el archivo de copia de seguridad. %1$s" @@ -1977,9 +1977,9 @@ Denunciar - Las claves de acceso no coinciden. - La clave de acceso anterior es incorrecta. - Introduce una clave de acceso nueva. + Las contraseñas no coinciden. + La frase de contraseña anterior es incorrecta. + Introduce una frase de contraseña nueva. ¿Vincular este dispositivo? @@ -2012,10 +2012,10 @@ - Introducir clave de acceso + Introducir frase de contraseña Icono de Signal - Enviar clave de acceso - Clave de acceso no válida. + Enviar frase de contraseña + Frase de contraseña no válida. Desbloquear Signal Signal Android: Pantalla de bloqueo @@ -2992,9 +2992,9 @@ Rechazar llamada - Clave de acceso anterior - Clave de acceso nueva - Repetir clave de acceso nueva + Frase de contraseña anterior + Frase de contraseña nueva + Repetir frase de contraseña nueva Invitar a Signal @@ -3393,9 +3393,9 @@ Ver historial de cambios - Crear clave de acceso + Crear frase de contraseña Seleccionar contactos - Cambiar clave de acceso + Cambiar frase de contraseña Verificar número de seguridad Vista previa del archivo Detalles del mensaje @@ -3506,11 +3506,11 @@ Los chats silenciados que hayas archivado permanecerán archivados aunque recibas un nuevo mensaje. Generar vista previa de enlaces Elige mostrar las vistas previas de los enlaces directamente de los sitios web para los mensajes que envías. - Cambiar clave de acceso - Cambia tu clave de acceso + Cambiar frase de contraseña + Cambia tu frase de contraseña - Activar bloqueo de pantalla con clave de acceso - Bloquea la pantalla y las notificaciones con una clave de acceso + Activar bloqueo de pantalla con frase de contraseña + Bloquea la pantalla y las notificaciones con una frase de contraseña Seguridad de la pantalla Bloquea automáticamente Signal tras el intervalo de inactividad que especifiques Contraseña de tiempo de inactividad @@ -4258,11 +4258,11 @@ Continuar Ahora no Migrar base de datos de Signal - Clave de acceso a la copia de seguridad - Las copias de seguridad se guardarán en una unidad de almacenamiento externo y se cifrarán con la clave de acceso que aparece más abajo. Necesitas esta clave para poder restaurar la copia de seguridad. - Necesitas esta clave para poder restaurar la copia de seguridad. + Frase de contraseña de la copia de seguridad + Las copias de seguridad se guardarán en una unidad de almacenamiento externo y se cifrarán con la frase de contraseña que aparece más abajo. Necesitas esta contraseña para poder restaurar la copia de seguridad. + Necesitas esta contraseña para poder restaurar la copia de seguridad. Carpeta - He anotado esta clave. Comprendo que sin ella no podré restaurar la copia de seguridad. + He anotado esta contraseña. Comprendo que sin ella no podré restaurar la copia de seguridad. Restaurar copia Transferir o restaurar cuenta Transferir cuenta @@ -4270,12 +4270,12 @@ Copias de seguridad de los chats Transferir cuenta Transferir cuenta a un dispositivo Android nuevo - Introducir clave de la copia de seguridad + Introducir contraseña de la copia de seguridad Restaurar No se pueden importar nuevas copias de seguridad La copia de seguridad contiene datos mal formados - Clave de acceso a la copia de seguridad incorrecta + Frase de contraseña la copia de seguridad incorrecta Comprobando… %1$d mensajes completados… ¿Restaurar desde copia de seguridad? @@ -4292,10 +4292,10 @@ Seleccionar carpeta Se ha copiado al portapapeles No hay selector de archivos disponible. - Introduce tu clave de acceso a la copia de seguridad para verificarla + Introduce tu frase de contraseña de la copia de seguridad para verificarla Verificar - ¡Clave de acceso correcta! - Clave de acceso incorrecta + ¡Frase de contraseña correcta! + Frase de contraseña incorrecta Creando copia de seguridad… Verificando copia de seguridad de Signal… @@ -4575,7 +4575,7 @@ Eliminar del grupo ¿Retirar los permisos de admin a %1$s? - "%1$s podrá modificar los detalles de este grupo y la lista de participantes." + "%1$s podrá editar los detalles de este grupo y la lista de participantes." ¿Eliminar a %1$s del grupo? @@ -4687,7 +4687,7 @@ - La conexión Wi-Fi es débil. Se ha cambiado a datos del teléfono. + La conexión Wi-Fi es débil. Se ha cambiado a datos móviles. Eliminar tu cuenta significa: @@ -4719,7 +4719,7 @@ No se ha podido completar el proceso de eliminación de tu cuenta. Comprueba tu conexión de red e inténtalo de nuevo. - Buscar países + Buscar país Omitir @@ -4747,12 +4747,12 @@ Restablecer color del chat ¿Restablecer color del chat? Elegir fondo de pantalla - Tema oscuro atenúa el fondo - Nombre del contacto + Atenuar fondo con tema oscuro + Nombre de contacto Restablecer Vista previa del fondo ¿Quieres cambiar los colores de todos tus chats? - ¿Quieres cambiar los fondos de todos los chats? + ¿Quieres cambiar los fondos de todos tus chats? Restablecer predeterminados Restablecer todos Restablecer predeterminado @@ -4769,7 +4769,7 @@ Vista previa Elegir fondo de pantalla Deslizar para ver más fondos de pantalla - Elige un fondo para todos los chats + Elige un fondo para todos los chats. Elegir fondo para %1$s Signal necesita acceso al almacenamiento para mostrar la galería. @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Libera %1$s de espacio para restaurar tus archivos multimedia. - Restoring media + Restaurando archivos multimedia - Restore paused + Restauración en pausa - Restore complete + Restauración completada Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s de %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Configurar @@ -7410,11 +7410,11 @@ Copias de seguridad desactivadas - %1$s/month + %1$s/mes Your backup plan is free - Renews %1$s + Se renueva el %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Actualizar %1$d/%2$d diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index c701844a47..0ab07d61da 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Oma meedia taastamiseks vabasta %1$s ruumi. - Restoring media + Meedia taastamine - Restore paused + Taastamine peatatud - Restore complete + Taastamine õnnestus Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s / %2$s @@ -7410,11 +7410,11 @@ Varundamine on keelatud - %1$s/month + %1$s/kuu Your backup plan is free - Renews %1$s + Uueneb %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Uuenda %1$d/%2$d diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 7bb9485109..77456e9bbf 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -6001,7 +6001,7 @@ %1$d erantzun - Story no longer hidden + Istorioa jada ez dago ezkutatuta Gehitu @@ -7287,22 +7287,22 @@ - Free up %1$s of space to restore your media. + Multimedia-edukia leheneratzeko, utzi %1$s libre. - Restoring media + Multimedia-edukia leheneratzen - Restore paused + Leheneratzea pausatu egin da - Restore complete + Leheneratzea osatuta - Waiting for Wi-Fi… + Wifiaren zain… - No internet… + Ez dago Internetik… - Device has low battery + Gailuak bateria gutxi du - %1$s of %2$s + %1$s / %2$s @@ -7368,13 +7368,13 @@ - %1$s/month, renews %2$s + Hilean %1$s (berritze-data: %2$s) - Last backup %1$s + Azken babeskopia: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Babeskopia automatikoak, Signal-en muturretik muturrera enkriptatutako biltegiratze-zerbitzu seguruarekin. - Set up + Konfiguratu @@ -7390,9 +7390,9 @@ Egin babeskopiak mugikorra erabiliz - View backup key + Ikusi babeskopia-gakoa - Unlock to view backup key + Desblokeatu babeskopia-gakoa ikusteko Desaktibatu eta ezabatu babeskopiak @@ -7406,27 +7406,27 @@ Babeskopia gauez sortuko da. - Backup plan + Babeskopia-plana Babeskopiak desgaituta daude - %1$s/month + %1$s/hilabete - Your backup plan is free + Zure babeskopia-plana doakoa da - Renews %1$s + Berritze-data: %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Egin mezu-historiaren babeskopia, gailuz aldatzean edo Signal berriro instalatzean ez dezazun daturik galdu. - Other ways to backup + Babeskopiak egiteko beste modu batzuk - On-device backups + Gailuko babeskopiak - Save your backups to a folder on this device + Gorde babeskopiak gailu honetako karpeta batean - Manage or cancel + Kudeatu edo utzi bertan behera - Upgrade + Hobetu %1$d / %2$d diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 28469a0355..d246074a10 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + برای بازیابی رسانه خود، %1$s فضا آزاد کنید. - Restoring media + در حال بازیابی رسانه - Restore paused + بازیابی موقتاً متوقف شد - Restore complete + بازگردانی کامل شد Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s از %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + راه‌اندازی @@ -7410,11 +7410,11 @@ پشتیبان‌ها غیرفعال شده‌اند - %1$s/month + %1$s در ماه Your backup plan is free - Renews %1$s + در %1$s تمدید می‌شود Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + ارتقا %1$d/%2$d diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 2d3e9c17c5..cfc9dbeb7c 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Vapauta %1$s tilaa median palauttamiseksi. - Restoring media + Palautetaan mediaa - Restore paused + Palauttaminen keskeytetty - Restore complete + Palautus suoritettu Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s / %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Määritä @@ -7410,11 +7410,11 @@ Varmuuskopiot poistettu käytöstä - %1$s/month + %1$s/kk Your backup plan is free - Renews %1$s + Uusiutuu %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Päivitä %1$d/%2$d diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2bc55a3f69..abdfe5a4c4 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -678,7 +678,7 @@ Messages contenant des liens - Soyez vigilant lorsque des inconnus vous envoient des messages contenant des liens vers des sites Internet. Ne consultez jamais les liens envoyés par des personnes en qui vous n’avez pas confiance. + Soyez vigilant lorsque des inconnus vous envoient des messages contenant des liens vers des sites web. Ne consultez jamais les liens envoyés par des personnes en qui vous n’avez pas confiance. Fausses entreprises et institutions @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Libérez %1$s d’espace pour restaurer vos médias. - Restoring media + Restauration des médias en cours - Restore paused + Restauration mise en pause - Restore complete + La restauration est terminée. Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s sur %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Configurer @@ -7410,11 +7410,11 @@ Sauvegardes désactivées - %1$s/month + %1$s/mois Your backup plan is free - Renews %1$s + Renouvellement %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Convertir %1$d/%2$d diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 5b9ee20ed2..e359cdd30a 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -7761,13 +7761,13 @@ - Free up %1$s of space to restore your media. + Saor %1$s de spás le do mheáin a aischur. - Restoring media + Meáin á n-aischur - Restore paused + Aischur curtha ar sos - Restore complete + Tá an t-aischur críochnaithe Waiting for Wi-Fi… @@ -7776,7 +7776,7 @@ Device has low battery - %1$s of %2$s + %1$s as %2$s @@ -7848,7 +7848,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Socraigh @@ -7884,11 +7884,11 @@ Cúltacaí díchumasaithe - %1$s/month + %1$s/mí Your backup plan is free - Renews %1$s + Athnuachan %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7900,7 +7900,7 @@ Manage or cancel - Upgrade + Uasghrádaigh %1$d / %2$d diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index f759398cc2..b2f37d2a8a 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Libera %1$s de espazo para poder restaurar os arquivos multimedia. - Restoring media + Restaurando arquivos multimedia - Restore paused + Recuperación parada - Restore complete + Restablecemento completo Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s de %2$s @@ -7410,11 +7410,11 @@ Copia de seguranza desactivada - %1$s/month + %1$s/mes Your backup plan is free - Renews %1$s + Renóvase o %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Actualizar %1$d/%2$d diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 67926ee859..0efe394008 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + તમારા મીડિયાને રિસ્ટોર કરવા માટે %1$s જગ્યા ખાલી કરો. - Restoring media + મીડિયા રિસ્ટોર થાય છે - Restore paused + રિસ્ટોર થોભાવ્યું - Restore complete + રિસ્ટોર પૂરુ Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$sની%2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + સેટ કરો @@ -7410,11 +7410,11 @@ બેકઅપ અક્ષમ છે - %1$s/month + %1$s/માસ Your backup plan is free - Renews %1$s + %1$sના રોજ રિન્યૂ થશે Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + અપગ્રેડ કરો %1$d/%2$d diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 7085341449..619c8273d1 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + अपना मीडिया रीस्टोर करने के लिए, %1$s स्पेस खाली करें। - Restoring media + मीडिया रीस्टोर किया जा रहा है - Restore paused + रीस्टोर पॉज़ किया गया - Restore complete + पुनर्स्थापित पूर्ण Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s का %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + सेटअप करें @@ -7410,11 +7410,11 @@ बैकअप बंद किया गया - %1$s/month + %1$s/महीना Your backup plan is free - Renews %1$s + %1$s को नवीकृत होता है Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + अपग्रेड %1$d/%2$d diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 4c2f0fc03f..3fa50bb6d5 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Oslobodite %1$s prostora da biste preuzeli svoje medijske zapise. - Restoring media + Vraćanje medijskih zapisa - Restore paused + Postupak je pauziran - Restore complete + Dovršeno vraćanje sigurnosne kopije Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s od %2$s @@ -7690,7 +7690,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Postavi @@ -7726,11 +7726,11 @@ Sigurnosno kopiranje je onemogućeno - %1$s/month + %1$s mjesečno Your backup plan is free - Renews %1$s + Obnavlja se %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Ažuriraj %1$d/%2$d diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index b0c4ddaca9..1a238e8105 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Szabadíts fel %1$s helyet a médiafájlok visszaállításához. - Restoring media + Médiafájl visszaállítása - Restore paused + Visszaállítás szünetel - Restore complete + Visszaállítás kész Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s / %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Beállítás @@ -7410,11 +7410,11 @@ Biztonsági mentés kikapcsolva - %1$s/month + %1$s/hó Your backup plan is free - Renews %1$s + Megújul ekkor: %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Frissítés %1$d/%2$d diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index a3a92103eb..1cac187072 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + Kosongkan penyimpanan sebesar %1$s untuk memulihkan media Anda. - Restoring media + Memulihkan media - Restore paused + Pemulihan dijeda - Restore complete + Pemulihan selesai Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$sdari %2$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Buat Nama @@ -7252,11 +7252,11 @@ Pencadangan dinonaktifkan - %1$s/month + %1$s/bulan Your backup plan is free - Renews %1$s + Memperpanjang %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + Pemutakhiran %1$d/%2$d diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 630a5ba5ab..4997f18b9a 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Libera %1$s di spazio per ripristinare i tuoi media. - Restoring media + Ripristino dei media in corso - Restore paused + Ripristino in pausa - Restore complete + Ripristino completato Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s di %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Imposta @@ -7410,11 +7410,11 @@ Backup disattivati - %1$s/month + %1$s al mese Your backup plan is free - Renews %1$s + Rinnovo %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Aggiorna %1$d/%2$d diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 626481acb8..76bf5461ea 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + יש לפנות %1$s של שטח כדי לשחזר את המדיה שלך. - Restoring media + מדיה בשחזור - Restore paused + שחזור הושהה - Restore complete + שחזור הושלם Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s מתוך %2$s @@ -7690,7 +7690,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + הגדרה @@ -7726,11 +7726,11 @@ גיבויים הושבתו - %1$s/month + %1$s לחודש Your backup plan is free - Renews %1$s + מתחדש ב–%1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + שדרג %1$d/%2$d diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 9810b347b9..82511c2189 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + メディアを復元するために%1$sの空き容量を確保してください。 - Restoring media + メディアを復元中 - Restore paused + 復元が中断されました - Restore complete + 復元が完了しました Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s/%2$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + 設定 @@ -7252,11 +7252,11 @@ バックアップがオフになりました - %1$s/month + %1$s/月プラン Your backup plan is free - Renews %1$s + 更新 %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + アップグレードする %1$d/%2$d diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index b11997e0b7..1f3652e26e 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + გაათავისუფლე %1$s ადგილი, რათა შენი მედია ფაილები აღადგინო. - Restoring media + მედია ფაილების აღდგენა - Restore paused + აღდგენა დროებით შეჩერდა - Restore complete + აღდგენა დასრულებულია Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s %2$s-დან @@ -7410,11 +7410,11 @@ სარეზერვო კოპირება გამორთულია - %1$s/month + %1$s/თვეში Your backup plan is free - Renews %1$s + განახლდება %1$s-ში Back up your message history so you never lose data when you get a new phone or reinstall Signal. diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 723d80e016..592a167ea7 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Мультимедианы қалпына келтіру үшін %1$s орын босатыңыз. - Restoring media + Мультимедианы қалпына келтіру - Restore paused + Қалпына келтіру кідіртілді - Restore complete + Қалпына келтірілді Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s / %2$s @@ -7410,11 +7410,11 @@ Резервтік көшірме өшірілді - %1$s/month + айына %1$s Your backup plan is free - Renews %1$s + %1$s жаңарады Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Жаңарту %1$d/%2$d diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index c202ed4e84..54fd0e6688 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + បង្កើនទំហំផ្ទុក %1$s ដើម្បីស្ដារមេឌៀរបស់អ្នកឡើងវិញ។ - Restoring media + កំពុងស្ដារមេឌៀឡើងវិញ - Restore paused + ការស្តារឡើងវិញត្រូវបានផ្អាក - Restore complete + ការស្តារជោគជ័យ Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s នៃ%2$s @@ -7252,11 +7252,11 @@ ការបម្រុងទុកត្រូវបានបិទ - %1$s/month + %1$s/ខែ Your backup plan is free - Renews %1$s + បន្ត %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + ដំឡើង %1$d/%2$d diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 28f08a8777..c5c49f2c36 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + ನಿಮ್ಮ ಮೀಡಿಯಾವನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲು %1$s ಸ್ಥಳಾವಕಾಶವನ್ನು ಮುಕ್ತಗೊಳಿಸಿ.\n\n - Restoring media + ಮೀಡಿಯಾ ರಿಸ್ಟೋರ್ ಮಾಡಲಾಗುತ್ತಿದೆ - Restore paused + ವಿರಾಮಗೊಳಿಸಿರುವುದನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಿ - Restore complete + ಮರುಸ್ಥಾಪನೆ ಪೂರ್ಣಗೊಂಡಿದೆ Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %2$s ನಲ್ಲಿ %1$s @@ -7410,11 +7410,11 @@ ಬ್ಯಾಕಪ್‌ಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ - %1$s/month + %1$s/ತಿಂಗಳು Your backup plan is free - Renews %1$s + ನವೀಕರಿಸುತ್ತದೆ %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + ಅಪ್ಗ್ರೇಡ್ %1$d/%2$d/ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 1597d44f5c..36c1e0d487 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + 미디어를 복원하려면 %1$s의 여유 공간을 확보하세요. - Restoring media + 미디어 복원 중 - Restore paused + 전송을 일시 중지함 - Restore complete + 복구가 완료되었습니다. Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %2$s 중 %1$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + 설정 @@ -7252,11 +7252,11 @@ 백업 사용 안 함 - %1$s/month + %1$s/월 Your backup plan is free - Renews %1$s + %1$s 갱신 Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + 업그레이드 %1$d/%2$d diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 0bc4fd8e68..25bdba51e0 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + Медиа файлдарыңызды калыбына келтирүү үчүн сактоо мейкиндигинен %1$s орун бошотуңуз. - Restoring media + Медиа файлдарыңыз калыбына келүүдө - Restore paused + Калыбына келтирүү тындырылды - Restore complete + Калыбына келди Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %2$s ичинен %1$s @@ -7252,11 +7252,11 @@ Камдык көчүрмөлөр өчүрүлгөн - %1$s/month + Айына %1$s Your backup plan is free - Renews %1$s + %1$s жаңырат Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + Жаңыртуу %1$d/%2$d diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index f6d5f3a40d..463e9c8ec0 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Atlaisvinkite iki %1$s vietos, kad galėtumėte atkurti savo įrašus. - Restoring media + Įrašai atkuriami - Restore paused + Atkūrimas pristabdytas - Restore complete + Atkūrimas užbaigtas Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s iš %2$s @@ -7726,11 +7726,11 @@ Atsarginis kopijavimas išjungtas - %1$s/month + %1$s/mėnesį Your backup plan is free - Renews %1$s + Atnaujinama %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Naujinti %1$d/%2$d diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index d282e1f07b..82942f1109 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -7445,13 +7445,13 @@ - Free up %1$s of space to restore your media. + Lai atjaunotu multividi, atbrīvojiet %1$s vietas krātuvē . - Restoring media + Multivide tiek atjaunota - Restore paused + Atjaunošana apturēta - Restore complete + Atjaunošana pabeigta Waiting for Wi-Fi… @@ -7460,7 +7460,7 @@ Device has low battery - %1$s of %2$s + %1$s no %2$s @@ -7568,11 +7568,11 @@ Rezerves kopijas ir atspējotas - %1$s/month + %1$s/mēnesī Your backup plan is free - Renews %1$s + Atjaunojas %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7584,7 +7584,7 @@ Manage or cancel - Upgrade + Paaugstināt versiju %1$d/%2$d diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 55064b6c9f..bc9ad47a79 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Ослободете простор од %1$s за да ги вратите вашите медиумски датотеки. - Restoring media + Враќање на медиумски датотеки - Restore paused + Враќањето е паузирано - Restore complete + Враќањето е завршено Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s од %2$s @@ -7410,11 +7410,11 @@ Резервните копии се оневозможени - %1$s/month + %1$s/месец Your backup plan is free - Renews %1$s + Се обновува на %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Надградба %1$d/%2$d diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index a877cee0b5..5eab04d8cf 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + നിങ്ങളുടെ മീഡിയ പുനഃസ്ഥാപിക്കാൻ %1$s ഇടം സൃഷ്‌ടിക്കുക. - Restoring media + മീഡിയ പുനഃസ്ഥാപിക്കുന്നു - Restore paused + പുനഃസ്ഥാപിക്കൽ താൽക്കാലികമായി നിർത്തി - Restore complete + വീണ്ടെടുക്കൽ പൂർത്തിയായി Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %2$s ൽ %1$s @@ -7410,11 +7410,11 @@ ബാക്കപ്പുകൾ പ്രവർത്തനരഹിതമാക്കി - %1$s/month + %1$s/ മാസം Your backup plan is free - Renews %1$s + %1$s പുതുക്കുന്നു Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + അപ്‌ഗ്രേഡ് ചെയ്യുക %1$d/%2$d diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index 0afd89ea22..58467e2fe3 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + आपला मिडीया पुर्नस्थापित करण्यासाठी %1$s जागा मोकळी करा. - Restoring media + मिडीया पुर्नस्थापित करत आहे - Restore paused + विराम दिलेले पुर्नस्थित करा - Restore complete + पुनर्संचयन पूर्ण Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s पैकी %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + सेट अप @@ -7410,11 +7410,11 @@ बॅकअप अक्षम केले - %1$s/month + %1$s/महिना Your backup plan is free - Renews %1$s + %1$s नी नूतनीकरण केले Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + श्रेणीसुधारणा करा %1$d/%2$d diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index a4d8b6047d..d6ede8c990 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + Kosongkan %1$s ruang untuk memulihkan media anda. - Restoring media + Memulihkan media - Restore paused + Pemulihan dijeda - Restore complete + Restore selesai Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s daripada %2$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Tetapkan @@ -7252,11 +7252,11 @@ Sandaran dilumpuhkan - %1$s/month + %1$s/bulan Your backup plan is free - Renews %1$s + Memperbaharui %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + Naik taraf %1$d/%2$d diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 5726e1b962..e143ed75a5 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + သင့်မီဒီယာကိုပြန်လည်ရယူရန် နေရာလွတ် %1$s ထွက်လာအောင် ရှင်းထုတ်ပါ။ - Restoring media + မီဒီယာကို ပြန်လည်ရယူနေသည် - Restore paused + ပြန်လည်ရယူခြင်းကို ခေတ္တရပ်ထားသည် - Restore complete + ပြီးပြည့်စုံသောပြန်ယူပါ Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s ၏ %2$s @@ -7252,11 +7252,11 @@ ဘက်ခ်အပ် ပိတ်ထားသည် - %1$s/month + တစ်လလျှင် %1$s Your backup plan is free - Renews %1$s + %1$s တွင် သက်တမ်းတိုးသည် Back up your message history so you never lose data when you get a new phone or reinstall Signal. diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 3df7c592d1..14aaf4aec3 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Slett %1$s for å gjenopprette mediefilene dine. - Restoring media + Gjenoppretter mediefiler - Restore paused + Nedlastingen er satt på pause - Restore complete + Gjenoppretting er utført Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s av %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Velg brukernavn @@ -7410,11 +7410,11 @@ Sikkerhetskopiering slått av - %1$s/month + %1$s per måned Your backup plan is free - Renews %1$s + Fornyes %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Oppdater %1$d/%2$d diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 24df9700f6..5ed707c540 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -3444,7 +3444,7 @@ Dit bericht - Recentelijk gebruikt + Onlangs gebruikt Smileys & mensen Dieren & natuur Eten & drinken @@ -5273,7 +5273,7 @@ Aangepaste meldingen - Recentelijk gebruikt + Onlangs gebruikt 0,5x @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Maak %1$s ruimte vrij om je media te herstellen. - Restoring media + Media aan het herstellen - Restore paused + Terugzetten gepauzeerd - Restore complete + Gegevens herstellen is voltooid Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s van %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Instellen @@ -7410,11 +7410,11 @@ Back-ups uitgeschakeld - %1$s/month + %1$s/maand Your backup plan is free - Renews %1$s + Wordt %1$s vernieuwd Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Omzetten %1$d/%2$d diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 6a091b412b..3f5f65d245 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + ਆਪਣਾ ਮੀਡੀਆ ਰੀਸਟੋਰ ਕਰਨ ਲਈ %1$s ਥਾਂ ਖਾਲੀ ਕਰੋ।\n\n - Restoring media + ਮੀਡੀਆ ਰੀਸਟੋਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ - Restore paused + ਰੀਸਟੋਰ ਕਰਨਾ ਰੋਕਿਆ ਗਿਆ - Restore complete + ਬਹਾਲ ਕਰਨ ਦਾ ਕਾਰਜ ਪੂਰਾ ਹੋਇਆ Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %2$s ਚੋਂ %1$s @@ -7410,11 +7410,11 @@ ਬੈਕਅੱਪ ਨੂੰ ਅਸਮਰੱਥ ਕੀਤਾ ਗਿਆ - %1$s/month + %1$s/ਮਹੀਨਾ Your backup plan is free - Renews %1$s + %1$s ਨੂੰ ਰੀਨਿਊ ਹੁੰਦੀ ਹੈ Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + ਅੱਪਗ੍ਰੇਡ ਕਰੋ %1$d/%2$d diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a6509f41dc..e312bf2800 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Aby przywrócić multimedia, zwolnij %1$s miejsca. - Restoring media + Przywracanie multimediów - Restore paused + Przywracanie wstrzymane - Restore complete + Przywracanie zakończone Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s z %2$s @@ -7690,7 +7690,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Ustaw @@ -7726,11 +7726,11 @@ Kopie zapasowe wyłączone - %1$s/month + %1$s/miesiąc Your backup plan is free - Renews %1$s + Przedłużane %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Aktualizuj %1$d/%2$d diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 1eb91aa1ce..e502a6692e 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Libere %1$s de espaço para restaurar seus arquivos de mídia. - Restoring media + Restaurando arquivos de mídia - Restore paused + Restauração interrompida - Restore complete + Restauração concluída Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s de %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Configurar @@ -7410,11 +7410,11 @@ Backups desativados - %1$s/month + %1$s/mês Your backup plan is free - Renews %1$s + Renova em %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Atualizar %1$d/%2$d diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index aa9be4bfe0..694938908a 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Liberte até %1$s de espaço para restaurar os seus ficheiros. - Restoring media + A restaurar os ficheiros - Restore paused + Restauro pausado - Restore complete + Restauro completo Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s de %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Configure @@ -7410,11 +7410,11 @@ Cópias de segurança desativadas - %1$s/month + %1$s/mês Your backup plan is free - Renews %1$s + Renovações %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 15256bfef2..dab4f22355 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -7445,13 +7445,13 @@ - Free up %1$s of space to restore your media. + Eliberează %1$s spațiu și restaurează-ți fișierele. - Restoring media + Se restaurează fișierele - Restore paused + Restaurare întreruptă - Restore complete + Restaurarea a fost finalizată Waiting for Wi-Fi… @@ -7460,7 +7460,7 @@ Device has low battery - %1$s of %2$s + %1$s din %2$s @@ -7532,7 +7532,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Stabilește @@ -7568,11 +7568,11 @@ Backup-uri dezactivate - %1$s/month + %1$s/lună Your backup plan is free - Renews %1$s + Se reînnoiește %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7584,7 +7584,7 @@ Manage or cancel - Upgrade + Actualizează %1$d/%2$d diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0bf3f612b2..69d57fbe26 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Освободите %1$s места для восстановления медиафайлов. - Restoring media + Восстановление медиафайлов - Restore paused + Восстановление приостановлено - Restore complete + Восстановление завершено Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s из %2$s @@ -7690,7 +7690,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Настроить @@ -7726,11 +7726,11 @@ Резервное копирование отключено - %1$s/month + %1$s/месяц Your backup plan is free - Renews %1$s + Продлится %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Обновить %1$d/%2$d diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index ddfc45ace4..b823d7ff1c 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Ak chcete obnoviť svoje médiá, uvoľnite %1$s. - Restoring media + Obnovujú sa médiá - Restore paused + Obnovovanie bolo pozastavené - Restore complete + Obnova dokončená Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s z %2$s @@ -7690,7 +7690,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Nastaviť @@ -7726,11 +7726,11 @@ Zálohy sú vypnuté - %1$s/month + %1$s/mesiac Your backup plan is free - Renews %1$s + Obnoví sa %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Aktualizovať %1$d/%2$d diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 0ca118399e..715b905c2a 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Sprostite %1$s prostora za shranjevanje za obnovitev medijev. - Restoring media + Obnovitev medijev - Restore paused + Obnovitev je prekinjena - Restore complete + Obnovitev uspešna Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s od %2$s @@ -7726,11 +7726,11 @@ Varnostno kopiranje izklopljeno - %1$s/month + %1$s/mesec Your backup plan is free - Renews %1$s + Obnovitev naročnine: %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Posodobi %1$d/%2$d diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 4d7ec7fed4..eac14816b7 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Liro %1$s hapësirë për të rikthyer median. - Restoring media + Duke rikthyer median - Restore paused + Rikthimi u ndërpre - Restore complete + Rikthim i plotësuar Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s nga %2$s @@ -7410,11 +7410,11 @@ Kopjeruajtjet u çaktivizuan - %1$s/month + %1$s/muaj Your backup plan is free - Renews %1$s + Rinovohet më %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Përmirësojeni %1$d/%2$d diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 2dc491d596..2483d3108e 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -6001,7 +6001,7 @@ Одговора: %1$d - Story no longer hidden + Прича више није скривена Додај @@ -7287,22 +7287,22 @@ - Free up %1$s of space to restore your media. + Ослободите %1$s меморијског простора да бисте вратили медије. - Restoring media + Враћање медија - Restore paused + Враћање је паузирано - Restore complete + Враћање је завршено - Waiting for Wi-Fi… + Чекамо Wi-Fi… - No internet… + Нема интернета… - Device has low battery + Батерија уређаја је скоро празна - %1$s of %2$s + %1$s од %2$s @@ -7368,13 +7368,13 @@ - %1$s/month, renews %2$s + %1$s месечно, обнавља се %2$s - Last backup %1$s + Последња резервна копија: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Аутоматске резервне копије уз помоћ Signal-ове безбедне потпуно шифроване услуге складиштења - Set up + Подесите @@ -7390,9 +7390,9 @@ Резервне копије преко мобилне мреже - View backup key + Прикажи кључ за резервне копије - Unlock to view backup key + Откључајте да видите кључ за резервне копије Искључи и избриши резервне копије @@ -7406,27 +7406,27 @@ Резервна копија ће бити направљена преко ноћи. - Backup plan + План за резервне копије Резервне копије су онемогућене - %1$s/month + %1$s месечно - Your backup plan is free + Овај план за резервне копије је бесплатан - Renews %1$s + Обнавља се на дан %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Направите резервне копије историје порука да не бисте изгубили податке када набавите нови телефон или поново инсталирате Signal. - Other ways to backup + Други начини за прављење резервних копија - On-device backups + Резервне копије на уређају - Save your backups to a folder on this device + Сачувајте резервне копије у фолдер на овом уређају - Manage or cancel + Измени или откажи - Upgrade + Ажурирај %1$d/%2$d diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 87c8ca5bb7..20f9b9c3ff 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Frigör %1$s utrymme för att återställa dina media. - Restoring media + Återställer media - Restore paused + Återställning pausad - Restore complete + Återställningen är klar Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s av %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Ställ in @@ -7410,11 +7410,11 @@ Säkerhetskopior inaktiverade - %1$s/month + %1$s/månad Your backup plan is free - Renews %1$s + Förnyas %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Uppgradera %1$d/%2$d diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index f0ffb31af9..42da18ddab 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Futa nafasi ya %1$s ili kurejesha video na picha zako. - Restoring media + Inarejesha video na picha - Restore paused + Urejeshaji umesitishwa - Restore complete + Hatua ya kurejesha imekamilika Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s kwa %2$s @@ -7410,11 +7410,11 @@ Chelezo zimezimwa - %1$s/month + %1$s/mwezi Your backup plan is free - Renews %1$s + Kuhuishwa %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Boresha %1$d/%2$d diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 7672371a5f..98c834f1cb 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + உங்கள் மீடியாவை மீட்டமைக்க %1$s இடத்தைக் காலியக்கவும். - Restoring media + மீடியாவை மீட்டமைக்கிறது - Restore paused + மீட்டமைப்பு இடைநிறுத்தப்பட்டது - Restore complete + மீட்டெடுப்பு முடிந்தது Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %2$s இன் %1$s @@ -7410,11 +7410,11 @@ காப்புப்பிரதிகள் முடிக்கப்பட்டுள்ளன - %1$s/month + %1$s/மாதம் Your backup plan is free - Renews %1$s + %1$s அன்று புதுப்பிக்கப்படும் Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + மேம்படுத்தல் %1$d/%2$d diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 2519304f7b..de42b0c836 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + మీ మీడియాను పునరుద్ధరించడానికి స్థలంలో %1$s ని ఖాళీ చేయండి.\n\n - Restoring media + మీడియాను పునరుద్ధరిస్తోంది - Restore paused + పాజ్ చేసిన దానిని పునరుద్ధరించండి - Restore complete + పునరుద్ధరణ పూర్తయింది Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s ఆఫ్ %2$s @@ -7410,11 +7410,11 @@ బ్యాకప్‌లు నిలిపివేయబడ్డాయి - %1$s/month + %1$s/నెల Your backup plan is free - Renews %1$s + %1$s ను పునరుద్ధరిస్తుంది Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + అభివృద్ధి %1$d/%2$d diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 60de010eee..ab6c00a871 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + โปรดเพิ่มพื้นที่จัดเก็บ %1$s เพื่อกู้คืนไฟล์สื่อของคุณ - Restoring media + กำลังกู้คืนไฟล์สื่อ - Restore paused + การกู้คืนหยุดชั่วคราว - Restore complete + กู้คืนเสร็จสิ้น Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s ของ %2$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + ตั้งชื่อผู้ใช้ @@ -7252,11 +7252,11 @@ การสำรองข้อมูลถูกปิดใช้งาน - %1$s/month + %1$s/เดือน Your backup plan is free - Renews %1$s + ต่ออายุ %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + ปรับรุ่น %1$d/%2$d diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 1943647726..bb07a57fc9 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -7287,11 +7287,11 @@ - Free up %1$s of space to restore your media. + Magbawas ng %1$s ng space para ma-restore ang iyong media. - Restoring media + Nire-restore ang media - Restore paused + Naka-pause ang pag-restore Restore complete @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s sa %2$s @@ -7410,11 +7410,11 @@ Naka-disable ang backups - %1$s/month + %1$s/buwan Your backup plan is free - Renews %1$s + Mare-renew sa %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3c8a9890f0..fa1471c54a 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + Medyanı geri yüklemek için %1$s alan boşalt. - Restoring media + Medya geri yükleniyor - Restore paused + Geri yükleme duraklatıldı - Restore complete + Geri yükleme tamamlandı Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$s / %2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Ayarla @@ -7410,11 +7410,11 @@ Yedeklemeler devre dışı - %1$s/month + %1$s/ay Your backup plan is free - Renews %1$s + %1$s tarihinde yenilenir Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + Yükselt %1$d/%2$d diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 452c7d1914..f9d438a4c3 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + مېدىيارىڭىزنى ئەسلىگە كەلتۈرۈش ئۈچۈن %1$s بوشلۇق بىكارلاڭ.\n\n - Restoring media + مېدىيانى ئەسلىگە كەلتۈرىۋاتىدۇ - Restore paused + توختاشنى ئەسلىگە كەلتۈرۈڭ - Restore complete + ئەسلىگە كەلتۈرۈش تاماملاندى Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s نىڭ%2$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + بەلگىلەش @@ -7252,11 +7252,11 @@ زاپاسلاش چەكلەنگەن - %1$s/month + %1$s/ئايلىقى Your backup plan is free - Renews %1$s + يېڭىلىنىش ۋاقتى %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + يېڭىلاش %1$d/%2$d diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 6f88fb19dc..81c51507ff 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -7603,13 +7603,13 @@ - Free up %1$s of space to restore your media. + Звільніть %1$s, щоб відновити медіафайли. - Restoring media + Відновлення медіафайлів - Restore paused + Відновлення припинено - Restore complete + Відновлення завершено Waiting for Wi-Fi… @@ -7618,7 +7618,7 @@ Device has low battery - %1$s of %2$s + %1$s з %2$s @@ -7690,7 +7690,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Установити @@ -7726,11 +7726,11 @@ Резервне копіювання вимкнено - %1$s/month + %1$s/місяць Your backup plan is free - Renews %1$s + Оновлюється %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7742,7 +7742,7 @@ Manage or cancel - Upgrade + Оновити %1$d/%2$d diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index bec5f762a5..2d0f96cec6 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -7287,13 +7287,13 @@ - Free up %1$s of space to restore your media. + اپنے میڈیا کو بحال کرنے کے لیے %1$s جگہ خالی کریں۔ - Restoring media + میڈیا بحال ہو رہا ہے - Restore paused + موقوف عمل کو بحال کریں - Restore complete + بحالی مکمل Waiting for Wi-Fi… @@ -7302,7 +7302,7 @@ Device has low battery - %1$s of %2$s + %1$sکا%2$s @@ -7374,7 +7374,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + سیٹ اپ @@ -7410,11 +7410,11 @@ بیک اپس غیر فعال کر دیے گئے - %1$s/month + %1$s/ماہ Your backup plan is free - Renews %1$s + %1$s کی تجدید کرتا ہے Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7426,7 +7426,7 @@ Manage or cancel - Upgrade + اپ گریڈ %1$d/%2$d diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index a2d4e499ac..6e399b184a 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + Giải phóng %1$s dung lượng để khôi phục tập tin đa phương tiện của bạn. - Restoring media + Đang khôi phục tập tin đa phương tiện - Restore paused + Khôi phục được tạm dừng - Restore complete + Hoàn tất khôi phục Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s trên tổng số %2$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + Thiết Lập @@ -7252,11 +7252,11 @@ Đã tắt sao lưu - %1$s/month + %1$s/tháng Your backup plan is free - Renews %1$s + Gia hạn %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + Nâng cấp %1$d/%2$d diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index f7dea6075c..330a36625a 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + 請你搵多 %1$s 空間嚟還原媒體。 - Restoring media + 還原緊媒體 - Restore paused + 還原已暫停 - Restore complete + 還原搞掂 Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s / %2$s @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + 設定 @@ -7252,11 +7252,11 @@ 停用咗備份 - %1$s/month + 每月 %1$s Your backup plan is free - Renews %1$s + 到 %1$s 續期 Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + 升級 %1$d/%2$d diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 51b5ba0cd0..2da97484d3 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + 请释放 %1$s 的存储空间以恢复您的媒体。 - Restoring media + 正在恢复媒体 - Restore paused + 恢复已暂停 - Restore complete + 还原完成 Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + %1$s / %2$s 匹配 @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + 设置 @@ -7252,11 +7252,11 @@ 已停用备份 - %1$s/month + %1$s/月 Your backup plan is free - Renews %1$s + 到 %1$s 续期 Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + 升级 %1$d/%2$d diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index bdcd6ad725..80e70f4b95 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + 釋放 %1$s 的空間來還原你的媒體。 - Restoring media + 正在還原媒體 - Restore paused + 還原已暫停 - Restore complete + 還原完成 Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + 第 %1$s 個,共 %2$s 個 @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + 設定 @@ -7252,11 +7252,11 @@ 備份已停用 - %1$s/month + %1$s/月 Your backup plan is free - Renews %1$s + %1$s 續期 Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + 升級 %1$d/%2$d diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 2320076116..2cc3919e67 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -7129,13 +7129,13 @@ - Free up %1$s of space to restore your media. + 釋放 %1$s 的空間來還原你的媒體。 - Restoring media + 正在還原媒體 - Restore paused + 還原已暫停 - Restore complete + 回復完成 Waiting for Wi-Fi… @@ -7144,7 +7144,7 @@ Device has low battery - %1$s of %2$s + 第 %1$s 個,共 %2$s 個 @@ -7216,7 +7216,7 @@ Automatic backups with Signal\'s secure end-to-end encrypted storage service. - Set up + 設定 @@ -7252,11 +7252,11 @@ 備份已停用 - %1$s/month + %1$s/月 Your backup plan is free - Renews %1$s + 續約 %1$s Back up your message history so you never lose data when you get a new phone or reinstall Signal. @@ -7268,7 +7268,7 @@ Manage or cancel - Upgrade + 升級 %1$d/%2$d diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index a199b21b23..e3a9bdd456 100644 --- a/app/static-ips.gradle.kts +++ b/app/static-ips.gradle.kts @@ -1,6 +1,6 @@ rootProject.extra["service_ips"] = """new String[]{"13.248.212.111","76.223.92.165"}""" -rootProject.extra["storage_ips"] = """new String[]{"142.251.40.211"}""" -rootProject.extra["cdn_ips"] = """new String[]{"52.85.247.123","52.85.247.56","52.85.247.61","52.85.247.8"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.251.40.147"}""" +rootProject.extra["cdn_ips"] = """new String[]{"18.161.21.122","18.161.21.4","18.161.21.66","18.161.21.70"}""" rootProject.extra["cdn2_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" rootProject.extra["cdn3_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" rootProject.extra["sfu_ips"] = """new String[]{"35.244.250.191"}""" From ca0062f46e2c307d3bb6d04d73eb26af46f35bd2 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 4 Oct 2024 13:09:45 -0400 Subject: [PATCH 49/53] Bump version to 7.19.1 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c7957e8e5a..f6e69c2476 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1466 -val canonicalVersionName = "7.19.0" +val canonicalVersionCode = 1467 +val canonicalVersionName = "7.19.1" val currentHotfixVersion = 0 val maxHotfixVersions = 100 From d0162d0b21a307a3122cd720e075fccabb29e0ef Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Mon, 7 Oct 2024 13:38:32 -0700 Subject: [PATCH 50/53] Update emojis in about status. --- .../groups/ui/GroupMemberListAdapter.java | 2 +- .../profiles/manage/EditAboutFragment.java | 18 ------------------ .../securesms/recipients/Recipient.kt | 5 +---- .../recipients/ui/about/AboutSheet.kt | 2 +- .../main/res/layout/edit_about_fragment.xml | 11 ----------- app/src/main/res/values/strings.xml | 2 -- .../java/org/signal/core/util/StringUtil.java | 2 -- 7 files changed, 3 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupMemberListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupMemberListAdapter.java index 6373ca1855..fc7a0e3328 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupMemberListAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupMemberListAdapter.java @@ -438,7 +438,7 @@ void bind(@NonNull GroupMemberEntry memberEntry, boolean isSelected) { pendingMembers.getInviteCount(), displayName, pendingMembers.getInviteCount()); - bindImageAndText(inviter, displayText, inviter.getFilteredAbout()); + bindImageAndText(inviter, displayText, inviter.getAbout()); if (pendingMembers.isCancellable() && adminActionsListener != null) { popupMenu.setMenu(R.menu.others_invite_pending_menu, diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutFragment.java index f96c6d6955..20c590ee5d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutFragment.java @@ -65,7 +65,6 @@ public class EditAboutFragment extends Fragment implements EditProfileActivity.E private ImageView emojiView; private EditText bodyView; private TextView countView; - private TextView errorView; private CircularProgressMaterialButton saveButton; private EditAboutViewModel viewModel; private LifecycleDisposable lifecycleDisposable; @@ -82,7 +81,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat this.emojiView = view.findViewById(R.id.edit_about_emoji); this.bodyView = view.findViewById(R.id.edit_about_body); this.countView = view.findViewById(R.id.edit_about_count); - this.errorView = view.findViewById(R.id.edit_about_error); this.saveButton = view.findViewById(R.id.edit_about_save); lifecycleDisposable = new LifecycleDisposable(); @@ -96,7 +94,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat EditTextUtil.addGraphemeClusterLimitFilter(bodyView, ABOUT_MAX_GLYPHS); this.bodyView.addTextChangedListener(new AfterTextChanged(editable -> { - checkValidText(editable.toString()); trimFieldToMaxByteLength(editable); presentCount(editable.toString()); })); @@ -131,21 +128,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat ViewUtil.focusAndMoveCursorToEndAndOpenKeyboard(bodyView); } - private void checkValidText(String text) { - boolean isInvalid = false; - for (Character emoji : StringUtil.FILTERED_EMOJIS) { - if (text.contains(Character.toString(emoji))) { - isInvalid = true; - break; - } - } - - int colorRes = isInvalid ? R.color.signal_colorError : R.color.signal_colorPrimary; - bodyView.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(requireContext(), colorRes))); - errorView.setVisibility(isInvalid ? View.VISIBLE : View.GONE); - saveButton.setEnabled(!isInvalid); - } - @Override public void onSaveInstanceState(@NonNull Bundle outState) { outState.putString(KEY_SELECTED_EMOJI, selectedEmoji); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt index d7965a6214..b6bc37f98d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt @@ -367,11 +367,8 @@ class Recipient( /** The badge to feature on a recipient's avatar, if any. */ val featuredBadge: Badge? = badges.firstOrNull() - /** A string filtering out banned emojis from the about text */ - val filteredAbout: String? by lazy { about?.filterNot { StringUtil.FILTERED_EMOJIS.contains(it) } } - /** A string combining the about emoji + text for displaying various places. */ - val combinedAboutAndEmoji: String? by lazy { listOf(aboutEmoji, filteredAbout).filter { it.isNotNullOrBlank() }.joinToString(separator = " ").nullIfBlank() } + val combinedAboutAndEmoji: String? by lazy { listOf(aboutEmoji, about).filter { it.isNotNullOrBlank() }.joinToString(separator = " ").nullIfBlank() } /** Whether or not we should blur the recipient's avatar when showing it in the chat list and other locations. */ val shouldBlurAvatar: Boolean diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt index 9aaf2863a0..3e41e53287 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt @@ -96,7 +96,7 @@ class AboutSheet : ComposeBottomSheetDialogFragment() { displayName = recipient.get().getDisplayName(requireContext()), shortName = recipient.get().getShortDisplayName(requireContext()), profileName = recipient.get().profileName.toString(), - about = recipient.get().filteredAbout, + about = recipient.get().about, verified = verified, hasAvatar = recipient.get().profileAvatarFileDetails.hasFile(), recipientForAvatar = recipient.get(), diff --git a/app/src/main/res/layout/edit_about_fragment.xml b/app/src/main/res/layout/edit_about_fragment.xml index 5f3643a345..5bb293652f 100644 --- a/app/src/main/res/layout/edit_about_fragment.xml +++ b/app/src/main/res/layout/edit_about_fragment.xml @@ -59,17 +59,6 @@ app:layout_constraintTop_toTopOf="@id/edit_about_body" app:layout_constraintBottom_toBottomOf="@id/edit_about_body"/> - - Free to chat Taking a break Working on something new - - One or more characters is invalid. Edit group diff --git a/core-util/src/main/java/org/signal/core/util/StringUtil.java b/core-util/src/main/java/org/signal/core/util/StringUtil.java index f1719c4dc4..4afca60c0a 100644 --- a/core-util/src/main/java/org/signal/core/util/StringUtil.java +++ b/core-util/src/main/java/org/signal/core/util/StringUtil.java @@ -22,8 +22,6 @@ public final class StringUtil { '\u200B', // zero-width space '\u2800'); // braille blank - public static final List FILTERED_EMOJIS = List.of('\u2713', '\u2714', '\u2611', '\u221A', '\u26C9', '\u26CA', '\u26DB'); - private static final Pattern ALL_ASCII_PATTERN = Pattern.compile("^[\\x00-\\x7F]*$"); private static final class Bidi { From db2dc473e07d507933d71bc0f7354b9b24bf68ab Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 7 Oct 2024 16:51:27 -0400 Subject: [PATCH 51/53] Improve network reliability. --- .../thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt index a8899f34ed..c863f75fb3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt @@ -173,7 +173,7 @@ open class SignalServiceNetworkAccess(context: Context) { HostConfig("https://inbox.google.com", G_HOST, GMAIL_CONNECTION_SPEC) ) - private val fUrls = arrayOf("https://github.githubassets.com", "https://pinterest.com", "https://www.redditstatic.com") + private val fUrls = arrayOf("https://slate.com", "https://www.zesty.io", "https://www.redditstatic.com") private val fConfig: SignalServiceConfiguration = SignalServiceConfiguration( signalServiceUrls = fUrls.map { SignalServiceUrl(it, F_SERVICE_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(), From b51c27983eae2941c1c0bfe1e476aea9d826988d Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 7 Oct 2024 16:59:37 -0400 Subject: [PATCH 52/53] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 2 - app/src/main/res/values-ar/strings.xml | 260 ++++++++++----------- app/src/main/res/values-az/strings.xml | 36 ++- app/src/main/res/values-bg/strings.xml | 38 ++- app/src/main/res/values-bn/strings.xml | 34 ++- app/src/main/res/values-bs/strings.xml | 36 ++- app/src/main/res/values-ca/strings.xml | 2 - app/src/main/res/values-cs/strings.xml | 36 ++- app/src/main/res/values-da/strings.xml | 2 - app/src/main/res/values-de/strings.xml | 2 - app/src/main/res/values-el/strings.xml | 34 ++- app/src/main/res/values-es/strings.xml | 64 +++-- app/src/main/res/values-et/strings.xml | 36 ++- app/src/main/res/values-eu/strings.xml | 2 - app/src/main/res/values-fa/strings.xml | 34 ++- app/src/main/res/values-fi/strings.xml | 34 ++- app/src/main/res/values-fr/strings.xml | 104 ++++----- app/src/main/res/values-ga/strings.xml | 2 - app/src/main/res/values-gl/strings.xml | 36 ++- app/src/main/res/values-gu/strings.xml | 36 ++- app/src/main/res/values-hi/strings.xml | 34 ++- app/src/main/res/values-hr/strings.xml | 34 ++- app/src/main/res/values-hu/strings.xml | 34 ++- app/src/main/res/values-in/strings.xml | 42 ++-- app/src/main/res/values-it/strings.xml | 36 ++- app/src/main/res/values-iw/strings.xml | 2 - app/src/main/res/values-ja/strings.xml | 34 ++- app/src/main/res/values-ka/strings.xml | 36 ++- app/src/main/res/values-kk/strings.xml | 2 - app/src/main/res/values-km/strings.xml | 48 ++-- app/src/main/res/values-kn/strings.xml | 36 ++- app/src/main/res/values-ko/strings.xml | 2 - app/src/main/res/values-ky/strings.xml | 2 - app/src/main/res/values-lt/strings.xml | 36 ++- app/src/main/res/values-lv/strings.xml | 36 ++- app/src/main/res/values-mk/strings.xml | 36 ++- app/src/main/res/values-ml/strings.xml | 38 ++- app/src/main/res/values-mr/strings.xml | 2 - app/src/main/res/values-ms/strings.xml | 34 ++- app/src/main/res/values-my/strings.xml | 38 ++- app/src/main/res/values-nb/strings.xml | 40 ++-- app/src/main/res/values-nl/strings.xml | 4 +- app/src/main/res/values-pa/strings.xml | 44 ++-- app/src/main/res/values-pl/strings.xml | 2 - app/src/main/res/values-pt-rBR/strings.xml | 2 - app/src/main/res/values-pt/strings.xml | 34 ++- app/src/main/res/values-ro/strings.xml | 2 - app/src/main/res/values-ru/strings.xml | 34 ++- app/src/main/res/values-sk/strings.xml | 34 ++- app/src/main/res/values-sl/strings.xml | 40 ++-- app/src/main/res/values-sq/strings.xml | 36 ++- app/src/main/res/values-sr/strings.xml | 2 - app/src/main/res/values-sv/strings.xml | 34 ++- app/src/main/res/values-sw/strings.xml | 36 ++- app/src/main/res/values-ta/strings.xml | 2 - app/src/main/res/values-te/strings.xml | 36 ++- app/src/main/res/values-th/strings.xml | 38 ++- app/src/main/res/values-tl/strings.xml | 2 - app/src/main/res/values-tr/strings.xml | 34 ++- app/src/main/res/values-ug/strings.xml | 2 - app/src/main/res/values-uk/strings.xml | 46 ++-- app/src/main/res/values-ur/strings.xml | 34 ++- app/src/main/res/values-vi/strings.xml | 34 ++- app/src/main/res/values-yue/strings.xml | 38 ++- app/src/main/res/values-zh-rCN/strings.xml | 34 ++- app/src/main/res/values-zh-rHK/strings.xml | 34 ++- app/src/main/res/values-zh-rTW/strings.xml | 34 ++- 67 files changed, 985 insertions(+), 1119 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index e304f2d16c..684b8e6d02 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -3319,8 +3319,6 @@ Kan nou klets Ek rus tans Werk tans aan iets nuuts - - Een of meer karakters is ongeldig. Redigeer groep diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index fa8dfe5423..c8ab947fb2 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -2153,7 +2153,7 @@ المدفوعات: %1$s - عمليات الدفع + المدفوعات تمَّ الإبلاغ عنها كرِسالة غير مرغوب فيها @@ -2222,7 +2222,7 @@ %1$d مجموعة إضافية - الإبلاغ… + إبلاغ… العبارات السرية غير متطابقة! @@ -2307,19 +2307,19 @@ - أرسل رسالة قصيرة + أرسِل كود رسالة قصيرة - التسجيل في سيجنال - بحاجة إلى مساعدة مع الرقم التعريفي الشخصي في أندرويد + التسجيل في سيجنال - تحتاج مساعدة لتسجيل رقم التعريف الشخصي (PIN) في أندرويد - إنَّ الـPIN الخاص بك عِبارة عَن رمز مكون من %1$d+ خانات أو أكثر والتي يُمكن أن تكون إما أرقامًا أو حُروفًا.\n\nإذا لم تستطع تَذكُر الـPIN الخاص بك، يمكنك إنشاء رقم جديد. + رقم التعريف الشخصي (PIN) الخاص بك عِبارة عَن كود مُكوَّن من %1$d+ خانات أو أكثر والتي يُمكن أن تكون إما أرقامًا أو حُروفًا.\n\nإذا لم تستطع تَذكُّر رقم التعريف الشخصي (PIN) الخاص بك، يمكنك إنشاء رقم جديد. - إذا لم تستطع تَذكُر الـPIN الخاص بك، يمكنك إنشاء رقم جديد. + إذا لم تستطع تَذكُّر رقم التعريف الشخصي (PIN) الخاص بك، يمكنك إنشاء رقم جديد. - لم تعد تتوفر على تخمينات الرقم التعريفي الشخصي، لكن يمكنك الوصول إلى حساب سيجنال الخاص بك عبر إنشاء رقم جديد. + نفدَت تخميناتك لرقم التعريف الشخصي (PIN)، لكن يمكنك الوصول إلى حساب سيجنال الخاص بك عبر إنشاء رقم تعريف شخصي (PIN) جديد. تحذير - إذا قمتَ بتعطيل رمز التعريف الشخصي، ستفقد جميع بياناتك عند قيامك بإعادة التسجيل في سيجنال، إلّا إذا قمتَ باسترجاعها بواسطة النسخ الاحتياطي اليدوي. لا يمكنك تفعيل قفل التسجيل أثناء تعطيل رمز التعريف الشخصي. + إذا قمتَ بتعطيل رقم التعريف الشخصي (PIN)، ستفقد جميع بياناتك عند قيامك بإعادة التسجيل في سيجنال، إلّا إذا قمتَ باسترجاعها بواسطة النسخ الاحتياطي اليدوي. لا يمكنك تفعيل قفل التسجيل أثناء تعطيل رقم التعريف الشخصي (PIN). إلغاء رقم التعريف الشخصي @@ -2341,7 +2341,7 @@ قُم بالتحقُّق لمواصلة المراسلة لمنع وصول الرسائل غير المرغوب فيها في سيجنال، يُرجى إتمام التحقُّق. - بعد التحقق، يمكنك مواصلة التراسل. ستُرسَل تلقائيًا لك كل رسالة متوقفة مؤقتًا. + بعد التحقُّق، يمكنك مواصلة التراسل. ستُرسَل تلقائيًا كل رسالة متوقفة مؤقتًا. أنت @@ -2364,9 +2364,9 @@ المتلقي غير متاح فشل في الشبكة! يُرجى التحقّق من اتصالك بالانترنت ثم حاول مرة أُخرى. - الرقم غير مسجل! - الرقم المطلوب لا يدعم مكالمات صوتية مشفرة! - عُلم + الرقم غير مُسجَّل! + الرقم المطلوب لا يدعم مكالمات صوتية آمنة! + تم @@ -2383,28 +2383,28 @@ الكاميرا غير مشغلة - يُرجى اللمس هنا لتشغيل مكالمتك بالصورة. - للاتصال بـ%1$s، يحتاج سيجنال صلاحية الوصول إلى الكاميرا + يُرجى النقر هنا لتشغيل مكالمة الفيديو لديك. + للاتصال بـ %1$s، يحتاج سيجنال صلاحية الوصول إلى الكاميرا سيجنال‏ %1$s يتصل… - يَجري إعادة الاِتصال.. + تجري إعادة الاتصال… - تم رفض إذن بلوتوث + تمَّ رفض إذن بلوتوث - يُرجى تمكين إذن \"الأجهزة المجاورة\" لاستخدام البلوتوث أثناء المكالمة. + يُرجى تمكين إذن \"الأجهزة القريبة\" لاستخدام البلوتوث أثناء المكالمة. فتح الإعدادات ليس الآن - الموافقة على %1$d طلب؟ - الموافقة على %1$d طلب؟ - الموافقة على %1$d طلبين؟ - الموافقة على %1$d طلبات؟ - الموافقة على %1$d طلبًا؟ - الموافقة على %1$d طلب؟ + أترغبُ بالموافقة على %1$d طلب؟ + أترغبُ بالموافقة على %1$d طلب؟ + أترغبُ بالموافقة على %1$d طلبين؟ + أترغبُ بالموافقة على %1$d طلباتٍ؟ + أترغبُ بالموافقة على %1$d طلبًا؟ + أترغبُ بالموافقة على %1$d طلب؟ موافقة على الكل @@ -2415,23 +2415,23 @@ سيُضاف %1$d شخصين إلى المكالمة. سيُضاف %1$d أشخاص إلى المكالمة. سيُضاف %1$d شخصًا إلى المكالمة. - سيُضاف %1$d شخص إلى المكالمة. + سيُضاف %1$d شخصٍ إلى المكالمة. - رفض %1$d طلب؟ - رفض %1$d طلب؟ - رفض %1$d طلبين؟ - رفض %1$d طلبات؟ - رفض %1$d طلبًا؟ - رفض %1$d طلب؟ + أترغبُ برفض %1$d طلب؟ + أترغبُ برفض %1$d طلب؟ + أترغبُ برفض %1$d طلبين؟ + أترغبُ برفض %1$d طلبات؟ + أترغبُ برفض %1$d طلبًا؟ + أترغبُ برفض %1$d طلب؟ لن يتم إضافة %1$d شخص إلى المكالمة. لن يتم إضافة %1$d شخص إلى المكالمة. لن يتم إضافة %1$d شخصين إلى المكالمة. - لن يتم إضافة %1$d أشخاص إلى المكالمة. + لن يتم إضافة %1$d أشخاصٍ إلى المكالمة. لن يتم إضافة %1$d شخصًا إلى المكالمة. لن يتم إضافة %1$d شخص إلى المكالمة. @@ -2451,16 +2451,16 @@ لا أحد (%1$d) شخص واحد (%1$d) شخصان (%1$d) - %1$d أفراد - %1$d فردا - %1$d فرد + %1$d أشخاص + %1$d شخصًا + %1$d شخصٍ - رُفض طلب الانضمام + رُفِضَ طلب الانضمام - لقد رُفضَ طلب انضمامك إلى هذه المكالمة. + رُفِضَ طلب انضمامك إلى هذه المكالمة. - إزالة من المكالمة + تمَّت الإزالة من المكالمة قام شخص ما بإزالتك من المكالمة. @@ -2490,11 +2490,11 @@ بدء المكالمة الانضمام للمكالمة المكالمة ممتلئة - وصل عدد المشاركين إلى %1$d مشاركا، وهو الحد الأقصى في هذه المكالمة. يُرجى المحاولة مرة أخرى لاحقا. - الفيديو الخاص بك غير مشغل + وصل عدد المشاركين إلى %1$d مشاركًا، وهو الحد الأقصى في هذه المكالمة. يُرجى المحاولة مرّة أخرى لاحقًا. + توقَّف الفيديو لديك إعادة الاتصال جارية… - قيد الانضمام… - غير متّصل + يجري الانضمام… + توقَّف الاتصال رابط مكالمة سيجنال @@ -2504,49 +2504,49 @@ في انتظار السماح بالانضمام… - سَيتصل سيجنال بـ%1$s + سَيتصل سيجنال بـ %1$s سَيتصل سيجنال بـ %1$s و%2$s - سوف يرن سيجنال على %1$s و%2$s و%3$d - سوف يرن سيجنال على %1$s و%2$s و%3$d آخر - سوف يرن سيجنال على %1$s و%2$s و%3$d آخريْن - سوف يرن سيجنال على %1$s و%2$s و%3$d آخرين - سوف يرن سيجنال على %1$s و%2$s و%3$d آخرين + سَيتصل سيجنال بـ %1$s و%2$s و%3$d + سَيتصل سيجنال بـ %1$s و%2$s و%3$d آخر + سَيتصل سيجنال بـ %1$s و%2$s و%3$d آخريْن + سَيتصل سيجنال بـ %1$s و%2$s و%3$d آخرين + سَيتصل سيجنال بـ %1$s و%2$s و%3$d آخرين سَيتصل سيجنال بـ %1$s و%2$s و%3$d آخرين - سيتم إشعار %1$s - سيتم إشعار %1$s و%2$s + سيتمُّ إشعار %1$s + سيتمُّ إشعار %1$s و%2$s - سيتم إشعار %1$s و%2$s. (%3$d) - سيتم إشعار %1$s و%2$s و%3$d آخر - سيتم إشعار %1$s و%2$s و%3$d آخريْن - سيتم إشعار %1$s و%2$s و%3$d آخرين - سيتم إشعار %1$s و%2$s و%3$d آخرين - سيتم إشعار %1$s و%2$s و%3$d آخرين + سيتمُّ إشعار %1$s و%2$s. (%3$d) + سيتمُّ إشعار %1$s و%2$s و%3$d آخر + سيتمُّ إشعار %1$s و%2$s و%3$d آخريْن + سيتمُّ إشعار %1$s و%2$s و%3$d آخرين + سيتمُّ إشعار %1$s و%2$s و%3$d آخرين + سيتمُّ إشعار %1$s و%2$s و%3$d آخرين - الإتصال بـ %1$s - الإتصال بـ %1$s و%2$s + جارٍ الاتصال بـ %1$s + جارٍ الاتصال بـ %1$s و%2$s - يرن على %1$s و%2$s. (%3$d) - يرن على %1$sو %2$s و%3$d آخر - يرن على %1$s و%2$s و%3$d آخريْن - يرن على %1$s و%2$s و%3$d آخرين - يرن على %1$s و%2$s و%3$d آخرين - الإتصال بـ %1$s و%2$s و%3$d آخرين + جارٍ الاتصال بـ %1$s و%2$s و(%3$d) + جارٍ الاتصال بـ %1$sو %2$s و%3$d آخر + جارٍ الاتصال بـ %1$s و%2$s و%3$d آخريْن + جارٍ الاتصال بـ %1$s و%2$s و%3$d آخرين + جارٍ الاتصال بـ %1$s و%2$s و%3$d آخرين + جارٍ الاتصال بـ %1$s و%2$s و%3$d آخرين - يتصل بك %1$s + %1$s يتصل بك %1$s يتصل بك وبـ %2$s - %1$s يتصل بك وبـ%2$s وبـ %3$s + %1$s يتصل بك وبـ %2$s وبـ %3$s - إن %1$s يتصل بك وبـ%2$s وبـ %3$s. (%4$d) - إن %1$s يتصل بك وبـ%2$s وبـ %3$s ويـ %4$d آخر - إن %1$s يتصل بك وبـ%2$s وبـ %3$s و بـ %4$d آخريْن - إن %1$s يتصل بك وبـ%2$s وبـ %3$s وبـ %4$d آخرين - إن %1$s يتصل بك وبـ%2$s وبـ %3$s وبـ %4$d آخرين - %1$s يتصل بك وبـ%2$s وبـ %3$s وبـ %4$d آخرين + %1$s يتصل بك وبـ %2$s وبـ %3$s و(%4$d) آخرين + %1$s يتصل بك وبـ %2$s وبـ %3$s وبـ %4$d آخرين + %1$s يتصل بك وبـ %2$s وبـ %3$s و بـ %4$d آخريْن + %1$s يتصل بك وبـ %2$s وبـ %3$s وبـ %4$d آخرين + %1$s يتصل بك وبـ %2$s وبـ %3$s وبـ %4$d آخرين + %1$s يتصل بك وبـ %2$s وبـ %3$s وبـ %4$d آخرين لا يوجد أحد هنا @@ -2556,12 +2556,12 @@ العضوان %1$s و %2$s موجودان في هذه المكالمة - الأعضاء %1$s و %2$s و%3$d آخر موجود في هذه مكالمة جماعية - الأعضاء %1$s و %2$s و%3$d آخر موجود في هذه مكالمة جماعية - الأعضاء %1$s و %2$s و%3$d آخران موجودان في هذه مكالمة جماعية - الأعضاء %1$s و%2$s و%3$d آخرون موجودون في هذه مكالمة جماعية - الأعضاء %1$s و%2$s و%3$d آخرون موجودون في هذه مكالمة جماعية - الأعضاء %1$s و %2$s و%3$d آخرون موجودون في هذه مكالمة جماعية + الأعضاء %1$s و%2$s و%3$d آخر موجود في هذه المكالمة + الأعضاء %1$s و %2$s و%3$d آخر موجود في هذه المكالمة + الأعضاء %1$s و %2$s و%3$d آخران موجودان في هذه المكالمة + الأعضاء %1$s و%2$s و%3$d آخرون موجودون في هذه المكالمة + الأعضاء %1$s و%2$s و%3$d آخرون موجودون في هذه المكالمة + الأعضاء %1$s و %2$s و%3$d آخرون موجودون في هذه المكالمة @@ -2571,7 +2571,7 @@ تبديل الكاميرا - تبديل الكتم + وضع الكتم إجراءات إضافية @@ -2580,17 +2580,17 @@ تفعيل الرنين الجماعي - حدث خطأ في واجهة المستخدم. يُرجى الإبلاغ عن هذا الخطأ للمطورين. + حدث خطأ في واجهة المستخدم. يُرجى الإبلاغ عن هذا الخطأ للمطوِّرين. - تعذّر اكتشاف إدخال / إخراج صوتي مؤهل. + تعذّر اكتشاف إدخال/إخراج صوتي مُؤهَّل. - أيقونة تمثل جهاز البلوتوث. + أيقونة تُمثِّل جهاز البلوتوث. - أيقونة تمثل سماعة رأس سلكية. + أيقونة تمثِّل سماعة رأس سلكية. - أيقونة تمثل مكبر صوت. + أيقونة تمثِّل مُكبِّر صوت. - أيقونة تمثل سماعة الجهاز. + أيقونة تمثِّل سماعة الجهاز. رفع اليد @@ -2721,30 +2721,30 @@ - تم حظر %1$s + تمَّ حظر %1$s المزيد من المعلومات - كلاكما لن يتلقى صوت الآخر ولا صورته. - لا يمكن تلقي الصوت والصورة من %1$s - لا يمكن تلقي الصوت والصورة من %1$s - ربما يكون ذلك بسبب عدم تحقّق الشخص من تغيير رقم أمانك أو بسبب وجود مشكلة في جهازه أو قيامه بحظرك. + كلاكما لن يتلقَّى الصوت أو الفيديو من الآخر. + لا يمكن تلقِّي الصوت والفيديو من %1$s + لا يمكن تلقِّي الصوت والفيديو من %1$s + ربما يكون ذلك بسبب عدم تحقُّق الشخص من تغيير رقم أمانك أو بسبب وجود مشكلة في جهازه أو بسبب قيامه بحظرك. - سحب لعرض الشاشة المُشارَكة + اسحب لعرض مشاركة الشاشة - الخادم الوكيل - عنوان الوكيل - ألديك رغبة في استخدام عنوان الوكيل هذا ؟ - استخدم الوكيل - نجح في الاتصال بالوكيل. + خادم الوكيل + عنوان البروكسي + ألديك رغبة في استخدام عنوان البروكسي هذا؟ + استخدم البروكسي + تمَّ الاتصال بنجاح بالبروكسي. فشل الإرسال - إتمام التَحَقُق + إتمام التحقُّق - يجب عليك اختيار بلدك - يجب عليك تحديد رمز دولتك + يجب عليك تحديد بلدك + يجب عليك تحديد رمز دولتك. يُرجى إدخال رقم هاتف صحيح للتسجيل. الرقم غير صحيح @@ -2757,30 +2757,30 @@ التحقق الإضافي ضروري سيُرسل رمز تحقق إلى هذا الرقم. قد يتم تطبيق أسعار الناقل. - ستأتيك مكالمة هاتفية للتحقق من هذا الرقم. + ستأتيك مكالمة هاتفية للتحقُّق من هذا الرقم. تعديل الرقم خدمات Google Play غير موجودة - خدمات Google Play غير موجودة في هذا الجهاز. لا زال بإمكانك استخدام سيجنال، ولكن هذا الضبط قد يؤدي إلى ضعف في موثوقية أو أداء البرنامج ان لم تكن مستخدما متقدما، أو لم تكن مشغلا ل Android ROM من سوق ثانوية، أو تعتقد أن رؤيتك لهذه الرسالة ناجم عن خطأ، يرجى التواصل مع support@signal.org للمساعدة فى اكتشاف المشكلة واصلاحها. + خدمات Google Play غير موجودة في هذا الجهاز. لا زال بإمكانك استخدام سيجنال، ولكن هذا الضبط قد يؤدي إلى ضعف في الموثوقية أو الأداء. إن لم تكن مستخدمًا مُتقدمًا، أو لم تكن مُشغِّلًا لـ Android ROM من جهة خارجية، أو تعتقد أن رؤيتك لهذه الرسالة ناجمة عن خطأ، يُرجى التواصل مع support@signal.org للمساعدة في اكتشاف الأخطاء وإصلاحها. فهمت - خطأ في خدمات Play - جاري تحديث خدمات Google Play أو أنها غير موجودة مؤقتاً. يرجى المحاولة مجدداً. + خطأ في خدمات Google Play + جارٍ تحديث خدمات Google Play أو أنها غير موجودة مؤقتًا. يُرجى المحاولة مرّة أخرى. الشروط وسياسة الخصوصية - يحتاج سيجنال إلى أذونات جهات الاتصال والوسائط للمساعدة في التواصل مع أصدقائك وإرسال الرسائل. يتم تحميل جهات اتصالك باستخدام نظام اكتشاف جهة الاتصال الخاص في سيجنال، مما يعني أنها مشفرة من الطرفين وغير مرئية على الإطلاق لخدمة سيجنال. - يحتاج سيجنال إلى أذونات جهات الاتصال للمساعدة في التواصل مع أصدقائك. يتم تحميل جهات اتصالك باستخدام نظام اكتشاف جهة الاتصال الخاص في سيجنال، مما يعني أنها مشفرة من الطرفين وغير مرئية على الإطلاق لخدمة سيجنال. - لقد قمت بمحاولات كثيرة للتسجيل بهذا الرقم. الرجاء المحاولة لاحقاً. + يحتاج سيجنال إلى أذونات جهات الاتصال والوسائط للمساعدة في التواصل مع أصدقائك وإرسال الرسائل. يتم تحميل جهات اتصالك باستخدام نظام اكتشاف جهة الاتصال الخاص في سيجنال، وهذا يعني أنها مُشفَّرة من الطرفين وغير مرئية على الإطلاق لخدمة سيجنال. + يحتاج سيجنال إلى أذونات جهات الاتصال للمساعدة في التواصل مع أصدقائك. يتم تحميل جهات اتصالك باستخدام نظام اكتشاف جهة الاتصال الخاص في سيجنال، وهذا يعني أنها مُشفَّرة من الطرفين وغير مرئية على الإطلاق لخدمة سيجنال. + قمتَ بمحاولات كثيرة للتسجيل بهذا الرقم. يُرجى المحاولة لاحقًا. لقد قمت بمحاولات كثيرة للتسجيل بهذا الرقم. يُرجى المحاولة لاحقاً %1$s. - لا يمكن الاتصال بالخدمة. الرجاء التأكد من الإتصال بالشبكة والمحاولة مرة أخرى. + لا يمكن الاتصال بالخدمة. يُرجى التأكُّد من الاتصال بالشبكة والمحاولة مرّة أخرى. تعذّر إرسال رمز التحقق عبر رسالة SMS. حاول الحصول على رمزك عبر مكالمة نصية بدلاً من ذلك. تعذّر طلب رمز تحقق. يُرجى التحقّق من اتصالك بالانترنت ثم حاول مرة أُخرى. - نسق العدد غير المعياري + تنسيق العدد غير قياسي - ‫يظهر أن العدد الذي قمت بإدخاله (%1$s) ليس بالنسق المعياري.\n\nأ قصدك هو %2$s ؟ - سيجنال Android - نسق رقم الهاتف + ‫يظهر أن العدد الذي قمتَ بإدخاله (%1$s) ليس بتنسيق قياسي.\n\nهل تقصد هو %2$s؟ + سيجنال أندرويد - تنسيق رقم الهاتف - طُلبَت مكالمة + طُلِبَت مكالمة تم طلب رسالة قصيرة @@ -2810,7 +2810,7 @@ رقم الهاتف رمز البلد اتصال - رمز التحقق + رمز التحقُّق أعِد إرسال الرمز هل تواجه مشكلة في التسجيل؟ @@ -3715,8 +3715,6 @@ مُتفرّغ للدردشة أنا في استراحة أعمل حاليا على شيء جديد - - رمز واحد أو أكثر غير صالح. تعديل المجموعة @@ -6529,7 +6527,7 @@ %1$d جواب - Story no longer hidden + لم تعد القصة مخفية إضافة @@ -7928,11 +7926,11 @@ اكتملَت عملية الاستعادة. - Waiting for Wi-Fi… + في انتظار شبكة الواي فاي… - No internet… + لا يوجد اتصال بالإنترنت… - Device has low battery + بطارية الجهاز منخفضة %1$s من %2$s @@ -8000,11 +7998,11 @@ - %1$s/month, renews %2$s + %1$s/شهريًا، التجديد في %2$s - Last backup %1$s + أحدث نسخة احتياطية %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + نسخ احتياطية تلقائية مع خدمة سيجنال للتخزين المُشفَّر من طرف إلى طرف. إعداد @@ -8022,9 +8020,9 @@ إجراء النسخ الاحتياطي باستخدام البيانات الخلوية - View backup key + عرض مفتاح النسخ الاحتياطي - Unlock to view backup key + افتح القفل لعرض مفتاح النسخ الاحتياطي إيقاف وحذف النسخ الاحتياطية @@ -8038,25 +8036,25 @@ سيتم إنشاء نسخة احتياطية خلال الليل. - Backup plan + خطة النسخ الاحتياطي تعطيل النُسخ الاحتياطية - %1$s/شهر + %1$s/شهريًا - Your backup plan is free + خطة النسخ الاحتياطي الخاصة بك مجانية - تجديد %1$s + التجديد في %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + انسخ سجل رسائلك كي لا تفقد بياناتك أبدًا عندما تستخدم هاتفًا جديدًا أو تعيد تثبيت سيجنال. - Other ways to backup + طرق أخرى للنسخ الاحتياطي - On-device backups + النسخ الاحتياطية على الجهاز - Save your backups to a folder on this device + احفظ نسخك الاحتياطية في مجلد على هذا الجهاز - Manage or cancel + إدارة أو إلغاء الترقية diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index a276548527..6de712b9d6 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -3319,8 +3319,6 @@ Çat üçün açıqdır Dincəlir Yeni bir şeylər üzərində işləyir - - Bir və ya daha çox simvol səhvdir. Qrupa düzəliş et @@ -6001,7 +5999,7 @@ %1$d cavab - Story no longer hidden + Hekayə artıq gizli deyil Əlavə et @@ -7296,11 +7294,11 @@ Geri yükləmə tamamlandı - Waiting for Wi-Fi… + Wi-Fi-ın qoşulması gözlənilir… - No internet… + İnternet yoxdur… - Device has low battery + Cihazın batareyası zəifdir %1$s /%2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/aylıq, %2$s tarixində yenilənir - Last backup %1$s + Son nüsxəçıxarma: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal-ın təhlükəsiz tam şəkildə şifrələnmiş saxlama xidməti ilə avtomatik çıxarılan ehtiyat nüsxələr. - Set up + Quraşdır @@ -7390,9 +7388,9 @@ Mobil cihazdan istifadə edərək ehtiyat nüsxə çıxarma - View backup key + Ehtiyat nüsxə paroluna bax - Unlock to view backup key + Ehtiyat nüsxə paroluna baxmaq üçün kilidi açın Ehtiyat nüsxəni söndür və sil @@ -7406,25 +7404,25 @@ Ehtiyat nüsxə gecə çıxarılacaq. - Backup plan + Ehtiyat nüsxə planı Ehtiyat nüsxələr qeyri-aktivdir %1$s/ay - Your backup plan is free + Ehtiyat nüsxə planınız pulsuzdur %1$s yenilənir - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Yeni telefon aldığınız və ya Signal-ı təkrar quraşdırdığınız zaman məlumatları itirməmək üçün mesaj tarixçəsinin ehtiyat nüsxəsini çıxarın. - Other ways to backup + Ehtiyat nüsxəçıxarmanın digər yolları - On-device backups + Cihaz daxili ehtiyat nüsxələr - Save your backups to a folder on this device + Ehtiyat nüsxələrinizi bu cihazdakı bir qovluqda saxlayın - Manage or cancel + İdarə et və ya ləğv et Yüksəlt diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index ce4e66ddb4..15f4874fef 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -3319,8 +3319,6 @@ На линия за чат В почивка Работи по нещо ново - - Един или повече знаци са невалидни. Промяна на групата @@ -6001,7 +5999,7 @@ %1$d отговора - Story no longer hidden + Историята вече не е скрита Добави @@ -7296,11 +7294,11 @@ Възстановяването е завършено - Waiting for Wi-Fi… + Изчакване на Wi-Fi… - No internet… + Няма интернет… - Device has low battery + Батерията на устройството е ниска %1$s от %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/месец, подновява се на %2$s - Last backup %1$s + Последно резервно копие %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Автоматични резервни копия, криптирани от край до край със защитената услуга за съхранение на Signal. - Set up + Настройване @@ -7390,9 +7388,9 @@ Резервно копиране с мобилна мрежа - View backup key + Преглед на резервния ключ - Unlock to view backup key + Отключете, за да видите резервния ключ Изключване и изтриване на резервното копие @@ -7406,27 +7404,27 @@ Резервното копие ще бъде създадено през нощта. - Backup plan + План за резервно копиране Резервните копия са деактивирани %1$s/месец - Your backup plan is free + Вашият резервен план е безплатен Подновява се на %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Създайте резервно копие на историята на съобщенията си, за да не изгубите данните, когато си вземете нов телефон или преинсталирате Signal. - Other ways to backup + Други начини за резервно копиране - On-device backups + Резервни копия на устройството - Save your backups to a folder on this device + Запазете резервните си копия в папка на това устройство - Manage or cancel + Управление или отмяна - Обновяване + Надграждане %1$d/%2$d diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 572c7d9a84..82ee7961a5 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -3319,8 +3319,6 @@ ফ্রি-তে চ্যাট করুন একটি বিরতি নিচ্ছে নতুন কিছুতে কাজ করছে - - এক বা একাধিক ক্যারেক্টার অকার্যকর। গ্রুপ সংশোধন করুন @@ -6001,7 +5999,7 @@ %1$d উত্তর - Story no longer hidden + স্টোরি আর লুকানো নেই যোগ করুন @@ -7296,11 +7294,11 @@ পুনরুদ্ধার সম্পূর্ণ - Waiting for Wi-Fi… + Wi-Fi-এর জন্য অপেক্ষা করা হচ্ছে… - No internet… + ইন্টারনেট সংযোগ নেই… - Device has low battery + ডিভাইসের ব্যাটারির চার্জ কম %1$s এর %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/মাস, নবায়ন %2$s - Last backup %1$s + সর্বশেষ ব্যাকআপ %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal-এর সুরক্ষিত এন্ড-টু-এন্ড এনক্রিপ্ট করা স্টোরেজ পরিষেবা সহ স্বয়ংক্রিয় ব্যাকআপ। সেট আপ করুন @@ -7390,9 +7388,9 @@ সেলুলার ব্যবহার করে ব্যাকআপ করুন - View backup key + ব্যাকআপ কি দেখুন - Unlock to view backup key + ব্যাকআপ কি দেখতে আনলক করুন বন্ধ করুন এবং ব্যাকআপ মুছুন @@ -7406,25 +7404,25 @@ রাতের বেলা ব্যাকআপ তৈরি করা হবে। - Backup plan + ব্যাকআপ প্ল্যান ব্যাকআপ নিষ্ক্রিয় করা হয়েছে %1$s/মাস - Your backup plan is free + আপনার ব্যাকআপ প্ল্যান ফ্রি %1$s নবায়ন করছে - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + আপনার মেসেজের ইতিহাসের ব্যাকআপ নিন, যাতে আপনি যখন কোনো নতুন ফোন ব্যবহার করা শুরু করবেন বা Signal পুনরায় ইনস্টল করবেন তখন আপনি কখনোই ডেটা হারাবেন না। - Other ways to backup + ব্যাকআপ রাখার অন্যান্য উপায় - On-device backups + ডিভাইসের মধ্যেই ব্যাকআপ - Save your backups to a folder on this device + এই ডিভাইসের একটি ফোল্ডারে আপনার ব্যাকআপগুলো সংরক্ষণ করুন - Manage or cancel + নিয়ন্ত্রণ বা বাতিল করুন আপগ্রেড করুন diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 5fb60c83e7..16d0048619 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -3517,8 +3517,6 @@ Dostupni za chat Na pauzi Radim na nečemu novom - - Jedan ili više znakova je nevažeći. Uredi grupu @@ -6265,7 +6263,7 @@ %1$d odgovora - Story no longer hidden + Priča više nije sakrivena Dodaj @@ -7612,11 +7610,11 @@ Vraćanje podataka je završeno - Waiting for Wi-Fi… + Čeka se Wi-Fi… - No internet… + Nema interneta… - Device has low battery + Baterija uređaja je slaba %1$s od %2$s @@ -7684,13 +7682,13 @@ - %1$s/month, renews %2$s + %1$s mjesečno, obnavlja se %2$s - Last backup %1$s + Zadnja kreirana kopija: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatsko kreiranje rezervnih kopija sa Signalovim sigurnom sveobuhvatnom uslugom pohranjivanja. - Set up + Postavi @@ -7706,9 +7704,9 @@ Kreirajte sigurnosnu kopiju pomoću mobilne mreže - View backup key + Pogledajte rezervni ključ - Unlock to view backup key + Otključajte da vidite rezervni ključ Isključiti i izbrisati sigurnosne kopije? @@ -7722,25 +7720,25 @@ Sigurnosna kopija će biti kreirana tokom noći. - Backup plan + Paket rezervne kopije Rezervne kopije su onemogućene. %1$s/mjesečno - Your backup plan is free + Vaš paket rezervne kopije je besplatan Obnavlja se %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Kreirajte sigurnosnu kopiju historije poruka da nikada ne izgubite podatke kada nabavite novi telefon ili ponovo instalirate Signal. - Other ways to backup + Drugi načini za kreiranje rezervnih kopija - On-device backups + Sigurnosne kopije na uređaju - Save your backups to a folder on this device + Sačuvajte sigurnosne kopije u folderu na ovom uređaju - Manage or cancel + Upravljajte ili otkažite Upgrade diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 3027c89e19..7f3855e742 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -3319,8 +3319,6 @@ Lliure per xatejar-hi Faig un descans. Treballo en una cosa nova. - - Un o més caràcters no son vàlids. Edita el grup diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index df02dce1a6..9279cf0cbe 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -3517,8 +3517,6 @@ Rád/a pokecám Dávám si pauzu Pracuji na něčem novém - - Jeden nebo více znaků je neplatných. Editovat skupinu @@ -6265,7 +6263,7 @@ %1$d odpovědí - Story no longer hidden + Příběh již není skrytý Přidat @@ -7607,16 +7605,16 @@ Obnovování médií - Obnovení pozastaveno + Obnovování pozastaveno Obnova dokončena - Waiting for Wi-Fi… + Čekání na Wi-Fi… - No internet… + Bez připojení k internetu… - Device has low battery + Zařízení má téměř vybitou baterii %1$s z %2$s @@ -7684,11 +7682,11 @@ - %1$s/month, renews %2$s + %1$s / měsíc, obnovení %2$s - Last backup %1$s + Poslední záloha %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatické zálohování pomocí zabezpečené end-to-end šifrované služby úložiště aplikace Signal. Nastavit @@ -7706,9 +7704,9 @@ Zálohování pomocí mobilní sítě - View backup key + Zobrazit klíč zálohy - Unlock to view backup key + Odemknout pro zobrazení klíče zálohy Vypnout a odstranit zálohování @@ -7722,25 +7720,25 @@ Záloha bude vytvořena přes noc. - Backup plan + Plán zálohování Zálohování zakázáno %1$s / měsíčně - Your backup plan is free + Váš plán zálohování je zdarma Obnovení %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Zálohujte si historii zpráv. Už nikdy nepřijdete o data, když si pořídíte nový telefon nebo přeinstalujete aplikaci Signal. - Other ways to backup + Další způsoby zálohování - On-device backups + Zálohování přímo v zařízení - Save your backups to a folder on this device + Ukládání záloh do složky v tomto zařízení - Manage or cancel + Spravovat nebo zrušit Aktualizovat diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 2d7f22f971..a9b737cda7 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -3319,8 +3319,6 @@ Klar til en chat Tager en pause Arbejder på noget nyt - - Et eller flere tegn er ugyldige. Redigér gruppe diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5140a03861..0a78deb6af 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -3319,8 +3319,6 @@ Frei für Chats Mache eine Pause Arbeite an etwas Neuem - - Ein oder mehrere Zeichen sind ungültig. Gruppe bearbeiten diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 1f90b298a2..9432e41bff 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -3319,8 +3319,6 @@ Ελάτε να συζητήσουμε Κάνω διάλειμμα Ετοιμάζω κάτι καινούργιο - - Ένας ή περισσότεροι χαρακτήρες δεν είναι έγκυροι. Επεξεργασία ομάδας @@ -6001,7 +5999,7 @@ %1$d απαντήσεις - Story no longer hidden + Η ιστορία δεν είναι πλέον κρυφή Προσθήκη @@ -7296,11 +7294,11 @@ Η ανάκτηση ολοκληρώθηκε - Waiting for Wi-Fi… + Αναμονή για Wi-Fi… - No internet… + Χωρίς διαδίκτυο… - Device has low battery + Η συσκευή έχει χαμηλή μπαταρία %1$s από %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/μήνα, ανανεώνεται %2$s - Last backup %1$s + Τελευταίο αντίγραφο ασφαλείας %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Τα αυτόματα αντίγραφα ασφαλείας του Signal διασφαλίζουν υπηρεσία κρυπτογραφημένης αποθήκευσης από άκρο σε άκρο. Ρύθμιση @@ -7390,9 +7388,9 @@ Αντίγραφο ασφαλείας με δεδομένα κινητής - View backup key + Προβολή εφεδρικού κλειδιού - Unlock to view backup key + Ξεκλείδωσε για να δεις το εφεδρικό κλειδί Απενεργοποίηση και διαγραφή αντιγράφων ασφαλείας @@ -7406,25 +7404,25 @@ Το αντίγραφο ασφαλείας θα δημιουργηθεί κατά τη διάρκεια της νύχτας. - Backup plan + Πρόγραμμα αντιγράφων ασφαλείας Τα αντίγραφα ασφαλείας απενεργοποιήθηκαν %1$s/μήνα - Your backup plan is free + Το πρόγραμμα αντιγράφων σαφαλείας σου είναι δωρεάν Ανανεώνεται %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Δημιούργησε αντίγραφα ασφαλείας του ιστορικού μηνυμάτων σου, ώστε να μην χάνεις ποτέ δεδομένα αν αγοράσεις νέο τηλέφωνο ή επανεγκαταστήσεις το Signal. - Other ways to backup + Άλλοι τρόποι δημιουργίας αντιγράφων ασφαλείας - On-device backups + Αντίγραφα ασφαλείας στη συσκευή - Save your backups to a folder on this device + Αποθήκευσε τα αντίγραφα ασφαλείας σου σε έναν φάκελο σε αυτήν τη συσκευή - Manage or cancel + Διαχείριση ή ακύρωση Αναβάθμιση diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 8c23c04990..d438364b89 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1591,7 +1591,7 @@ Comenzar Nuevo grupo Invitar personas - Colores del chat + Colores Foto de perfil @@ -3319,8 +3319,6 @@ Libre para chatear Tomándome un descanso Trabajando en cosas nuevas - - Uno o más caracteres no son válidos. Editar grupo @@ -4376,7 +4374,7 @@ Usa los ajustes de bloqueo de tu dispositivo Android para desbloquear Signal. - El bloqueo de pantalla está activado y Signal está protegido con los ajustes de bloqueo de tu dispositivo. Desbloquea Signal del mismo modo que desbloqueas normalmente tu teléfono, ya sea con el reconocimiento facial, el desbloqueo por huella dactilar, un PIN, una contraseña o un patrón. + El bloqueo de pantalla está activado y Signal está protegido con los ajustes de bloqueo de tu dispositivo. Desbloquea Signal del mismo modo que desbloqueas normalmente tu teléfono, ya sea mediante reconocimiento facial, huella dactilar, PIN, contraseña o patrón. Contactar con asistencia @@ -4437,13 +4435,13 @@ Restaura tus mensajes desde una copia de seguridad local. Si no los restauras ahora, no podrás hacerlo más tarde. Restaurar copia de seguridad local Restaurar copia de seguridad de Signal - Restaurar todos tus mensajes de texto y tus archivos multimedia de los últimos 30 días + Restaura todos tus mensajes de texto y tus archivos multimedia de los últimos 30 días Más opciones Cancelar Iniciar sesión sin transferir - Continuar sin transferir tus mensajes ni archivos multimedia + Continúa sin transferir tus mensajes ni archivos multimedia Abre Signal en tu dispositivo Android anterior @@ -4497,7 +4495,7 @@ Wi-Fi En la pantalla de Wi-Fi Direct, elimina todos los grupos guardados y desvincula cualquier dispositivo invitado o conectado. Pantalla Wi-Fi Direct - Desactiva y vuelve a activar Wi-Fi en ambos dispositivos. + Desactiva y vuelve a activar la conexión Wi-Fi en ambos dispositivos. Asegúrate de que ambos dispositivos estén en modo de transferencia. Ir a la página de asistencia Reintentar @@ -4743,7 +4741,7 @@ Color del chat - Restablecer colores del chat + Restablecer colores de los chats Restablecer color del chat ¿Restablecer color del chat? Elegir fondo de pantalla @@ -4770,22 +4768,22 @@ Elegir fondo de pantalla Deslizar para ver más fondos de pantalla Elige un fondo para todos los chats. - Elegir fondo para %1$s + Elige un fondo para \"%1$s\". Signal necesita acceso al almacenamiento para mostrar la galería. Pellizca la imagen para ampliarla y arrástrala para ajustar. - Elegir fondo para todos los chats. - Elegir fondo para %1$s. + Elige un fondo para todos los chats. + Elige un fondo para \"%1$s\". No se ha podido establecer el fondo de pantalla. Difuminar foto Acerca de MobileCoin MobileCoin es una nueva moneda digital enfocada en la privacidad. - Añadir fondos + Añadiendo fondos Puedes añadir fondos para usar en Signal enviando MobileCoin a la dirección de tu cartera. Retirar fondos Puedes transferir tus fondos en cualquier momento a una plataforma de intercambio que acepte MobileCoin. Tan solo tienes que hacer una transferencia a tu cuenta en esa plataforma. @@ -4797,7 +4795,7 @@ Guardar tu frase Actualiza tu PIN - Si tienes un saldo elevado, te recomendamos cambiar tu PIN por un PIN alfanumérico que añada protección extra a tu cuenta. + Si tienes un saldo elevado, te recomendamos cambiar tu PIN de Signal a un PIN alfanumérico que añada protección extra a tu cuenta. Actualizar PIN @@ -4830,7 +4828,7 @@ ¡Tienes saldo! Es el momento de guardar tu frase de recuperación, una clave de 24 palabras que puedes usar para restaurar tu saldo. Es el momento de guardar tu frase de recuperación, una clave de 24 palabras que puedes usar para restaurar tu saldo. - Tu frase de recuperación es una clave de %1$d palabras que es única para ti. Úsala para restaurar tu saldo. + Tu frase de recuperación es una clave de %1$d palabras que es única para ti. Úsala para restaurar tu saldo. Comenzar Introducir manualmente Pegar del portapapeles @@ -4848,7 +4846,7 @@ Frase de recuperación Siguiente Frase de recuperación no válida - Comprueba que hayas introducido %1$d palabras e inténtalo de nuevo. + Comprueba que hayas introducido %1$d palabras e inténtalo de nuevo. @@ -4857,7 +4855,7 @@ Siguiente Editar Tu frase de recuperación - Anota las siguientes %1$d palabras en orden. Guárdalas en un lugar seguro. + Anota las siguientes %1$d palabras en orden. Guárdalas en un lugar seguro. Comprueba que hayas introducido la frase de recuperación correctamente. No hagas una captura de pantalla ni la envíes por correo electrónico. Cuenta de pagos restaurada. @@ -4871,7 +4869,7 @@ Confirmar frase de recuperación Introduce las siguientes palabras de tu frase de recuperación. Palabra n.° %1$d - Mostrar la frase otra vez + Ver la frase otra vez Listo Frase de recuperación confirmada @@ -6001,7 +5999,7 @@ %1$d respuestas - Story no longer hidden + La historia ya no está oculta Añadir @@ -7296,11 +7294,11 @@ Restauración completada - Waiting for Wi-Fi… + Esperando red Wi-Fi… - No internet… + Sin conexión a Internet - Device has low battery + El dispositivo tiene poca batería %1$s de %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/mes · Se renueva el %2$s - Last backup %1$s + Última copia de seguridad %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Copias de seguridad automáticas con el servicio de almacenamiento seguro y cifrado de extremo a extremo de Signal. Configurar @@ -7390,9 +7388,9 @@ Copia de seguridad con conexión móvil - View backup key + Ver clave de copia de seguridad - Unlock to view backup key + Desbloquear para ver clave de copia de seguridad Desactivar y eliminar copias de seguridad @@ -7406,25 +7404,25 @@ La copia de seguridad se creará durante la noche. - Backup plan + Plan de copias de seguridad Copias de seguridad desactivadas %1$s/mes - Your backup plan is free + Tu plan de copias de seguridad es gratis Se renueva el %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Haz una copia de seguridad de tu historial de mensajes para no perder ningún dato cuando compras un teléfono nuevo o reinstalas Signal. - Other ways to backup + Otras formas de hacer copias de seguridad - On-device backups + Copias de seguridad en el dispositivo - Save your backups to a folder on this device + Guarda tus copias de seguridad en una carpeta de este dispositivo - Manage or cancel + Gestionar o cancelar Actualizar diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 0ab07d61da..fd488858a1 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -3319,8 +3319,6 @@ Lobisemiseks vaba Puhkepaus Töötan millegi uue kallal - - Üks või rohkem tähemärke ei ole kasutatavad. Muuda gruppi @@ -6001,7 +5999,7 @@ %1$d vastust - Story no longer hidden + Lugu ei ole enam peidetud Lisa @@ -7296,11 +7294,11 @@ Taastamine õnnestus - Waiting for Wi-Fi… + Ootan WiFit … - No internet… + Internetti ei ole … - Device has low battery + Seadme akutase on madal %1$s / %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/kuu, uueneb %2$s - Last backup %1$s + Viimane varukoopia: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automaatsed varukoopiad Signali turvalise otspunktkrüptitud salvestusteenuse abil. - Set up + Seadista @@ -7390,9 +7388,9 @@ Varunda mobiiliside kaudu - View backup key + Vaata varukoopia võtit - Unlock to view backup key + Ava, et vaadata varukoopia võtit Lülita välja ja kustuta varukoopia @@ -7406,25 +7404,25 @@ Varukoopia luuakse öösel. - Backup plan + Varundamisplaan Varundamine on keelatud %1$s/kuu - Your backup plan is free + Sul on tasuta varundamisplaan Uueneb %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Varunda oma sõnumiajalugu, et mitte kaotada andmeid, kui telefoni välja vahetad ja Signali uuesti paigaldad. - Other ways to backup + Muud viisid varundamiseks - On-device backups + Seadmesisesed varukoopiad - Save your backups to a folder on this device + Salvesta oma varukoopiad selles seadmes olevasse kausta - Manage or cancel + Halda või tühista Uuenda diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 77456e9bbf..9151a309fd 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -3319,8 +3319,6 @@ Txateatzeko libre Atsedena hartzen Zerbait berrian lanean - - Karaktere batek edo gehiagok ez dute balio. Taldea editatu diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index d246074a10..cb58e0095f 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -3319,8 +3319,6 @@ آمادهٔ گفتگو در حال استراحت در حال کار روی یک چیز جدید - - یک یا چند نویسه نامعتبر است. ویرایش گروه @@ -6001,7 +5999,7 @@ %1$d پاسخ - Story no longer hidden + استوری دیگر پنهان نیست افزودن @@ -7296,11 +7294,11 @@ بازگردانی کامل شد - Waiting for Wi-Fi… + در انتظار Wi-Fi… - No internet… + اینترنت متصل نیست… - Device has low battery + باتری دستگاه کم است %1$s از %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/ماه، در %2$s تمدید می‌شود - Last backup %1$s + آخرین پشتیبان‌گیری %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + پشتیبان‌گیری خودکار با سرویس ذخیره‌سازی سرتاسر رمزگذاری‌شده و امن سیگنال. راه‌اندازی @@ -7390,9 +7388,9 @@ پشتیبان‌گیری با استفاده از اینترنت همراه - View backup key + مشاهده کلید پشتیبان - Unlock to view backup key + برای مشاهده کلید پشتیبان، قفل را باز کنید خاموش کردن و پاک کردن پشتیبان @@ -7406,25 +7404,25 @@ پشتیبان‌گیری در طول شب انجام می‌شود. - Backup plan + طرح پشتیبان پشتیبان‌ها غیرفعال شده‌اند %1$s در ماه - Your backup plan is free + طرح پشتیبان شما خالی است در %1$s تمدید می‌شود - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + از تاریخچه پیام خود پشتیبان‌گیری کنید تا بعد از خرید تلفن جدید یا نصب مجدد سیگنال، هرگز اطلاعات خود را از دست ندهید. - Other ways to backup + سایر روش‌های پشتیبان‌گیری - On-device backups + پشتیبان‌گیری در دستگاه - Save your backups to a folder on this device + نسخه پشتیبان خود را در پوشه‌ای در این دستگاه ذخیره کنید - Manage or cancel + مدیریت یا لغو ارتقا diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index cfc9dbeb7c..66b8bc500c 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -3319,8 +3319,6 @@ Juttutuulella Tauolla Luomassa uutta - - Yksi tai useampi merkki on virheellinen. Muokkaa ryhmää @@ -6001,7 +5999,7 @@ %1$d vastausta - Story no longer hidden + Tarina ei ole enää piilotettu Lisää @@ -7296,11 +7294,11 @@ Palautus suoritettu - Waiting for Wi-Fi… + Odotetaan Wi-Fi-yhteyttä… - No internet… + Ei internetyhteyttä… - Device has low battery + Laitteen akku on vähissä %1$s / %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s / kk, uusiutuu %2$s - Last backup %1$s + Varmuuskopioitu viimeksi: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automaattiset varmuuskopiot Signalin turvallisella, päästä päähän salatulla tallennuspalvelulla. Määritä @@ -7390,9 +7388,9 @@ Varmuuskopioi matkapuhelinverkon kautta - View backup key + Näytä varmuuskopion avain - Unlock to view backup key + Avaa lukitus nähdäksesi varmuuskopion avaimen Kytke varmuuskopio pois käytöstä ja poista se @@ -7406,25 +7404,25 @@ Varmuuskopio luodaan yön aikana. - Backup plan + Varmuuskopioinnin tilaus Varmuuskopiot poistettu käytöstä %1$s/kk - Your backup plan is free + Varmuuskopioinnin tilaus on ilmainen Uusiutuu %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Varmuuskopioi viestihistoriasi, jotta et koskaan menetä tietoja, kun hankit uuden puhelimen tai asennat Signalin uudelleen. - Other ways to backup + Muita tapoja varmuuskopioida - On-device backups + Varmuuskopiot laitteella - Save your backups to a folder on this device + Tallenna varmuuskopiot tämän laitteen kansioon - Manage or cancel + Hallinnoi tai peruuta Päivitä diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index abdfe5a4c4..70cc195df7 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -98,7 +98,7 @@ Accéder aux paramètres - Vous n’avez accordé à Signal qu’un accès limité à vos photos et vidéos. + Vous n\'avez accordé à Signal qu\'un accès limité à vos photos et vidéos. Aucune photo disponible. Sélectionnez des photos et des vidéos à afficher ici ou modifiez les autorisations. @@ -468,7 +468,7 @@ Message Signal Envoyer un message - Passons à Signal %1$s + Et si on passait à Signal ? %1$s Veuillez choisir un contact Le fichier joint dépasse la limite de taille pour le type de message que vous envoyez. Enregistrement audio impossible. @@ -537,7 +537,7 @@ Ce contact n’utilise plus Signal. Invitez-le à rejoindre la plateforme pour reprendre votre conversation. - Inviter à Signal + Inviter sur Signal Vous recevrez un rappel bientôt. @@ -1508,13 +1508,13 @@ Annuler Envoi… L’invitation a été envoyée. - Inviter à Signal + Inviter sur Signal Envoyer un texto (%1$d) Envoyer %1$d invitation par SMS ? Envoyer %1$d invitations par SMS ? - Passons à Signal : %1$s + Et si on passait à Signal ? %1$s Vous semblez n’avoir aucune appli vers laquelle partager. @@ -1860,7 +1860,7 @@ Infos de paiement indisponibles - Les informations de paiement sont indisponibles, car Signal a récupéré vos messages à partir d’un appareil qui ne contenait pas l’historique de cette transaction. Cela n’a aucune incidence sur le solde de votre compte, ni sur l’état de vos anciens paiements. + Les informations relatives à ce paiement sont indisponibles, car Signal a récupéré vos messages à partir d\'un appareil qui ne contenait pas l\'historique de cette transaction. Cela n\'a aucune incidence sur le solde de votre compte, ni sur l\'état de vos anciens paiements. @@ -2053,7 +2053,7 @@ Envoyer un code par SMS - Inscription à Signal - Besoin d’aide pour inscrire un PIN sur un appareil Android + Inscription à Signal – Besoin d\'aide : code PIN sous Android Votre PIN est un code numérique ou alphanumérique d’au moins %1$d caractères que vous avez créé.\n\nSi vous avez oublié votre PIN, vous pouvez en créer un autre. @@ -2515,13 +2515,13 @@ Ajouter aux contacts - Inviter à Signal + Inviter sur Signal Message Signal Appel Signal Ajouter aux contacts - Inviter à Signal + Inviter sur Signal Message Signal @@ -2949,7 +2949,7 @@ - Pour répondre à l’appel, accordez à Signal l’accès à votre microphone. + Pour répondre à l\'appel, autorisez Signal à accéder à votre microphone. Pour répondre à l’appel vidéo, veuillez autoriser Signal à accéder à votre micro et votre appareil photo. Pour passer et recevoir des appels, Signal doit accéder au microphone et à l\'appareil photo, mais vous lui en avez interdit l\'accès. Ouvrez l\'application Paramètres de votre appareil, puis touchez Applications > Signal > Autorisations et activez les options \"Microphone\" et \"Appareil photo\". @@ -2997,7 +2997,7 @@ Répéter la nouvelle phrase de passe - Inviter à Signal + Inviter sur Signal Nouveau groupe Actualiser la liste des contacts @@ -3319,8 +3319,6 @@ Prêt à converser Je prends une pause Je travaille sur quelque chose de nouveau - - Au moins un des caractères n\'est pas valide. Modifier le groupe @@ -3695,7 +3693,7 @@ Désactivé Activé Adresse du proxy - N’utilisez un proxy que s’il vous est impossible de vous connecter à Signal via les données mobiles ou via le Wi-Fi. + N\'utilisez un proxy que s\'il vous est impossible de vous connecter à Signal via les données mobiles ou le Wi-Fi. Partager Enregistrer Connexion au proxy… @@ -3765,7 +3763,7 @@ Détails - Vous pouvez désormais utiliser Signal pour envoyer et recevoir des MobileCoin. Tous les paiements sont soumis aux conditions d’utilisation de MobileCoins et de MobileCoin Wallet. Des problèmes pourraient survenir. Les paiements et les soldes que vous pourriez perdre sont irrécupérables. + Vous pouvez utiliser Signal pour envoyer et recevoir des MobileCoins. Tous les paiements sont soumis aux conditions d\'utilisation de MobileCoin et de MobileCoin Wallet. Avertissement : des problèmes peuvent se produire et entraîner la perte de paiements et de soldes. De telles pertes sont irrécupérables. Activer Afficher les CGU de MobileCoin Les paiements ne sont plus proposés dans Signal. Vous pouvez encore transférer des fonds vers une plateforme de change, mais vous ne pouvez plus ni envoyer, ni recevoir de paiements, ni ajouter de fonds. @@ -3848,10 +3846,10 @@ Ajouter des fonds - L’adresse de votre portefeuille + Adresse de votre portefeuille Copier A été copié dans le presse-papiers - Pour ajouter des fonds, envoyez des MobileCoins à l’adresse de votre porte-monnaie. Lancez une transaction de votre compte vers une plateforme de change qui accepte les MobileCoins, puis lisez le code QR ou copiez l’adresse de votre porte-monnaie. + Pour ajouter des fonds, envoyez des MobileCoins à l\'adresse de votre portefeuille. Démarrez une transaction depuis votre compte sur une plateforme de change qui accepte les MobileCoins, puis scannez le code QR ou copiez l\'adresse de votre portefeuille. @@ -3883,12 +3881,12 @@ Transférer Lire le code QR - À : lisez ou saisissez l’adresse du portefeuille - Vous pouvez transférer des MobileCoins en finalisant un transfert vers l’adresse de porte-monnaie indiquée par la plateforme de change. L’adresse de porte-monnaie est une chaîne de chiffres et de lettres généralement située sous le code QR. + Destinataire : scannez ou saisissez l\'adresse du portefeuille + Pour virer des MobileCoins, effectuez un virement vers l\'adresse de portefeuille fournie par la plateforme de change. L\'adresse de portefeuille est une suite de chiffres et de lettres qui s\'affiche généralement sous le code QR. Suivant Adresse invalide - Vérifiez l’adresse de porte-monnaie vers laquelle vous tentez de faire un transfert, puis réessayez. - Vous ne pouvez pas effectuer de transferts vers l’adresse de votre propre porte-monnaie Signal. Saisissez l’adresse de porte-monnaie de votre compte sur une plateforme de change prise en charge. + Veuillez vérifier l\'adresse de portefeuille vers laquelle vous tentez d\'effectuer un virement et réessayer. + Vous ne pouvez pas effectuer de virements vers l\'adresse de votre propre portefeuille Signal. Saisissez l\'adresse de portefeuille associée à votre autre compte. Celui-ci doit être hébergé sur une plateforme de change compatible. Pour lire un code QR, Signal a besoin d’accéder à l’appareil photo. Signal a besoin de l\'autorisation Appareil photo pour scanner un code QR. Accédez à Paramètres > Autorisations et activez \"Appareil photo\". Pour lire un code QR, Signal a besoin d’accéder à l’appareil photo. @@ -4535,7 +4533,7 @@ Allez sur votre nouvel appareil - Vos données relatives à Signal ont été transférées vers votre nouvel appareil. Pour terminer le processus de transfert, vous devez poursuivre l’inscription sur votre nouvel appareil. + Vos données Signal ont été transférées vers votre nouvel appareil. Pour terminer le processus de transfert, poursuivez l\'inscription sur votre nouvel appareil. Fermer @@ -4786,7 +4784,7 @@ À propos de MobileCoin MobileCoin est une nouvelle monnaie numérique axée sur la protection des données personnelles. Ajouter des fonds - Vous pouvez ajouter des fonds à utiliser dans Signal en envoyant des MobileCoin à l’adresse de votre portefeuille. + Pour virer des fonds à utiliser dans Signal, envoyez des MobileCoins à l\'adresse de votre portefeuille. Encaisser Vous pouvez encaisser des MobileCoins n’importe quand sur une plateforme de change qui accepte les MobileCoins. Il vous suffit d’effectuer un transfert vers votre compte sur cette plateforme de change. Masquer cette carte ? @@ -4807,13 +4805,13 @@ Désactiver le portefeuille Votre solde - Il est recommandé de transférer vos fonds vers une autre adresse de portefeuille avant de désactiver les paiements. Si vous choisissez de ne pas transférer vos fonds maintenant, ils resteront dans votre portefeuille lié à Signal si vous réactivez les paiements. + Il est recommandé de transférer vos fonds vers une autre adresse de portefeuille avant de désactiver les paiements. Si vous choisissez de ne pas transférer vos fonds maintenant, ils seront conservés dans le portefeuille associé à Signal si vous réactivez les paiements. Transférer le solde restant Désactiver dans transférer Désactiver Désactiver sans transférer ? - Votre solde restera dans votre porte-monnaie lié à Signal si vous choisissez de réactiver les paiements. - Erreur lors de la désactivation du portefeuille. + Votre solde sera conservé dans le portefeuille associé à Signal si vous réactivez les paiements.. + Une erreur s\'est produite lors de la désactivation du portefeuille. @@ -5606,7 +5604,7 @@ Allez sur Google Pay Impossible de traiter le paiement de l’abonnement - Nous avons des difficultés à encaisser le paiement de votre don mensuel à Signal. Vérifiez que votre mode de paiement est à jour. Si ce n’est pas le cas, mettez-le à jour dans Google Pay. Signal essaiera de traiter à nouveau le paiement dans quelques jours. + Nous ne parvenons pas à encaisser le paiement de votre don mensuel à Signal. Votre mode de paiement est-il à jour ? Si tel n\'est pas le cas, nous vous invitons à le mettre à jour dans Google Pay. Signal réessaiera de traiter le paiement dans quelques jours. Merci ! Ne plus afficher Contacter l’assistance @@ -5919,7 +5917,7 @@ Montant - Nous vous remercions de votre soutien à Signal. Votre contribution aide à alimenter la mission de développement d’une technologie ouverte de protection de la vie privée et des données personnelles, qui protège la liberté d’expression et permet à des millions de personnes une communication sécurisée à l’échelle planétaire. Si vous êtes résident des États-Unis d\'Amérique, veuillez conserver ce reçu pour vos dossiers fiscaux. La Fondation de la technologie Signal (Signal Technology Foundation) est un organisme exempt de taxes aux États-Unis d’Amérique conformément à la section 501c du code des taxes intérieures des É.-U.. Notre ID fiscal fédéral est 82-4506840. + Merci de soutenir Signal. Votre contribution nous aide à mener à bien notre mission : développer une technologie open source axée sur la confidentialité, qui favorise la liberté d\'expression et offre des communications sécurisées à des millions de personnes du monde entier. Si vous résidez aux États-Unis, conservez ce reçu pour le communiquer aux services fiscaux. La Signal Technology Foundation est un organisme à but non lucratif, régi par le code fédéral des impôts des États-Unis. Notre numéro d\'identification fiscale est le suivant : 82-4506840. %1$s - %2$s @@ -6001,7 +5999,7 @@ %1$d réponses - Story no longer hidden + La story n\'est plus masquée. Ajouter @@ -6287,9 +6285,9 @@ Merci pour votre soutien ! - %1$s a effectué un don à Signal de votre part ! Affichez votre soutien à Signal sur votre profil. + %1$s a fait un don à Signal pour vous ! Vous pouvez afficher votre soutien à Signal sur votre profil. - Vous avez effectué un don à Signal de la part de %1$s. Il/elle aura la possibilité d\'afficher son soutien sur son profil. + Vous avez fait un don à Signal pour %1$s. Cette personne aura la possibilité d\'afficher son soutien sur son profil. Échanger @@ -6297,7 +6295,7 @@ Récupération du macaron… - Vous avez effectué un don à Signal de la part de %1$s. Il/elle aura la possibilité d\'afficher son soutien sur son profil. + Vous avez effectué un don à Signal pour %1$s. Cette personne aura la possibilité d\'afficher son soutien sur son profil. Votre macaron est arrivé à expiration @@ -7293,14 +7291,14 @@ Restauration mise en pause - La restauration est terminée. + Restauration terminée - Waiting for Wi-Fi… + En attente de Wi-Fi… - No internet… + Aucune connexion Internet… - Device has low battery + Batterie faible %1$s sur %2$s @@ -7355,7 +7353,7 @@ Cette fonctionnalité nécessite une mise à niveau - L’optimisation de l’espace de stockage n’est disponible qu’avec la version payante du forfait de sauvegarde Signal. Donnez un coup de pouce à Signal et passez à la version payante pour utiliser cette fonctionnalité. + L\'optimisation de l\'espace de stockage n\'est disponible qu\'avec la version payante du forfait de sauvegarde Signal. Donnez un coup de pouce à Signal et passez à la version payante pour utiliser cette fonctionnalité. Mettre à niveau @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/mois, prochaine échéance le %2$s - Last backup %1$s + Dernière sauvegarde : %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal sauvegarde automatiquement vos données avec son service de stockage sécurisé et chiffré de bout en bout. Configurer @@ -7384,15 +7382,15 @@ Détails de la sauvegarde - Taille de la sauvegarde + Taille de la sauvegarde : - Fréquence de sauvegarde + Fréquence de sauvegarde : Sauvegarder via les données cellulaires - View backup key + Afficher la clé de sauvegarde - Unlock to view backup key + Déverrouillez l\'appareil pour afficher la clé de sauvegarde Désactiver et supprimer la sauvegarde @@ -7406,27 +7404,27 @@ Signal créera une sauvegarde cette nuit. - Backup plan + Forfait de sauvegarde Sauvegardes désactivées %1$s/mois - Your backup plan is free + Votre forfait de sauvegarde est gratuit - Renouvellement %1$s + Prochaine échéance le %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Sauvegardez l\'historique de vos messages pour conserver toutes vos données – même si vous changez de téléphone ou réinstallez Signal. - Other ways to backup + Autres méthodes de sauvegarde - On-device backups + Sauvegardes sur l\'appareil - Save your backups to a folder on this device + Enregistrez vos sauvegardes dans un dossier sur cet appareil - Manage or cancel + Gérer ou résilier - Convertir + Mettre à niveau %1$d/%2$d diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index e359cdd30a..04434d605e 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -3616,8 +3616,6 @@ Tá le fáil chun comhrá a bheith agam Ag glacadh sosa Táim ag obair ar rud nua - - Tá carachtar amháin nó níos mó neamhbhailí. Cuir an Grúpa in Eagar diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index b2f37d2a8a..c94490ce5b 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -3319,8 +3319,6 @@ Libre para conversar Tomando un descanso Traballando en algo novo - - Un ou máis caracteres non son válidos. Editar grupo @@ -6001,7 +5999,7 @@ %1$d respostas - Story no longer hidden + A historia xa non está oculta Engadir @@ -7296,11 +7294,11 @@ Restablecemento completo - Waiting for Wi-Fi… + Esperando a wifi… - No internet… + Sen Internet… - Device has low battery + O dispositivo ten pouca batería %1$s de %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/mes, renóvase o %2$s - Last backup %1$s + Última copia %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Copias de seguranza automáticas mediante o servizo de almacenamento seguro e cifrado de extremo a extremo de Signal. - Set up + Configurar @@ -7390,9 +7388,9 @@ Realizar cos datos móbiles - View backup key + Ver clave de seguranza - Unlock to view backup key + Desbloquear para ver clave de seguranza Desactivar e eliminar a copia de seguranza @@ -7406,25 +7404,25 @@ A copia de seguranza crearase durante a noite. - Backup plan + Plan de copia de seguranza Copia de seguranza desactivada %1$s/mes - Your backup plan is free + O teu plan é gratuíto Renóvase o %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Fai unha copia de seguranza do teu historial de mensaxes para que nunca perdas datos cando cambies de teléfono ou volvas instalar Signal. - Other ways to backup + Outras formas de realizar copias de seguranza - On-device backups + Copias de seguranza no dispositivo - Save your backups to a folder on this device + Garda as túas copias de seguranza nun cartafol deste dispositivo - Manage or cancel + Xestionar ou cancelar Actualizar diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 0efe394008..d6e42b68f4 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -3319,8 +3319,6 @@ ચેટ કરવા માટે મુક્ત વિરામ લઇ રહયા છે કંઈક નવીન પર કામ કરી રહ્યા છીએ - - એક કે વધુ અક્ષરો અમાન્ય છે. ગ્રુપમાં ફેરફાર કરો @@ -6001,7 +5999,7 @@ %1$d જવાબો - Story no longer hidden + સ્ટોરી હવે છુપાવેલી નથી ઉમેરો @@ -7293,14 +7291,14 @@ રિસ્ટોર થોભાવ્યું - રિસ્ટોર પૂરુ + રિસ્ટોર પૂર્ણ થયું - Waiting for Wi-Fi… + વાઇ-ફાઇની રાહ જોઈ રહ્યાં છીએ… - No internet… + ઇન્ટરનેટ નથી… - Device has low battery + ડિવાઇસની બેટરી ઓછી છે %1$sની%2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/મહિને, %2$sના રોજ રિન્યૂ થશે - Last backup %1$s + છેલ્લું બેકઅપ %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signalની સુરક્ષિત, એન્ડ-ટૂ-એન્ડ એન્ક્રિપ્ટેડ સ્ટોરેજ સેવા સાથે ઓટોમેટિક બેકઅપ. સેટ કરો @@ -7390,9 +7388,9 @@ સેલ્યુલર ડેટાનો ઉપયોગ કરીને બેકઅપ લો - View backup key + બેકઅપ કી જુઓ - Unlock to view backup key + બેકઅપ કી જોવા માટે અનલૉક કરો બેકઅપ બંધ કરો અને ડિલીટ કરો @@ -7406,25 +7404,25 @@ બેકઅપ રાત્રિ દરમિયાન લેવામાં આવશે. - Backup plan + બેકઅપ પ્લાન બેકઅપ અક્ષમ છે %1$s/માસ - Your backup plan is free + તમારો બેકઅપ પ્લાન મફત છે %1$sના રોજ રિન્યૂ થશે - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + તમારી મેસેજ હિસ્ટ્રીનું બેકઅપ લો જેથી જ્યારે તમે નવો ફોન લો અથવા Signalને ફરીથી ઇન્સ્ટોલ કરો ત્યારે તમે ક્યારેય ડેટા ગુમાવશો નહીં. - Other ways to backup + બેકઅપ લેવાની અન્ય રીતો - On-device backups + ડિવાઇસ પરના બેકઅપ - Save your backups to a folder on this device + તમારા બેકઅપને આ ડિવાઇસ પરના ફોલ્ડરમાં સાચવો - Manage or cancel + મેનેજ કરો અથવા રદ કરો અપગ્રેડ કરો diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 619c8273d1..190dbf2d54 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -3319,8 +3319,6 @@ चैट करने के लिए हाज़िर ब्रेक पर हैं किसी नई चीज़ पर काम कर रहे हैं - - एक या ज़्यादा वर्ण अमान्य हैं। समूह संपादित करें @@ -6001,7 +5999,7 @@ %1$d जवाब - Story no longer hidden + स्टोरी अब छिपी हुई नहीं है जोड़ें @@ -7296,11 +7294,11 @@ पुनर्स्थापित पूर्ण - Waiting for Wi-Fi… + वाई-फ़ाई का इंतज़ार किया जा रहा है… - No internet… + इंटरनेट नहीं है… - Device has low battery + डिवाइस की बैटरी कम है %1$s का %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/महीना, %2$s को रिन्यू होगा - Last backup %1$s + पिछला बैकअप %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal की सुरक्षित एंड-टू-एंड एनक्रिप्टेड स्टोरेज सेवा के साथ ऑटोमैटिक बैकअप। सेटअप करें @@ -7390,9 +7388,9 @@ सेल्यूलर का उपयोग करके बैकअप लें - View backup key + बैकअप की देखें - Unlock to view backup key + बैकअप की देखने के लिए अनलॉक करें बैकअप बंद करें और डिलीट करें @@ -7406,25 +7404,25 @@ बैकअप को रात के समय बनाया जाएगा। - Backup plan + बैकअप प्लान बैकअप बंद किया गया %1$s/महीना - Your backup plan is free + आपका बैकअप प्लान मुफ़्त है %1$s को नवीकृत होता है - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + अपना संदेश इतिहास बैकअप करें, ताकि नया फ़ोन लेने या Signal को फिर से इंस्टॉल करने पर आपका डेटा न खोए। - Other ways to backup + बैकअप करने के दूसरे तरीके - On-device backups + डिवाइस पर बैकअप - Save your backups to a folder on this device + इस डिवाइस के किसी फ़ोल्डर में अपने बैकअप सेव करें - Manage or cancel + प्रबंधित या रद्द करें अपग्रेड diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 3fa50bb6d5..f6c9fa6866 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -3517,8 +3517,6 @@ Slobodan/na za razgovor Na pauzi Radim na nečem novom - - Jedan ili više znakova nisu valjani. Uredi grupu @@ -6265,7 +6263,7 @@ %1$d odgovora - Story no longer hidden + Priča više nije skrivena Dodaj @@ -7612,11 +7610,11 @@ Dovršeno vraćanje sigurnosne kopije - Waiting for Wi-Fi… + Čeka se Wi-Fi veza… - No internet… + Nema interneta… - Device has low battery + Niska razina baterije %1$s od %2$s @@ -7684,11 +7682,11 @@ - %1$s/month, renews %2$s + %1$s mjesečno, obnavlja se %2$s - Last backup %1$s + Posljednje sigurnosno kopiranje: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatsko sigurnosno kopiranje pomoću Signalovog sustava pohrane koji je zaštićen sveobuhvatnim šifriranjem. Postavi @@ -7706,9 +7704,9 @@ Sigurnosno kopiranje putem mobilne mreže - View backup key + Prikaži ključ za sigurnosnu kopiju - Unlock to view backup key + Otključajte za prikaz ključa za sigurnosnu kopiju Isključi i izbriši sigurnosnu kopiju @@ -7722,25 +7720,25 @@ Sigurnosna kopija bit će izrađena tijekom noćnih sati. - Backup plan + Plan za sigurnosno kopiranje Sigurnosno kopiranje je onemogućeno %1$s mjesečno - Your backup plan is free + Vaš plan za sigurnosno kopiranje je besplatan Obnavlja se %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Stvorite sigurnosnu kopiju svojih razgovora kako ih ne biste izgubili prilikom zamjene telefona ili ponovne instalacije Signala. - Other ways to backup + Druge opcije sigurnosnog kopiranja - On-device backups + Sigurnosne kopije na uređaju - Save your backups to a folder on this device + Spremite svoje sigurnosne kopije u mapi na ovom uređaju - Manage or cancel + Upravljajte postavkama ili otkažite Ažuriraj diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 1a238e8105..97dd29006f 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -3319,8 +3319,6 @@ Nyitott a csevegésre Lazulok pár percet Valami izgalmas újdonságon dolgozom - - Egy vagy több karakter érvénytelen. Csoport szerkesztése @@ -6001,7 +5999,7 @@ %1$d válasz - Story no longer hidden + A történet már nincs elrejtve Hozzáadás @@ -7296,11 +7294,11 @@ Visszaállítás kész - Waiting for Wi-Fi… + Várakozás a wifi-re… - No internet… + Nincs internet… - Device has low battery + A készülék akkumulátor-töltöttsége alacsony %1$s / %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/hó, %2$s dátumban megújul - Last backup %1$s + Utolsó biztonsági mentés: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatikus biztonsági mentések a Signal biztonságos végpontok közötti titkosított tárolási szolgáltatásával. Beállítás @@ -7390,9 +7388,9 @@ Biztonsági mentés mobilhálózaton keresztül - View backup key + Biztonsági kulcs megtekintése - Unlock to view backup key + A biztonsági kulcs megtekintéséhez oldd fel a zárolást Biztonsági mentés kikapcsolása és törlése @@ -7406,25 +7404,25 @@ A biztonsági mentés éjszaka kerül létrehozásra. - Backup plan + Biztonsági terv Biztonsági mentés kikapcsolva %1$s/hó - Your backup plan is free + A biztonsági terv ingyenes Megújul ekkor: %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Készíts biztonsági másolatot az üzenetelőzményeidről, hogy soha ne veszíts el adatot új telefon vásárlásakor vagy a Signal újratelepítésekor. - Other ways to backup + A biztonsági mentés egyéb módjai - On-device backups + Biztonsági mentések az eszközön - Save your backups to a folder on this device + Mentsd el a biztonsági másolatokat egy mappába ezen az eszközön - Manage or cancel + Kezelés vagy törlés Frissítés diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 1cac187072..7006648537 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -3220,8 +3220,6 @@ Siap ngobrol Istirahat Sedang mengerjakan sesuatu yang baru - - Satu atau beberapa karakter tidak valid. Sunting grup @@ -5869,7 +5867,7 @@ %1$d balasan - Story no longer hidden + Cerita tidak lagi disembunyikan Tambahkan @@ -7138,13 +7136,13 @@ Pemulihan selesai - Waiting for Wi-Fi… + Menunggu Wi-Fi … - No internet… + Tidak ada internet … - Device has low battery + Daya perangkat rendah - %1$sdari %2$s + %1$s dari %2$s @@ -7210,13 +7208,13 @@ - %1$s/month, renews %2$s + %1$s/bulan, diperpanjang %2$s - Last backup %1$s + Pencadangan terakhir %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Pencadangan otomatis dengan layanan penyimpanan terenkripsi ujung ke ujung yang aman dari Signal. - Buat Nama + Siapkan @@ -7232,9 +7230,9 @@ Cadangkan dengan data seluler - View backup key + Lihat kunci pencadangan - Unlock to view backup key + Buka untuk melihat kunci pencadangan Nonaktifkan dan hapus pencadangan @@ -7248,27 +7246,27 @@ Pencadangan akan diproses dalam semalam. - Backup plan + Paket pencadangan Pencadangan dinonaktifkan %1$s/bulan - Your backup plan is free + Paket pencadangan Anda gratis - Memperpanjang %1$s + Diperpanjang %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Cadangkan riwayat pesan sehingga data Anda tidak akan hilang saat Anda memakai ponsel baru atau menginstal ulang Signal. - Other ways to backup + Cara lain untuk mencadangkan - On-device backups + Pencadangan di perangkat - Save your backups to a folder on this device + Simpan cadangan Anda ke folder di perangkat ini - Manage or cancel + Kelola atau batalkan - Pemutakhiran + Perbarui %1$d/%2$d diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 4997f18b9a..e93e988116 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -3319,8 +3319,6 @@ Disponibile per chiacchierare Mi sto prendendo una pausa Al lavoro su qualcosa di nuovo - - Uno o più caratteri non sono validi. Modifica gruppo @@ -6001,7 +5999,7 @@ %1$d risposte - Story no longer hidden + Storia di nuovo visibile Aggiungi @@ -7296,11 +7294,11 @@ Ripristino completato - Waiting for Wi-Fi… + In attesa del Wi-Fi… - No internet… + Niente Internet… - Device has low battery + Batteria scarica %1$s di %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/mese, si rinnova il giorno: %2$s - Last backup %1$s + Ultimo backup %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Backup automatici e sicuri con il servizio di archiviazione crittografato end-to-end di Signal. Imposta @@ -7390,9 +7388,9 @@ Backup usando i dati cellulare - View backup key + Vedi la chiave per i backup - Unlock to view backup key + Sblocca per vedere la chiave per il backup Disattiva ed elimina backup @@ -7406,27 +7404,27 @@ Il backup verrà creato stanotte. - Backup plan + Piano del backup Backup disattivati %1$s al mese - Your backup plan is free + Piano gratuito per i backup Rinnovo %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Salva la cronologia dei tuoi messaggi con dei backup per non perdere neanche una virgola quando compri un nuovo telefono o reinstalli Signal. - Other ways to backup + Altre modalità di backup - On-device backups + Backup su dispositivo - Save your backups to a folder on this device + Salva i tuoi backup in una cartella sul tuo dispositivo - Manage or cancel + Gestisci o cancella - Aggiorna + Esegui l\'upgrade %1$d/%2$d diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 76bf5461ea..bc0646a8d7 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -3517,8 +3517,6 @@ פנוי/ה לצ\'אט לוקח הפסקה עובד על משהו חדש - - תו אחד או יותר אינו חוקי. ערוך קבוצה diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 82511c2189..261aaa063e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -3220,8 +3220,6 @@ チャット歓迎 休憩中 新しいことをしています - - 1つ以上の文字が無効です。 グループを編集 @@ -5869,7 +5867,7 @@ 返信 %1$d件 - Story no longer hidden + ストーリーは非公開ではなくなりました 追加する @@ -7138,11 +7136,11 @@ 復元が完了しました - Waiting for Wi-Fi… + Wi-Fiの待機中… - No internet… + インターネット接続がありません… - Device has low battery + 端末のバッテリー残量が少なくなっています %1$s/%2$s @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + %1$s/月、更新日 %2$s - Last backup %1$s + 最終バックアップ:%1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signalによる、安全なエンドツーエンドの暗号化ストレージサービスによる自動バックアップです。 設定 @@ -7232,9 +7230,9 @@ モバイルデータ通信を使ってバックアップする - View backup key + バックアップキーを表示する - Unlock to view backup key + ロックを解除してバックアップキーを表示する バックアップをオフにして消去しますか? @@ -7248,25 +7246,25 @@ バックアップは夜間に作成されます。 - Backup plan + バックアッププラン バックアップがオフになりました %1$s/月プラン - Your backup plan is free + 現在のバックアッププランは無料プランです 更新 %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + メッセージ履歴をバックアップしておくと、電話を買い替えたり、Signalを再インストールしたりしてもデータが失われることはありません。 - Other ways to backup + その他のバックアップ方法 - On-device backups + 端末にバックアップする - Save your backups to a folder on this device + この端末のフォルダにバックアップを保存します - Manage or cancel + 変更またはキャンセルする アップグレードする diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 1f3652e26e..84c85c2b92 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -3319,8 +3319,6 @@ სცალია მიმოწერისთვის Შესვენება რაღაც ახალზე მუშაობა - - ერთი ან მეტი სიმბოლო არასწორია. ჯგუფის რედაქტირება @@ -6001,7 +5999,7 @@ %1$d პასუხი - Story no longer hidden + Story აღარაა დამალული დამატება @@ -7296,11 +7294,11 @@ აღდგენა დასრულებულია - Waiting for Wi-Fi… + ველოდებით Wi-Fi-ს… - No internet… + ინტერნეტი არ არის… - Device has low battery + მოწყობილობა ჯდება %1$s %2$s-დან @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/თვეში, განახლდების თარიღი %2$s - Last backup %1$s + ბოლო სათადარიგო ასლის შექმნის თარიღი: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + შექმენი სათადარიგო ასლები Signal-ის დაშიფრული შენახვის სერვისით. - Set up + დაყენება @@ -7390,9 +7388,9 @@ სარეზერვო კოპიების შექმნა მობილური ინტერნეტის გამოყენებით - View backup key + სათადარიგო ასლების გასაღების ნახვა - Unlock to view backup key + სათადარიგო ასლების გასაღების სანახავად მოხსენი ბლოკი გამორთვა და სარეზერვო კოპიების წაშლა @@ -7406,25 +7404,25 @@ სარეზერვო კოპიები ღამით შეიქმნება. - Backup plan + სათადარიგო ასლების არჩეული გამოწერა სარეზერვო კოპირება გამორთულია %1$s/თვეში - Your backup plan is free + შენი სათადარიგო ასლების გამოწერა უფასოა განახლდება %1$s-ში - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + შექმენი შენი მიმოწერის ისტორიის სათადარიგო ასლები, რათა ახალი მობილურის შეძენისას ან Signal-ის თავიდან გადმოწერისას მონაცემები არ დაკარგო. - Other ways to backup + სათადარიგო ასლების შექმნის სხვა გზები - On-device backups + მოწყობილობაზე არსებული სათადარიგო ასლები - Save your backups to a folder on this device + შეინახე შენი სათადარიგო ასლები ამ მოწყობილობის საქაღალდეში - Manage or cancel + მართვა ან გაუქმება Upgrade diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 592a167ea7..8e9e56e26c 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -3319,8 +3319,6 @@ Чат арқылы сөйлесуге дайын Үзіліс Жаңа жобамен жұмыс істеп жатырмын - - Бір немесе бірнеше таңба дұрыс емес. Топты өзгерту diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index 54fd0e6688..c5d9597c3a 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -3220,8 +3220,6 @@ ឥតគិតថ្លៃសម្រាប់ការឈែត សម្រាកបន្ថិច ធ្វើការលើអ្វីដែលថ្មី - - តួអក្សរមួយ ឬច្រើនមិនត្រឹមត្រូវទេ។ កែប្រែក្រុម @@ -5869,7 +5867,7 @@ ចំនួនឆ្លើយតប %1$d - Story no longer hidden + រឿងរ៉ាវត្រូវបានបង្ហាញវិញ បន្ថែម @@ -7129,22 +7127,22 @@ - បង្កើនទំហំផ្ទុក %1$s ដើម្បីស្ដារមេឌៀរបស់អ្នកឡើងវិញ។ + បង្កើនទំហំផ្ទុក %1$s ដើម្បីស្ដារមេឌៀរបស់អ្នកមកវិញ។ - កំពុងស្ដារមេឌៀឡើងវិញ + កំពុងស្ដារមេឌៀមកវិញ - ការស្តារឡើងវិញត្រូវបានផ្អាក + ការស្តារត្រូវបានផ្អាក - ការស្តារជោគជ័យ + ការស្តារបានបញ្ចប់ - Waiting for Wi-Fi… + កំពុងរង់ចាំ Wi-Fi… - No internet… + មិនមានអ៊ីនធឺណិត… - Device has low battery + ឧបករណ៍ជិតអស់ថ្ម - %1$s នៃ%2$s + %1$s នៃ %2$s @@ -7210,13 +7208,13 @@ - %1$s/month, renews %2$s + %1$s/ខែ, បន្ត %2$s - Last backup %1$s + ការបម្រុងទុកចុងក្រោយ៖ %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + ការបម្រុងទុកដោយស្វ័យប្រវត្តិជាមួយនឹងសេវាកម្មផ្ទុកទិន្នន័យដែលបានអ៊ីនគ្រីបទាំងសងខាងដ៏មានសុវត្ថិភាពរបស់ Signal។ - Set up + រៀបចំ @@ -7232,9 +7230,9 @@ បម្រុងទុកដោយប្រើអ៊ីនធឺណិតទូរសព្ទ - View backup key + មើលសោបម្រុងទុក - Unlock to view backup key + ដោះសោដើម្បីមើលសោបម្រុងទុក បិទ និងលុបការបម្រុងទុក @@ -7248,27 +7246,27 @@ ការបម្រុងទុកនឹងត្រូវបានបង្កើតឡើងក្នុងពេលមួយយប់។ - Backup plan + គម្រោងបម្រុងទុក ការបម្រុងទុកត្រូវបានបិទ %1$s/ខែ - Your backup plan is free + គម្រោងបម្រុងទុករបស់អ្នកគឺឥតគិតថ្លៃ បន្ត %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + បម្រុងទុកប្រវត្តិសាររបស់អ្នក ដើម្បីកុំឱ្យបាត់បង់ទិន្នន័យនៅពេលអ្នកមានទូរសព្ទថ្មី ឬដំឡើង Signal ឡើងវិញ។ - Other ways to backup + វិធីផ្សេងទៀតដើម្បីបម្រុងទុក - On-device backups + ការបម្រុងទុកនៅលើឧបករណ៍ - Save your backups to a folder on this device + រក្សាទុកការបម្រុងទុករបស់អ្នកក្នុងថតឯកសារនៅលើឧបករណ៍នេះ - Manage or cancel + គ្រប់គ្រង ឬបោះបង់ - ដំឡើង + ដំឡើងកម្រិត %1$d/%2$d diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index c5c49f2c36..48fbd60249 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -3319,8 +3319,6 @@ ಚಾಟ್ ಮಾಡಲು ಮುಕ್ತ ವಿರಾಮ ತೆಗೆದುಕೊಳ್ಳುವುದು ಏನೋ ಹೊಸದರ ಕುರಿತು ಕೆಲಸ ಮಾಡುವುದು - - ಒಂದು ಅಥವಾ ಹೆಚ್ಚಿನ ಅಕ್ಷರಗಳು ಅಮಾನ್ಯವಾಗಿವೆ. ಗುಂಪನ್ನು ಬದಲಾಯಿಸಿ @@ -6001,7 +5999,7 @@ %1$d ಉತ್ತರಗಳು - Story no longer hidden + ಸ್ಟೋರಿ ಇನ್ನು ಮುಂದೆ ಮರೆಯಾಗಿರುವುದಿಲ್ಲ ಸೇರಿಸಿ @@ -7296,11 +7294,11 @@ ಮರುಸ್ಥಾಪನೆ ಪೂರ್ಣಗೊಂಡಿದೆ - Waiting for Wi-Fi… + Wi-Fi ಗಾಗಿ ಕಾಯುತ್ತಿದೆ… - No internet… + ಇಂಟರ್ ನೆಟ್ ಇಲ್ಲ… - Device has low battery + ಡಿವೈಸ್‍ನ ಬ್ಯಾಟರಿ ಕಡಿಮೆ ಇದೆ %2$s ನಲ್ಲಿ %1$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/ತಿಂಗಳು, ನವೀಕರಣಗೊಳ್ಳುತ್ತದೆ %2$s - Last backup %1$s + ಕೊನೆಯ ಬ್ಯಾಕಪ್%1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal ನ ಸುರಕ್ಷಿತ ಎಂಡ್ ಟು ಎಂಡ್ ಎನ್ಕ್ರಿಪ್ಟ್ ಆಗಿರುವ ಸ್ಟೋರೇಜ್ ಸೇವೆಯೊಂದಿಗೆ ಅಟೋಮ್ಯಾಟಿಕ್ ಬ್ಯಾಕಪ್‌ಗಳು - Set up + ಸೆಟ್ ಅಪ್ @@ -7390,9 +7388,9 @@ ಸೆಲ್ಯುಲರ್ ಬಳಸಿಕೊಂಡು ಬ್ಯಾಕಪ್ ಮಾಡಿ - View backup key + ಬ್ಯಾಕಪ್ ಕೀ ವೀಕ್ಷಿಸಿ - Unlock to view backup key + ಬ್ಯಾಕಪ್ ಕೀ ವೀಕ್ಷಿಸಲು ಅನ್ ಲಾಕ್ ಮಾಡಿ ಬ್ಯಾಕಪ್ ಆಫ್ ಮಾಡಿ ಮತ್ತು ಅಳಿಸಿ @@ -7406,25 +7404,25 @@ ರಾತ್ರಿಯಲ್ಲಿ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಚಿಸಲಾಗುತ್ತದೆ. - Backup plan + ಬ್ಯಾಕಪ್ ಪ್ಲಾನ್ ಬ್ಯಾಕಪ್‌ಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ %1$s/ತಿಂಗಳು - Your backup plan is free + ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಪ್ಲಾನ್ ಉಚಿತ ನವೀಕರಿಸುತ್ತದೆ %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + ನಿಮ್ಮ ಮೆಸೇಜ್ ಇತಿಹಾಸವನ್ನು ಬ್ಯಾಕಪ್ ಮಾಡಿ, ಇದರಿಂದ ನೀವು ಹೊಸ ಫೋನ್ ಪಡೆದಾಗ ಅಥವಾ Signal ರೀಇನ್‍ಸ್ಟಾಲ್ ಮಾಡಿದಾಗ ಡೇಟಾವನ್ನು ಕಳೆದುಕೊಳ್ಳುವುದಿಲ್ಲ. - Other ways to backup + ಬ್ಯಾಕಪ್ ಮಾಡುವ ಇತರೆ ಮಾರ್ಗಗಳು - On-device backups + ಆನ್-ಡಿವೈಸ್ ಬ್ಯಾಕಪ್‌ಗಳು - Save your backups to a folder on this device + ನಿಮ್ಮ ಬ್ಯಾಕಪ್‌ನ್ನು ಈ ಡಿವೈಸ್‌ನಲ್ಲಿ ಒಂದು ಫೋಲ್ಡರಿನಲ್ಲಿ ಸೇವ್ ಮಾಡಿ - Manage or cancel + ನಿರ್ವಹಿಸಿ ಅಥವಾ ರದ್ದು ಮಾಡಿ ಅಪ್ಗ್ರೇಡ್ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 36c1e0d487..91100f7c5e 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -3220,8 +3220,6 @@ 대화 가능 쉬는 중 새로운 것에 도전하는 중 - - 잘못된 문자가 하나 이상 포함되어 있습니다. 그룹 편집 diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 25bdba51e0..28adeec452 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -3220,8 +3220,6 @@ Маектешкенге даярмын Тыныгуу алдым Жаңы нерсенин үстүнөн иштеп жатам - - Бир же бир нече символ жарабайт. Топту оңдоо diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 463e9c8ec0..a83185cb33 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -3517,8 +3517,6 @@ Galiu kalbėti Man pertrauka Dirbu ties kažkuo nauju - - Vienas ar daugiau simbolių yra netinkami. Taisyti grupę @@ -6265,7 +6263,7 @@ %1$d atsakymas - Story no longer hidden + Istorija nebėra paslėpta Pridėti @@ -7612,11 +7610,11 @@ Atkūrimas užbaigtas - Waiting for Wi-Fi… + Laukiama belaidžio ryšio (Wi-Fi)… - No internet… + Nėra interneto… - Device has low battery + Įrenginio baterija senka %1$s iš %2$s @@ -7684,13 +7682,13 @@ - %1$s/month, renews %2$s + %1$s/mėn., atsinaujina %2$s - Last backup %1$s + Paskutinė atsarginė kopija %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatinės atsarginės kopijos naudojant „Signal“ saugią, užšifruotą saugyklos paslaugą. - Set up + Nustatyti @@ -7706,9 +7704,9 @@ Daryti atsargines kopijas naudojant mobilųjį ryšį - View backup key + Peržiūrėti atsarginės kopijos raktą - Unlock to view backup key + Atrakinkite, kad peržiūrėtumėte atsarginės kopijos raktą Išjungti ir ištrinti atsargines kopijas @@ -7722,25 +7720,25 @@ Atsarginė kopija bus sukurta per naktį. - Backup plan + Atsarginės kopijos planas Atsarginis kopijavimas išjungtas %1$s/mėnesį - Your backup plan is free + Jūsų atsarginės kopijos planas yra nemokamas Atnaujinama %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Sukurkite savo žinučių istorijos atsarginę kopiją, kad niekada neprarastumėte duomenų įsigiję naują telefoną arba iš naujo įdiegę „Signal“. - Other ways to backup + Kiti atsarginės kopijos kūrimo būdai - On-device backups + Atsarginės kopijos įrenginyje - Save your backups to a folder on this device + Išsaugokite atsargines kopijas į aplanką šiame įrenginyje - Manage or cancel + Tvarkyti arba atšaukti Naujinti diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 82942f1109..115547ae9a 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -3418,8 +3418,6 @@ Atvērts sarunai Paņēmis pārtraukumu Strādāju pie kaut kā jauna - - Viena vai vairākas rakstzīmes nav derīgas. Rediģēt grupu @@ -6133,7 +6131,7 @@ %1$d atbildes - Story no longer hidden + Stāsts vairs nav paslēpts Pievienot @@ -7454,11 +7452,11 @@ Atjaunošana pabeigta - Waiting for Wi-Fi… + Gaida Wi-Fi… - No internet… + Nav interneta… - Device has low battery + Zems ierīces uzlādes līmenis %1$s no %2$s @@ -7526,13 +7524,13 @@ - %1$s/month, renews %2$s + %1$s mēnesī, atjaunošanas datums: %2$s - Last backup %1$s + Pēdējā rezerves kopija: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automātiska rezerves kopiju izveide, izmantojot Signal drošās un pilnībā šifrētās krātuves pakalpojumu. - Set up + Iestatīt @@ -7548,9 +7546,9 @@ Izveidot rezerves kopiju, izmantojot mobilos datus - View backup key + Skatīt rezerves kopijas atslēgu - Unlock to view backup key + Atbloķējiet, lai skatītu rezerves kopijas atslēgu Izslēgt rezerves kopijas izveidi un dzēst to @@ -7564,25 +7562,25 @@ Rezerves kopija tiks izveidota pa nakti. - Backup plan + Rezerves kopiju plāns Rezerves kopijas ir atspējotas %1$s/mēnesī - Your backup plan is free + Jums ir bezmaksas rezerves kopiju plāns Atjaunojas %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Izveidojiet ziņu vēstures rezerves kopiju, lai nezaudētu datus, nomainot tālruni vai pārinstalējot Signal. - Other ways to backup + Citi rezerves kopijas veidi - On-device backups + Rezerves kopijas ierīcē - Save your backups to a folder on this device + Saglabājiet rezerves kopijas šīs ierīces mapē - Manage or cancel + Pārvaldīt vai atcelt Paaugstināt versiju diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index bc9ad47a79..754cbd221e 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -3319,8 +3319,6 @@ Слободно за муабет На пауза Работам на нешто ново - - Еден или повеќе знаци беа неважечки. Уреди група @@ -6001,7 +5999,7 @@ %1$d одговори - Story no longer hidden + Приказната повеќе не е скриена Додај @@ -7296,11 +7294,11 @@ Враќањето е завршено - Waiting for Wi-Fi… + Се чека Wi-Fi… - No internet… + Нема интернет… - Device has low battery + Уредот има празна батерија %1$s од %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/месечно, се обновува на %2$s - Last backup %1$s + Последна резервна копија %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Автоматски резервни копии со безбедната и целосно шифрирана услуга за складирање на Signal. - Set up + Поставете @@ -7390,9 +7388,9 @@ Правење резервна копија користејќи мобилен интернет - View backup key + Видете го клучот за резервни копии - Unlock to view backup key + Отклучете за да го видите клучот за резервни копии Исклучи и избриши ги резервните копии @@ -7406,25 +7404,25 @@ Резервната копија ќе се прави во текот на ноќта. - Backup plan + Претплатен пакет на резервни копии Резервните копии се оневозможени %1$s/месец - Your backup plan is free + Вашиот претплатен пакет на резервни копии е бесплатен Се обновува на %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Направете резервна копија на вашата историја на пораки за да не губите податоци кога ќе земете нов телефон или ќе ја реинсталирате Signal апликацијата. - Other ways to backup + Други начини за правење резервна копија - On-device backups + Резервни копии на уредот - Save your backups to a folder on this device + Зачувајте ги вашите резервни копии во папка на овој уред - Manage or cancel + Измени или откажи Надградба diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 5eab04d8cf..142c8d26bd 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -3319,8 +3319,6 @@ ചാറ്റ് ചെയ്യാൻ സമയമുണ്ടേ ഒരു ഇടവേളയിലാണ് പുതിയ ഒരു കാര്യം ചെയ്യുന്നു - - ഒന്നോ അതിലധികമോ പ്രതീകങ്ങൾ അസാധുവാണ്. ഗ്രൂപ്പ് എഡിറ്റുചെയ്യുക @@ -6001,7 +5999,7 @@ %1$d മറുപടികൾ - Story no longer hidden + സ്റ്റോറി ഇനി മറയ്ക്കില്ല ചേർക്കുക @@ -7293,14 +7291,14 @@ പുനഃസ്ഥാപിക്കൽ താൽക്കാലികമായി നിർത്തി - വീണ്ടെടുക്കൽ പൂർത്തിയായി + പുനഃസ്ഥാപിക്കൽ പൂർത്തിയായി - Waiting for Wi-Fi… + Wi-Fi-യ്‌ക്കായി കാത്തിരിക്കുന്നു… - No internet… + ഇൻ്റർനെറ്റ് ഇല്ല… - Device has low battery + ഉപകരണത്തിലെ ബാറ്ററി കുറവാണ് %2$s ൽ %1$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/മാസം, പുതുക്കുന്ന തീയതി: %2$s - Last backup %1$s + അവസാന ബാക്കപ്പ്: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal-ൻ്റെ സുരക്ഷിതമായ എൻഡ്-ടു-എൻഡ് എൻക്രിപ്റ്റഡ് സ്റ്റോറേജ് സേവനത്തോടുകൂടിയ സ്വയമേവയുള്ള ബാക്കപ്പുകൾ. - Set up + സജ്ജമാക്കുക @@ -7390,9 +7388,9 @@ മൊബൈൽ ഡാറ്റ ഉപയോഗിച്ച് ബാക്കപ്പ് ചെയ്യുക - View backup key + ബാക്കപ്പ് കീ കാണുക - Unlock to view backup key + ബാക്കപ്പ് കീ കാണുന്നതിന് അൺലോക്ക് ചെയ്യുക ബാക്കപ്പ് ഓഫാക്കി ഇല്ലാതാക്കുക @@ -7406,25 +7404,25 @@ ഒറ്റരാത്രികൊണ്ട് ബാക്കപ്പ് സൃഷ്ടിക്കപ്പെടും. - Backup plan + ബാക്കപ്പ് പ്ലാൻ ബാക്കപ്പുകൾ പ്രവർത്തനരഹിതമാക്കി %1$s/ മാസം - Your backup plan is free + നിങ്ങളുടെ ബാക്കപ്പ് പ്ലാൻ സൗജന്യമാണ് %1$s പുതുക്കുന്നു - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + നിങ്ങളുടെ സന്ദേശ ചരിത്രം ബാക്കപ്പ് ചെയ്യുക, അതുവഴി നിങ്ങൾക്ക് ഒരു പുതിയ ഫോൺ ലഭിക്കുമ്പോഴോ Signal വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുമ്പോഴോ ഒരിക്കലും ഡാറ്റ നഷ്‌ടപ്പെടില്ല. - Other ways to backup + ബാക്കപ്പ് ചെയ്യാനുള്ള മറ്റ് വഴികൾ - On-device backups + ഉപകരണത്തിലെ ബാക്കപ്പുകൾ - Save your backups to a folder on this device + ഈ ഉപകരണത്തിലെ ഒരു ഫോൾഡറിലേക്ക് നിങ്ങളുടെ ബാക്കപ്പുകൾ സംരക്ഷിക്കുക - Manage or cancel + നിയന്ത്രിക്കുക അല്ലെങ്കിൽ റദ്ദാക്കുക അപ്‌ഗ്രേഡ് ചെയ്യുക diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index 58467e2fe3..62cab565fa 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -3319,8 +3319,6 @@ चॅट करण्यासाठी मोकळा आराम करत आहे काहीतरी नवीन काम करत आहे - - एक किंवा अधिक वर्ण अवैध आहेत. गट संपादन करा diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index d6ede8c990..e10e83f84f 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -3220,8 +3220,6 @@ Ada masa untuk sembang Berehat sebentar Mengusahakan sesuatu yang baharu - - Satu atau lebih aksara tidak sah. Edit kumpulan @@ -5869,7 +5867,7 @@ %1$d balasan - Story no longer hidden + Cerita tidak lagi tersembunyi Tambah @@ -7138,11 +7136,11 @@ Restore selesai - Waiting for Wi-Fi… + Menunggu Wi-Fi… - No internet… + Tiada internet… - Device has low battery + Bateri peranti lemah %1$s daripada %2$s @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + %1$s/bulan, diperbaharui pada %2$s - Last backup %1$s + Sandaran terakhir %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Sandaran automatik dengan perkhidmatan storan disulitkan hujung ke hujung yang selamat oleh Signal. Tetapkan @@ -7232,9 +7230,9 @@ Sandarkan menggunakan selular - View backup key + Lihat kunci sandaran - Unlock to view backup key + Buka kunci untuk melihat kunci sandaran Matikan dan padamkan sandaran @@ -7248,25 +7246,25 @@ Sandaran akan dibuat semalaman. - Backup plan + Pelan sandaran Sandaran dilumpuhkan %1$s/bulan - Your backup plan is free + Pelan sandaran anda adalah percuma Memperbaharui %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Sandarkan sejarah mesej anda supaya anda tidak kehilangan data apabila menggunakan telefon baharu atau memasang semula Signal. - Other ways to backup + Cara lain untuk membuat sandaran - On-device backups + Sandaran pada peranti - Save your backups to a folder on this device + Simpan sandaran anda ke folder pada peranti ini - Manage or cancel + Urus atau batalkan Naik taraf diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index e143ed75a5..1b8cde0e23 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -3220,8 +3220,6 @@ အခမဲ့ချက်(တ်)နိုင်သည် အနားယူခြင်း အရာအသစ်တစ်ခု ကိုလုပ်ဆောင်နေသည် - - တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော စာလုံးများသည် အကျုံးမဝင်ပါ။ အဖွဲ့ကို ပြင်မည် @@ -5869,7 +5867,7 @@ ပြန်စာ %1$d ခု - Story no longer hidden + စတိုရီကို ဝှက်မထားတော့ပါ ပေါင်းထည့်မယ် @@ -7135,14 +7133,14 @@ ပြန်လည်ရယူခြင်းကို ခေတ္တရပ်ထားသည် - ပြီးပြည့်စုံသောပြန်ယူပါ + ပြန်လည်ရယူခြင်း ပြီးစီးပါပြီ - Waiting for Wi-Fi… + Wi-Fi ကို စောင့်နေသည်… - No internet… + အင်တာနက်မရှိ… - Device has low battery + ဘက်ထရီအားနည်းနေသည် %1$s ၏ %2$s @@ -7210,13 +7208,13 @@ - %1$s/month, renews %2$s + တစ်လလျှင် %1$s ၊ %2$s တွင် သက်တမ်းတိုးမည် - Last backup %1$s + နောက်ဆုံးဘက်ခ်အပ် %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal ၏ လုံခြုံသော ဟိုဘက်သည်ဘက် ကုဒ်ပြောင်းဝှက်ထားသည့် သိုလှောင်ရေးဝန်ဆောင်မှုဖြင့် အလိုအလျောက် ဘက်ခ်အပ်။ - Set up + သတ်မှတ်ရန် @@ -7232,9 +7230,9 @@ ဆယ်လူလာသုံး၍ ဘက်ခ်အပ်လုပ်ပါ - View backup key + ဘက်ခ်အပ်ကီးကိုကြည့်ရန် - Unlock to view backup key + ဘက်ခ်အပ်ကီးကိုကြည့်ရန် လော့ခ်ဖွင့်ပါ ပိတ်ပြီး ဘက်ခ်အပ်ကို ဖျက်ပါ @@ -7248,25 +7246,25 @@ ဘက်ခ်အပ်ကို ညတွင်းချင်း ဖန်တီးပါမည်။ - Backup plan + ဘက်ခ်အပ်အစီအစဉ် ဘက်ခ်အပ် ပိတ်ထားသည် တစ်လလျှင် %1$s - Your backup plan is free + သင်၏ ဘက်ခ်အပ်အစီအစဉ်သည် အခမဲ့ဖြစ်သည် %1$s တွင် သက်တမ်းတိုးသည် - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + ဖုန်းအသစ်ရသောအခါ သို့မဟုတ် Signal ကို ပြန်လည်ထည့်သွင်းသည့်အခါ ဒေတာမဆုံးရှုံးစေရန် မက်ဆေ့ချ်မှတ်တမ်းကို ဘက်ခ်အပ်လုပ်ပါ။ - Other ways to backup + ဘက်ခ်အပ်လုပ်ရန် အခြားနည်းလမ်းများ - On-device backups + စက်ပေါ်တွင် ဘက်ခ်အပ်လုပ်ရန် - Save your backups to a folder on this device + ဘက်ခ်အပ်ကို ဤစက်ပေါ်ရှိ ဖိုဒါတစ်ခုတွင် သိမ်းဆည်းပါ - Manage or cancel + စီမံရန် သို့မဟုတ် ပယ်ဖျက်ရန် Upgrade diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 14aaf4aec3..60c89b9366 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -3319,8 +3319,6 @@ Ledig til samtale Tar en pause Jobber med noe nytt - - Teksten inneholder ugyldige tegn. Rediger gruppe @@ -6001,7 +5999,7 @@ %1$d svar - Story no longer hidden + Storyen er ikke lenger skjult Legg til @@ -7291,16 +7289,16 @@ Gjenoppretter mediefiler - Nedlastingen er satt på pause + Gjenopprettingen er satt på pause Gjenoppretting er utført - Waiting for Wi-Fi… + Venter på Wi-Fi … - No internet… + Ikke koblet til internett … - Device has low battery + Enheten har lavt batteri %1$s av %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s per måned, fornyes %2$s - Last backup %1$s + Siste sikkerhetskopi: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatisk sikkerhetskopiering med Signals trygge, ende-til-ende-krypterte lagringstjeneste. - Velg brukernavn + Konfigurer @@ -7390,9 +7388,9 @@ Sikkerhetskopiering via mobilnettverket - View backup key + Se sikkerhetskode - Unlock to view backup key + Lås opp for å se koden Slå av og slett sikkerhetskopi @@ -7406,27 +7404,27 @@ Sikkerhetskopiene opprettes på nattestid. - Backup plan + Abonnement for sikkerhetskopiering Sikkerhetskopiering slått av %1$s per måned - Your backup plan is free + Abonnementet er gratis Fornyes %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Ta en sikkerhetskopi av meldingsloggen din, slik at du ikke mister dataene dine når du bytter telefon eller installerer Signal på nytt. - Other ways to backup + Andre muligheter for sikkerhetskopiering - On-device backups + På enheten - Save your backups to a folder on this device + Lagre sikkerhetskopiene i en mappe på enheten - Manage or cancel + Administrer eller avslutt - Oppdater + Oppgrader %1$d/%2$d diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 5ed707c540..73f4ad696c 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -3319,8 +3319,6 @@ Beschikbaar om te praten Ik wil even offline zijn Ik werk aan iets nieuws - - Een of meer karakters zijn ongeldig. Groep bewerken @@ -5269,7 +5267,7 @@ Niet gedempt Vermeldingen Altijd een melding bij vermeldingen - Geen meldingen over vermeldingen + Geen meldingen als gedempt Aangepaste meldingen diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 3f5f65d245..ab4b360cdc 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -3319,8 +3319,6 @@ ਗੱਲਾਂ ਕਰਨ ਲਈ ਵੇਹਲੇ ਸਾਹ ਲਵੋ ਕੁਝ ਨਵਾਂ ਬਣਾ ਰਹੇ ਹਾਂ - - ਇੱਕ ਜਾਂ ਵੱਧ ਅੱਖਰ ਅਵੈਧ ਹਨ। ਗਰੁੱਪ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰੋ @@ -6001,7 +5999,7 @@ %1$d ਜਵਾਬ - Story no longer hidden + ਸਟੋਰੀ ਹੁਣ ਲੁਕੀ ਹੋਈ ਨਹੀਂ ਹੈ ਜੋੜੋ @@ -7287,22 +7285,22 @@ - ਆਪਣਾ ਮੀਡੀਆ ਰੀਸਟੋਰ ਕਰਨ ਲਈ %1$s ਥਾਂ ਖਾਲੀ ਕਰੋ।\n\n + ਆਪਣਾ ਮੀਡੀਆ ਰੀਸਟੋਰ ਕਰਨ ਲਈ %1$s ਥਾਂ ਖਾਲੀ ਕਰੋ। ਮੀਡੀਆ ਰੀਸਟੋਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ਰੀਸਟੋਰ ਕਰਨਾ ਰੋਕਿਆ ਗਿਆ - ਬਹਾਲ ਕਰਨ ਦਾ ਕਾਰਜ ਪੂਰਾ ਹੋਇਆ + ਰੀਸਟੋਰ ਕਰਨ ਦਾ ਕਾਰਜ ਪੂਰਾ ਹੋਇਆ - Waiting for Wi-Fi… + Wi-Fi ਦੀ ਉਡੀਕ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ… - No internet… + ਇੰਟਰਨੈੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ… - Device has low battery + ਡਿਵਾਈਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ - %2$s ਚੋਂ %1$s + %2$s ਵਿੱਚੋਂ %1$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/ਮਹੀਨਾ, ਰੀਨਿਊ ਹੋਣ ਦੀ ਮਿਤੀ: %2$s - Last backup %1$s + ਆਖਰੀ ਬੈਕਅੱਪ ਦੀ ਮਿਤੀ: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal ਦੀ ਸਿਰੇ-ਤੋਂ-ਸਿਰੇ ਤੱਕ ਇਨਕ੍ਰਿਪਟਡ ਸਟੋਰੇਜ ਸੇਵਾ ਦੇ ਨਾਲ ਆਟੋਮੈਟਿਕ ਬੈਕਅੱਪ। - Set up + ਸੈੱਟ ਅੱਪ ਕਰੋ @@ -7390,9 +7388,9 @@ ਸੈਲੂਲਰ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਬੈਕਅੱਪ ਲਓ - View backup key + ਬੈਕਅੱਪ ਕੁੰਜੀ ਦੇਖੋ - Unlock to view backup key + ਬੈਕਅੱਪ ਕੁੰਜੀ ਦੇਖਣ ਲਈ ਅਨਲੌਕ ਕਰੋ ਬੈਕਅੱਪ ਬੰਦ ਕਰੋ ਅਤੇ ਮਿਟਾਓ @@ -7406,25 +7404,25 @@ ਬੈਕਅੱਪ ਰਾਤੋ-ਰਾਤ ਬਣਾਇਆ ਜਾਵੇਗਾ। - Backup plan + ਬੈਕਅੱਪ ਪਲਾਨ ਬੈਕਅੱਪ ਨੂੰ ਅਸਮਰੱਥ ਕੀਤਾ ਗਿਆ %1$s/ਮਹੀਨਾ - Your backup plan is free + ਤੁਹਾਡਾ ਬੈਕਅੱਪ ਪਲਾਨ ਮੁਫ਼ਤ ਹੈ - %1$s ਨੂੰ ਰੀਨਿਊ ਹੁੰਦੀ ਹੈ + ਰੀਨਿਊ ਹੋਣ ਦੀ ਮਿਤੀ: %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + ਆਪਣੇ ਪੁਰਾਣੇ ਸੁਨੇਹਿਆਂ ਦਾ ਬੈਕਅੱਪ ਲਓ ਤਾਂ ਜੋ ਨਵਾਂ ਫ਼ੋਨ ਲੈਣ \'ਤੇ ਜਾਂ Signal ਨੂੰ ਦੁਬਾਰਾ ਇੰਸਟਾਲ ਕਰਨ \'ਤੇ ਵੀ ਤੁਹਾਡਾ ਡਾਟਾ ਸੁਰੱਖਿਅਤ ਰਹੇ। - Other ways to backup + ਬੈਕਅੱਪ ਲੈਣ ਦੇ ਹੋਰ ਤਰੀਕੇ - On-device backups + ਡਿਵਾਈਸ-ਉੱਤੇ ਬੈਕਅੱਪ - Save your backups to a folder on this device + ਆਪਣੇ ਬੈਕਅੱਪ ਨੂੰ ਇਸ ਡਿਵਾਈਸ \'ਤੇ ਇੱਕ ਫੋਲਡਰ ਵਿੱਚ ਸੁਰੱਖਿਅਤ ਕਰੋ - Manage or cancel + ਪ੍ਰਬੰਧਿਤ ਕਰੋ ਜਾਂ ਰੱਦ ਕਰੋ ਅੱਪਗ੍ਰੇਡ ਕਰੋ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e312bf2800..11de8f7194 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -3517,8 +3517,6 @@ Chętny na czat Robię przerwę Pracuję nad czymś nowym - - Co najmniej jeden znak jest nieprawidłowy. Edytuj grupę diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index e502a6692e..2e3664defb 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -3319,8 +3319,6 @@ Disponível para chat Relaxando Trabalhando em algo novo - - Um ou mais caracteres são inválidos. Editar grupo diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 694938908a..c9f248557e 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -3319,8 +3319,6 @@ Livre para conversar A fazer uma pausa A trabalhar em algo novo - - Um ou mais caracteres são inválidos. Editar o grupo @@ -6001,7 +5999,7 @@ %1$d respostas - Story no longer hidden + A história já não está oculta Adicionar @@ -7296,11 +7294,11 @@ Restauro completo - Waiting for Wi-Fi… + À espera de Wi-Fi… - No internet… + Sem internet… - Device has low battery + O dispositivo tem pouca bateria %1$s de %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/mês, renova a %2$s - Last backup %1$s + Ultima cópia de segurança %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Cópias de segurança automáticas com o serviço seguro de armazenamento encriptado de ponta a ponta do Signal. Configure @@ -7390,9 +7388,9 @@ Fazer cópia de segurança utilizando ligação móvel - View backup key + Ver chave da cópia de segurança - Unlock to view backup key + Desbloqueie para ver a chave da cópia de segurança Desative e elimine a cópia de segurança @@ -7406,25 +7404,25 @@ A cópia de segurança será criada durante a noite. - Backup plan + Plano de cópia de segurança Cópias de segurança desativadas %1$s/mês - Your backup plan is free + O seu plano de cópia de segurança é grátis Renovações %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Efetue cópias de segurança do seu histórico de mensagens para nunca perder dados ao mudar de telemóvel ou reinstalar o Signal. - Other ways to backup + Outras formas de fazer cópias de segurança - On-device backups + Cópias de segurança no dispositivo - Save your backups to a folder on this device + Guarde as suas cópias de segurança numa pasta neste dispositivo - Manage or cancel + Gerir ou cancelar Upgrade diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index dab4f22355..52ec20f81d 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -3418,8 +3418,6 @@ Disponibil/ă pentru conversații Într-o pauză Lucrez la ceva nou - - Unul sau mai multe caractere sunt nevalide. Editează grupul diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 69d57fbe26..67b16dc6b3 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -3517,8 +3517,6 @@ Свободен для чата Делаю перерыв Работаю над кое-чем новым - - Один или несколько символов недействительны. Редактировать группу @@ -6265,7 +6263,7 @@ %1$d ответов - Story no longer hidden + История больше не скрыта Добавить @@ -7612,11 +7610,11 @@ Восстановление завершено - Waiting for Wi-Fi… + Ожидание Wi-Fi… - No internet… + Нет интернета… - Device has low battery + Батарея устройства разряжена %1$s из %2$s @@ -7684,11 +7682,11 @@ - %1$s/month, renews %2$s + %1$s/месяц · продлится %2$s - Last backup %1$s + Последняя резервная копия %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Автоматическое резервное копирование с помощью службы безопасного хранения Signal, защищённой сквозным шифрованием. Настроить @@ -7706,9 +7704,9 @@ Резервное копирование с помощью сотовых данных - View backup key + Просмотреть резервный ключ - Unlock to view backup key + Разблокируйте для просмотра резервного ключа Отключить и удалить резервную копию @@ -7722,25 +7720,25 @@ Резервная копия будет создана за одну ночь. - Backup plan + План резервного копирования Резервное копирование отключено %1$s/месяц - Your backup plan is free + У вас бесплатный план резервного копирования Продлится %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Создайте резервную копию истории сообщений, и вы не потеряете ни одно сообщение при покупке нового телефона или переустановке Signal. - Other ways to backup + Другие способы резервного копирования - On-device backups + Резервное копирование на устройстве - Save your backups to a folder on this device + Сохраняйте резервные копии в папке на этом устройстве - Manage or cancel + Управлять или отменить Обновить diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index b823d7ff1c..7a9238cada 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -3517,8 +3517,6 @@ Môžeme početovať Dávam si prestávku Pracujem na niečom novom - - Jeden alebo viac znakov je neplatných. Upraviť skupinu @@ -6265,7 +6263,7 @@ %1$d odpovedí - Story no longer hidden + Príbeh už nie je skrytý Pridať @@ -7612,11 +7610,11 @@ Obnova dokončená - Waiting for Wi-Fi… + Čaká sa na Wi-Fi… - No internet… + Žiadne internetové pripojenie… - Device has low battery + Zariadenie má slabú batériu %1$s z %2$s @@ -7684,11 +7682,11 @@ - %1$s/month, renews %2$s + %1$s/mesiac, obnovenie %2$s - Last backup %1$s + Posledná záloha %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatické zálohovanie pomocou zabezpečeného úložiska Signal chráneného end-to-end šifrovaním. Nastaviť @@ -7706,9 +7704,9 @@ Zálohovať pomocou mobilnej siete - View backup key + Zobraziť záložný kľúč - Unlock to view backup key + Ak chcete zobraziť záložný kľúč, odomknite Vypnúť a vymazať zálohu @@ -7722,25 +7720,25 @@ Zálohovanie prebehne počas noci. - Backup plan + Plán zálohovania Zálohy sú vypnuté %1$s/mesiac - Your backup plan is free + Váš plán zálohovania je bezplatný Obnoví sa %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Zálohujte si históriu správ, aby ste pri výmene telefónu alebo opätovnom nainštalovaní Signalu už nikdy neprišli o dáta. - Other ways to backup + Iné spôsoby zálohovania - On-device backups + Zálohy na zariadení - Save your backups to a folder on this device + Uložte si zálohy do priečinka na tomto zariadení - Manage or cancel + Spravovať alebo zrušiť Aktualizovať diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 715b905c2a..b6506e0697 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -3517,8 +3517,6 @@ Za klepet! Možgani na off Delam na nečem … - - Eden ali več znakov je neveljavnih. Uredi skupino @@ -6265,7 +6263,7 @@ %1$d odgovorov - Story no longer hidden + Zgodba ni več skrita Dodaj @@ -7607,16 +7605,16 @@ Obnovitev medijev - Obnovitev je prekinjena + Obnovitev je začasno zaustavljena - Obnovitev uspešna + Obnovitev je bila uspešna - Waiting for Wi-Fi… + Čakanje na Wi-Fi … - No internet… + Ni internetne povezave … - Device has low battery + Naprava ima prazno baterijo %1$s od %2$s @@ -7684,13 +7682,13 @@ - %1$s/month, renews %2$s + %1$s/mesec, obnovi se %2$s - Last backup %1$s + Zadnja varnostna kopija: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Samodejno varnostno kopiranje s Signalovo varno storitvijo šifriranega shranjevanja od konca do konca. - Set up + Nastavi @@ -7706,9 +7704,9 @@ Varnostno kopiranje z uporabo mobilnega omrežja - View backup key + Ogled varnostnega ključa - Unlock to view backup key + Odklenite za ogled varnostnega ključa Izklopi in izbriši varnostno kopiranje @@ -7722,25 +7720,25 @@ Varnostna kopija bo ustvarjena čez noč. - Backup plan + Načrt varnostne kopije Varnostno kopiranje izklopljeno %1$s/mesec - Your backup plan is free + Vaš načrt varnostne kopije je brezplačen Obnovitev naročnine: %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Ustvarite varnostno kopijo zgodovine sporočil, da ne izgubite podatkov, ko dobite nov telefon ali znova namestite Signal. - Other ways to backup + Drugi načini varnostnega kopiranja - On-device backups + Varnostne kopije v napravi - Save your backups to a folder on this device + Varnostne kopije shranite v mapo v tej napravi - Manage or cancel + Upravljajte ali prekličite Posodobi diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index eac14816b7..4994ba09be 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -3319,8 +3319,6 @@ Bisedo falas Pushoni ca Po merrem me diçka të re - - Një ose më shumë karaktere janë të pavlefshme. Përpunoni grup @@ -6001,7 +5999,7 @@ %1$d përgjigje - Story no longer hidden + Postimi i përkohshëm nuk fshihet më Shtoje @@ -7296,11 +7294,11 @@ Rikthim i plotësuar - Waiting for Wi-Fi… + Në pritje të Wi-Fi… - No internet… + Nuk ka internet… - Device has low battery + Pajisja ka pak bateri %1$s nga %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/muaj, Rinovohet më %2$s - Last backup %1$s + Kopjeruajtja e fundit: %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Kopjeruajtjet automatike me shërbimin e sigurt të ruajtjes së koduar nga skaji në skaj të Signal. - Set up + Konfiguro @@ -7390,9 +7388,9 @@ Kopjeruaj duke perdorur internetin celular - View backup key + Shiko kodin e kopjeruajtjes - Unlock to view backup key + Zhblloko për të parë kodin e kopjeruajtjes Të çaktivizohen dhe fshihen kopjeruajtjet @@ -7406,25 +7404,25 @@ Kopjeruajtja do të krijohet brenda natës. - Backup plan + Plani i kopjeruajtjes Kopjeruajtjet u çaktivizuan %1$s/muaj - Your backup plan is free + Plani i kopjeruajtjes është falas Rinovohet më %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Bëj kopjeruajtje të historikut të mesazheve në mënyrë që të mos i humbasësh kurrë të dhënat kur merr një telefon të ri ose kur e riinstalon sërish Signal. - Other ways to backup + Mënyra të tjera për kopjeruajtje - On-device backups + Kopjeruajtjet në pajisje - Save your backups to a folder on this device + Ruaj kopjeruajtjet në një dosje në këtë pajisje - Manage or cancel + Menaxho ose anulo Përmirësojeni diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 2483d3108e..c69b1cab1c 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -3319,8 +3319,6 @@ Можемо да ћаскамо Правим паузу Радим на нечему новом - - Један или више знакова су неважећи. Уређивање групе diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 20f9b9c3ff..ef3266451f 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -3319,8 +3319,6 @@ Tillgänglig för att chatta Tar en rast Arbetar på något nytt - - Ett eller flera tecken är ogiltiga. Redigera grupp @@ -6001,7 +5999,7 @@ %1$d svar - Story no longer hidden + Storyn döljs inte längre Lägg till @@ -7296,11 +7294,11 @@ Återställningen är klar - Waiting for Wi-Fi… + Väntar på wifi … - No internet… + Inget internet … - Device has low battery + Enheten har låg batterinivå %1$s av %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/månad, förnyas %2$s - Last backup %1$s + Senaste säkerhetskopia %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Automatiska säkerhetskopieringar med Signals säkra totalsträckskrypterade lagringstjänst. Ställ in @@ -7390,9 +7388,9 @@ Säkerhetskopiera med mobilnät - View backup key + Visa säkerhetskopieringsnyckel - Unlock to view backup key + Lås upp för att visa säkerhetskopieringsnyckel Stänga av och ta bort säkerhetskopia @@ -7406,25 +7404,25 @@ Säkerhetskopia kommer att skapas över natten. - Backup plan + Säkerhetskopieringsplan Säkerhetskopior inaktiverade %1$s/månad - Your backup plan is free + Din säkerhetskopieringsplan är gratis Förnyas %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Säkerhetskopiera din meddelandehistorik så att du aldrig förlorar data när du skaffar en ny telefon eller installerar om Signal. - Other ways to backup + Andra sätt att säkerhetskopiera - On-device backups + Säkerhetskopiering på enheten - Save your backups to a folder on this device + Spara dina säkerhetskopior i en mapp på den här enheten - Manage or cancel + Hantera eller avbryt Uppgradera diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 42da18ddab..9bf7fbefc4 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -3319,8 +3319,6 @@ Ninapatikana kupiga gumzo Nimeenda mapumziko kidogo Napangia mambo mapya - - Herufi moja au zaidi ni batili. Hariri kikundi @@ -6001,7 +5999,7 @@ Majibu %1$d - Story no longer hidden + Stori haijafichwa tena Ongeza @@ -7296,11 +7294,11 @@ Hatua ya kurejesha imekamilika - Waiting for Wi-Fi… + Inasubiria Wi-Fi… - No internet… + Hakuna muunganisho wa mtandao… - Device has low battery + Chaji ya betri ya kifaa imepungua %1$s kwa %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/mwezi, kusasisha %2$s - Last backup %1$s + Uhifadhi nakala wa mwisho %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Hifadhi nakala kiotomatiki ukitumia huduma salama za uhifadhi wa Signal zilizosimbwa fiche. - Set up + Weka mipangilio @@ -7390,9 +7388,9 @@ Weka nakala kwa kutumia mtandao - View backup key + Tazama ufunguo mbadala - Unlock to view backup key + Fungua ili kutazama ufunguo mbadala Zima na ufute nakala @@ -7406,25 +7404,25 @@ Hifadhi nakala itaundwa usiku. - Backup plan + Mpango wa uhifadhi nakala Chelezo zimezimwa %1$s/mwezi - Your backup plan is free + Mpango wako wa uhifadhi nakala ni bure Kuhuishwa %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Hifadhi nakala ya historia ya jumbe wako ili usiwahi kupoteza data unapopata simu mpya au kusakinisha upya Signal. - Other ways to backup + Njia zingine za kuhifadhi nakala - On-device backups + Uhifadhi nakala wa kwenye kifaa - Save your backups to a folder on this device + Hifadhi nakala zako kwenye folda katika kifaa hiki - Manage or cancel + Dhibiti au ghairi Boresha diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 98c834f1cb..3f8ef205c4 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -3319,8 +3319,6 @@ சாட் செய்ய நேரம் இருக்கிறது ஓய்வெடுத்து கொண்டிருக்கிறேன் ஒரு புதிய விஷயத்தில் ஈடுபட்டுள்ளேன் - - ஒன்று அல்லது அதற்கு மேற்பட்ட எழுத்துக்கள் தவறாக உள்ளன. குழுவைத் திருத்து diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index de42b0c836..996e3be494 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -3319,8 +3319,6 @@ చాట్ చేయడానికి ఉచితం విశ్రాంతి తీసుకొంటున్నా క్రొత్తదానిపై పని చేస్తోంది - - ఒకటి లేదా అంతకంటే ఎక్కువ అక్షరాలు చెల్లవు. సమూహాన్ని మార్చు @@ -6001,7 +5999,7 @@ %1$d రిప్లైలు - Story no longer hidden + ఇకపై కధ దాచబడదు చేర్చు @@ -7296,11 +7294,11 @@ పునరుద్ధరణ పూర్తయింది - Waiting for Wi-Fi… + Wi-Fi కోసం నిరీక్షిస్తోంది… - No internet… + ఇంటర్నెట్ లేదు… - Device has low battery + పరికరంలో తక్కువ బ్యాటరీ ఉంది %1$s ఆఫ్ %2$s @@ -7368,13 +7366,13 @@ - %1$s/month, renews %2$s + %1$s/నెలకు, %2$s న పునరుద్ధరించబడుతుంది - Last backup %1$s + మునుపటి బ్యాకప్ %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal యొక్క సురక్షితమైన ఎండ్-టు-ఎండ్ గుప్తీకరించబడిన నిల్వ సేవతో ఆటోమేటిక్ బ్యాకప్స్. - Set up + సెటప్ చేయండి @@ -7390,9 +7388,9 @@ సెల్యులార్ ఉపయోగించి బ్యాకప్ చేయండి - View backup key + బ్యాకప్ కీని వీక్షించండి - Unlock to view backup key + బ్యాకప్ కీని వీక్షించడంచడానికి అన్‌లాక్ చేయండి బ్యాకప్‌ను ఆఫ్ చేసి మరియు తొలగించండి @@ -7406,25 +7404,25 @@ బ్యాకప్ రాత్రికి రాత్రి సృష్టించబడుతుంది. - Backup plan + బ్యాకప్ ప్రణాళిక బ్యాకప్‌లు నిలిపివేయబడ్డాయి %1$s/నెల - Your backup plan is free + మీ బ్యాకప్ ప్రణాళిక ఉచితం %1$s ను పునరుద్ధరిస్తుంది - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + మీ సందేశ చరిత్రను బ్యాకప్ చేసుకోండి దానివల్ల మీరు కొత్త ఫోన్ కొన్నప్పుడు గానీ లేదా Signal ను తిరిగి ఇన్‌స్టాల్ చేసినప్పుడు గానీ డేటాను ఎప్పటికీ కోల్పోరు. - Other ways to backup + బ్యాకప్ చేయడానికి ఇతర మార్గాలు - On-device backups + పరికరంలో బ్యాకప్స్ - Save your backups to a folder on this device + ఈ పరికరంలోని ఒక ఫోల్డర్‌లోకి మీ బ్యాకప్‌లను భద్రపరచండి - Manage or cancel + నిర్వహించండి లేదా రద్దు చేయండి అభివృద్ధి diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index ab6c00a871..492910e0ed 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -3220,8 +3220,6 @@ สะดวกคุย อยู่ระหว่างพัก อยู่ระหว่างการทำอะไรใหม่ๆ - - มีอักขระที่ไม่ถูกต้องอย่างน้อยหนึ่งตัว แก้ไขกลุ่ม @@ -5869,7 +5867,7 @@ ตอบกลับ %1$d - Story no longer hidden + ยกเลิกการซ่อนสตอรี่แล้ว เพิ่ม @@ -7138,13 +7136,13 @@ กู้คืนเสร็จสิ้น - Waiting for Wi-Fi… + กำลังรอสัญญาณ Wi-Fi… - No internet… + ไม่มีการเชื่อมต่ออินเทอร์เน็ต… - Device has low battery + อุปกรณ์มีแบตเตอรี่ต่ำ - %1$s ของ %2$s + %1$s จาก %2$s @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + %1$s/เดือน ต่ออายุ %2$s - Last backup %1$s + สำรองข้อมูลล่าสุด %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + สำรองข้อมูลอัตโนมัติด้วยบริการจัดเก็บที่ปลอดภัยและถูกเข้ารหัสตั้งแต่ต้นทางถึงปลายทางของ Signal ตั้งชื่อผู้ใช้ @@ -7232,9 +7230,9 @@ สำรองข้อมูลขณะใช้อินเทอร์เน็ตมือถือ - View backup key + ดูกุญแจสำรอง - Unlock to view backup key + ปลดล็อกเพื่อดูกุญแจสำรอง ปิดใช้งานและลบข้อมูลสำรอง @@ -7248,27 +7246,27 @@ ระบบจะสำรองข้อมูลในช่วงกลางคืน - Backup plan + แพ็กเกจสำรองข้อมูล การสำรองข้อมูลถูกปิดใช้งาน %1$s/เดือน - Your backup plan is free + แพ็กเกจสำรองข้อมูลที่คุณเลือกไม่มีค่าใช้จ่าย ต่ออายุ %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + สำรองประวัติการส่งข้อความของคุณ ช่วยเก็บรักษาข้อมูลแม้ในกรณีที่คุณเปลี่ยนโทรศัพท์เครื่องใหม่หรือติดตั้ง Signal อีกครั้ง - Other ways to backup + วิธีอื่นในการสำรองข้อมูล - On-device backups + สำรองข้อมูลไว้บนอุปกรณ์ - Save your backups to a folder on this device + บันทึกข้อมูลสำรองของคุณไว้ในโฟลเดอร์บนอุปกรณ์เครื่องนี้ - Manage or cancel + จัดการหรือยกเลิก - ปรับรุ่น + อัปเกรด %1$d/%2$d diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index bb07a57fc9..9f2bc5d555 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -3319,8 +3319,6 @@ Free to chat Taking a break Working on something new - - Invalid ang isa o mahigit pang characters. I-edit ang grupo diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index fa1471c54a..0f53d8141f 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -3319,8 +3319,6 @@ Sohbete açık Dinleniyor Yeni bir şeyler üzerine çalışıyor - - Bir veya daha fazla karakter geçersiz. Grubu düzenle @@ -6001,7 +5999,7 @@ %1$d yanıt - Story no longer hidden + Kısa hikaye artık gizlenmiyor Ekle @@ -7296,11 +7294,11 @@ Geri yükleme tamamlandı - Waiting for Wi-Fi… + Wi-Fi bekleniyor… - No internet… + İnternet yok… - Device has low battery + Cihazın pili zayıf %1$s / %2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/ay, yenilenme %2$s - Last backup %1$s + Son yedekleme %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal\'in güvenli uçtan uca şifrelenmiş depolama hizmeti ile otomatik yedeklemeler. Ayarla @@ -7390,9 +7388,9 @@ Hücresel bağlantı kullanarak yedekleme - View backup key + Yedekleme anahtarını görüntüle - Unlock to view backup key + Yedekleme anahtarını görüntülemek için kilidi aç Yedeklemeyi kapat ve sil @@ -7406,25 +7404,25 @@ Yedekleme gece boyunca oluşturulur. - Backup plan + Yedekleme planı Yedeklemeler devre dışı %1$s/ay - Your backup plan is free + Yedekleme planın ücretsizdir %1$s tarihinde yenilenir - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Mesaj geçmişini yedekle böylece yeni bir telefon aldığında veya Signal\'i yeniden yüklediğinde asla veri kaybetmezsin. - Other ways to backup + Diğer yedekleme yolları - On-device backups + Cihazda yedeklemeler - Save your backups to a folder on this device + Yedeklemelerini bu cihazdaki bir klasöre kaydet - Manage or cancel + Yönet veya iptal et Yükselt diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index f9d438a4c3..8b46ebe42a 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -3220,8 +3220,6 @@ پاراڭلىشايلى ئارام ئارايلى يېڭىلىقنى سىنايلى - - بىر ياكى بىر نەچچە بەلگىلەر ئىناۋەتسىز. گۇرۇپپا تەھرىر diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 81c51507ff..90250cb2b2 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -547,7 +547,7 @@ Ця людина більше не використовує Signal. Запросіть її в Signal, щоб спілкуватися тут і надалі. - Запросити до Signal + Запросити в Signal Незабаром ви отримаєте нове нагадування. @@ -1592,7 +1592,7 @@ Закрити Надсилання… Запрошення надіслано! - Запросити до Signal + Запросити в Signal Надіслати SMS (%1$d) Надіслати %1$d SMS-запрошення? @@ -2567,8 +2567,8 @@ Проксі-сервер Адреса проксі-сервера Використовувати цю адресу проксі-сервера? - Використати проксі-сервер - Ви під\'єдналися до проксі-сервера. + Використовувати проксі-сервер + Ви з\'єдналися з проксі-сервером. Не вдалося надіслати @@ -2685,13 +2685,13 @@ Додати до контактів - Запросити до Signal + Запросити в Signal Повідомлення Signal Виклик у Signal Додати до контактів - Запросити до Signal + Запросити в Signal Повідомлення Signal @@ -3175,7 +3175,7 @@ Повторіть нову фразу-пароль - Запросити до Signal + Запросити в Signal Нова група Оновити контакти @@ -3517,8 +3517,6 @@ Маю час початитися Відпочиваю Працюю над чимось новим - - Ви використали принаймні один недозволений символ. Редагувати групу @@ -3815,10 +3813,10 @@ Вимкнути персоналізоване навчання клавіатури. Це налаштування спрацьовує не у всіх випадках. Ваша клавіатура може його ігнорувати. - При використанні мобільних даних - При використанні Wi-Fi - Під час роумінгу - Автозавантаження медіа + Через мобільний інтернет + Через Wi-Fi + У роумінгу + Автозавантаження медіафайлів Історія повідомлень Використання пам\'яті Фото @@ -3894,22 +3892,22 @@ Захищений відправник Дозволити від будь-кого Отримувати повідомлення від захищених відправників, які не з вашого списку контактів, або з якими ви не поділилися профілем. - Проксі - Використати проксі + Проксі-сервер + Використовувати проксі-сервер Вимк. Увімк. - Адреса проксі - Використовуйте проксі тільки якщо ви не можете підключитися до Signal через мобільні дані або Wi-Fi. + Адреса проксі-сервера + Використовуйте проксі-сервер, тільки якщо не можете встановити з\'єднання з Signal через мобільний інтернет або Wi-Fi. Поділитися Зберегти Триває з\'єднання з проксі-сервером… - Під\'єднано до проксі + З\'єднання з проксі-сервером установлено З\'єднання не вдалася! - Помилка з\'єднання із проксі. Перевірте адресу проксі і спробуйте ще раз. + Помилка з\'єднання з проксі-сервером. Перевірте, чи правильно введено адресу, й повторіть спробу. Ви з\'єднані з проксі-сервером. Проксі-сервер можна завжди вимкнути в налаштуваннях. Успіх Не вдалось підключитися - Введіть адресу проксі + Введіть адресу проксі-сервера Розмір панелі навігації @@ -5358,7 +5356,7 @@ Якість медіафайлів Якість медіафайлів, які ви надсилаєте - Відправлення медіа високої якості споживатиме більше даних. + Надсилання медіафайлів високої якості споживатиме більше трафіку. Висока @@ -5475,7 +5473,7 @@ Пошук Тимчасові повідомлення - Звуки та сповіщення + Звуки і сповіщення Дані з контактів телефона Переглянути код безпеки @@ -6865,7 +6863,7 @@ Покинути Signal для підтвердження доната? - Після підтвердження поверніться до Signal, щоб завершити обробку доната. + Після підтвердження поверніться в Signal, щоб завершити обробку доната. @@ -7481,7 +7479,7 @@ Невірний номер телефону - Запросити до Signal + Запросити в Signal Ім\'я користувача не знайдено diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 2d0f96cec6..8e9cb5ee41 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -3319,8 +3319,6 @@ چیٹ کرنے کے لیے دستیاب ایک وقفے پر کسی نئی چیز پر کام کرنا - - ایک یا زیادہ حروف غلط ہیں۔ گروپ میں ترمیم کریں @@ -6001,7 +5999,7 @@ %1$d جوابات - Story no longer hidden + سٹوری مزید چھپی ہوئی نہیں ہے شامل کریں @@ -7296,11 +7294,11 @@ بحالی مکمل - Waiting for Wi-Fi… + Wi-Fi کا انتظار ہو رہا ہے… - No internet… + انٹرنیٹ نہیں ہے… - Device has low battery + ڈیوائس کی بیٹری کم ہے %1$sکا%2$s @@ -7368,11 +7366,11 @@ - %1$s/month, renews %2$s + %1$s/ماہانہ، %2$s کو تجدید ہوتی ہے - Last backup %1$s + %1$s کو آخری بیک اپ - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Signal کی محفوظ اینڈ ٹو اینڈ انکرپٹ کردہ اسٹوریج سروس کے حامل خودکار بیک اپس۔ سیٹ اپ @@ -7390,9 +7388,9 @@ سیلولر کا استعمال کرتے ہوئے بیک اپ کریں - View backup key + بیک اپ کیی دیکھیں - Unlock to view backup key + بیک اپ کیی دیکھنے کے لیے ان لاک کریں بند کریں اور بیک اپ حذف کریں @@ -7406,25 +7404,25 @@ بیک اپ رات تیار کیا جائے گا۔ - Backup plan + بیک اپ پلان بیک اپس غیر فعال کر دیے گئے %1$s/ماہ - Your backup plan is free + آپ کا بیک اپ پلان مفت ہے %1$s کی تجدید کرتا ہے - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + اپنی میسج ہسٹری کا بیک اپ کریں تاکہ جب بھی آپ کوئی نیا فون لیں یا Signal کو دوبارہ انسٹال کریں تو اس صورت میں آپ کا ڈیٹا کبھی بھی ضائع نہ ہو۔ - Other ways to backup + بیک اپ کے دیگر طریقے - On-device backups + آن ڈیوائس بیک اپس - Save your backups to a folder on this device + اپنے بیک اپس کو اس ڈیوائس کے کسی فولڈر میں محفوظ کریں - Manage or cancel + نظم کریں یا منسوخ کریں اپ گریڈ diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 6e399b184a..a206948541 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -3220,8 +3220,6 @@ Rảnh để trò chuyện Đang nghỉ ngơi Đang làm một dự án mới - - Có một hoặc nhiều ký tự không hợp lệ. Sửa nhóm @@ -5869,7 +5867,7 @@ %1$d câu trả lời - Story no longer hidden + Story hiện không còn ẩn Thêm @@ -7138,11 +7136,11 @@ Hoàn tất khôi phục - Waiting for Wi-Fi… + Đang chờ kết nối Wi-Fi… - No internet… + Không có kết nối mạng… - Device has low battery + Thiết bị hiện yếu pin %1$s trên tổng số %2$s @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + %1$s/tháng, gia hạn %2$s - Last backup %1$s + Sao lưu gần đây nhất %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + Sao lưu tự động với dịch vụ lưu trữ bảo mật, được mã hóa đầu cuối của Signal. Thiết Lập @@ -7232,9 +7230,9 @@ Sao lưu bằng dữ liệu di động - View backup key + Xem mã khóa sao lưu - Unlock to view backup key + Mở khóa để xem mã khóa sao lưu Tắt và xóa bản sao lưu @@ -7248,25 +7246,25 @@ Bản sao lưu sẽ được tạo trong đêm. - Backup plan + Gói sao lưu Đã tắt sao lưu %1$s/tháng - Your backup plan is free + Bạn đang sử dụng gói miễn phí Gia hạn %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + Sao lưu lịch sử tin nhắn để không bao giờ mất dữ liệu khi bạn có điện thoại mới hoặc cài lại Signal. - Other ways to backup + Cách sao lưu khác - On-device backups + Bản sao lưu trên thiết bị - Save your backups to a folder on this device + Lưu bản sao lưu của bạn trong một thư mục trên thiết bị này - Manage or cancel + Quản lý hoặc hủy Nâng cấp diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index 330a36625a..2b07df3f3b 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -3220,8 +3220,6 @@ 以咪高風大預言一個十年 不必搜索物證 想一想 神話的要點 顛覆角度 哪懼無路 - - 一個或多個字元無效。 編輯個谷 @@ -5869,7 +5867,7 @@ %1$d 個回覆 - Story no longer hidden + 限時動態唔再隱藏 加入去 @@ -7135,14 +7133,14 @@ 還原已暫停 - 還原搞掂 + 還原已完成 - Waiting for Wi-Fi… + 等緊 Wi-Fi… - No internet… + 連唔到上網… - Device has low battery + 裝置電量偏低 %1$s / %2$s @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + 每月 %1$s · %2$s 續期 - Last backup %1$s + 上次備份 %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + 用 Signal 嘅端對端加密儲存服務安全自動備份。 設定 @@ -7232,9 +7230,9 @@ 用流動數據備份 - View backup key + 睇吓備份金鑰 - Unlock to view backup key + 解鎖就會睇到備份金鑰 閂咗佢同埋刪除備份 @@ -7248,25 +7246,25 @@ 備份將會通宵建立。 - Backup plan + 備份計劃 停用咗備份 每月 %1$s - Your backup plan is free + 你嘅備份計劃係免費嘅 - 到 %1$s 續期 + %1$s 續期 - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + 備份你嘅訊息紀錄,當你換新手機或者重新安裝 Signal 嗰陣,就唔會冇咗啲資料啦。 - Other ways to backup + 其他備份方式 - On-device backups + 裝置上備份 - Save your backups to a folder on this device + 將備份儲存喺呢部機嘅資料夾 - Manage or cancel + 管理或者取消 升級 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2da97484d3..e435967332 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -3220,8 +3220,6 @@ 陪聊不收费 休息一下下 打磨新玩意儿 - - 一个或多个字符无效。 编辑群组 @@ -5869,7 +5867,7 @@ %1$d 个回复 - Story no longer hidden + 动态已公开 添加 @@ -7138,11 +7136,11 @@ 还原完成 - Waiting for Wi-Fi… + 正在等待 Wi-Fi 连接… - No internet… + 没有网络… - Device has low battery + 设备电池电量不足 %1$s / %2$s 匹配 @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + %1$s/月,%2$s续期 - Last backup %1$s + 上次备份 %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + 通过 Signal 的安全端对端加密存储服务自动备份。 设置 @@ -7232,9 +7230,9 @@ 使用蜂窝网络备份 - View backup key + 查看备份密钥 - Unlock to view backup key + 解锁以查看备份密钥 关闭并删除备份 @@ -7248,25 +7246,25 @@ 备份将在夜间创建。 - Backup plan + 备份套餐 已停用备份 %1$s/月 - Your backup plan is free + 您使用的是免费备份套餐 到 %1$s 续期 - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + 备份消息记录可以确保您在更换手机或重装 Signal 时不会丢失数据。 - Other ways to backup + 其他备份方式 - On-device backups + 设备本地备份 - Save your backups to a folder on this device + 将您的备份保存在本设备的文件夹中 - Manage or cancel + 管理或取消 升级 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 80e70f4b95..8b871e664e 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -3220,8 +3220,6 @@ 即使要蒙著我嘴 我亦可高呼 人間有味是清歡 新世紀 我請你要有準備 - - 部分字元無效。 編輯群組 @@ -5869,7 +5867,7 @@ %1$d 則回覆 - Story no longer hidden + 限時動態不再隱藏 新增 @@ -7138,11 +7136,11 @@ 還原完成 - Waiting for Wi-Fi… + 等待 Wi-Fi… - No internet… + 沒有網路連線… - Device has low battery + 裝置電池電量不足 第 %1$s 個,共 %2$s 個 @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + %1$s/月 · %2$s 續訂 - Last backup %1$s + 上次備份 %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + 使用 Signal 的安全端對端加密儲存服務自動備份。 設定 @@ -7232,9 +7230,9 @@ 使用行動數據備份 - View backup key + 檢視備份金鑰 - Unlock to view backup key + 解鎖以檢視備份金鑰 關閉並刪除備份 @@ -7248,25 +7246,25 @@ 備份將會通宵建立。 - Backup plan + 備份計畫 備份已停用 %1$s/月 - Your backup plan is free + 你的備份計畫是免費的 %1$s 續期 - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + 備份你的訊息紀錄,讓你在使用新手機或重新安裝 Signal 時,絕不會遺失資料。 - Other ways to backup + 其他備份方式 - On-device backups + 裝置上備份 - Save your backups to a folder on this device + 將備份儲存到此裝置上的資料夾 - Manage or cancel + 辦理或取消 升級 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 2cc3919e67..f22788c42f 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -3220,8 +3220,6 @@ 有空聊天 正在休息中 從事新事物 - - 部分字元無效。 編輯群組 @@ -5869,7 +5867,7 @@ %1$d 則回覆 - Story no longer hidden + 限時動態不再隱藏 新增 @@ -7138,11 +7136,11 @@ 回復完成 - Waiting for Wi-Fi… + 等待 Wi-Fi… - No internet… + 沒有網路連線… - Device has low battery + 裝置電池電量不足 第 %1$s 個,共 %2$s 個 @@ -7210,11 +7208,11 @@ - %1$s/month, renews %2$s + %1$s/月 · %2$s 續訂 - Last backup %1$s + 上次備份 %1$s - Automatic backups with Signal\'s secure end-to-end encrypted storage service. + 使用 Signal 的安全端對端加密儲存服務自動備份。 設定 @@ -7232,9 +7230,9 @@ 使用行動數據備份 - View backup key + 檢視備份金鑰 - Unlock to view backup key + 解鎖以檢視備份金鑰 關閉並刪除備份 @@ -7248,25 +7246,25 @@ 備份將會通宵建立。 - Backup plan + 備份計畫 備份已停用 %1$s/月 - Your backup plan is free + 你的備份計畫是免費的 續約 %1$s - Back up your message history so you never lose data when you get a new phone or reinstall Signal. + 備份你的訊息紀錄,讓你在使用新手機或重新安裝 Signal 時,絕不會遺失資料。 - Other ways to backup + 其他備份方式 - On-device backups + 裝置上備份 - Save your backups to a folder on this device + 將備份儲存到此裝置上的資料夾 - Manage or cancel + 辦理或取消 升級 From 1db7358bfa04fcebf351bbf226f7b973a635ec12 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 7 Oct 2024 17:00:16 -0400 Subject: [PATCH 53/53] Bump version to 7.19.2 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f6e69c2476..a3c592b265 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1467 -val canonicalVersionName = "7.19.1" +val canonicalVersionCode = 1468 +val canonicalVersionName = "7.19.2" val currentHotfixVersion = 0 val maxHotfixVersions = 100