From 046b8da8806c6891d2837222f7edba0a4dfbd1a9 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 11 Apr 2024 11:07:55 -0400 Subject: [PATCH 001/113] Add missing static IPs. Fixes #13513 --- app/build.gradle.kts | 1 + .../thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt | 4 +++- app/static-ips.gradle.kts | 1 + build-logic/plugins/src/main/java/translations.gradle | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 21f99fab7b..9400d1b705 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -195,6 +195,7 @@ android { buildConfigField("String[]", "SIGNAL_CONTENT_PROXY_IPS", rootProject.extra["content_proxy_ips"] as String) buildConfigField("String[]", "SIGNAL_CDSI_IPS", rootProject.extra["cdsi_ips"] as String) buildConfigField("String[]", "SIGNAL_SVR2_IPS", rootProject.extra["svr2_ips"] as String) + buildConfigField("String[]", "SIGNAL_KEY_BACKUP_IPS", rootProject.extra["key_backup_ips"] as String) buildConfigField("String", "SIGNAL_AGENT", "\"OWA\"") buildConfigField("String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\"") buildConfigField("String", "SVR2_MRENCLAVE", "\"a6622ad4656e1abcd0bc0ff17c229477747d2ded0495c4ebee7ed35c1789fa97\"") 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 6eafb03261..4ed388b261 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt @@ -48,10 +48,12 @@ open class SignalServiceNetworkAccess(context: Context) { BuildConfig.STORAGE_URL.stripProtocol() to BuildConfig.SIGNAL_STORAGE_IPS.toSet(), BuildConfig.SIGNAL_CDN_URL.stripProtocol() to BuildConfig.SIGNAL_CDN_IPS.toSet(), BuildConfig.SIGNAL_CDN2_URL.stripProtocol() to BuildConfig.SIGNAL_CDN2_IPS.toSet(), + BuildConfig.SIGNAL_CDN3_URL.stripProtocol() to BuildConfig.SIGNAL_CDN3_IPS.toSet(), BuildConfig.SIGNAL_SFU_URL.stripProtocol() to BuildConfig.SIGNAL_SFU_IPS.toSet(), BuildConfig.CONTENT_PROXY_HOST.stripProtocol() to BuildConfig.SIGNAL_CONTENT_PROXY_IPS.toSet(), BuildConfig.SIGNAL_CDSI_URL.stripProtocol() to BuildConfig.SIGNAL_CDSI_IPS.toSet(), - BuildConfig.SIGNAL_SVR2_URL.stripProtocol() to BuildConfig.SIGNAL_SVR2_IPS.toSet() + BuildConfig.SIGNAL_SVR2_URL.stripProtocol() to BuildConfig.SIGNAL_SVR2_IPS.toSet(), + BuildConfig.SIGNAL_KEY_BACKUP_URL.stripProtocol() to BuildConfig.SIGNAL_KEY_BACKUP_IPS.toSet() ) ) ) diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index 4ca12e906a..5f1d9b88fe 100644 --- a/app/static-ips.gradle.kts +++ b/app/static-ips.gradle.kts @@ -7,3 +7,4 @@ rootProject.extra["sfu_ips"] = """new String[]{"34.36.104.134"}""" 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"}""" +rootProject.extra["key_backup_ips"] = """new String[]{"240.0.0.1"}""" diff --git a/build-logic/plugins/src/main/java/translations.gradle b/build-logic/plugins/src/main/java/translations.gradle index 06a3658ebb..0f6a0c0f56 100644 --- a/build-logic/plugins/src/main/java/translations.gradle +++ b/build-logic/plugins/src/main/java/translations.gradle @@ -117,6 +117,7 @@ task resolveStaticIps { rootProject.extra["content_proxy_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("contentproxy.signal.org")}\"\"\" rootProject.extra["svr2_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("svr2.signal.org")}\"\"\" rootProject.extra["cdsi_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdsi.signal.org")}\"\"\" + rootProject.extra["key_backup_ips"] = "\"\"${staticIpResolver.resolveToBuildConfig("api.backup.signal.org")}\"\"\" """.stripIndent().trim() + "\n" } } From 8617a074adfed0176053d2b61957341b5bbae0df Mon Sep 17 00:00:00 2001 From: tedgravlin Date: Sun, 4 Feb 2024 23:00:56 -0500 Subject: [PATCH 002/113] Update CLA link in PR template. --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 543e006459..63d416fa08 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,7 +2,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 signed the [Contributor License Agreement](https://whispersystems.org/cla/) +- [ ] I have signed the [Contributor License Agreement](https://signal.org/cla/) ### Contributor checklist From 689eacd61893b2cdff68c5ef106898e45a59767e Mon Sep 17 00:00:00 2001 From: Clark Date: Fri, 12 Apr 2024 11:57:34 -0400 Subject: [PATCH 003/113] Add initial support for backup and restore of message and media to staging. Co-authored-by: Cody Henthorne --- .../securesms/backup/v2/ImportExportTest.kt | 38 +- .../conversation/ConversationItemPreviewer.kt | 4 +- .../database/AttachmentTableTest_deduping.kt | 7 +- app/src/main/AndroidManifest.xml | 4 + .../attachments/ArchivedAttachment.kt | 88 ++++ .../securesms/attachments/Attachment.kt | 6 +- .../attachments/AttachmentCreator.kt | 4 +- .../thoughtcrime/securesms/attachments/Cdn.kt | 53 +++ .../attachments/DatabaseAttachment.kt | 27 +- .../attachments/PointerAttachment.kt | 42 +- .../attachments/TombstoneAttachment.kt | 41 +- .../securesms/attachments/UriAttachment.kt | 2 +- .../securesms/backup/RestoreState.kt | 36 ++ .../securesms/backup/v2/BackupRepository.kt | 237 ++++++++-- .../backup/v2/BackupRestoreManager.kt | 46 ++ .../backup/v2/BatchArchiveMediaResult.kt | 39 ++ .../v2/database/ChatItemExportIterator.kt | 62 ++- .../v2/database/ChatItemImportInserter.kt | 54 ++- .../database/MessageTableBackupExtensions.kt | 4 +- .../v2/processor/ChatItemBackupProcessor.kt | 2 +- .../ui/MessageBackupsTestRestoreActivity.kt | 21 + .../ui/MessageBackupsTestRestoreViewModel.kt | 19 +- .../InternalBackupPlaygroundFragment.kt | 170 +++++-- .../InternalBackupPlaygroundViewModel.kt | 271 +++++++---- .../conversation/ConversationItem.java | 6 +- .../v2/data/ConversationDataSource.kt | 8 + .../securesms/database/AttachmentTable.kt | 265 ++++++++++- .../securesms/database/GroupTable.kt | 5 +- .../securesms/database/MediaTable.kt | 3 + .../securesms/database/MessageTable.kt | 6 +- .../helpers/SignalDatabaseMigrations.kt | 6 +- .../V224_AddAttachmentArchiveColumns.kt | 22 + .../securesms/jobs/ArchiveAttachmentJob.kt | 78 ++++ .../securesms/jobs/AttachmentDownloadJob.java | 350 --------------- .../securesms/jobs/AttachmentDownloadJob.kt | 424 ++++++++++++++++++ .../jobs/AvatarGroupsV1DownloadJob.java | 2 +- .../securesms/jobs/BackupMessagesJob.kt | 113 +++++ .../securesms/jobs/BackupRestoreJob.kt | 105 +++++ .../securesms/jobs/BackupRestoreMediaJob.kt | 83 ++++ .../securesms/jobs/JobManagerFactories.java | 5 + .../securesms/jobs/PushSendJob.java | 2 +- .../securesms/jobs/RestoreAttachmentJob.kt | 400 +++++++++++++++++ .../securesms/keyvalue/BackupValues.kt | 47 +- .../migrations/LegacyMigrationJob.java | 2 +- .../notifications/NotificationIds.java | 1 + .../releasechannel/ReleaseChannel.kt | 7 +- .../service/AttachmentProgressService.kt | 2 +- .../service/BackupProgressService.kt | 120 +++++ .../thoughtcrime/securesms/stories/Stories.kt | 2 +- app/src/main/protowire/Backup.proto | 48 +- app/src/main/protowire/JobData.proto | 4 + app/src/main/res/values/strings.xml | 6 + .../sms/UploadDependencyGraphTest.kt | 7 +- .../securesms/database/FakeMessageRecords.kt | 15 +- .../java/org/signal/core/util/logging/Log.kt | 7 + .../api/SignalServiceMessageReceiver.java | 61 ++- .../api/SignalServiceMessageSender.java | 4 +- .../signalservice/api/archive/ArchiveApi.kt | 9 + .../archive/ArchiveGetBackupInfoResponse.kt | 2 + .../GetArchiveCdnCredentialsResponse.kt | 15 + .../signalservice/api/backup/BackupId.kt | 8 + .../signalservice/api/backup/BackupKey.kt | 32 +- .../signalservice/api/backup/MediaId.kt | 5 +- .../signalservice/api/backup/MediaName.kt | 24 + .../crypto/AttachmentCipherInputStream.java | 51 ++- .../SignalServiceAttachmentRemoteId.java | 67 --- .../SignalServiceAttachmentRemoteId.kt | 54 +++ .../api/util/AttachmentPointerUtil.java | 8 +- .../internal/push/PushServiceSocket.java | 49 +- .../websocket/WebSocketConnection.java | 2 +- .../api/crypto/AttachmentCipherTest.java | 125 ++++++ 71 files changed, 3199 insertions(+), 745 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/attachments/Cdn.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/RestoreState.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRestoreManager.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/BatchArchiveMediaResult.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V224_AddAttachmentArchiveColumns.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentJob.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreMediaJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/service/BackupProgressService.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/GetArchiveCdnCredentialsResponse.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaName.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt index f7d55cf983..12acc4f3f9 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt @@ -1008,17 +1008,47 @@ class ImportExportTest { attachmentLocator = FilePointer.AttachmentLocator( cdnKey = "coolCdnKey", cdnNumber = 2, - uploadTimestamp = System.currentTimeMillis() + uploadTimestamp = System.currentTimeMillis(), + key = (1..32).map { it.toByte() }.toByteArray().toByteString(), + size = 12345, + digest = (1..32).map { it.toByte() }.toByteArray().toByteString() ), - key = (1..32).map { it.toByte() }.toByteArray().toByteString(), contentType = "image/png", - size = 12345, fileName = "very_cool_picture.png", width = 100, height = 200, caption = "Love this cool picture!", incrementalMacChunkSize = 0 - ) + ), + wasDownloaded = true + ), + MessageAttachment( + pointer = FilePointer( + invalidAttachmentLocator = FilePointer.InvalidAttachmentLocator(), + contentType = "image/png", + width = 100, + height = 200, + caption = "Love this cool picture! Too bad u cant download it", + incrementalMacChunkSize = 0 + ), + wasDownloaded = false + ), + MessageAttachment( + pointer = FilePointer( + backupLocator = FilePointer.BackupLocator( + "digestherebutimlazy", + cdnNumber = 3, + key = (1..32).map { it.toByte() }.toByteArray().toByteString(), + digest = (1..64).map { it.toByte() }.toByteArray().toByteString(), + size = 12345 + ), + contentType = "image/png", + width = 100, + height = 200, + caption = "Love this cool picture! Too bad u cant download it", + incrementalMacChunkSize = 0 + ), + wasDownloaded = true ) ) ) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt index 16f6ca75af..2ea7db24ab 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt @@ -7,6 +7,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.signal.core.util.ThreadUtil +import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.PointerAttachment import org.thoughtcrime.securesms.conversation.v2.ConversationActivity import org.thoughtcrime.securesms.database.MessageType @@ -15,7 +16,6 @@ import org.thoughtcrime.securesms.mms.IncomingMessage import org.thoughtcrime.securesms.mms.OutgoingMessage import org.thoughtcrime.securesms.profiles.ProfileName import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.releasechannel.ReleaseChannel import org.thoughtcrime.securesms.testing.SignalActivityRule import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId @@ -137,7 +137,7 @@ class ConversationItemPreviewer { private fun attachment(): SignalServiceAttachmentPointer { return SignalServiceAttachmentPointer( - ReleaseChannel.CDN_NUMBER, + Cdn.CDN_3.cdnNumber, SignalServiceAttachmentRemoteId.from(""), "image/webp", null, 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 54f1138ad3..d5f8015ce2 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 @@ -14,6 +14,7 @@ import org.junit.runner.RunWith import org.signal.core.util.Base64 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.database.AttachmentTable.TransformProperties import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -742,7 +743,7 @@ class AttachmentTableTest_deduping { assertArrayEquals(lhsAttachment.remoteDigest, rhsAttachment.remoteDigest) assertArrayEquals(lhsAttachment.incrementalDigest, rhsAttachment.incrementalDigest) assertEquals(lhsAttachment.incrementalMacChunkSize, rhsAttachment.incrementalMacChunkSize) - assertEquals(lhsAttachment.cdnNumber, rhsAttachment.cdnNumber) + assertEquals(lhsAttachment.cdn.cdnNumber, rhsAttachment.cdn.cdnNumber) } fun assertDoesNotHaveRemoteFields(attachmentId: AttachmentId) { @@ -751,7 +752,7 @@ class AttachmentTableTest_deduping { assertNull(databaseAttachment.remoteLocation) assertNull(databaseAttachment.remoteDigest) assertNull(databaseAttachment.remoteKey) - assertEquals(0, databaseAttachment.cdnNumber) + assertEquals(0, databaseAttachment.cdn.cdnNumber) } fun assertSkipTransform(attachmentId: AttachmentId, state: Boolean) { @@ -776,7 +777,7 @@ class AttachmentTableTest_deduping { AttachmentTable.TRANSFER_PROGRESS_DONE, databaseAttachment.size, // size null, - 3, // cdnNumber + Cdn.CDN_3, // cdnNumber location, key, digest, diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f419d81f70..b292a8337c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1162,6 +1162,10 @@ android:name=".service.AttachmentProgressService" android:exported="false"/> + + diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt new file mode 100644 index 0000000000..4c732d27a6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.attachments + +import android.net.Uri +import android.os.Parcel +import org.signal.core.util.Base64 +import org.thoughtcrime.securesms.blurhash.BlurHash +import org.thoughtcrime.securesms.database.AttachmentTable + +class ArchivedAttachment : Attachment { + + @JvmField + val archiveCdn: Int + + @JvmField + val archiveMediaName: String + + @JvmField + val archiveMediaId: String + + constructor( + contentType: String?, + size: Long, + cdn: Cdn, + cdnKey: ByteArray, + archiveMediaName: String, + archiveMediaId: String, + digest: ByteArray, + incrementalMac: ByteArray?, + incrementalMacChunkSize: Int?, + width: Int?, + height: Int?, + caption: String?, + blurHash: String?, + voiceNote: Boolean, + borderless: Boolean, + gif: Boolean, + quote: Boolean + ) : super( + contentType = contentType ?: "", + quote = quote, + transferState = AttachmentTable.TRANSFER_NEEDS_RESTORE, + size = size, + fileName = null, + cdn = cdn, + remoteLocation = null, + remoteKey = Base64.encodeWithoutPadding(cdnKey), + remoteDigest = digest, + incrementalDigest = incrementalMac, + fastPreflightId = null, + voiceNote = voiceNote, + borderless = borderless, + videoGif = gif, + width = width ?: 0, + height = height ?: 0, + incrementalMacChunkSize = incrementalMacChunkSize ?: 0, + uploadTimestamp = 0, + caption = caption, + stickerLocator = null, + blurHash = BlurHash.parseOrNull(blurHash), + audioHash = null, + transformProperties = null + ) { + this.archiveCdn = cdn.cdnNumber + this.archiveMediaName = archiveMediaName + this.archiveMediaId = archiveMediaId + } + + constructor(parcel: Parcel) : super(parcel) { + archiveCdn = parcel.readInt() + archiveMediaName = parcel.readString()!! + archiveMediaId = parcel.readString()!! + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeInt(archiveCdn) + dest.writeString(archiveMediaName) + dest.writeString(archiveMediaId) + } + + override val uri: Uri? = null + override val publicUri: Uri? = null +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt index 0330e91e6c..8d88953e76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt @@ -29,7 +29,7 @@ abstract class Attachment( @JvmField val fileName: String?, @JvmField - val cdnNumber: Int, + val cdn: Cdn, @JvmField val remoteLocation: String?, @JvmField @@ -76,7 +76,7 @@ abstract class Attachment( transferState = parcel.readInt(), size = parcel.readLong(), fileName = parcel.readString(), - cdnNumber = parcel.readInt(), + cdn = Cdn.deserialize(parcel.readInt()), remoteLocation = parcel.readString(), remoteKey = parcel.readString(), remoteDigest = ParcelUtil.readByteArray(parcel), @@ -103,7 +103,7 @@ abstract class Attachment( dest.writeInt(transferState) dest.writeLong(size) dest.writeString(fileName) - dest.writeInt(cdnNumber) + dest.writeInt(cdn.serialize()) dest.writeString(remoteLocation) dest.writeString(remoteKey) ParcelUtil.writeByteArray(dest, remoteDigest) diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt index e70b2b61cf..4a9b2ae5cc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt @@ -17,7 +17,8 @@ object AttachmentCreator : Parcelable.Creator { DATABASE(DatabaseAttachment::class.java, "database"), POINTER(PointerAttachment::class.java, "pointer"), TOMBSTONE(TombstoneAttachment::class.java, "tombstone"), - URI(UriAttachment::class.java, "uri") + URI(UriAttachment::class.java, "uri"), + ARCHIVED(ArchivedAttachment::class.java, "archived") } @JvmStatic @@ -34,6 +35,7 @@ object AttachmentCreator : Parcelable.Creator { Subclass.POINTER -> PointerAttachment(source) Subclass.TOMBSTONE -> TombstoneAttachment(source) Subclass.URI -> UriAttachment(source) + Subclass.ARCHIVED -> ArchivedAttachment(source) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/Cdn.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/Cdn.kt new file mode 100644 index 0000000000..9131944d47 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/Cdn.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.attachments + +import org.signal.core.util.IntSerializer + +/** + * Attachments/media can come from and go to multiple CDN locations depending on when and where + * they were uploaded. This class represents the CDNs where attachments/media can live. + */ +enum class Cdn(private val value: Int) { + S3(-1), + CDN_0(0), + CDN_2(2), + CDN_3(3); + + val cdnNumber: Int + get() { + return when (this) { + S3 -> -1 + CDN_0 -> 0 + CDN_2 -> 2 + CDN_3 -> 3 + } + } + + fun serialize(): Int { + return Serializer.serialize(this) + } + + companion object Serializer : IntSerializer { + override fun serialize(data: Cdn): Int { + return data.value + } + + override fun deserialize(data: Int): Cdn { + return values().first { it.value == data } + } + + fun fromCdnNumber(cdnNumber: Int): Cdn { + return when (cdnNumber) { + -1 -> S3 + 0 -> CDN_0 + 2 -> CDN_2 + 3 -> CDN_3 + else -> throw UnsupportedOperationException() + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt index 1fb11a06e2..1c0f4bfc30 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt @@ -25,6 +25,15 @@ class DatabaseAttachment : Attachment { @JvmField val dataHash: String? + @JvmField + val archiveCdn: Int + + @JvmField + val archiveMediaName: String? + + @JvmField + val archiveMediaId: String? + private val hasThumbnail: Boolean val displayOrder: Int @@ -37,7 +46,7 @@ class DatabaseAttachment : Attachment { transferProgress: Int, size: Long, fileName: String?, - cdnNumber: Int, + cdn: Cdn, location: String?, key: String?, digest: ByteArray?, @@ -57,13 +66,16 @@ class DatabaseAttachment : Attachment { transformProperties: TransformProperties?, displayOrder: Int, uploadTimestamp: Long, - dataHash: String? + dataHash: String?, + archiveCdn: Int, + archiveMediaName: String?, + archiveMediaId: String? ) : super( contentType = contentType!!, transferState = transferProgress, size = size, fileName = fileName, - cdnNumber = cdnNumber, + cdn = cdn, remoteLocation = location, remoteKey = key, remoteDigest = digest, @@ -88,6 +100,9 @@ class DatabaseAttachment : Attachment { this.dataHash = dataHash this.hasThumbnail = hasThumbnail this.displayOrder = displayOrder + this.archiveCdn = archiveCdn + this.archiveMediaName = archiveMediaName + this.archiveMediaId = archiveMediaId } constructor(parcel: Parcel) : super(parcel) { @@ -97,6 +112,9 @@ class DatabaseAttachment : Attachment { hasThumbnail = ParcelUtil.readBoolean(parcel) mmsId = parcel.readLong() displayOrder = parcel.readInt() + archiveCdn = parcel.readInt() + archiveMediaName = parcel.readString() + archiveMediaId = parcel.readString() } override fun writeToParcel(dest: Parcel, flags: Int) { @@ -107,6 +125,9 @@ class DatabaseAttachment : Attachment { ParcelUtil.writeBoolean(dest, hasThumbnail) dest.writeLong(mmsId) dest.writeInt(displayOrder) + dest.writeInt(archiveCdn) + dest.writeString(archiveMediaName) + dest.writeString(archiveMediaId) } override val uri: Uri? diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt index 66175b0a33..6b988cf75f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt @@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.stickers.StickerLocator import org.whispersystems.signalservice.api.InvalidMessageStructureException import org.whispersystems.signalservice.api.messages.SignalServiceAttachment -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.util.AttachmentPointerUtil import org.whispersystems.signalservice.internal.push.DataMessage import java.util.Optional @@ -21,7 +20,7 @@ class PointerAttachment : Attachment { transferState: Int, size: Long, fileName: String?, - cdnNumber: Int, + cdn: Cdn, location: String, key: String?, digest: ByteArray?, @@ -42,7 +41,7 @@ class PointerAttachment : Attachment { transferState = transferState, size = size, fileName = fileName, - cdnNumber = cdnNumber, + cdn = cdn, remoteLocation = location, remoteKey = key, remoteDigest = digest, @@ -83,7 +82,7 @@ class PointerAttachment : Attachment { @JvmStatic @JvmOverloads - fun forPointer(pointer: Optional, stickerLocator: StickerLocator? = null, fastPreflightId: String? = null): Optional { + fun forPointer(pointer: Optional, stickerLocator: StickerLocator? = null, fastPreflightId: String? = null, transferState: Int = AttachmentTable.TRANSFER_PROGRESS_PENDING): Optional { if (!pointer.isPresent || !pointer.get().isPointer) { return Optional.empty() } @@ -97,10 +96,10 @@ class PointerAttachment : Attachment { return Optional.of( PointerAttachment( contentType = pointer.get().contentType, - transferState = AttachmentTable.TRANSFER_PROGRESS_PENDING, + transferState = transferState, size = pointer.get().asPointer().size.orElse(0).toLong(), fileName = pointer.get().asPointer().fileName.orElse(null), - cdnNumber = pointer.get().asPointer().cdnNumber, + cdn = Cdn.fromCdnNumber(pointer.get().asPointer().cdnNumber), location = pointer.get().asPointer().remoteId.toString(), key = encodedKey, digest = pointer.get().asPointer().digest.orElse(null), @@ -120,35 +119,6 @@ class PointerAttachment : Attachment { ) } - fun forPointer(pointer: SignalServiceDataMessage.Quote.QuotedAttachment): Optional { - val thumbnail = pointer.thumbnail - - return Optional.of( - PointerAttachment( - contentType = pointer.contentType, - transferState = AttachmentTable.TRANSFER_PROGRESS_PENDING, - size = (if (thumbnail != null) thumbnail.asPointer().size.orElse(0) else 0).toLong(), - fileName = pointer.fileName, - cdnNumber = thumbnail?.asPointer()?.cdnNumber ?: 0, - location = thumbnail?.asPointer()?.remoteId?.toString() ?: "0", - key = if (thumbnail != null && thumbnail.asPointer().key != null) encodeWithPadding(thumbnail.asPointer().key) else null, - digest = thumbnail?.asPointer()?.digest?.orElse(null), - incrementalDigest = thumbnail?.asPointer()?.incrementalDigest?.orElse(null), - incrementalMacChunkSize = thumbnail?.asPointer()?.incrementalMacChunkSize ?: 0, - fastPreflightId = null, - voiceNote = false, - borderless = false, - videoGif = false, - width = thumbnail?.asPointer()?.width ?: 0, - height = thumbnail?.asPointer()?.height ?: 0, - uploadTimestamp = thumbnail?.asPointer()?.uploadTimestamp ?: 0, - caption = thumbnail?.asPointer()?.caption?.orElse(null), - stickerLocator = null, - blurHash = null - ) - ) - } - fun forPointer(quotedAttachment: DataMessage.Quote.QuotedAttachment): Optional { val thumbnail: SignalServiceAttachment? = try { if (quotedAttachment.thumbnail != null) { @@ -166,7 +136,7 @@ class PointerAttachment : Attachment { transferState = AttachmentTable.TRANSFER_PROGRESS_PENDING, size = (if (thumbnail != null) thumbnail.asPointer().size.orElse(0) else 0).toLong(), fileName = quotedAttachment.fileName, - cdnNumber = thumbnail?.asPointer()?.cdnNumber ?: 0, + cdn = Cdn.fromCdnNumber(thumbnail?.asPointer()?.cdnNumber ?: 0), location = thumbnail?.asPointer()?.remoteId?.toString() ?: "0", key = if (thumbnail != null && thumbnail.asPointer().key != null) encodeWithPadding(thumbnail.asPointer().key) else null, digest = thumbnail?.asPointer()?.digest?.orElse(null), diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt index ef7a371ff0..efbf88b404 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.attachments import android.net.Uri import android.os.Parcel +import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable /** @@ -17,7 +18,7 @@ class TombstoneAttachment : Attachment { transferState = AttachmentTable.TRANSFER_PROGRESS_DONE, size = 0, fileName = null, - cdnNumber = 0, + cdn = Cdn.CDN_0, remoteLocation = null, remoteKey = null, remoteDigest = null, @@ -37,6 +38,44 @@ class TombstoneAttachment : Attachment { transformProperties = null ) + constructor( + contentType: String?, + incrementalMac: ByteArray?, + incrementalMacChunkSize: Int?, + width: Int?, + height: Int?, + caption: String?, + blurHash: String?, + voiceNote: Boolean = false, + borderless: Boolean = false, + gif: Boolean = false, + quote: Boolean + ) : super( + contentType = contentType ?: "", + quote = quote, + transferState = AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE, + size = 0, + fileName = null, + cdn = Cdn.CDN_0, + remoteLocation = null, + remoteKey = null, + remoteDigest = null, + incrementalDigest = incrementalMac, + fastPreflightId = null, + voiceNote = voiceNote, + borderless = borderless, + videoGif = gif, + width = width ?: 0, + height = height ?: 0, + incrementalMacChunkSize = incrementalMacChunkSize ?: 0, + uploadTimestamp = 0, + caption = caption, + stickerLocator = null, + blurHash = BlurHash.parseOrNull(blurHash), + audioHash = null, + transformProperties = null + ) + constructor(parcel: Parcel) : super(parcel) override val uri: Uri? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt index f158bd367a..39e3f02a18 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt @@ -69,7 +69,7 @@ class UriAttachment : Attachment { transferState = transferState, size = size, fileName = fileName, - cdnNumber = 0, + cdn = Cdn.CDN_0, remoteLocation = null, remoteKey = null, remoteDigest = null, diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/RestoreState.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/RestoreState.kt new file mode 100644 index 0000000000..4b7110748b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/RestoreState.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup + +import org.signal.core.util.LongSerializer + +enum class RestoreState(val id: Int, val inProgress: Boolean) { + FAILED(-1, false), + NONE(0, false), + PENDING(1, true), + RESTORING_DB(2, true), + RESTORING_MEDIA(3, true); + + companion object { + val serializer: LongSerializer = Serializer() + } + + class Serializer : LongSerializer { + override fun serialize(data: RestoreState): Long { + return data.id.toLong() + } + + override fun deserialize(data: Long): RestoreState { + return when (data.toInt()) { + FAILED.id -> FAILED + PENDING.id -> PENDING + RESTORING_DB.id -> RESTORING_DB + RESTORING_MEDIA.id -> RESTORING_MEDIA + else -> NONE + } + } + } +} 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 38680114eb..499bf9f929 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 @@ -1,5 +1,5 @@ /* - * Copyright 2023 Signal Messenger, LLC + * Copyright 2024 Signal Messenger, LLC * SPDX-License-Identifier: AGPL-3.0-only */ @@ -14,6 +14,7 @@ import org.signal.libsignal.messagebackup.MessageBackup.ValidationResult import org.signal.libsignal.messagebackup.MessageBackupKey import org.signal.libsignal.protocol.ServiceId.Aci import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.backup.v2.database.ChatItemImportInserter import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackupRestore @@ -37,17 +38,20 @@ import org.thoughtcrime.securesms.recipients.RecipientId import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse import org.whispersystems.signalservice.api.archive.ArchiveMediaRequest -import org.whispersystems.signalservice.api.archive.ArchiveMediaResponse import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential -import org.whispersystems.signalservice.api.archive.BatchArchiveMediaResponse import org.whispersystems.signalservice.api.archive.DeleteArchivedMediaRequest +import org.whispersystems.signalservice.api.archive.GetArchiveCdnCredentialsResponse import org.whispersystems.signalservice.api.backup.BackupKey +import org.whispersystems.signalservice.api.backup.MediaName import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.internal.crypto.PaddingInputStream import java.io.ByteArrayOutputStream +import java.io.File import java.io.InputStream +import java.io.OutputStream import kotlin.time.Duration.Companion.milliseconds object BackupRepository { @@ -55,10 +59,8 @@ object BackupRepository { private val TAG = Log.tag(BackupRepository::class.java) private const val VERSION = 1L - fun export(plaintext: Boolean = false): ByteArray { + fun export(outputStream: OutputStream, append: (ByteArray) -> Unit, plaintext: Boolean = false) { val eventTimer = EventTimer() - - val outputStream = ByteArrayOutputStream() val writer: BackupExportWriter = if (plaintext) { PlainTextBackupWriter(outputStream) } else { @@ -66,11 +68,11 @@ object BackupRepository { key = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey(), aci = SignalStore.account().aci!!, outputStream = outputStream, - append = { mac -> outputStream.write(mac) } + append = append ) } - val exportState = ExportState(System.currentTimeMillis()) + val exportState = ExportState(backupTime = System.currentTimeMillis(), allowMediaBackup = true) writer.use { writer.write( @@ -110,7 +112,11 @@ object BackupRepository { } Log.d(TAG, "export() ${eventTimer.stop().summary}") + } + fun export(plaintext: Boolean = false): ByteArray { + val outputStream = ByteArrayOutputStream() + export(outputStream = outputStream, append = { mac -> outputStream.write(mac) }, plaintext = plaintext) return outputStream.toByteArray() } @@ -124,11 +130,13 @@ object BackupRepository { fun import(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData, plaintext: Boolean = false) { val eventTimer = EventTimer() + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + val frameReader = if (plaintext) { PlainTextBackupReader(inputStreamFactory()) } else { EncryptedBackupReader( - key = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey(), + key = backupKey, aci = selfData.aci, streamLength = length, dataStream = inputStreamFactory @@ -160,7 +168,7 @@ object BackupRepository { SignalDatabase.recipients.setProfileSharing(selfId, true) eventTimer.emit("setup") - val backupState = BackupState() + val backupState = BackupState(backupKey) val chatItemInserter: ChatItemImportInserter = ChatItemBackupProcessor.beginImport(backupState) for (frame in frameReader) { @@ -281,6 +289,24 @@ object BackupRepository { .also { Log.i(TAG, "OverallResult: $it") } is NetworkResult.Success } + fun downloadBackupFile(destination: File, listener: ProgressListener? = null): Boolean { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return api + .triggerBackupIdReservation(backupKey) + .then { getAuthCredential() } + .then { credential -> + api.getBackupInfo(backupKey, credential) + } + .then { info -> getCdnReadCredentials().map { it.headers to info } } + .map { pair -> + val (cdnCredentials, info) = pair + val messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver() + messageReceiver.retrieveBackup(info.cdn!!, cdnCredentials, "backups/${info.backupDir}/${info.backupName}", destination, listener) + } is NetworkResult.Success + } + /** * Returns an object with details about the remote backup state. */ @@ -296,7 +322,7 @@ object BackupRepository { } } - fun archiveMedia(attachment: DatabaseAttachment): NetworkResult { + fun archiveMedia(attachment: DatabaseAttachment): NetworkResult { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() @@ -304,16 +330,23 @@ object BackupRepository { .triggerBackupIdReservation(backupKey) .then { getAuthCredential() } .then { credential -> - api.archiveAttachmentMedia( - backupKey = backupKey, - serviceCredential = credential, - item = attachment.toArchiveMediaRequest(backupKey) - ) + val mediaName = attachment.getMediaName() + val request = attachment.toArchiveMediaRequest(mediaName, backupKey) + api + .archiveAttachmentMedia( + backupKey = backupKey, + serviceCredential = credential, + item = request + ) + .map { Triple(mediaName, request.mediaId, it) } + } + .map { (mediaName, mediaId, response) -> + SignalDatabase.attachments.setArchiveData(attachmentId = attachment.attachmentId, archiveCdn = response.cdn, archiveMediaName = mediaName.name, archiveMediaId = mediaId) } - .also { Log.i(TAG, "backupMediaResult: $it") } + .also { Log.i(TAG, "archiveMediaResult: $it") } } - fun archiveMedia(attachments: List): NetworkResult { + fun archiveMedia(databaseAttachments: List): NetworkResult { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() @@ -321,24 +354,55 @@ object BackupRepository { .triggerBackupIdReservation(backupKey) .then { getAuthCredential() } .then { credential -> - api.archiveAttachmentMedia( - backupKey = backupKey, - serviceCredential = credential, - items = attachments.map { it.toArchiveMediaRequest(backupKey) } - ) + val requests = mutableListOf() + val mediaIdToAttachmentId = mutableMapOf() + val attachmentIdToMediaName = mutableMapOf() + + databaseAttachments.forEach { + val mediaName = it.getMediaName() + val request = it.toArchiveMediaRequest(mediaName, backupKey) + requests += request + mediaIdToAttachmentId[request.mediaId] = it.attachmentId + attachmentIdToMediaName[it.attachmentId] = mediaName.name + } + + api + .archiveAttachmentMedia( + backupKey = backupKey, + serviceCredential = credential, + items = requests + ) + .map { BatchArchiveMediaResult(it, mediaIdToAttachmentId, attachmentIdToMediaName) } } - .also { Log.i(TAG, "backupMediaResult: $it") } + .map { result -> + result + .successfulResponses + .forEach { + val attachmentId = result.mediaIdToAttachmentId(it.mediaId) + val mediaName = result.attachmentIdToMediaName(attachmentId) + SignalDatabase.attachments.setArchiveData(attachmentId = attachmentId, archiveCdn = it.cdn!!, archiveMediaName = mediaName, archiveMediaId = it.mediaId) + } + result + } + .also { Log.i(TAG, "archiveMediaResult: $it") } } fun deleteArchivedMedia(attachments: List): NetworkResult { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - val mediaToDelete = attachments.map { - DeleteArchivedMediaRequest.ArchivedMediaObject( - cdn = 3, // TODO [cody] store and reuse backup cdn returned from copy/move call - mediaId = backupKey.deriveMediaId(Base64.decode(it.dataHash!!)).toString() - ) + val mediaToDelete = attachments + .filter { it.archiveMediaId != null } + .map { + DeleteArchivedMediaRequest.ArchivedMediaObject( + cdn = it.archiveCdn, + mediaId = it.archiveMediaId!! + ) + } + + if (mediaToDelete.isEmpty()) { + Log.i(TAG, "No media to delete, quick success") + return NetworkResult.Success(Unit) } return getAuthCredential() @@ -349,7 +413,101 @@ object BackupRepository { mediaToDelete = mediaToDelete ) } - .also { Log.i(TAG, "deleteBackupMediaResult: $it") } + .map { + SignalDatabase.attachments.clearArchiveData(attachments.map { it.attachmentId }) + } + .also { Log.i(TAG, "deleteArchivedMediaResult: $it") } + } + + fun debugDeleteAllArchivedMedia(): NetworkResult { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return debugGetArchivedMediaState() + .then { archivedMedia -> + val mediaToDelete = archivedMedia + .map { + DeleteArchivedMediaRequest.ArchivedMediaObject( + cdn = it.cdn, + mediaId = it.mediaId + ) + } + + if (mediaToDelete.isEmpty()) { + Log.i(TAG, "No media to delete, quick success") + NetworkResult.Success(Unit) + } else { + getAuthCredential() + .then { credential -> + api.deleteArchivedMedia( + backupKey = backupKey, + serviceCredential = credential, + mediaToDelete = mediaToDelete + ) + } + } + } + .map { + SignalDatabase.attachments.clearAllArchiveData() + } + .also { Log.i(TAG, "debugDeleteAllArchivedMediaResult: $it") } + } + + /** + * Retrieve credentials for reading from the backup cdn. + */ + fun getCdnReadCredentials(): NetworkResult { + val cached = SignalStore.backup().cdnReadCredentials + if (cached != null) { + return NetworkResult.Success(cached) + } + + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return getAuthCredential() + .then { credential -> + api.getCdnReadCredentials( + backupKey = backupKey, + serviceCredential = credential + ) + } + .also { + if (it is NetworkResult.Success) { + SignalStore.backup().cdnReadCredentials = it.result + } + } + .also { Log.i(TAG, "getCdnReadCredentialsResult: $it") } + } + + /** + * Retrieves backupDir and mediaDir, preferring cached value if available. + * + * These will only ever change if the backup expires. + */ + fun getCdnBackupDirectories(): NetworkResult { + val cachedBackupDirectory = SignalStore.backup().cachedBackupDirectory + val cachedBackupMediaDirectory = SignalStore.backup().cachedBackupMediaDirectory + + if (cachedBackupDirectory != null && cachedBackupMediaDirectory != null) { + return NetworkResult.Success(BackupDirectories(cachedBackupDirectory, cachedBackupMediaDirectory)) + } + + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return getAuthCredential() + .then { credential -> + api.getBackupInfo(backupKey, credential).map { + BackupDirectories(it.backupDir!!, it.mediaDir!!) + } + } + .also { + if (it is NetworkResult.Success) { + SignalStore.backup().cachedBackupDirectory = it.result.backupDir + SignalStore.backup().cachedBackupMediaDirectory = it.result.mediaDir + } + } } /** @@ -380,15 +538,20 @@ object BackupRepository { val profileKey: ProfileKey ) - private fun DatabaseAttachment.toArchiveMediaRequest(backupKey: BackupKey): ArchiveMediaRequest { - val mediaSecrets = backupKey.deriveMediaSecrets(Base64.decode(dataHash!!)) + fun DatabaseAttachment.getMediaName(): MediaName { + return MediaName.fromDigest(remoteDigest!!) + } + + private fun DatabaseAttachment.toArchiveMediaRequest(mediaName: MediaName, backupKey: BackupKey): ArchiveMediaRequest { + val mediaSecrets = backupKey.deriveMediaSecrets(mediaName) + return ArchiveMediaRequest( sourceAttachment = ArchiveMediaRequest.SourceAttachment( - cdn = cdnNumber, + cdn = cdn.cdnNumber, key = remoteLocation!! ), objectLength = AttachmentCipherStreamUtil.getCiphertextLength(PaddingInputStream.getPaddedSize(size)).toInt(), - mediaId = mediaSecrets.id.toString(), + mediaId = mediaSecrets.id.encode(), hmacKey = Base64.encodeWithPadding(mediaSecrets.macKey), encryptionKey = Base64.encodeWithPadding(mediaSecrets.cipherKey), iv = Base64.encodeWithPadding(mediaSecrets.iv) @@ -396,12 +559,14 @@ object BackupRepository { } } -class ExportState(val backupTime: Long) { +data class BackupDirectories(val backupDir: String, val mediaDir: String) + +class ExportState(val backupTime: Long, val allowMediaBackup: Boolean) { val recipientIds = HashSet() val threadIds = HashSet() } -class BackupState { +class BackupState(val backupKey: BackupKey) { val backupToLocalRecipientId = HashMap() val chatIdToLocalThreadId = HashMap() val chatIdToLocalRecipientId = HashMap() diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRestoreManager.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRestoreManager.kt new file mode 100644 index 0000000000..0a716f3363 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRestoreManager.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2 + +import org.signal.core.util.concurrent.SignalExecutors +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.database.model.MessageRecord +import org.thoughtcrime.securesms.database.model.MmsMessageRecord +import org.thoughtcrime.securesms.jobs.RestoreAttachmentJob + +/** + * Responsible for managing logic around restore prioritization + */ +object BackupRestoreManager { + + private val reprioritizedAttachments: HashSet = HashSet() + + /** + * Raise priority of all attachments for the included message records. + * + * This is so we can make certain attachments get downloaded more quickly + */ + fun prioritizeAttachmentsIfNeeded(messageRecords: List) { + SignalExecutors.BOUNDED.execute { + synchronized(this) { + val restoringAttachments: List = messageRecords + .mapNotNull { (it as? MmsMessageRecord?)?.slideDeck?.slides } + .flatten() + .mapNotNull { it.asAttachment() as? DatabaseAttachment } + .filter { it.transferState == AttachmentTable.TRANSFER_RESTORE_IN_PROGRESS && !reprioritizedAttachments.contains(it.attachmentId) } + .map { it.attachmentId } + + reprioritizedAttachments += restoringAttachments + + if (restoringAttachments.isNotEmpty()) { + RestoreAttachmentJob.modifyPriorities(restoringAttachments.toSet(), 1) + } + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BatchArchiveMediaResult.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BatchArchiveMediaResult.kt new file mode 100644 index 0000000000..a8a14b02c9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BatchArchiveMediaResult.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2 + +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.whispersystems.signalservice.api.archive.BatchArchiveMediaResponse + +/** + * Result of attempting to batch copy multiple attachments at once with helpers for + * processing the collection of mini-responses. + */ +data class BatchArchiveMediaResult( + private val response: BatchArchiveMediaResponse, + private val mediaIdToAttachmentId: Map, + private val attachmentIdToMediaName: Map +) { + val successfulResponses: Sequence + get() = response + .responses + .asSequence() + .filter { it.status == 200 } + + val sourceNotFoundResponses: Sequence + get() = response + .responses + .asSequence() + .filter { it.status == 410 } + + fun mediaIdToAttachmentId(mediaId: String): AttachmentId { + return mediaIdToAttachmentId[mediaId]!! + } + + fun attachmentIdToMediaName(attachmentId: AttachmentId): String { + return attachmentIdToMediaName[attachmentId]!! + } +} 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 6813fce25b..b1cd0e8f25 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 @@ -17,7 +17,9 @@ 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.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.backup.v2.BackupRepository.getMediaName import org.thoughtcrime.securesms.backup.v2.proto.CallChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.ChatItem import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage @@ -36,6 +38,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage import org.thoughtcrime.securesms.backup.v2.proto.Text import org.thoughtcrime.securesms.backup.v2.proto.ThreadMergeChatUpdate +import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.database.GroupReceiptTable import org.thoughtcrime.securesms.database.MessageTable import org.thoughtcrime.securesms.database.MessageTypes @@ -73,7 +76,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.BodyRange as BackupBodyRange * * 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) : Iterator, Closeable { +class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: Int, private val archiveMedia: Boolean) : Iterator, Closeable { companion object { private val TAG = Log.tag(ChatItemExportIterator::class.java) @@ -139,6 +142,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: builder.expiresInMs = null } MessageTypes.isProfileChange(record.type) -> { + if (record.body == null) continue builder.updateMessage = ChatUpdateMessage( profileChange = try { val decoded: ByteArray = Base64.decode(record.body!!) @@ -354,24 +358,46 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } private fun DatabaseAttachment.toBackupAttachment(): MessageAttachment { + val builder = FilePointer.Builder() + builder.contentType = contentType + builder.incrementalMac = incrementalDigest?.toByteString() + builder.incrementalMacChunkSize = incrementalMacChunkSize + builder.fileName = fileName + builder.width = width + builder.height = height + builder.caption = caption + builder.blurHash = blurHash?.hash + + if (remoteKey.isNullOrBlank() || remoteDigest == null || size == 0L) { + builder.invalidAttachmentLocator = FilePointer.InvalidAttachmentLocator() + } else { + if (archiveMedia) { + builder.backupLocator = FilePointer.BackupLocator( + mediaName = archiveMediaName ?: this.getMediaName().toString(), + cdnNumber = if (archiveMediaName != null) archiveCdn else Cdn.CDN_3.cdnNumber, // TODO (clark): Update when new proto with optional cdn is landed + key = decode(remoteKey).toByteString(), + size = this.size.toInt(), + digest = remoteDigest.toByteString() + ) + } else { + if (remoteLocation.isNullOrBlank()) { + builder.invalidAttachmentLocator = FilePointer.InvalidAttachmentLocator() + } else { + builder.attachmentLocator = FilePointer.AttachmentLocator( + cdnKey = this.remoteLocation, + cdnNumber = this.cdn.cdnNumber, + uploadTimestamp = this.uploadTimestamp, + key = decode(remoteKey).toByteString(), + size = this.size.toInt(), + digest = remoteDigest.toByteString() + ) + } + } + } return MessageAttachment( - pointer = FilePointer( - attachmentLocator = FilePointer.AttachmentLocator( - cdnKey = this.remoteLocation ?: "", - cdnNumber = this.cdnNumber, - uploadTimestamp = this.uploadTimestamp - ), - key = if (remoteKey != null) decode(remoteKey).toByteString() else null, - contentType = this.contentType, - size = this.size.toInt(), - incrementalMac = this.incrementalDigest?.toByteString(), - incrementalMacChunkSize = this.incrementalMacChunkSize, - fileName = this.fileName, - width = this.width, - height = this.height, - caption = this.caption, - blurHash = this.blurHash?.hash - ) + pointer = builder.build(), + wasDownloaded = this.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE || this.transferState == AttachmentTable.TRANSFER_NEEDS_RESTORE, + flag = if (voiceNote) MessageAttachment.Flag.VOICE_MESSAGE else if (videoGif) MessageAttachment.Flag.GIF else if (borderless) MessageAttachment.Flag.BORDERLESS else MessageAttachment.Flag.NONE ) } 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 8c6df58438..0e74e35538 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 @@ -13,8 +13,11 @@ import org.signal.core.util.logging.Log import org.signal.core.util.orNull import org.signal.core.util.requireLong import org.signal.core.util.toInt +import org.thoughtcrime.securesms.attachments.ArchivedAttachment import org.thoughtcrime.securesms.attachments.Attachment +import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.PointerAttachment +import org.thoughtcrime.securesms.attachments.TombstoneAttachment import org.thoughtcrime.securesms.backup.v2.BackupState import org.thoughtcrime.securesms.backup.v2.proto.BodyRange import org.thoughtcrime.securesms.backup.v2.proto.ChatItem @@ -26,6 +29,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.Reaction import org.thoughtcrime.securesms.backup.v2.proto.SendStatus import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage +import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.database.CallTable import org.thoughtcrime.securesms.database.GroupReceiptTable import org.thoughtcrime.securesms.database.MessageTable @@ -48,11 +52,12 @@ import org.thoughtcrime.securesms.mms.QuoteModel import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.util.JsonUtils +import org.whispersystems.signalservice.api.backup.MediaName import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.internal.push.DataMessage import java.util.Optional /** @@ -570,12 +575,12 @@ class ChatItemImportInserter( pointer.attachmentLocator.cdnNumber, SignalServiceAttachmentRemoteId.from(pointer.attachmentLocator.cdnKey), contentType, - pointer.key?.toByteArray(), - Optional.ofNullable(pointer.size), + pointer.attachmentLocator.key.toByteArray(), + Optional.ofNullable(pointer.attachmentLocator.size), Optional.empty(), pointer.width ?: 0, pointer.height ?: 0, - Optional.empty(), + Optional.ofNullable(pointer.attachmentLocator.digest.toByteArray()), Optional.ofNullable(pointer.incrementalMac?.toByteArray()), pointer.incrementalMacChunkSize ?: 0, Optional.ofNullable(fileName), @@ -586,14 +591,51 @@ class ChatItemImportInserter( Optional.ofNullable(pointer.blurHash), pointer.attachmentLocator.uploadTimestamp ) - return PointerAttachment.forPointer(Optional.of(signalAttachmentPointer)).orNull() + return PointerAttachment.forPointer( + pointer = Optional.of(signalAttachmentPointer), + transferState = if (wasDownloaded) AttachmentTable.TRANSFER_NEEDS_RESTORE else AttachmentTable.TRANSFER_PROGRESS_PENDING + ).orNull() + } else if (pointer.invalidAttachmentLocator != null) { + return TombstoneAttachment( + contentType = contentType, + incrementalMac = pointer.incrementalMac?.toByteArray(), + incrementalMacChunkSize = pointer.incrementalMacChunkSize, + width = pointer.width, + height = pointer.height, + caption = pointer.caption, + blurHash = pointer.blurHash, + voiceNote = flag == MessageAttachment.Flag.VOICE_MESSAGE, + borderless = flag == MessageAttachment.Flag.BORDERLESS, + gif = flag == MessageAttachment.Flag.GIF, + quote = false + ) + } else if (pointer.backupLocator != null) { + return ArchivedAttachment( + contentType = contentType, + size = pointer.backupLocator.size.toLong(), + cdn = Cdn.fromCdnNumber(pointer.backupLocator.cdnNumber), + cdnKey = pointer.backupLocator.key.toByteArray(), + archiveMediaName = pointer.backupLocator.mediaName, + archiveMediaId = backupState.backupKey.deriveMediaId(MediaName(pointer.backupLocator.mediaName)).encode(), + digest = pointer.backupLocator.digest.toByteArray(), + incrementalMac = pointer.incrementalMac?.toByteArray(), + incrementalMacChunkSize = pointer.incrementalMacChunkSize, + width = pointer.width, + height = pointer.height, + caption = pointer.caption, + blurHash = pointer.blurHash, + voiceNote = flag == MessageAttachment.Flag.VOICE_MESSAGE, + borderless = flag == MessageAttachment.Flag.BORDERLESS, + gif = flag == MessageAttachment.Flag.GIF, + quote = false + ) } return null } private fun Quote.QuotedAttachment.toLocalAttachment(): Attachment? { return thumbnail?.toLocalAttachment(this.contentType, this.fileName) - ?: if (this.contentType == null) null else PointerAttachment.forPointer(SignalServiceDataMessage.Quote.QuotedAttachment(contentType = this.contentType!!, fileName = this.fileName, thumbnail = null)).orNull() + ?: if (this.contentType == null) null else PointerAttachment.forPointer(quotedAttachment = DataMessage.Quote.QuotedAttachment(contentType = this.contentType, fileName = this.fileName, thumbnail = null)).orNull() } private class MessageInsert(val contentValues: ContentValues, val followUp: ((Long) -> Unit)?) 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 775423cdca..f4a6b5cfce 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 @@ -16,7 +16,7 @@ import java.util.concurrent.TimeUnit private val TAG = Log.tag(MessageTable::class.java) private const val BASE_TYPE = "base_type" -fun MessageTable.getMessagesForBackup(backupTime: Long): ChatItemExportIterator { +fun MessageTable.getMessagesForBackup(backupTime: Long, archiveMedia: Boolean): ChatItemExportIterator { val cursor = readableDatabase .select( MessageTable.ID, @@ -64,7 +64,7 @@ fun MessageTable.getMessagesForBackup(backupTime: Long): ChatItemExportIterator .orderBy("${MessageTable.DATE_RECEIVED} ASC") .run() - return ChatItemExportIterator(cursor, 100) + return ChatItemExportIterator(cursor, 100, archiveMedia) } fun MessageTable.createChatItemInserter(backupState: BackupState): ChatItemImportInserter { 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 807e2931e6..9e998fc946 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 @@ -19,7 +19,7 @@ object ChatItemBackupProcessor { val TAG = Log.tag(ChatItemBackupProcessor::class.java) fun export(exportState: ExportState, emitter: BackupFrameEmitter) { - SignalDatabase.messages.getMessagesForBackup(exportState.backupTime).use { chatItems -> + SignalDatabase.messages.getMessagesForBackup(exportState.backupTime, exportState.allowMediaBackup).use { chatItems -> for (chatItem in chatItems) { if (exportState.threadIds.contains(chatItem.chatId)) { emitter.emit(Frame(chatItem = chatItem)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreActivity.kt index 853ccb80e7..956f5997a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreActivity.kt @@ -26,6 +26,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -80,6 +81,15 @@ class MessageBackupsTestRestoreActivity : BaseActivity() { .fillMaxSize() .padding(16.dp) ) { + Buttons.LargePrimary( + onClick = this@MessageBackupsTestRestoreActivity::restoreFromServer, + enabled = !state.importState.inProgress + ) { + Text("Restore") + } + + Spacer(modifier = Modifier.height(8.dp)) + Row( verticalAlignment = Alignment.CenterVertically ) { @@ -120,9 +130,20 @@ class MessageBackupsTestRestoreActivity : BaseActivity() { } } } + if (state.importState == MessageBackupsTestRestoreViewModel.ImportState.RESTORED) { + SideEffect { + RegistrationUtil.maybeMarkRegistrationComplete() + ApplicationDependencies.getJobManager().add(ProfileUploadJob()) + startActivity(MainActivity.clearTop(this)) + } + } } } + private fun restoreFromServer() { + viewModel.restore() + } + private fun continueRegistration() { if (Recipient.self().profileName.isEmpty || !AvatarHelper.hasAvatar(this, Recipient.self().id)) { val main = MainActivity.clearTop(this) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt index 5213febed5..fc3774f37c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt @@ -15,8 +15,12 @@ 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.orNull import org.signal.libsignal.zkgroup.profiles.ProfileKey import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.JobTracker +import org.thoughtcrime.securesms.jobs.BackupRestoreJob import org.thoughtcrime.securesms.recipients.Recipient import java.io.InputStream @@ -40,6 +44,19 @@ class MessageBackupsTestRestoreViewModel : ViewModel() { } } + fun restore() { + _state.value = _state.value.copy(importState = ImportState.IN_PROGRESS) + disposables += Single.fromCallable { + val jobState = ApplicationDependencies.getJobManager().runSynchronously(BackupRestoreJob(), 120_000) + jobState.orNull() == JobTracker.JobState.SUCCESS + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy { + _state.value = _state.value.copy(importState = ImportState.RESTORED) + } + } + fun onPlaintextToggled() { _state.value = _state.value.copy(plaintext = !_state.value.plaintext) } @@ -54,6 +71,6 @@ class MessageBackupsTestRestoreViewModel : ViewModel() { ) enum class ImportState(val inProgress: Boolean = false) { - NONE, IN_PROGRESS(true) + NONE, IN_PROGRESS(true), RESTORED } } 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 b1ece67d5d..78563ed818 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 @@ -29,6 +29,11 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.Checkbox import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHostState @@ -37,6 +42,8 @@ import androidx.compose.material3.Switch import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -46,10 +53,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +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 org.signal.core.ui.Buttons import org.signal.core.ui.Dividers import org.signal.core.ui.Snackbars @@ -57,10 +66,13 @@ import org.signal.core.ui.theme.SignalTheme import org.signal.core.util.bytes import org.signal.core.util.getLength import org.signal.core.util.roundedString +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.BackupState import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.BackupUploadState import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.ScreenState import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.keyvalue.SignalStore class InternalBackupPlaygroundFragment : ComposeFragment() { @@ -114,6 +126,8 @@ class InternalBackupPlaygroundFragment : ComposeFragment() { } Tabs( + onBack = { findNavController().popBackStack() }, + onDeleteAllArchivedMedia = { viewModel.deleteAllArchivedMedia() }, mainContent = { Screen( state = state, @@ -149,25 +163,32 @@ class InternalBackupPlaygroundFragment : ComposeFragment() { } validateFileLauncher.launch(intent) - } + }, + onTriggerBackupJobClicked = { viewModel.triggerBackupJob() }, + onRestoreFromRemoteClicked = { viewModel.restoreFromRemote() } ) }, mediaContent = { snackbarHostState -> MediaList( + enabled = SignalStore.backup().canReadWriteToArchiveCdn, state = mediaState, snackbarHostState = snackbarHostState, - backupAttachmentMedia = { viewModel.backupAttachmentMedia(it) }, - deleteBackupAttachmentMedia = { viewModel.deleteBackupAttachmentMedia(it) }, - batchBackupAttachmentMedia = { viewModel.backupAttachmentMedia(it) }, - batchDeleteBackupAttachmentMedia = { viewModel.deleteBackupAttachmentMedia(it) } + archiveAttachmentMedia = { viewModel.archiveAttachmentMedia(it) }, + deleteArchivedMedia = { viewModel.deleteArchivedMedia(it) }, + batchArchiveAttachmentMedia = { viewModel.archiveAttachmentMedia(it) }, + batchDeleteBackupAttachmentMedia = { viewModel.deleteArchivedMedia(it) }, + restoreArchivedMedia = { viewModel.restoreArchivedMedia(it) } ) } ) } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun Tabs( + onBack: () -> Unit, + onDeleteAllArchivedMedia: () -> Unit, mainContent: @Composable () -> Unit, mediaContent: @Composable (snackbarHostState: SnackbarHostState) -> Unit ) { @@ -179,13 +200,36 @@ fun Tabs( Scaffold( snackbarHost = { Snackbars.Host(snackbarHostState) }, topBar = { - TabRow(selectedTabIndex = tabIndex) { - tabs.forEachIndexed { index, tab -> - Tab( - text = { Text(tab) }, - selected = index == tabIndex, - onClick = { tabIndex = index } - ) + Column { + TopAppBar( + title = { + Text("Backup Playground") + }, + navigationIcon = { + IconButton(onClick = onBack) { + Icon( + painter = painterResource(R.drawable.symbol_arrow_left_24), + tint = MaterialTheme.colorScheme.onSurface, + contentDescription = null + ) + } + }, + actions = { + if (tabIndex == 1 && SignalStore.backup().canReadWriteToArchiveCdn) { + TextButton(onClick = onDeleteAllArchivedMedia) { + Text(text = "Delete All") + } + } + } + ) + TabRow(selectedTabIndex = tabIndex) { + tabs.forEachIndexed { index, tab -> + Tab( + text = { Text(tab) }, + selected = index == tabIndex, + onClick = { tabIndex = index } + ) + } } } } @@ -209,7 +253,9 @@ fun Screen( onSaveToDiskClicked: () -> Unit = {}, onValidateFileClicked: () -> Unit = {}, onUploadToRemoteClicked: () -> Unit = {}, - onCheckRemoteBackupStateClicked: () -> Unit = {} + onCheckRemoteBackupStateClicked: () -> Unit = {}, + onTriggerBackupJobClicked: () -> Unit = {}, + onRestoreFromRemoteClicked: () -> Unit = {} ) { Surface { Column( @@ -239,6 +285,13 @@ fun Screen( Text("Export") } + Buttons.LargePrimary( + onClick = onTriggerBackupJobClicked, + enabled = !state.backupState.inProgress + ) { + Text("Trigger Backup Job") + } + Dividers.Default() Buttons.LargeTonal( @@ -280,6 +333,10 @@ fun Screen( } } + BackupState.BACKUP_JOB_DONE -> { + StateLabel("Backup complete and uploaded") + } + BackupState.IMPORT_IN_PROGRESS -> { StateLabel("Import in progress...") } @@ -324,6 +381,10 @@ fun Screen( Spacer(modifier = Modifier.height(8.dp)) + Buttons.LargePrimary(onClick = onRestoreFromRemoteClicked) { + Text("Restore from remote") + } + when (state.uploadState) { BackupUploadState.NONE -> { StateLabel("") @@ -357,13 +418,24 @@ private fun StateLabel(text: String) { @OptIn(ExperimentalFoundationApi::class) @Composable fun MediaList( + enabled: Boolean, state: InternalBackupPlaygroundViewModel.MediaState, snackbarHostState: SnackbarHostState, - backupAttachmentMedia: (InternalBackupPlaygroundViewModel.BackupAttachment) -> Unit, - deleteBackupAttachmentMedia: (InternalBackupPlaygroundViewModel.BackupAttachment) -> Unit, - batchBackupAttachmentMedia: (Set) -> Unit, - batchDeleteBackupAttachmentMedia: (Set) -> Unit + archiveAttachmentMedia: (InternalBackupPlaygroundViewModel.BackupAttachment) -> Unit, + deleteArchivedMedia: (InternalBackupPlaygroundViewModel.BackupAttachment) -> Unit, + batchArchiveAttachmentMedia: (Set) -> Unit, + batchDeleteBackupAttachmentMedia: (Set) -> Unit, + restoreArchivedMedia: (InternalBackupPlaygroundViewModel.BackupAttachment) -> Unit ) { + if (!enabled) { + Text( + text = "You do not have read/write to archive cdn enabled via SignalStore.backup()", + modifier = Modifier + .padding(16.dp) + ) + return + } + LaunchedEffect(state.error?.id) { state.error?.let { snackbarHostState.showSnackbar(it.errorText) @@ -384,51 +456,88 @@ fun MediaList( .combinedClickable( onClick = { if (selectionState.selecting) { - selectionState = selectionState.copy(selected = if (selectionState.selected.contains(attachment.mediaId)) selectionState.selected - attachment.mediaId else selectionState.selected + attachment.mediaId) + selectionState = selectionState.copy(selected = if (selectionState.selected.contains(attachment.id)) selectionState.selected - attachment.id else selectionState.selected + attachment.id) } }, onLongClick = { - selectionState = if (selectionState.selecting) MediaMultiSelectState() else MediaMultiSelectState(selecting = true, selected = setOf(attachment.mediaId)) + selectionState = if (selectionState.selecting) MediaMultiSelectState() else MediaMultiSelectState(selecting = true, selected = setOf(attachment.id)) } ) .padding(horizontal = 16.dp, vertical = 8.dp) ) { if (selectionState.selecting) { Checkbox( - checked = selectionState.selected.contains(attachment.mediaId), + checked = selectionState.selected.contains(attachment.id), onCheckedChange = { selected -> - selectionState = selectionState.copy(selected = if (selected) selectionState.selected + attachment.mediaId else selectionState.selected - attachment.mediaId) + selectionState = selectionState.copy(selected = if (selected) selectionState.selected + attachment.id else selectionState.selected - attachment.id) } ) } Column(modifier = Modifier.weight(1f, true)) { - Text(text = "Attachment ${attachment.title}") + Text(text = attachment.title) Text(text = "State: ${attachment.state}") } - if (attachment.state == InternalBackupPlaygroundViewModel.BackupAttachment.State.INIT || - attachment.state == InternalBackupPlaygroundViewModel.BackupAttachment.State.IN_PROGRESS - ) { + if (attachment.state == InternalBackupPlaygroundViewModel.BackupAttachment.State.IN_PROGRESS) { CircularProgressIndicator() } else { Button( enabled = !selectionState.selecting, onClick = { when (attachment.state) { - InternalBackupPlaygroundViewModel.BackupAttachment.State.LOCAL_ONLY -> backupAttachmentMedia(attachment) - InternalBackupPlaygroundViewModel.BackupAttachment.State.UPLOADED -> deleteBackupAttachmentMedia(attachment) + InternalBackupPlaygroundViewModel.BackupAttachment.State.ATTACHMENT_CDN, + InternalBackupPlaygroundViewModel.BackupAttachment.State.LOCAL_ONLY -> archiveAttachmentMedia(attachment) + + InternalBackupPlaygroundViewModel.BackupAttachment.State.UPLOADED_UNDOWNLOADED, + InternalBackupPlaygroundViewModel.BackupAttachment.State.UPLOADED_FINAL -> selectionState = selectionState.copy(expandedOption = attachment.dbAttachment.attachmentId) + else -> throw AssertionError("Unsupported state: ${attachment.state}") } } ) { Text( text = when (attachment.state) { + InternalBackupPlaygroundViewModel.BackupAttachment.State.ATTACHMENT_CDN, InternalBackupPlaygroundViewModel.BackupAttachment.State.LOCAL_ONLY -> "Backup" - InternalBackupPlaygroundViewModel.BackupAttachment.State.UPLOADED -> "Remote Delete" + + InternalBackupPlaygroundViewModel.BackupAttachment.State.UPLOADED_UNDOWNLOADED, + InternalBackupPlaygroundViewModel.BackupAttachment.State.UPLOADED_FINAL -> "Options..." + else -> throw AssertionError("Unsupported state: ${attachment.state}") } ) + + DropdownMenu( + expanded = attachment.dbAttachment.attachmentId == selectionState.expandedOption, + onDismissRequest = { selectionState = selectionState.copy(expandedOption = null) } + ) { + DropdownMenuItem( + text = { Text("Remote Delete") }, + onClick = { + selectionState = selectionState.copy(expandedOption = null) + deleteArchivedMedia(attachment) + } + ) + + DropdownMenuItem( + text = { Text("Pseudo Restore") }, + onClick = { + selectionState = selectionState.copy(expandedOption = null) + restoreArchivedMedia(attachment) + } + ) + + if (attachment.dbAttachment.dataHash != null && attachment.state == InternalBackupPlaygroundViewModel.BackupAttachment.State.UPLOADED_UNDOWNLOADED) { + DropdownMenuItem( + text = { Text("Re-copy with hash") }, + onClick = { + selectionState = selectionState.copy(expandedOption = null) + archiveAttachmentMedia(attachment) + } + ) + } + } } } } @@ -451,7 +560,7 @@ fun MediaList( Text("Cancel") } Button(onClick = { - batchBackupAttachmentMedia(selectionState.selected) + batchArchiveAttachmentMedia(selectionState.selected) selectionState = MediaMultiSelectState() }) { Text("Backup") @@ -469,7 +578,8 @@ fun MediaList( private data class MediaMultiSelectState( val selecting: Boolean = false, - val selected: Set = emptySet() + val selected: Set = emptySet(), + val expandedOption: AttachmentId? = null ) @Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO) 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 c785ef55fc..91414a28f1 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 @@ -10,30 +10,38 @@ import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Completable 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 io.reactivex.rxjava3.schedulers.Schedulers -import org.signal.core.util.Base64 import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.backup.v2.BackupMetadata import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.database.MessageType import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.ArchiveAttachmentJob +import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob +import org.thoughtcrime.securesms.jobs.AttachmentUploadJob +import org.thoughtcrime.securesms.jobs.BackupMessagesJob +import org.thoughtcrime.securesms.jobs.BackupRestoreJob +import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.mms.IncomingMessage import org.thoughtcrime.securesms.recipients.Recipient import org.whispersystems.signalservice.api.NetworkResult -import org.whispersystems.signalservice.api.backup.BackupKey +import org.whispersystems.signalservice.api.backup.MediaName import java.io.ByteArrayInputStream import java.io.InputStream import java.util.UUID -import kotlin.random.Random +import kotlin.time.Duration.Companion.seconds class InternalBackupPlaygroundViewModel : ViewModel() { - private val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - var backupData: ByteArray? = null val disposables = CompositeDisposable() @@ -57,6 +65,17 @@ class InternalBackupPlaygroundViewModel : ViewModel() { } } + fun triggerBackupJob() { + _state.value = _state.value.copy(backupState = BackupState.EXPORT_IN_PROGRESS) + + disposables += Single.fromCallable { ApplicationDependencies.getJobManager().runSynchronously(BackupMessagesJob(), 120_000) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy { + _state.value = _state.value.copy(backupState = BackupState.BACKUP_JOB_DONE) + } + } + fun import() { backupData?.let { _state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS) @@ -68,7 +87,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { disposables += Single.fromCallable { BackupRepository.import(it.size.toLong(), { ByteArrayInputStream(it) }, selfData, plaintext = plaintext) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe { nothing -> + .subscribeBy { backupData = null _state.value = _state.value.copy(backupState = BackupState.NONE) } @@ -85,7 +104,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { disposables += Single.fromCallable { BackupRepository.import(length, inputStreamFactory, selfData, plaintext = plaintext) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe { nothing -> + .subscribeBy { backupData = null _state.value = _state.value.copy(backupState = BackupState.NONE) } @@ -98,7 +117,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { disposables += Single.fromCallable { BackupRepository.validate(length, inputStreamFactory, selfData) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe { nothing -> + .subscribeBy { backupData = null _state.value = _state.value.copy(backupState = BackupState.NONE) } @@ -142,47 +161,77 @@ class InternalBackupPlaygroundViewModel : ViewModel() { } } + fun restoreFromRemote() { + _state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS) + + disposables += Single.fromCallable { + ApplicationDependencies + .getJobManager() + .startChain(BackupRestoreJob()) + .then(BackupRestoreMediaJob()) + .enqueueAndBlockUntilCompletion(120.seconds.inWholeMilliseconds) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy { + _state.value = _state.value.copy(backupState = BackupState.NONE) + } + } + fun loadMedia() { disposables += Single .fromCallable { SignalDatabase.attachments.debugGetLatestAttachments() } .subscribeOn(Schedulers.io()) .observeOn(Schedulers.single()) .subscribeBy { - _mediaState.set { update(attachments = it.map { a -> BackupAttachment.from(backupKey, a) }) } + _mediaState.set { update(attachments = it.map { a -> BackupAttachment(dbAttachment = a) }) } } + } + fun archiveAttachmentMedia(attachments: Set) { disposables += Single - .fromCallable { BackupRepository.debugGetArchivedMediaState() } + .fromCallable { + val toArchive = mediaState.value + .attachments + .filter { attachments.contains(it.dbAttachment.attachmentId) } + .map { it.dbAttachment } + + BackupRepository.archiveMedia(toArchive) + } .subscribeOn(Schedulers.io()) .observeOn(Schedulers.single()) + .doOnSubscribe { _mediaState.set { update(inProgress = inProgressMediaIds + attachments) } } + .doOnTerminate { _mediaState.set { update(inProgress = inProgressMediaIds - attachments) } } .subscribeBy { result -> when (result) { - is NetworkResult.Success -> _mediaState.set { update(archiveStateLoaded = true, backedUpMediaIds = result.result.map { it.mediaId }.toSet()) } + is NetworkResult.Success -> { + loadMedia() + result + .result + .sourceNotFoundResponses + .forEach { + reUploadAndArchiveMedia(result.result.mediaIdToAttachmentId(it.mediaId)) + } + } else -> _mediaState.set { copy(error = MediaStateError(errorText = "$result")) } } } } - fun backupAttachmentMedia(mediaIds: Set) { - disposables += Single.fromCallable { mediaIds.mapNotNull { mediaState.value.idToAttachment[it]?.dbAttachment }.toList() } - .map { BackupRepository.archiveMedia(it) } + fun archiveAttachmentMedia(attachment: BackupAttachment) { + disposables += Single.fromCallable { BackupRepository.archiveMedia(attachment.dbAttachment) } .subscribeOn(Schedulers.io()) .observeOn(Schedulers.single()) - .doOnSubscribe { _mediaState.set { update(inProgressMediaIds = inProgressMediaIds + mediaIds) } } - .doOnTerminate { _mediaState.set { update(inProgressMediaIds = inProgressMediaIds - mediaIds) } } + .doOnSubscribe { _mediaState.set { update(inProgress = inProgressMediaIds + attachment.dbAttachment.attachmentId) } } + .doOnTerminate { _mediaState.set { update(inProgress = inProgressMediaIds - attachment.dbAttachment.attachmentId) } } .subscribeBy { result -> when (result) { - is NetworkResult.Success -> { - val response = result.result - val successes = response.responses.filter { it.status == 200 } - val failures = response.responses - successes.toSet() - - _mediaState.set { - var updated = update(backedUpMediaIds = backedUpMediaIds + successes.map { it.mediaId }) - if (failures.isNotEmpty()) { - updated = updated.copy(error = MediaStateError(errorText = failures.toString())) - } - updated + is NetworkResult.Success -> loadMedia() + is NetworkResult.StatusCodeError -> { + if (result.code == 410) { + reUploadAndArchiveMedia(attachment.id) + } else { + _mediaState.set { copy(error = MediaStateError(errorText = "$result")) } } } @@ -191,49 +240,107 @@ class InternalBackupPlaygroundViewModel : ViewModel() { } } - fun backupAttachmentMedia(attachment: BackupAttachment) { - disposables += Single.fromCallable { BackupRepository.archiveMedia(attachment.dbAttachment) } + private fun reUploadAndArchiveMedia(attachmentId: AttachmentId) { + disposables += Single + .fromCallable { + ApplicationDependencies + .getJobManager() + .startChain(AttachmentUploadJob(attachmentId)) + .then(ArchiveAttachmentJob(attachmentId)) + .enqueueAndBlockUntilCompletion(15.seconds.inWholeMilliseconds) + } .subscribeOn(Schedulers.io()) .observeOn(Schedulers.single()) - .doOnSubscribe { _mediaState.set { update(inProgressMediaIds = inProgressMediaIds + attachment.mediaId) } } - .doOnTerminate { _mediaState.set { update(inProgressMediaIds = inProgressMediaIds - attachment.mediaId) } } + .doOnSubscribe { _mediaState.set { update(inProgress = inProgressMediaIds + attachmentId) } } + .doOnTerminate { _mediaState.set { update(inProgress = inProgressMediaIds - attachmentId) } } .subscribeBy { - when (it) { - is NetworkResult.Success -> { - _mediaState.set { update(backedUpMediaIds = backedUpMediaIds + attachment.mediaId) } - } - - else -> _mediaState.set { copy(error = MediaStateError(errorText = "$it")) } + if (it.isPresent && it.get().isComplete) { + loadMedia() + } else { + _mediaState.set { copy(error = MediaStateError(errorText = "Reupload slow or failed, try again")) } } } } - fun deleteBackupAttachmentMedia(mediaIds: Set) { - deleteBackupAttachmentMedia(mediaIds.mapNotNull { mediaState.value.idToAttachment[it] }.toList()) + fun deleteArchivedMedia(attachmentIds: Set) { + deleteArchivedMedia(mediaState.value.attachments.filter { attachmentIds.contains(it.dbAttachment.attachmentId) }) } - fun deleteBackupAttachmentMedia(attachment: BackupAttachment) { - deleteBackupAttachmentMedia(listOf(attachment)) + fun deleteArchivedMedia(attachment: BackupAttachment) { + deleteArchivedMedia(listOf(attachment)) } - private fun deleteBackupAttachmentMedia(attachments: List) { - val ids = attachments.map { it.mediaId }.toSet() + private fun deleteArchivedMedia(attachments: List) { + val ids = attachments.map { it.dbAttachment.attachmentId }.toSet() disposables += Single.fromCallable { BackupRepository.deleteArchivedMedia(attachments.map { it.dbAttachment }) } .subscribeOn(Schedulers.io()) .observeOn(Schedulers.single()) - .doOnSubscribe { _mediaState.set { update(inProgressMediaIds = inProgressMediaIds + ids) } } - .doOnTerminate { _mediaState.set { update(inProgressMediaIds = inProgressMediaIds - ids) } } + .doOnSubscribe { _mediaState.set { update(inProgress = inProgressMediaIds + ids) } } + .doOnTerminate { _mediaState.set { update(inProgress = inProgressMediaIds - ids) } } .subscribeBy { when (it) { - is NetworkResult.Success -> { - _mediaState.set { update(backedUpMediaIds = backedUpMediaIds - ids) } - } - + is NetworkResult.Success -> loadMedia() else -> _mediaState.set { copy(error = MediaStateError(errorText = "$it")) } } } } + fun deleteAllArchivedMedia() { + disposables += Single + .fromCallable { BackupRepository.debugDeleteAllArchivedMedia() } + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.single()) + .subscribeBy { result -> + when (result) { + is NetworkResult.Success -> loadMedia() + else -> _mediaState.set { copy(error = MediaStateError(errorText = "$result")) } + } + } + } + + fun restoreArchivedMedia(attachment: BackupAttachment) { + disposables += Completable + .fromCallable { + val recipientId = SignalStore.releaseChannelValues().releaseChannelRecipientId!! + val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(recipientId)) + + val message = IncomingMessage( + type = MessageType.NORMAL, + from = recipientId, + sentTimeMillis = System.currentTimeMillis(), + serverTimeMillis = System.currentTimeMillis(), + receivedTimeMillis = System.currentTimeMillis(), + body = "Restored from Archive!?", + serverGuid = UUID.randomUUID().toString() + ) + + val insertMessage = SignalDatabase.messages.insertMessageInbox(message, threadId).get() + + SignalDatabase.attachments.debugCopyAttachmentForArchiveRestore( + insertMessage.messageId, + attachment.dbAttachment + ) + + val archivedAttachment = SignalDatabase.attachments.getAttachmentsForMessage(insertMessage.messageId).first() + + ApplicationDependencies.getJobManager().add( + AttachmentDownloadJob( + messageId = insertMessage.messageId, + attachmentId = archivedAttachment.attachmentId, + manual = false, + forceArchiveDownload = true + ) + ) + } + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.single()) + .subscribeBy( + onError = { + _mediaState.set { copy(error = MediaStateError(errorText = "$it")) } + } + ) + } + override fun onCleared() { disposables.clear() } @@ -246,7 +353,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { ) enum class BackupState(val inProgress: Boolean = false) { - NONE, EXPORT_IN_PROGRESS(true), EXPORT_DONE, IMPORT_IN_PROGRESS(true) + NONE, EXPORT_IN_PROGRESS(true), EXPORT_DONE, BACKUP_JOB_DONE, IMPORT_IN_PROGRESS(true) } enum class BackupUploadState(val inProgress: Boolean = false) { @@ -261,67 +368,59 @@ class InternalBackupPlaygroundViewModel : ViewModel() { } data class MediaState( - val backupStateLoaded: Boolean = false, val attachments: List = emptyList(), - val backedUpMediaIds: Set = emptySet(), - val inProgressMediaIds: Set = emptySet(), + val inProgressMediaIds: Set = emptySet(), val error: MediaStateError? = null ) { - val idToAttachment: Map = attachments.associateBy { it.mediaId } - fun update( - archiveStateLoaded: Boolean = this.backupStateLoaded, attachments: List = this.attachments, - backedUpMediaIds: Set = this.backedUpMediaIds, - inProgressMediaIds: Set = this.inProgressMediaIds + inProgress: Set = this.inProgressMediaIds ): MediaState { - val updatedAttachments = if (archiveStateLoaded) { - attachments.map { - val state = if (inProgressMediaIds.contains(it.mediaId)) { - BackupAttachment.State.IN_PROGRESS - } else if (backedUpMediaIds.contains(it.mediaId)) { - BackupAttachment.State.UPLOADED + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + val updatedAttachments = attachments.map { + val state = if (inProgress.contains(it.dbAttachment.attachmentId)) { + BackupAttachment.State.IN_PROGRESS + } else if (it.dbAttachment.archiveMediaName != null) { + if (it.dbAttachment.remoteDigest != null) { + val mediaId = backupKey.deriveMediaId(MediaName(it.dbAttachment.archiveMediaName)).encode() + if (it.dbAttachment.archiveMediaId == mediaId) { + BackupAttachment.State.UPLOADED_FINAL + } else { + BackupAttachment.State.UPLOADED_UNDOWNLOADED + } } else { - BackupAttachment.State.LOCAL_ONLY + BackupAttachment.State.UPLOADED_UNDOWNLOADED } - - it.copy(state = state) + } else if (it.dbAttachment.dataHash == null) { + BackupAttachment.State.ATTACHMENT_CDN + } else { + BackupAttachment.State.LOCAL_ONLY } - } else { - attachments + + it.copy(state = state) } return copy( - backupStateLoaded = archiveStateLoaded, - attachments = updatedAttachments, - backedUpMediaIds = backedUpMediaIds + attachments = updatedAttachments ) } } data class BackupAttachment( val dbAttachment: DatabaseAttachment, - val state: State = State.INIT, - val mediaId: String = Base64.encodeUrlSafeWithPadding(Random.nextBytes(15)) + val state: State = State.LOCAL_ONLY ) { - val id: Any = dbAttachment.attachmentId + val id: AttachmentId = dbAttachment.attachmentId val title: String = dbAttachment.attachmentId.toString() enum class State { - INIT, + ATTACHMENT_CDN, LOCAL_ONLY, - UPLOADED, + UPLOADED_UNDOWNLOADED, + UPLOADED_FINAL, IN_PROGRESS } - - companion object { - fun from(backupKey: BackupKey, dbAttachment: DatabaseAttachment): BackupAttachment { - return BackupAttachment( - dbAttachment = dbAttachment, - mediaId = backupKey.deriveMediaId(Base64.decode(dbAttachment.dataHash!!)).toString() - ) - } - } } data class MediaStateError( diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 5138e6699c..ce2221fc27 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -2450,7 +2450,8 @@ public void onClick(View v, final List slides) { for (Slide slide : slides) { ApplicationDependencies.getJobManager().add(new AttachmentDownloadJob(messageRecord.getId(), ((DatabaseAttachment) slide.asAttachment()).attachmentId, - true)); + true, + false)); } } } @@ -2476,7 +2477,8 @@ public void onClick(View v, Slide slide) { setup(v, slide); jobManager.add(new AttachmentDownloadJob(messageRecord.getId(), attachmentId, - true)); + true, + false)); jobManager.addListener(queue, (job, jobState) -> { if (jobState.isComplete()) { cleanup(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt index 317e4d6907..5b7e2d3dbc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt @@ -10,6 +10,8 @@ import org.signal.core.util.Stopwatch import org.signal.core.util.logging.Log import org.signal.core.util.toInt import org.signal.paging.PagedDataSource +import org.thoughtcrime.securesms.BuildConfig +import org.thoughtcrime.securesms.backup.v2.BackupRestoreManager import org.thoughtcrime.securesms.conversation.ConversationData import org.thoughtcrime.securesms.conversation.ConversationMessage import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory @@ -20,6 +22,7 @@ import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord.Universal import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel @@ -122,6 +125,11 @@ class ConversationDataSource( records = MessageDataFetcher.updateModelsWithData(records, extraData).toMutableList() stopwatch.split("models") + if (BuildConfig.MESSAGE_BACKUP_RESTORE_ENABLED && SignalStore.backup().restoreState.inProgress) { + BackupRestoreManager.prioritizeAttachmentsIfNeeded(records) + stopwatch.split("restore") + } + val messages = records.map { record -> ConversationMessageFactory.createWithUnresolvedData( localContext, 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 a6e1064840..149bab4270 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -52,13 +52,16 @@ import org.signal.core.util.requireInt import org.signal.core.util.requireLong import org.signal.core.util.requireNonNullBlob import org.signal.core.util.requireNonNullString +import org.signal.core.util.requireObject import org.signal.core.util.requireString import org.signal.core.util.select import org.signal.core.util.toInt import org.signal.core.util.update import org.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.attachments.ArchivedAttachment 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.audio.AudioHash import org.thoughtcrime.securesms.blurhash.BlurHash @@ -140,6 +143,10 @@ class AttachmentTable( const val TRANSFORM_PROPERTIES = "transform_properties" const val DISPLAY_ORDER = "display_order" const val UPLOAD_TIMESTAMP = "upload_timestamp" + const val ARCHIVE_CDN = "archive_cdn" + const val ARCHIVE_MEDIA_NAME = "archive_media_name" + const val ARCHIVE_MEDIA_ID = "archive_media_id" + const val ARCHIVE_TRANSFER_FILE = "archive_transfer_file" const val ATTACHMENT_JSON_ALIAS = "attachment_json" @@ -150,6 +157,8 @@ class AttachmentTable( const val TRANSFER_PROGRESS_PENDING = 2 const val TRANSFER_PROGRESS_FAILED = 3 const val TRANSFER_PROGRESS_PERMANENT_FAILURE = 4 + const val TRANSFER_NEEDS_RESTORE = 5 + const val TRANSFER_RESTORE_IN_PROGRESS = 6 const val PREUPLOAD_MESSAGE_ID: Long = -8675309 private val PROJECTION = arrayOf( @@ -185,7 +194,11 @@ class AttachmentTable( DISPLAY_ORDER, UPLOAD_TIMESTAMP, DATA_HASH_START, - DATA_HASH_END + DATA_HASH_END, + ARCHIVE_CDN, + ARCHIVE_MEDIA_NAME, + ARCHIVE_MEDIA_ID, + ARCHIVE_TRANSFER_FILE ) const val CREATE_TABLE = """ @@ -222,7 +235,11 @@ class AttachmentTable( $DISPLAY_ORDER INTEGER DEFAULT 0, $UPLOAD_TIMESTAMP INTEGER DEFAULT 0, $DATA_HASH_START TEXT DEFAULT NULL, - $DATA_HASH_END TEXT DEFAULT NULL + $DATA_HASH_END TEXT DEFAULT NULL, + $ARCHIVE_CDN INTEGER DEFAULT 0, + $ARCHIVE_MEDIA_NAME TEXT DEFAULT NULL, + $ARCHIVE_MEDIA_ID TEXT DEFAULT NULL, + $ARCHIVE_TRANSFER_FILE TEXT DEFAULT NULL ) """ @@ -239,7 +256,6 @@ class AttachmentTable( val ATTACHMENT_POINTER_REUSE_THRESHOLD = 7.days.inWholeMilliseconds @JvmStatic - @JvmOverloads @Throws(IOException::class) fun newDataFile(context: Context): File { val partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE) @@ -388,6 +404,27 @@ class AttachmentTable( .flatten() } + fun getArchivableAttachments(): Cursor { + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$ARCHIVE_MEDIA_ID IS NULL AND $REMOTE_DIGEST IS NOT NULL AND ($TRANSFER_STATE = ? OR $TRANSFER_STATE = ?)", TRANSFER_PROGRESS_DONE.toString(), TRANSFER_NEEDS_RESTORE.toString()) + .orderBy("$ID DESC") + .run() + } + + fun getRestorableAttachments(batchSize: Int): List { + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$TRANSFER_STATE = ?", TRANSFER_NEEDS_RESTORE.toString()) + .limit(batchSize) + .orderBy("$ID DESC") + .run().readToList { + it.readAttachments() + }.flatten() + } + fun deleteAttachmentsForMessage(mmsId: Long): Boolean { Log.d(TAG, "[deleteAttachmentsForMessage] mmsId: $mmsId") @@ -679,6 +716,7 @@ class AttachmentTable( values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE) values.put(TRANSFER_FILE, null as String?) values.put(TRANSFORM_PROPERTIES, TransformProperties.forSkipTransform().serialize()) + values.put(ARCHIVE_TRANSFER_FILE, null as String?) db.update(TABLE_NAME) .values(values) @@ -734,7 +772,7 @@ class AttachmentTable( val values = contentValuesOf( TRANSFER_STATE to TRANSFER_PROGRESS_DONE, - CDN_NUMBER to attachment.cdnNumber, + CDN_NUMBER to attachment.cdn.serialize(), REMOTE_LOCATION to attachment.remoteLocation, REMOTE_DIGEST to attachment.remoteDigest, REMOTE_INCREMENTAL_DIGEST to attachment.incrementalDigest, @@ -774,7 +812,7 @@ class AttachmentTable( DATA_SIZE to sourceDataInfo.length, DATA_RANDOM to sourceDataInfo.random, TRANSFER_STATE to sourceAttachment.transferState, - CDN_NUMBER to sourceAttachment.cdnNumber, + CDN_NUMBER to sourceAttachment.cdn.serialize(), REMOTE_LOCATION to sourceAttachment.remoteLocation, REMOTE_DIGEST to sourceAttachment.remoteDigest, REMOTE_INCREMENTAL_DIGEST to sourceAttachment.incrementalDigest, @@ -865,7 +903,11 @@ class AttachmentTable( val attachmentId = if (attachment.uri != null) { insertAttachmentWithData(mmsId, attachment, attachment.quote) } else { - insertUndownloadedAttachment(mmsId, attachment, attachment.quote) + if (attachment is ArchivedAttachment) { + insertArchivedAttachment(mmsId, attachment, attachment.quote) + } else { + insertUndownloadedAttachment(mmsId, attachment, attachment.quote) + } } insertedAttachments[attachment] = attachmentId @@ -890,6 +932,75 @@ class AttachmentTable( return insertedAttachments } + fun debugCopyAttachmentForArchiveRestore( + mmsId: Long, + attachment: DatabaseAttachment + ) { + val copy = + """ + INSERT INTO $TABLE_NAME + ( + $MESSAGE_ID, + $CONTENT_TYPE, + $TRANSFER_STATE, + $CDN_NUMBER, + $REMOTE_LOCATION, + $REMOTE_DIGEST, + $REMOTE_INCREMENTAL_DIGEST, + $REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE, + $REMOTE_KEY, + $FILE_NAME, + $DATA_SIZE, + $VOICE_NOTE, + $BORDERLESS, + $VIDEO_GIF, + $WIDTH, + $HEIGHT, + $CAPTION, + $UPLOAD_TIMESTAMP, + $BLUR_HASH, + $DATA_SIZE, + $DATA_RANDOM, + $DATA_HASH_START, + $DATA_HASH_END, + $ARCHIVE_MEDIA_ID, + $ARCHIVE_MEDIA_NAME, + $ARCHIVE_CDN + ) + SELECT + $mmsId, + $CONTENT_TYPE, + $TRANSFER_PROGRESS_PENDING, + $CDN_NUMBER, + $REMOTE_LOCATION, + $REMOTE_DIGEST, + $REMOTE_INCREMENTAL_DIGEST, + $REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE, + $REMOTE_KEY, + $FILE_NAME, + $DATA_SIZE, + $VOICE_NOTE, + $BORDERLESS, + $VIDEO_GIF, + $WIDTH, + $HEIGHT, + $CAPTION, + ${System.currentTimeMillis()}, + $BLUR_HASH, + $DATA_SIZE, + $DATA_RANDOM, + $DATA_HASH_START, + $DATA_HASH_END, + "${attachment.archiveMediaId}", + "${attachment.archiveMediaName}", + ${attachment.archiveCdn} + FROM $TABLE_NAME + WHERE $ID = ${attachment.attachmentId.id} + """ + + writableDatabase.execSQL(copy) + } + /** * Updates the data stored for an existing attachment. This happens after transformations, like transcoding. */ @@ -956,6 +1067,24 @@ class AttachmentTable( return transferFile } + @Throws(IOException::class) + fun getOrCreateArchiveTransferFile(attachmentId: AttachmentId): File { + val existing = getArchiveTransferFile(writableDatabase, attachmentId) + if (existing != null) { + return existing + } + + val transferFile = newTransferFile() + + writableDatabase + .update(TABLE_NAME) + .values(ARCHIVE_TRANSFER_FILE to transferFile.absolutePath) + .where("$ID = ?", attachmentId.id) + .run() + + return transferFile + } + fun getDataFileInfo(attachmentId: AttachmentId): DataFileInfo? { return readableDatabase .select(ID, DATA_FILE, DATA_SIZE, DATA_RANDOM, DATA_HASH_START, DATA_HASH_END, TRANSFORM_PROPERTIES, UPLOAD_TIMESTAMP) @@ -1087,7 +1216,7 @@ class AttachmentTable( transferProgress = jsonObject.getInt(TRANSFER_STATE), size = jsonObject.getLong(DATA_SIZE), fileName = jsonObject.getString(FILE_NAME), - cdnNumber = jsonObject.getInt(CDN_NUMBER), + cdn = Cdn.deserialize(jsonObject.getInt(CDN_NUMBER)), location = jsonObject.getString(REMOTE_LOCATION), key = jsonObject.getString(REMOTE_KEY), digest = null, @@ -1116,7 +1245,10 @@ class AttachmentTable( transformProperties = TransformProperties.parse(jsonObject.getString(TRANSFORM_PROPERTIES)), displayOrder = jsonObject.getInt(DISPLAY_ORDER), uploadTimestamp = jsonObject.getLong(UPLOAD_TIMESTAMP), - dataHash = jsonObject.getString(DATA_HASH_END) + dataHash = jsonObject.getString(DATA_HASH_END), + archiveCdn = jsonObject.getInt(ARCHIVE_CDN), + archiveMediaName = jsonObject.getString(ARCHIVE_MEDIA_NAME), + archiveMediaId = jsonObject.getString(ARCHIVE_MEDIA_ID) ) } } @@ -1156,6 +1288,45 @@ class AttachmentTable( return readableDatabase.rawQuery(query, null) } + fun setArchiveData(attachmentId: AttachmentId, archiveCdn: Int, archiveMediaName: String, archiveMediaId: String) { + writableDatabase + .update(TABLE_NAME) + .values( + ARCHIVE_CDN to archiveCdn, + ARCHIVE_MEDIA_ID to archiveMediaId, + ARCHIVE_MEDIA_NAME to archiveMediaName + ) + .where("$ID = ?", attachmentId.id) + .run() + } + + fun clearArchiveData(attachmentIds: List) { + SqlUtil.buildCollectionQuery(ID, attachmentIds.map { it.id }) + .forEach { query -> + writableDatabase + .update(TABLE_NAME) + .values( + ARCHIVE_CDN to 0, + ARCHIVE_MEDIA_ID to null, + ARCHIVE_MEDIA_NAME to null + ) + .where(query.where, query.whereArgs) + .run() + } + } + + fun clearAllArchiveData() { + writableDatabase + .update(TABLE_NAME) + .values( + ARCHIVE_CDN to 0, + ARCHIVE_MEDIA_ID to null, + ARCHIVE_MEDIA_NAME to null + ) + .where("$ARCHIVE_CDN > 0 OR $ARCHIVE_MEDIA_ID IS NOT NULL OR $ARCHIVE_MEDIA_NAME IS NOT NULL") + .run() + } + /** * Deletes the data file if there's no strong references to other attachments. * If deleted, it will also clear all weak references (i.e. quotes) of the attachment. @@ -1338,7 +1509,7 @@ class AttachmentTable( put(MESSAGE_ID, messageId) put(CONTENT_TYPE, attachment.contentType) put(TRANSFER_STATE, attachment.transferState) - put(CDN_NUMBER, attachment.cdnNumber) + put(CDN_NUMBER, attachment.cdn.serialize()) put(REMOTE_LOCATION, attachment.remoteLocation) put(REMOTE_DIGEST, attachment.remoteDigest) put(REMOTE_INCREMENTAL_DIGEST, attachment.incrementalDigest) @@ -1373,6 +1544,59 @@ class AttachmentTable( return attachmentId } + /** + * Attachments need records in the database even if they haven't been downloaded yet. That allows us to store the info we need to download it, what message + * it's associated with, etc. We treat this case separately from attachments with data (see [insertAttachmentWithData]) because it's much simpler, + * and splitting the two use cases makes the code easier to understand. + * + * Callers are expected to later call [finalizeAttachmentAfterDownload] once they have downloaded the data for this attachment. + */ + @Throws(MmsException::class) + private fun insertArchivedAttachment(messageId: Long, attachment: ArchivedAttachment, quote: Boolean): AttachmentId { + Log.d(TAG, "[insertAttachment] Inserting attachment for messageId $messageId.") + + val attachmentId: AttachmentId = writableDatabase.withinTransaction { db -> + val contentValues = ContentValues().apply { + put(MESSAGE_ID, messageId) + put(CONTENT_TYPE, attachment.contentType) + put(TRANSFER_STATE, attachment.transferState) + put(CDN_NUMBER, attachment.cdn.serialize()) + put(REMOTE_LOCATION, attachment.remoteLocation) + put(REMOTE_DIGEST, attachment.remoteDigest) + put(REMOTE_INCREMENTAL_DIGEST, attachment.incrementalDigest) + put(REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE, attachment.incrementalMacChunkSize) + put(REMOTE_KEY, attachment.remoteKey) + put(FILE_NAME, StorageUtil.getCleanFileName(attachment.fileName)) + put(DATA_SIZE, attachment.size) + put(FAST_PREFLIGHT_ID, attachment.fastPreflightId) + put(VOICE_NOTE, attachment.voiceNote.toInt()) + put(BORDERLESS, attachment.borderless.toInt()) + put(VIDEO_GIF, attachment.videoGif.toInt()) + put(WIDTH, attachment.width) + put(HEIGHT, attachment.height) + put(QUOTE, quote) + put(CAPTION, attachment.caption) + put(UPLOAD_TIMESTAMP, attachment.uploadTimestamp) + put(ARCHIVE_CDN, attachment.archiveCdn) + put(ARCHIVE_MEDIA_NAME, attachment.archiveMediaName) + put(ARCHIVE_MEDIA_ID, attachment.archiveMediaId) + + attachment.stickerLocator?.let { sticker -> + put(STICKER_PACK_ID, sticker.packId) + put(STICKER_PACK_KEY, sticker.packKey) + put(STICKER_ID, sticker.stickerId) + put(STICKER_EMOJI, sticker.emoji) + } + } + + val rowId = db.insert(TABLE_NAME, null, contentValues) + AttachmentId(rowId) + } + + notifyAttachmentListeners() + return attachmentId + } + /** * Inserts an attachment with existing data. This is likely an outgoing attachment that we're in the process of sending. */ @@ -1462,7 +1686,7 @@ class AttachmentTable( contentValues.put(MESSAGE_ID, messageId) contentValues.put(CONTENT_TYPE, uploadTemplate?.contentType ?: attachment.contentType) contentValues.put(TRANSFER_STATE, attachment.transferState) // Even if we have a template, we let AttachmentUploadJob have the final say so it can re-check and make sure the template is still valid - contentValues.put(CDN_NUMBER, uploadTemplate?.cdnNumber ?: 0) + contentValues.put(CDN_NUMBER, uploadTemplate?.cdn?.serialize() ?: Cdn.CDN_0.serialize()) contentValues.put(REMOTE_LOCATION, uploadTemplate?.remoteLocation) contentValues.put(REMOTE_DIGEST, uploadTemplate?.remoteDigest) contentValues.put(REMOTE_INCREMENTAL_DIGEST, uploadTemplate?.incrementalDigest) @@ -1520,6 +1744,18 @@ class AttachmentTable( } } + private fun getArchiveTransferFile(db: SQLiteDatabase, attachmentId: AttachmentId): File? { + return db + .select(ARCHIVE_TRANSFER_FILE) + .from(TABLE_NAME) + .where("$ID = ?", attachmentId.id) + .limit(1) + .run() + .readToSingleObject { cursor -> + cursor.requireString(ARCHIVE_TRANSFER_FILE)?.let { File(it) } + } + } + private fun getAttachment(cursor: Cursor): DatabaseAttachment { val contentType = cursor.requireString(CONTENT_TYPE) @@ -1532,7 +1768,7 @@ class AttachmentTable( transferProgress = cursor.requireInt(TRANSFER_STATE), size = cursor.requireLong(DATA_SIZE), fileName = cursor.requireString(FILE_NAME), - cdnNumber = cursor.requireInt(CDN_NUMBER), + cdn = cursor.requireObject(CDN_NUMBER, Cdn.Serializer), location = cursor.requireString(REMOTE_LOCATION), key = cursor.requireString(REMOTE_KEY), digest = cursor.requireBlob(REMOTE_DIGEST), @@ -1552,7 +1788,10 @@ class AttachmentTable( transformProperties = TransformProperties.parse(cursor.requireString(TRANSFORM_PROPERTIES)), displayOrder = cursor.requireInt(DISPLAY_ORDER), uploadTimestamp = cursor.requireLong(UPLOAD_TIMESTAMP), - dataHash = cursor.requireString(DATA_HASH_END) + dataHash = cursor.requireString(DATA_HASH_END), + archiveCdn = cursor.requireInt(ARCHIVE_CDN), + archiveMediaName = cursor.requireString(ARCHIVE_MEDIA_NAME), + archiveMediaId = cursor.requireString(ARCHIVE_MEDIA_ID) ) } @@ -1603,7 +1842,7 @@ class AttachmentTable( return readableDatabase .select(*PROJECTION) .from(TABLE_NAME) - .where("$TRANSFER_STATE == $TRANSFER_PROGRESS_DONE AND $REMOTE_LOCATION IS NOT NULL AND $DATA_HASH_END IS NOT NULL") + .where("$REMOTE_LOCATION IS NOT NULL AND $REMOTE_KEY IS NOT NULL") .orderBy("$ID DESC") .limit(30) .run() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt index 884d0e207c..7f938d6224 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt @@ -56,6 +56,7 @@ import org.whispersystems.signalservice.api.groupsv2.findRequestingByAci import org.whispersystems.signalservice.api.groupsv2.toAciList import org.whispersystems.signalservice.api.groupsv2.toAciListWithUnknowns import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId import org.whispersystems.signalservice.api.push.DistributionId import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.push.ServiceId.ACI @@ -746,7 +747,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT values.put(MMS, groupId.isMms) if (avatar != null) { - values.put(AVATAR_ID, avatar.remoteId.v2.get()) + values.put(AVATAR_ID, (avatar.remoteId as SignalServiceAttachmentRemoteId.V2).cdnId) values.put(AVATAR_KEY, avatar.key) values.put(AVATAR_CONTENT_TYPE, avatar.contentType) values.put(AVATAR_DIGEST, avatar.digest.orElse(null)) @@ -822,7 +823,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT } if (avatar != null) { - put(AVATAR_ID, avatar.remoteId.v2.get()) + put(AVATAR_ID, (avatar.remoteId as SignalServiceAttachmentRemoteId.V2).cdnId) put(AVATAR_CONTENT_TYPE, avatar.contentType) put(AVATAR_KEY, avatar.key) put(AVATAR_DIGEST, avatar.digest.orElse(null)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt index 600a746024..87ac4bd225 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt @@ -50,6 +50,9 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_INCREMENTAL_DIGEST}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_HASH_END}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_CDN}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_MEDIA_NAME}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_MEDIA_ID}, ${MessageTable.TABLE_NAME}.${MessageTable.TYPE}, ${MessageTable.TABLE_NAME}.${MessageTable.DATE_SENT}, ${MessageTable.TABLE_NAME}.${MessageTable.DATE_RECEIVED}, 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 86bf3b612b..3e3cc862d7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -376,7 +376,11 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat '${AttachmentTable.BLUR_HASH}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.BLUR_HASH}, '${AttachmentTable.TRANSFORM_PROPERTIES}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.TRANSFORM_PROPERTIES}, '${AttachmentTable.DISPLAY_ORDER}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DISPLAY_ORDER}, - '${AttachmentTable.UPLOAD_TIMESTAMP}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.UPLOAD_TIMESTAMP} + '${AttachmentTable.UPLOAD_TIMESTAMP}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.UPLOAD_TIMESTAMP}, + '${AttachmentTable.DATA_HASH_END}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_HASH_END}, + '${AttachmentTable.ARCHIVE_CDN}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_CDN}, + '${AttachmentTable.ARCHIVE_MEDIA_NAME}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_MEDIA_NAME}, + '${AttachmentTable.ARCHIVE_MEDIA_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_MEDIA_ID} ) ) AS ${AttachmentTable.ATTACHMENT_JSON_ALIAS} """.toSingleLine() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 747b810686..27a2903153 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -81,6 +81,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V220_PreKeyConstrai import org.thoughtcrime.securesms.database.helpers.migration.V221_AddReadColumnToCallEventsTable import org.thoughtcrime.securesms.database.helpers.migration.V222_DataHashRefactor import org.thoughtcrime.securesms.database.helpers.migration.V223_AddNicknameAndNoteFieldsToRecipientTable +import org.thoughtcrime.securesms.database.helpers.migration.V224_AddAttachmentArchiveColumns /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -164,10 +165,11 @@ object SignalDatabaseMigrations { 220 to V220_PreKeyConstraints, 221 to V221_AddReadColumnToCallEventsTable, 222 to V222_DataHashRefactor, - 223 to V223_AddNicknameAndNoteFieldsToRecipientTable + 223 to V223_AddNicknameAndNoteFieldsToRecipientTable, + 224 to V224_AddAttachmentArchiveColumns ) - const val DATABASE_VERSION = 223 + const val DATABASE_VERSION = 224 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V224_AddAttachmentArchiveColumns.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V224_AddAttachmentArchiveColumns.kt new file mode 100644 index 0000000000..5c3018a402 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V224_AddAttachmentArchiveColumns.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds archive_cdn and archive_media to attachment. + */ +@Suppress("ClassName") +object V224_AddAttachmentArchiveColumns : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE attachment ADD COLUMN archive_cdn INTEGER DEFAULT 0") + db.execSQL("ALTER TABLE attachment ADD COLUMN archive_media_name TEXT DEFAULT NULL") + db.execSQL("ALTER TABLE attachment ADD COLUMN archive_media_id TEXT DEFAULT NULL") + db.execSQL("ALTER TABLE attachment ADD COLUMN archive_transfer_file TEXT DEFAULT NULL") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentJob.kt new file mode 100644 index 0000000000..312a517c00 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentJob.kt @@ -0,0 +1,78 @@ +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.protos.ArchiveAttachmentJobData +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException +import java.io.IOException +import java.util.concurrent.TimeUnit + +/** + * Copies and re-encrypts attachments from the attachment cdn to the archive cdn. + * + * Job will fail if the attachment isn't available on the attachment cdn, use [AttachmentUploadJob] to upload first if necessary. + */ +class ArchiveAttachmentJob private constructor(private val attachmentId: AttachmentId, parameters: Parameters) : BaseJob(parameters) { + + companion object { + private val TAG = Log.tag(ArchiveAttachmentJob::class.java) + + const val KEY = "ArchiveAttachmentJob" + + fun enqueueIfPossible(attachmentId: AttachmentId) { + if (!SignalStore.backup().canReadWriteToArchiveCdn) { + return + } + + ApplicationDependencies.getJobManager().add(ArchiveAttachmentJob(attachmentId)) + } + } + + constructor(attachmentId: AttachmentId) : this( + attachmentId = attachmentId, + parameters = Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build() + ) + + override fun serialize(): ByteArray = ArchiveAttachmentJobData(attachmentId.id).encode() + + override fun getFactoryKey(): String = KEY + + override fun onRun() { + if (!SignalStore.backup().canReadWriteToArchiveCdn) { + Log.w(TAG, "Do not have permission to read/write to archive cdn") + return + } + + val attachment = SignalDatabase.attachments.getAttachment(attachmentId) + + if (attachment == null) { + Log.w(TAG, "Unable to find attachment to archive: $attachmentId") + return + } + + BackupRepository.archiveMedia(attachment).successOrThrow() + } + + override fun onShouldRetry(e: Exception): Boolean { + return e is IOException && e !is NonSuccessfulResponseCodeException + } + + override fun onFailure() = Unit + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): ArchiveAttachmentJob { + val jobData = ArchiveAttachmentJobData.ADAPTER.decode(serializedData!!) + return ArchiveAttachmentJob(AttachmentId(jobData.attachmentId), parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java deleted file mode 100644 index bd4d619e4a..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.jobs; - -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import org.greenrobot.eventbus.EventBus; -import org.signal.core.util.Hex; -import org.signal.core.util.logging.Log; -import org.signal.libsignal.protocol.InvalidMacException; -import org.signal.libsignal.protocol.InvalidMessageException; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.thoughtcrime.securesms.database.AttachmentTable; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobLogger; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.notifications.v2.ConversationId; -import org.thoughtcrime.securesms.releasechannel.ReleaseChannel; -import org.thoughtcrime.securesms.s3.S3; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.thoughtcrime.securesms.util.AttachmentUtil; -import org.signal.core.util.Base64; -import org.thoughtcrime.securesms.util.FeatureFlags; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; -import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.push.exceptions.RangeException; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import okhttp3.Response; -import okhttp3.ResponseBody; -import okio.Okio; - -public final class AttachmentDownloadJob extends BaseJob { - - public static final String KEY = "AttachmentDownloadJob"; - - private static final String TAG = Log.tag(AttachmentDownloadJob.class); - - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_ATTACHMENT_ID = "part_row_id"; - private static final String KEY_MANUAL = "part_manual"; - - private final long messageId; - private final long attachmentId; - private final boolean manual; - - public AttachmentDownloadJob(long messageId, AttachmentId attachmentId, boolean manual) { - this(new Job.Parameters.Builder() - .setQueue(constructQueueString(attachmentId)) - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - messageId, - attachmentId, - manual); - } - - private AttachmentDownloadJob(@NonNull Job.Parameters parameters, long messageId, AttachmentId attachmentId, boolean manual) { - super(parameters); - - this.messageId = messageId; - this.attachmentId = attachmentId.id; - this.manual = manual; - } - - @Override - public @Nullable byte[] serialize() { - return new JsonJobData.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putLong(KEY_ATTACHMENT_ID, attachmentId) - .putBoolean(KEY_MANUAL, manual) - .serialize(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - public static String constructQueueString(AttachmentId attachmentId) { - return "AttachmentDownloadJob-" + attachmentId.id; - } - - @Override - public void onAdded() { - Log.i(TAG, "onAdded() messageId: " + messageId + " attachmentId: " + attachmentId + " manual: " + manual); - - final AttachmentTable database = SignalDatabase.attachments(); - final AttachmentId attachmentId = new AttachmentId(this.attachmentId); - final DatabaseAttachment attachment = database.getAttachment(attachmentId); - final boolean pending = attachment != null && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_DONE - && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE; - - if (pending && (manual || AttachmentUtil.isAutoDownloadPermitted(context, attachment))) { - Log.i(TAG, "onAdded() Marking attachment progress as 'started'"); - database.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_STARTED); - } - } - - @Override - public void onRun() throws Exception { - doWork(); - - if (!SignalDatabase.messages().isStory(messageId)) { - ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(0)); - } - } - - public void doWork() throws IOException, RetryLaterException { - Log.i(TAG, "onRun() messageId: " + messageId + " attachmentId: " + attachmentId + " manual: " + manual); - - final AttachmentTable database = SignalDatabase.attachments(); - final AttachmentId attachmentId = new AttachmentId(this.attachmentId); - final DatabaseAttachment attachment = database.getAttachment(attachmentId); - - if (attachment == null) { - Log.w(TAG, "attachment no longer exists."); - return; - } - - if (attachment.isPermanentlyFailed()) { - Log.w(TAG, "Attachment was marked as a permanent failure. Refusing to download."); - return; - } - - if (!attachment.isInProgress()) { - Log.w(TAG, "Attachment was already downloaded."); - return; - } - - if (!manual && !AttachmentUtil.isAutoDownloadPermitted(context, attachment)) { - Log.w(TAG, "Attachment can't be auto downloaded..."); - database.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_PENDING); - return; - } - - Log.i(TAG, "Downloading push part " + attachmentId); - database.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_STARTED); - - if (attachment.cdnNumber != ReleaseChannel.CDN_NUMBER) { - retrieveAttachment(messageId, attachmentId, attachment); - } else { - retrieveAttachmentForReleaseChannel(messageId, attachmentId, attachment); - } - } - - @Override - public void onFailure() { - Log.w(TAG, JobLogger.format(this, "onFailure() messageId: " + messageId + " attachmentId: " + attachmentId + " manual: " + manual)); - - final AttachmentId attachmentId = new AttachmentId(this.attachmentId); - markFailed(messageId, attachmentId); - } - - @Override - protected boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof PushNetworkException || - exception instanceof RetryLaterException; - } - - private void retrieveAttachment(long messageId, - final AttachmentId attachmentId, - final Attachment attachment) - throws IOException, RetryLaterException - { - long maxReceiveSize = FeatureFlags.maxAttachmentReceiveSizeBytes(); - - AttachmentTable database = SignalDatabase.attachments(); - File attachmentFile = database.getOrCreateTransferFile(attachmentId); - - try { - if (attachment.size > maxReceiveSize) { - throw new MmsException("Attachment too large, failing download"); - } - SignalServiceMessageReceiver messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver(); - SignalServiceAttachmentPointer pointer = createAttachmentPointer(attachment); - InputStream stream = messageReceiver.retrieveAttachment(pointer, - attachmentFile, - maxReceiveSize, - new SignalServiceAttachment.ProgressListener() { - @Override - public void onAttachmentProgress(long total, long progress) { - EventBus.getDefault().postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)); - } - - @Override - public boolean shouldCancel() { - return isCanceled(); - } - }); - database.finalizeAttachmentAfterDownload(messageId, attachmentId, stream); - } catch (RangeException e) { - Log.w(TAG, "Range exception, file size " + attachmentFile.length(), e); - if (attachmentFile.delete()) { - Log.i(TAG, "Deleted temp download file to recover"); - throw new RetryLaterException(e); - } else { - throw new IOException("Failed to delete temp download file following range exception"); - } - } catch (InvalidPartException | NonSuccessfulResponseCodeException | MmsException | MissingConfigurationException e) { - Log.w(TAG, "Experienced exception while trying to download an attachment.", e); - markFailed(messageId, attachmentId); - } catch (InvalidMessageException e) { - Log.w(TAG, "Experienced an InvalidMessageException while trying to download an attachment.", e); - if (e.getCause() instanceof InvalidMacException) { - Log.w(TAG, "Detected an invalid mac. Treating as a permanent failure."); - markPermanentlyFailed(messageId, attachmentId); - } else { - markFailed(messageId, attachmentId); - } - } - } - - private SignalServiceAttachmentPointer createAttachmentPointer(Attachment attachment) throws InvalidPartException { - if (TextUtils.isEmpty(attachment.remoteLocation)) { - throw new InvalidPartException("empty content id"); - } - - if (TextUtils.isEmpty(attachment.remoteKey)) { - throw new InvalidPartException("empty encrypted key"); - } - - try { - final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.remoteLocation); - final byte[] key = Base64.decode(attachment.remoteKey); - - if (attachment.remoteDigest != null) { - Log.i(TAG, "Downloading attachment with digest: " + Hex.toString(attachment.remoteDigest)); - } else { - Log.i(TAG, "Downloading attachment with no digest..."); - } - - return new SignalServiceAttachmentPointer(attachment.cdnNumber, remoteId, null, key, - Optional.of(Util.toIntExact(attachment.size)), - Optional.empty(), - 0, 0, - Optional.ofNullable(attachment.remoteDigest), - Optional.ofNullable(attachment.getIncrementalDigest()), - attachment.incrementalMacChunkSize, - Optional.ofNullable(attachment.fileName), - attachment.voiceNote, - attachment.borderless, - attachment.videoGif, - Optional.empty(), - Optional.ofNullable(attachment.blurHash).map(BlurHash::getHash), - attachment.uploadTimestamp); - } catch (IOException | ArithmeticException e) { - Log.w(TAG, e); - throw new InvalidPartException(e); - } - } - - private void retrieveAttachmentForReleaseChannel(long messageId, - final AttachmentId attachmentId, - final Attachment attachment) - throws IOException - { - try (Response response = S3.getObject(Objects.requireNonNull(attachment.fileName))) { - ResponseBody body = response.body(); - if (body != null) { - if (body.contentLength() > FeatureFlags.maxAttachmentReceiveSizeBytes()) { - throw new MmsException("Attachment too large, failing download"); - } - SignalDatabase.attachments().finalizeAttachmentAfterDownload(messageId, attachmentId, Okio.buffer(body.source()).inputStream()); - } - } catch (MmsException e) { - Log.w(TAG, "Experienced exception while trying to download an attachment.", e); - markFailed(messageId, attachmentId); - } - } - - private void markFailed(long messageId, AttachmentId attachmentId) { - try { - AttachmentTable database = SignalDatabase.attachments(); - database.setTransferProgressFailed(attachmentId, messageId); - } catch (MmsException e) { - Log.w(TAG, e); - } - } - - private void markPermanentlyFailed(long messageId, AttachmentId attachmentId) { - try { - AttachmentTable database = SignalDatabase.attachments(); - database.setTransferProgressPermanentFailure(attachmentId, messageId); - } catch (MmsException e) { - Log.w(TAG, e); - } - } - - public static boolean jobSpecMatchesAttachmentId(@NonNull JobSpec jobSpec, @NonNull AttachmentId attachmentId) { - if (!KEY.equals(jobSpec.getFactoryKey())) { - return false; - } - - final byte[] serializedData = jobSpec.getSerializedData(); - if (serializedData == null) { - return false; - } - - JsonJobData data = JsonJobData.deserialize(serializedData); - final AttachmentId parsed = new AttachmentId(data.getLong(KEY_ATTACHMENT_ID)); - return attachmentId.equals(parsed); - } - - @VisibleForTesting - static class InvalidPartException extends Exception { - InvalidPartException(String s) {super(s);} - InvalidPartException(Exception e) {super(e);} - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull AttachmentDownloadJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) { - JsonJobData data = JsonJobData.deserialize(serializedData); - - return new AttachmentDownloadJob(parameters, - data.getLong(KEY_MESSAGE_ID), - new AttachmentId(data.getLong(KEY_ATTACHMENT_ID)), - data.getBoolean(KEY_MANUAL)); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt new file mode 100644 index 0000000000..5969ea6908 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt @@ -0,0 +1,424 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.thoughtcrime.securesms.jobs + +import android.text.TextUtils +import androidx.annotation.VisibleForTesting +import okio.Source +import okio.buffer +import org.greenrobot.eventbus.EventBus +import org.signal.core.util.Base64 +import org.signal.core.util.Hex +import org.signal.core.util.logging.Log +import org.signal.libsignal.protocol.InvalidMacException +import org.signal.libsignal.protocol.InvalidMessageException +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.BackupRepository +import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.events.PartProgressEvent +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.JobLogger.format +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.mms.MmsException +import org.thoughtcrime.securesms.notifications.v2.ConversationId.Companion.forConversation +import org.thoughtcrime.securesms.s3.S3 +import org.thoughtcrime.securesms.transport.RetryLaterException +import org.thoughtcrime.securesms.util.AttachmentUtil +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.Util +import org.whispersystems.signalservice.api.backup.MediaName +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId +import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException +import org.whispersystems.signalservice.api.push.exceptions.RangeException +import java.io.File +import java.io.IOException +import java.util.Optional +import java.util.concurrent.TimeUnit + +/** + * Download attachment from locations as specified in their record. + */ +class AttachmentDownloadJob private constructor( + parameters: Parameters, + private val messageId: Long, + attachmentId: AttachmentId, + private val manual: Boolean, + private var forceArchiveDownload: Boolean +) : BaseJob(parameters) { + + companion object { + const val KEY = "AttachmentDownloadJob" + private val TAG = Log.tag(AttachmentDownloadJob::class.java) + + private const val KEY_MESSAGE_ID = "message_id" + private const val KEY_ATTACHMENT_ID = "part_row_id" + private const val KEY_MANUAL = "part_manual" + private const val KEY_FORCE_ARCHIVE = "force_archive" + + @JvmStatic + fun constructQueueString(attachmentId: AttachmentId): String { + return "AttachmentDownloadJob-" + attachmentId.id + } + + fun jobSpecMatchesAttachmentId(jobSpec: JobSpec, attachmentId: AttachmentId): Boolean { + if (KEY != jobSpec.factoryKey) { + return false + } + + val serializedData = jobSpec.serializedData ?: return false + val data = JsonJobData.deserialize(serializedData) + val parsed = AttachmentId(data.getLong(KEY_ATTACHMENT_ID)) + return attachmentId == parsed + } + } + + private val attachmentId: Long + + constructor(messageId: Long, attachmentId: AttachmentId, manual: Boolean, forceArchiveDownload: Boolean = false) : this( + Parameters.Builder() + .setQueue(constructQueueString(attachmentId)) + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + messageId, + attachmentId, + manual, + forceArchiveDownload + ) + + init { + this.attachmentId = attachmentId.id + } + + override fun serialize(): ByteArray? { + return JsonJobData.Builder() + .putLong(KEY_MESSAGE_ID, messageId) + .putLong(KEY_ATTACHMENT_ID, attachmentId) + .putBoolean(KEY_MANUAL, manual) + .putBoolean(KEY_FORCE_ARCHIVE, forceArchiveDownload) + .serialize() + } + + override fun getFactoryKey(): String { + return KEY + } + + override fun onAdded() { + Log.i(TAG, "onAdded() messageId: $messageId attachmentId: $attachmentId manual: $manual") + + val attachmentId = AttachmentId(attachmentId) + val attachment = SignalDatabase.attachments.getAttachment(attachmentId) + val pending = attachment != null && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_DONE && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE + + if (pending && (manual || AttachmentUtil.isAutoDownloadPermitted(context, attachment))) { + Log.i(TAG, "onAdded() Marking attachment progress as 'started'") + SignalDatabase.attachments.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_STARTED) + } + } + + @Throws(Exception::class) + public override fun onRun() { + doWork() + + if (!SignalDatabase.messages.isStory(messageId)) { + ApplicationDependencies.getMessageNotifier().updateNotification(context, forConversation(0)) + } + } + + @Throws(IOException::class, RetryLaterException::class) + fun doWork() { + Log.i(TAG, "onRun() messageId: $messageId attachmentId: $attachmentId manual: $manual") + + val attachmentId = AttachmentId(attachmentId) + val attachment = SignalDatabase.attachments.getAttachment(attachmentId) + + if (attachment == null) { + Log.w(TAG, "attachment no longer exists.") + return + } + + if (attachment.isPermanentlyFailed) { + Log.w(TAG, "Attachment was marked as a permanent failure. Refusing to download.") + return + } + + if (!attachment.isInProgress) { + Log.w(TAG, "Attachment was already downloaded.") + return + } + + if (!manual && !AttachmentUtil.isAutoDownloadPermitted(context, attachment)) { + Log.w(TAG, "Attachment can't be auto downloaded...") + SignalDatabase.attachments.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_PENDING) + return + } + + Log.i(TAG, "Downloading push part $attachmentId") + SignalDatabase.attachments.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_STARTED) + + when (attachment.cdn) { + Cdn.S3 -> retrieveAttachmentForReleaseChannel(messageId, attachmentId, attachment) + else -> retrieveAttachment(messageId, attachmentId, attachment) + } + } + + override fun onFailure() { + Log.w(TAG, format(this, "onFailure() messageId: $messageId attachmentId: $attachmentId manual: $manual")) + + val attachmentId = AttachmentId(attachmentId) + markFailed(messageId, attachmentId) + } + + override fun onShouldRetry(exception: Exception): Boolean { + return exception is PushNetworkException || + exception is RetryLaterException + } + + @Throws(IOException::class, RetryLaterException::class) + private fun retrieveAttachment( + messageId: Long, + attachmentId: AttachmentId, + attachment: DatabaseAttachment + ) { + val maxReceiveSize: Long = FeatureFlags.maxAttachmentReceiveSizeBytes() + val attachmentFile: File = SignalDatabase.attachments.getOrCreateTransferFile(attachmentId) + var archiveFile: File? = null + var useArchiveCdn = false + + try { + if (attachment.size > maxReceiveSize) { + throw MmsException("Attachment too large, failing download") + } + + useArchiveCdn = if (SignalStore.backup().canReadWriteToArchiveCdn && (forceArchiveDownload || attachment.remoteLocation == null)) { + if (attachment.archiveMediaName.isNullOrEmpty()) { + throw InvalidPartException("Invalid attachment configuration") + } + true + } else { + false + } + + val messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver() + val pointer = createAttachmentPointer(attachment, useArchiveCdn) + + val progressListener = object : SignalServiceAttachment.ProgressListener { + override fun onAttachmentProgress(total: Long, progress: Long) { + EventBus.getDefault().postSticky(PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)) + } + + override fun shouldCancel(): Boolean { + return this@AttachmentDownloadJob.isCanceled + } + } + + val stream = if (useArchiveCdn) { + archiveFile = SignalDatabase.attachments.getOrCreateArchiveTransferFile(attachmentId) + val cdnCredentials = BackupRepository.getCdnReadCredentials().successOrThrow().headers + + messageReceiver + .retrieveArchivedAttachment( + SignalStore.svr().getOrCreateMasterKey().deriveBackupKey().deriveMediaSecrets(MediaName(attachment.archiveMediaName!!)), + cdnCredentials, + archiveFile, + pointer, + attachmentFile, + maxReceiveSize, + progressListener + ) + } else { + messageReceiver + .retrieveAttachment( + pointer, + attachmentFile, + maxReceiveSize, + progressListener + ) + } + + SignalDatabase.attachments.finalizeAttachmentAfterDownload(messageId, attachmentId, stream) + } catch (e: RangeException) { + val transferFile = archiveFile ?: attachmentFile + Log.w(TAG, "Range exception, file size " + transferFile.length(), e) + if (transferFile.delete()) { + Log.i(TAG, "Deleted temp download file to recover") + throw RetryLaterException(e) + } else { + throw IOException("Failed to delete temp download file following range exception") + } + } catch (e: InvalidPartException) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, attachmentId) + } catch (e: NonSuccessfulResponseCodeException) { + if (SignalStore.backup().canReadWriteToArchiveCdn) { + if (e.code == 404 && !useArchiveCdn && attachment.archiveMediaName?.isNotEmpty() == true) { + Log.i(TAG, "Retrying download from archive CDN") + forceArchiveDownload = true + retrieveAttachment(messageId, attachmentId, attachment) + return + } else if (e.code == 401 && useArchiveCdn) { + SignalStore.backup().cdnReadCredentials = null + throw RetryLaterException(e) + } + } + + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, attachmentId) + } catch (e: MmsException) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, attachmentId) + } catch (e: MissingConfigurationException) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, 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) + } else { + markFailed(messageId, attachmentId) + } + } + } + + @Throws(InvalidPartException::class) + private fun createAttachmentPointer(attachment: DatabaseAttachment, useArchiveCdn: Boolean): SignalServiceAttachmentPointer { + if (TextUtils.isEmpty(attachment.remoteKey)) { + throw InvalidPartException("empty encrypted key") + } + + return try { + val remoteData: RemoteData = if (useArchiveCdn) { + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + val backupDirectories = BackupRepository.getCdnBackupDirectories().successOrThrow() + + RemoteData( + remoteId = SignalServiceAttachmentRemoteId.Backup( + backupDir = backupDirectories.backupDir, + mediaDir = backupDirectories.mediaDir, + mediaId = backupKey.deriveMediaId(MediaName(attachment.archiveMediaName!!)).encode() + ), + cdnNumber = attachment.archiveCdn + ) + } else { + if (attachment.remoteLocation.isNullOrEmpty()) { + throw InvalidPartException("empty content id") + } + + RemoteData( + remoteId = SignalServiceAttachmentRemoteId.from(attachment.remoteLocation), + cdnNumber = attachment.cdn.cdnNumber + ) + } + + val key = Base64.decode(attachment.remoteKey!!) + + if (attachment.remoteDigest != null) { + Log.i(TAG, "Downloading attachment with digest: " + Hex.toString(attachment.remoteDigest)) + } else { + Log.i(TAG, "Downloading attachment with no digest...") + } + + SignalServiceAttachmentPointer( + remoteData.cdnNumber, + remoteData.remoteId, + null, + key, + Optional.of(Util.toIntExact(attachment.size)), + Optional.empty(), + 0, + 0, + Optional.ofNullable(attachment.remoteDigest), + Optional.ofNullable(attachment.getIncrementalDigest()), + attachment.incrementalMacChunkSize, + Optional.ofNullable(attachment.fileName), + attachment.voiceNote, + attachment.borderless, + attachment.videoGif, + Optional.empty(), + Optional.ofNullable(attachment.blurHash).map { it.hash }, + attachment.uploadTimestamp + ) + } catch (e: IOException) { + Log.w(TAG, e) + throw InvalidPartException(e) + } catch (e: ArithmeticException) { + Log.w(TAG, e) + throw InvalidPartException(e) + } + } + + @Throws(IOException::class) + private fun retrieveAttachmentForReleaseChannel( + messageId: Long, + attachmentId: AttachmentId, + attachment: Attachment + ) { + try { + S3.getObject(attachment.fileName!!).use { response -> + val body = response.body() + if (body != null) { + if (body.contentLength() > FeatureFlags.maxAttachmentReceiveSizeBytes()) { + throw MmsException("Attachment too large, failing download") + } + SignalDatabase.attachments.finalizeAttachmentAfterDownload(messageId, attachmentId, (body.source() as Source).buffer().inputStream()) + } + } + } catch (e: MmsException) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, attachmentId) + } + } + + private fun markFailed(messageId: Long, attachmentId: AttachmentId) { + try { + SignalDatabase.attachments.setTransferProgressFailed(attachmentId, messageId) + } catch (e: MmsException) { + Log.w(TAG, e) + } + } + + private fun markPermanentlyFailed(messageId: Long, attachmentId: AttachmentId) { + try { + SignalDatabase.attachments.setTransferProgressPermanentFailure(attachmentId, messageId) + } catch (e: MmsException) { + Log.w(TAG, e) + } + } + + @VisibleForTesting + internal class InvalidPartException : Exception { + constructor(s: String?) : super(s) + constructor(e: Exception?) : super(e) + } + + private data class RemoteData(val remoteId: SignalServiceAttachmentRemoteId, val cdnNumber: Int) + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): AttachmentDownloadJob { + val data = JsonJobData.deserialize(serializedData) + return AttachmentDownloadJob( + parameters = parameters, + messageId = data.getLong(KEY_MESSAGE_ID), + attachmentId = AttachmentId(data.getLong(KEY_ATTACHMENT_ID)), + manual = data.getBoolean(KEY_MANUAL), + forceArchiveDownload = data.getBooleanOrDefault(KEY_FORCE_ARCHIVE, false) + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java index 69b8cb54f8..76b9dec005 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java @@ -85,7 +85,7 @@ public void onRun() throws IOException { attachment.deleteOnExit(); SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver(); - SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId(avatarId), contentType, key, Optional.of(0), Optional.empty(), 0, 0, digest, Optional.empty(), 0, fileName, false, false, false, Optional.empty(), Optional.empty(), System.currentTimeMillis()); + SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId.V2(avatarId), contentType, key, Optional.of(0), Optional.empty(), 0, 0, digest, Optional.empty(), 0, fileName, false, false, false, Optional.empty(), Optional.empty(), System.currentTimeMillis()); InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, AvatarHelper.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE); AvatarHelper.setAvatar(context, record.get().getRecipientId(), inputStream); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt new file mode 100644 index 0000000000..c3bd4080c1 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import android.database.Cursor +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.BuildConfig +import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.providers.BlobProvider +import org.whispersystems.signalservice.api.NetworkResult +import java.io.FileInputStream +import java.io.FileOutputStream + +/** + * Job that is responsible for exporting the DB as a backup proto and + * also uploading the resulting proto. + */ +class BackupMessagesJob private constructor(parameters: Parameters) : BaseJob(parameters) { + + companion object { + private val TAG = Log.tag(BackupMessagesJob::class.java) + + const val KEY = "BackupMessagesJob" + } + + constructor() : this( + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(Parameters.UNLIMITED) + .setMaxInstancesForFactory(2) + .build() + ) + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + private fun archiveAttachments() { + if (BuildConfig.MESSAGE_BACKUP_RESTORE_ENABLED) { + SignalStore.backup().canReadWriteToArchiveCdn = true + } + val batchSize = 100 + SignalDatabase.attachments.getArchivableAttachments().use { cursor -> + while (!cursor.isAfterLast) { + val attachments = cursor.readAttachmentBatch(batchSize) + + when (val archiveResult = BackupRepository.archiveMedia(attachments)) { + is NetworkResult.Success -> { + for (success in archiveResult.result.sourceNotFoundResponses) { + val attachmentId = archiveResult.result.mediaIdToAttachmentId(success.mediaId) + ApplicationDependencies + .getJobManager() + .startChain(AttachmentUploadJob(attachmentId)) + .then(ArchiveAttachmentJob(attachmentId)) + .enqueue() + } + } + + else -> { + Log.e(TAG, "Failed to archive $archiveResult") + } + } + } + } + } + + private fun Cursor.readAttachmentBatch(batchSize: Int): List { + val attachments = ArrayList() + for (i in 0 until batchSize) { + if (this.moveToNext()) { + attachments.addAll(SignalDatabase.attachments.getAttachments(this)) + } else { + break + } + } + return attachments + } + + override fun onRun() { + val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(ApplicationDependencies.getApplication()) + + val outputStream = FileOutputStream(tempBackupFile) + BackupRepository.export(outputStream = outputStream, append = { tempBackupFile.appendBytes(it) }, plaintext = false) + + FileInputStream(tempBackupFile).use { + BackupRepository.uploadBackupFile(it, tempBackupFile.length()) + } + + archiveAttachments() + if (!tempBackupFile.delete()) { + Log.e(TAG, "Failed to delete temp backup file") + } + } + + override fun onShouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): BackupMessagesJob { + return BackupMessagesJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt new file mode 100644 index 0000000000..274795e7ba --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt @@ -0,0 +1,105 @@ +/* + * 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.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.RestoreState +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.net.NotPushRegisteredException +import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.service.BackupProgressService +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener +import java.io.IOException + +/** + * Job that is responsible for restoring a backup from the server + */ +class BackupRestoreJob private constructor(parameters: Parameters) : BaseJob(parameters) { + + companion object { + private val TAG = Log.tag(BackupRestoreJob::class.java) + + const val KEY = "BackupRestoreJob" + } + + constructor() : this( + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(Parameters.UNLIMITED) + .setMaxInstancesForFactory(1) + .build() + ) + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onAdded() { + SignalStore.backup().restoreState = RestoreState.PENDING + } + + override fun onRun() { + if (!SignalStore.account().isRegistered) { + Log.e(TAG, "Not registered, cannot restore!") + throw NotPushRegisteredException() + } + + BackupProgressService.start(context, context.getString(R.string.BackupProgressService_title)).use { + restore(it) + } + } + + private fun restore(controller: BackupProgressService.Controller) { + SignalStore.backup().restoreState = RestoreState.RESTORING_DB + + val progressListener = object : ProgressListener { + override fun onAttachmentProgress(total: Long, progress: Long) { + controller.update( + title = context.getString(R.string.BackupProgressService_title_downloading), + progress = progress.toFloat() / total.toFloat(), + indeterminate = false + ) + } + + override fun shouldCancel() = isCanceled + } + + val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(ApplicationDependencies.getApplication()) + if (!BackupRepository.downloadBackupFile(tempBackupFile, progressListener)) { + Log.e(TAG, "Failed to download backup file") + throw IOException() + } + + controller.update( + title = context.getString(R.string.BackupProgressService_title), + progress = 0f, + indeterminate = true + ) + + val self = Recipient.self() + val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey)) + BackupRepository.import(length = tempBackupFile.length(), inputStreamFactory = tempBackupFile::inputStream, selfData = selfData, plaintext = false) + + SignalStore.backup().restoreState = RestoreState.RESTORING_MEDIA + } + + override fun onShouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): BackupRestoreJob { + return BackupRestoreJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreMediaJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreMediaJob.kt new file mode 100644 index 0000000000..039a5ef531 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreMediaJob.kt @@ -0,0 +1,83 @@ +/* + * 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 +import org.thoughtcrime.securesms.database.model.MmsMessageRecord +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.net.NotPushRegisteredException +import kotlin.time.Duration.Companion.days + +/** + * Job that is responsible for enqueueing attachment download + * jobs upon restore. + */ +class BackupRestoreMediaJob private constructor(parameters: Parameters) : BaseJob(parameters) { + + companion object { + private val TAG = Log.tag(BackupRestoreMediaJob::class.java) + + const val KEY = "BackupRestoreMediaJob" + } + + constructor() : this( + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(Parameters.UNLIMITED) + .setMaxInstancesForFactory(2) + .build() + ) + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onRun() { + if (!SignalStore.account().isRegistered) { + Log.e(TAG, "Not registered, cannot restore!") + throw NotPushRegisteredException() + } + + val jobManager = ApplicationDependencies.getJobManager() + val batchSize = 100 + val restoreTime = System.currentTimeMillis() + var restoreJobBatch: List + do { + val attachmentBatch = SignalDatabase.attachments.getRestorableAttachments(batchSize) + val messageIds = attachmentBatch.map { it.mmsId }.toSet() + val messageMap = SignalDatabase.messages.getMessages(messageIds).associate { it.id to (it as MmsMessageRecord) } + restoreJobBatch = SignalDatabase.attachments.getRestorableAttachments(batchSize).map { attachment -> + val message = messageMap[attachment.mmsId]!! + RestoreAttachmentJob( + messageId = attachment.mmsId, + attachmentId = attachment.attachmentId, + manual = false, + forceArchiveDownload = true, + fullSize = shouldRestoreFullSize(message, restoreTime, optimizeStorage = SignalStore.backup().optimizeStorage) + ) + } + jobManager.addAll(restoreJobBatch) + } while (restoreJobBatch.isNotEmpty()) + } + + private fun shouldRestoreFullSize(message: MmsMessageRecord, restoreTime: Long, optimizeStorage: Boolean): Boolean { + return ((restoreTime - message.dateSent) < 30.days.inWholeMilliseconds) || !optimizeStorage + } + + override fun onShouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): BackupRestoreMediaJob { + return BackupRestoreMediaJob(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 cb14eba729..2e6c57546f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -100,6 +100,7 @@ public static Map getJobFactories(@NonNull Application appl return new HashMap() {{ put(AccountConsistencyWorkerJob.KEY, new AccountConsistencyWorkerJob.Factory()); put(AnalyzeDatabaseJob.KEY, new AnalyzeDatabaseJob.Factory()); + put(ArchiveAttachmentJob.KEY, new ArchiveAttachmentJob.Factory()); put(AttachmentCompressionJob.KEY, new AttachmentCompressionJob.Factory()); put(AttachmentCopyJob.KEY, new AttachmentCopyJob.Factory()); put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory()); @@ -109,6 +110,9 @@ public static Map getJobFactories(@NonNull Application appl put(AutomaticSessionResetJob.KEY, new AutomaticSessionResetJob.Factory()); put(AvatarGroupsV1DownloadJob.KEY, new AvatarGroupsV1DownloadJob.Factory()); put(AvatarGroupsV2DownloadJob.KEY, new AvatarGroupsV2DownloadJob.Factory()); + put(BackupMessagesJob.KEY, new BackupMessagesJob.Factory()); + put(BackupRestoreJob.KEY, new BackupRestoreJob.Factory()); + put(BackupRestoreMediaJob.KEY, new BackupRestoreMediaJob.Factory()); put(BoostReceiptRequestResponseJob.KEY, new BoostReceiptRequestResponseJob.Factory()); put(CallLinkPeekJob.KEY, new CallLinkPeekJob.Factory()); put(CallLinkUpdateSendJob.KEY, new CallLinkUpdateSendJob.Factory()); @@ -193,6 +197,7 @@ public static Map getJobFactories(@NonNull Application appl put(ResumableUploadSpecJob.KEY, new ResumableUploadSpecJob.Factory()); put(RequestGroupV2InfoWorkerJob.KEY, new RequestGroupV2InfoWorkerJob.Factory()); put(RequestGroupV2InfoJob.KEY, new RequestGroupV2InfoJob.Factory()); + put(RestoreAttachmentJob.KEY, new RestoreAttachmentJob.Factory()); put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory()); put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory()); put(RetrieveRemoteAnnouncementsJob.KEY, new RetrieveRemoteAnnouncementsJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index 83b25dd2fb..5781de9aa9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -288,7 +288,7 @@ protected static Set enqueueCompressingAndUploadAttachmentsChains(@NonNu } } - return new SignalServiceAttachmentPointer(attachment.cdnNumber, + return new SignalServiceAttachmentPointer(attachment.cdn.getCdnNumber(), remoteId, attachment.contentType, key, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt new file mode 100644 index 0000000000..1a0799fc7f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt @@ -0,0 +1,400 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.thoughtcrime.securesms.jobs + +import android.text.TextUtils +import androidx.annotation.VisibleForTesting +import org.greenrobot.eventbus.EventBus +import org.signal.core.util.Base64 +import org.signal.core.util.Hex +import org.signal.core.util.logging.Log +import org.signal.libsignal.protocol.InvalidMacException +import org.signal.libsignal.protocol.InvalidMessageException +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.events.PartProgressEvent +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.JobLogger.format +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.mms.MmsException +import org.thoughtcrime.securesms.notifications.v2.ConversationId.Companion.forConversation +import org.thoughtcrime.securesms.transport.RetryLaterException +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.Util +import org.whispersystems.signalservice.api.backup.MediaName +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId +import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException +import org.whispersystems.signalservice.api.push.exceptions.RangeException +import java.io.File +import java.io.IOException +import java.util.Optional +import java.util.concurrent.TimeUnit + +/** + * Download attachment from locations as specified in their record. + */ +class RestoreAttachmentJob private constructor( + parameters: Parameters, + private val messageId: Long, + attachmentId: AttachmentId, + private val manual: Boolean, + private var forceArchiveDownload: Boolean, + private val fullSize: Boolean +) : BaseJob(parameters) { + + companion object { + const val KEY = "RestoreAttachmentJob" + private val TAG = Log.tag(AttachmentDownloadJob::class.java) + + private const val KEY_MESSAGE_ID = "message_id" + private const val KEY_ATTACHMENT_ID = "part_row_id" + private const val KEY_MANUAL = "part_manual" + private const val KEY_FORCE_ARCHIVE = "force_archive" + private const val KEY_FULL_SIZE = "full_size" + + @JvmStatic + fun constructQueueString(attachmentId: AttachmentId): String { + // TODO: decide how many queues + return "RestoreAttachmentJob" + } + + fun jobSpecMatchesAnyAttachmentId(jobSpec: JobSpec, ids: Set): Boolean { + if (KEY != jobSpec.factoryKey) { + return false + } + + val serializedData = jobSpec.serializedData ?: return false + val data = JsonJobData.deserialize(serializedData) + val parsed = AttachmentId(data.getLong(KEY_ATTACHMENT_ID)) + return ids.contains(parsed) + } + + fun modifyPriorities(ids: Set, priority: Int) { + val jobManager = ApplicationDependencies.getJobManager() + jobManager.update { spec -> + if (jobSpecMatchesAnyAttachmentId(spec, ids) && spec.priority != priority) { + spec.copy(priority = priority) + } else { + spec + } + } + } + } + + private val attachmentId: Long + + constructor(messageId: Long, attachmentId: AttachmentId, manual: Boolean, forceArchiveDownload: Boolean = false, fullSize: Boolean = true) : this( + Parameters.Builder() + .setQueue(constructQueueString(attachmentId)) + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + messageId, + attachmentId, + manual, + forceArchiveDownload, + fullSize + ) + + init { + this.attachmentId = attachmentId.id + } + + override fun serialize(): ByteArray? { + return JsonJobData.Builder() + .putLong(KEY_MESSAGE_ID, messageId) + .putLong(KEY_ATTACHMENT_ID, attachmentId) + .putBoolean(KEY_MANUAL, manual) + .putBoolean(KEY_FORCE_ARCHIVE, forceArchiveDownload) + .putBoolean(KEY_FULL_SIZE, fullSize) + .serialize() + } + + override fun getFactoryKey(): String { + return KEY + } + + override fun onAdded() { + Log.i(TAG, "onAdded() messageId: $messageId attachmentId: $attachmentId manual: $manual") + + val attachmentId = AttachmentId(attachmentId) + val attachment = SignalDatabase.attachments.getAttachment(attachmentId) + val pending = attachment != null && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_DONE && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE + if (attachment?.transferState == AttachmentTable.TRANSFER_NEEDS_RESTORE) { + Log.i(TAG, "onAdded() Marking attachment restore progress as 'started'") + SignalDatabase.attachments.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_RESTORE_IN_PROGRESS) + } + } + + @Throws(Exception::class) + public override fun onRun() { + doWork() + + if (!SignalDatabase.messages.isStory(messageId)) { + ApplicationDependencies.getMessageNotifier().updateNotification(context, forConversation(0)) + } + } + + @Throws(IOException::class, RetryLaterException::class) + fun doWork() { + Log.i(TAG, "onRun() messageId: $messageId attachmentId: $attachmentId manual: $manual") + + val attachmentId = AttachmentId(attachmentId) + val attachment = SignalDatabase.attachments.getAttachment(attachmentId) + + if (attachment == null) { + Log.w(TAG, "attachment no longer exists.") + return + } + + if (attachment.isPermanentlyFailed) { + Log.w(TAG, "Attachment was marked as a permanent failure. Refusing to download.") + return + } + + if (attachment.transferState != AttachmentTable.TRANSFER_NEEDS_RESTORE && attachment.transferState != AttachmentTable.TRANSFER_RESTORE_IN_PROGRESS) { + Log.w(TAG, "Attachment does not need to be restored.") + return + } + + retrieveAttachment(messageId, attachmentId, attachment) + } + + override fun onFailure() { + Log.w(TAG, format(this, "onFailure() messageId: $messageId attachmentId: $attachmentId manual: $manual")) + + val attachmentId = AttachmentId(attachmentId) + markFailed(messageId, attachmentId) + } + + override fun onShouldRetry(exception: Exception): Boolean { + return exception is PushNetworkException || + exception is RetryLaterException + } + + @Throws(IOException::class, RetryLaterException::class) + private fun retrieveAttachment( + messageId: Long, + attachmentId: AttachmentId, + attachment: DatabaseAttachment + ) { + val maxReceiveSize: Long = FeatureFlags.maxAttachmentReceiveSizeBytes() + val attachmentFile: File = SignalDatabase.attachments.getOrCreateTransferFile(attachmentId) + var archiveFile: File? = null + var useArchiveCdn = false + + try { + if (attachment.size > maxReceiveSize) { + throw MmsException("Attachment too large, failing download") + } + + useArchiveCdn = if (SignalStore.backup().canReadWriteToArchiveCdn && (forceArchiveDownload || attachment.remoteLocation == null)) { + if (attachment.archiveMediaName.isNullOrEmpty()) { + throw InvalidPartException("Invalid attachment configuration") + } + true + } else { + false + } + + val messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver() + val pointer = createAttachmentPointer(attachment, useArchiveCdn) + + val progressListener = object : SignalServiceAttachment.ProgressListener { + override fun onAttachmentProgress(total: Long, progress: Long) { + EventBus.getDefault().postSticky(PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)) + } + + override fun shouldCancel(): Boolean { + return this@RestoreAttachmentJob.isCanceled + } + } + + val stream = if (useArchiveCdn) { + archiveFile = SignalDatabase.attachments.getOrCreateArchiveTransferFile(attachmentId) + val cdnCredentials = BackupRepository.getCdnReadCredentials().successOrThrow().headers + + messageReceiver + .retrieveArchivedAttachment( + SignalStore.svr().getOrCreateMasterKey().deriveBackupKey().deriveMediaSecrets(MediaName(attachment.archiveMediaName!!)), + cdnCredentials, + archiveFile, + pointer, + attachmentFile, + maxReceiveSize, + progressListener + ) + } else { + messageReceiver + .retrieveAttachment( + pointer, + attachmentFile, + maxReceiveSize, + progressListener + ) + } + + SignalDatabase.attachments.finalizeAttachmentAfterDownload(messageId, attachmentId, stream) + } catch (e: RangeException) { + val transferFile = archiveFile ?: attachmentFile + Log.w(TAG, "Range exception, file size " + transferFile.length(), e) + if (transferFile.delete()) { + Log.i(TAG, "Deleted temp download file to recover") + throw RetryLaterException(e) + } else { + throw IOException("Failed to delete temp download file following range exception") + } + } catch (e: InvalidPartException) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, attachmentId) + } catch (e: NonSuccessfulResponseCodeException) { + if (SignalStore.backup().canReadWriteToArchiveCdn) { + if (e.code == 404 && !useArchiveCdn && attachment.archiveMediaName?.isNotEmpty() == true) { + Log.i(TAG, "Retrying download from archive CDN") + forceArchiveDownload = true + retrieveAttachment(messageId, attachmentId, attachment) + return + } else if (e.code == 401 && useArchiveCdn) { + SignalStore.backup().cdnReadCredentials = null + throw RetryLaterException(e) + } + } + + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, attachmentId) + } catch (e: MmsException) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, attachmentId) + } catch (e: MissingConfigurationException) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e) + markFailed(messageId, 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) + } else { + markFailed(messageId, attachmentId) + } + } + } + + @Throws(InvalidPartException::class) + private fun createAttachmentPointer(attachment: DatabaseAttachment, useArchiveCdn: Boolean): SignalServiceAttachmentPointer { + if (TextUtils.isEmpty(attachment.remoteKey)) { + throw InvalidPartException("empty encrypted key") + } + + return try { + val remoteData: RemoteData = if (useArchiveCdn) { + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + val backupDirectories = BackupRepository.getCdnBackupDirectories().successOrThrow() + + RemoteData( + remoteId = SignalServiceAttachmentRemoteId.Backup( + backupDir = backupDirectories.backupDir, + mediaDir = backupDirectories.mediaDir, + mediaId = backupKey.deriveMediaId(MediaName(attachment.archiveMediaName!!)).encode() + ), + cdnNumber = attachment.archiveCdn + ) + } else { + if (attachment.remoteLocation.isNullOrEmpty()) { + throw InvalidPartException("empty content id") + } + + RemoteData( + remoteId = SignalServiceAttachmentRemoteId.from(attachment.remoteLocation), + cdnNumber = attachment.cdn.cdnNumber + ) + } + + val key = Base64.decode(attachment.remoteKey!!) + + if (attachment.remoteDigest != null) { + Log.i(TAG, "Downloading attachment with digest: " + Hex.toString(attachment.remoteDigest)) + } else { + Log.i(TAG, "Downloading attachment with no digest...") + } + + SignalServiceAttachmentPointer( + remoteData.cdnNumber, + remoteData.remoteId, + null, + key, + Optional.of(Util.toIntExact(attachment.size)), + Optional.empty(), + 0, + 0, + Optional.ofNullable(attachment.remoteDigest), + Optional.ofNullable(attachment.getIncrementalDigest()), + attachment.incrementalMacChunkSize, + Optional.ofNullable(attachment.fileName), + attachment.voiceNote, + attachment.borderless, + attachment.videoGif, + Optional.empty(), + Optional.ofNullable(attachment.blurHash).map { it.hash }, + attachment.uploadTimestamp + ) + } catch (e: IOException) { + Log.w(TAG, e) + throw InvalidPartException(e) + } catch (e: ArithmeticException) { + Log.w(TAG, e) + throw InvalidPartException(e) + } + } + + private fun markFailed(messageId: Long, attachmentId: AttachmentId) { + try { + SignalDatabase.attachments.setTransferProgressFailed(attachmentId, messageId) + } catch (e: MmsException) { + Log.w(TAG, e) + } + } + + private fun markPermanentlyFailed(messageId: Long, attachmentId: AttachmentId) { + try { + SignalDatabase.attachments.setTransferProgressPermanentFailure(attachmentId, messageId) + } catch (e: MmsException) { + Log.w(TAG, e) + } + } + + @VisibleForTesting + internal class InvalidPartException : Exception { + constructor(s: String?) : super(s) + constructor(e: Exception?) : super(e) + } + + private data class RemoteData(val remoteId: SignalServiceAttachmentRemoteId, val cdnNumber: Int) + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): RestoreAttachmentJob { + val data = JsonJobData.deserialize(serializedData) + return RestoreAttachmentJob( + parameters = parameters, + messageId = data.getLong(KEY_MESSAGE_ID), + attachmentId = AttachmentId(data.getLong(KEY_ATTACHMENT_ID)), + manual = data.getBoolean(KEY_MANUAL), + forceArchiveDownload = data.getBooleanOrDefault(KEY_FORCE_ARCHIVE, false), + fullSize = data.getBooleanOrDefault(KEY_FULL_SIZE, true) + ) + } + } +} 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 fe5332e7d2..4e371b7cc4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -2,21 +2,44 @@ package org.thoughtcrime.securesms.keyvalue import com.fasterxml.jackson.annotation.JsonProperty import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.RestoreState import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential +import org.whispersystems.signalservice.api.archive.GetArchiveCdnCredentialsResponse import org.whispersystems.signalservice.internal.util.JsonUtil import java.io.IOException import kotlin.time.Duration import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.hours internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { companion object { val TAG = Log.tag(BackupValues::class.java) - val KEY_CREDENTIALS = "backup.credentials" + private const val KEY_CREDENTIALS = "backup.credentials" + private const val KEY_CDN_CAN_READ_WRITE = "backup.cdn.canReadWrite" + private const val KEY_CDN_READ_CREDENTIALS = "backup.cdn.readCredentials" + private const val KEY_CDN_READ_CREDENTIALS_TIMESTAMP = "backup.cdn.readCredentials.timestamp" + private const val KEY_RESTORE_STATE = "backup.restoreState" + + private const val KEY_CDN_BACKUP_DIRECTORY = "backup.cdn.directory" + private const val KEY_CDN_BACKUP_MEDIA_DIRECTORY = "backup.cdn.mediaDirectory" + + private const val KEY_OPTIMIZE_STORAGE = "backup.optimizeStorage" + + private val cachedCdnCredentialsExpiresIn: Duration = 12.hours } + private var cachedCdnCredentialsTimestamp: Long by longValue(KEY_CDN_READ_CREDENTIALS_TIMESTAMP, 0L) + private var cachedCdnCredentials: String? by stringValue(KEY_CDN_READ_CREDENTIALS, null) + var cachedBackupDirectory: String? by stringValue(KEY_CDN_BACKUP_DIRECTORY, null) + var cachedBackupMediaDirectory: String? by stringValue(KEY_CDN_BACKUP_MEDIA_DIRECTORY, null) + override fun onFirstEverAppLaunch() = Unit override fun getKeysToIncludeInBackup(): List = emptyList() + var canReadWriteToArchiveCdn: Boolean by booleanValue(KEY_CDN_CAN_READ_WRITE, false) + var restoreState: RestoreState by enumValue(KEY_RESTORE_STATE, RestoreState.NONE, RestoreState.serializer) + var optimizeStorage: Boolean by booleanValue(KEY_OPTIMIZE_STORAGE, false) + /** * Retrieves the stored credentials, mapped by the day they're valid. The day is represented as * the unix time (in seconds) of the start of the day. Wrapped in a [ArchiveServiceCredentials] @@ -36,6 +59,28 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { } } + var cdnReadCredentials: GetArchiveCdnCredentialsResponse? + get() { + val cacheAge = System.currentTimeMillis() - cachedCdnCredentialsTimestamp + val cached = cachedCdnCredentials + + return if (cached != null && (cacheAge > 0 && cacheAge < cachedCdnCredentialsExpiresIn.inWholeMilliseconds)) { + try { + JsonUtil.fromJson(cached, GetArchiveCdnCredentialsResponse::class.java) + } catch (e: IOException) { + Log.w(TAG, "Invalid JSON! Clearing.", e) + cachedCdnCredentials = null + null + } + } else { + null + } + } + set(value) { + cachedCdnCredentials = value?.let { JsonUtil.toJson(it) } + cachedCdnCredentialsTimestamp = System.currentTimeMillis() + } + /** * Adds the given credentials to the existing list of stored credentials. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/LegacyMigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/LegacyMigrationJob.java index 9b529d51d3..7a52900e09 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/LegacyMigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/LegacyMigrationJob.java @@ -255,7 +255,7 @@ private void schedulePendingIncomingParts(Context context) { attachmentDb.setTransferState(attachment.mmsId, attachment.attachmentId, AttachmentTable.TRANSFER_PROGRESS_DONE); } else if (record != null && !record.isOutgoing() && record.isPush()) { Log.i(TAG, "queuing new attachment download job for incoming push part " + attachment.attachmentId + "."); - ApplicationDependencies.getJobManager().add(new AttachmentDownloadJob(attachment.mmsId, attachment.attachmentId, false)); + ApplicationDependencies.getJobManager().add(new AttachmentDownloadJob(attachment.mmsId, attachment.attachmentId, false, false)); } reader.close(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java index 723747e3c3..a0bd2d38ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java @@ -8,6 +8,7 @@ public final class NotificationIds { public static final int FCM_FAILURE = 12; public static final int ATTACHMENT_PROGRESS = 50; + public static final int BACKUP_PROGRESS = 51; public static final int APK_UPDATE_PROMPT_INSTALL = 666; public static final int APK_UPDATE_FAILED_INSTALL = 667; public static final int APK_UPDATE_SUCCESSFUL_INSTALL = 668; diff --git a/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt b/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt index 14ae56500c..1d5a9b861e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.releasechannel +import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.PointerAttachment import org.thoughtcrime.securesms.database.MessageTable import org.thoughtcrime.securesms.database.MessageType @@ -20,8 +21,6 @@ import java.util.UUID */ object ReleaseChannel { - const val CDN_NUMBER = -1 - fun insertReleaseChannelMessage( recipientId: RecipientId, body: String, @@ -36,8 +35,8 @@ object ReleaseChannel { ): MessageTable.InsertResult? { val attachments: Optional> = if (media != null) { val attachment = SignalServiceAttachmentPointer( - CDN_NUMBER, - SignalServiceAttachmentRemoteId.from(""), + Cdn.S3.cdnNumber, + SignalServiceAttachmentRemoteId.S3, mediaType, null, Optional.empty(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/AttachmentProgressService.kt b/app/src/main/java/org/thoughtcrime/securesms/service/AttachmentProgressService.kt index a340d0ff1d..0ea40e1e68 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/AttachmentProgressService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/AttachmentProgressService.kt @@ -150,8 +150,8 @@ class AttachmentProgressService : SafeForegroundService() { /** Has to have separate setter to avoid infinite loops when [progress] and [indeterminate] interact. */ fun setIndeterminate(value: Boolean) { - indeterminate = value progress = 0f + indeterminate = value onControllersChanged(context) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/BackupProgressService.kt b/app/src/main/java/org/thoughtcrime/securesms/service/BackupProgressService.kt new file mode 100644 index 0000000000..466b19e4e6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/BackupProgressService.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.service + +import android.annotation.SuppressLint +import android.app.Notification +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import org.signal.core.util.PendingIntentFlags +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.MainActivity +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.notifications.NotificationIds +import java.util.concurrent.locks.ReentrantLock +import javax.annotation.CheckReturnValue +import kotlin.concurrent.withLock + +/** + * Foreground service to provide "long" run support to backup jobs. + */ +class BackupProgressService : SafeForegroundService() { + + companion object { + private val TAG = Log.tag(BackupProgressService::class) + + @SuppressLint("StaticFieldLeak") + private var controller: Controller? = null + private val controllerLock = ReentrantLock() + + private var title: String = "" + private var progress: Float = 0f + private var indeterminate: Boolean = true + + @CheckReturnValue + fun start(context: Context, startingTitle: String): Controller { + controllerLock.withLock { + if (controller != null) { + Log.w(TAG, "Starting service with existing controller") + } + + controller = Controller(context, startingTitle) + val started = SafeForegroundService.start(context, BackupProgressService::class.java) + if (started) { + Log.i(TAG, "[start] Starting") + } else { + Log.w(TAG, "[start] Service already started") + } + + return controller!! + } + } + + private fun stop(context: Context) { + SafeForegroundService.stop(context, BackupProgressService::class.java) + controllerLock.withLock { + controller = null + } + } + + private fun getForegroundNotification(context: Context): Notification { + return NotificationCompat.Builder(context, NotificationChannels.getInstance().OTHER) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(title) + .setProgress(100, (progress * 100).toInt(), indeterminate) + .setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.clearTop(context), PendingIntentFlags.mutable())) + .setVibrate(longArrayOf(0)) + .build() + } + } + + override val tag: String = TAG + override val notificationId: Int = NotificationIds.BACKUP_PROGRESS + + override fun getForegroundNotification(intent: Intent): Notification { + return getForegroundNotification(this) + } + + /** + * Use to update notification progress/state. + */ + class Controller(private val context: Context, startingTitle: String) : AutoCloseable { + + init { + title = startingTitle + progress = 0f + indeterminate = true + } + + fun update(title: String, progress: Float, indeterminate: Boolean) { + controllerLock.withLock { + if (this != controller) { + return + } + + BackupProgressService.title = title + BackupProgressService.progress = progress + BackupProgressService.indeterminate = indeterminate + + if (NotificationManagerCompat.from(context).activeNotifications.any { n -> n.id == NotificationIds.BACKUP_PROGRESS }) { + NotificationManagerCompat.from(context).notify(NotificationIds.BACKUP_PROGRESS, getForegroundNotification(context)) + } + } + } + + override fun close() { + controllerLock.withLock { + if (this == controller) { + stop(context) + } + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt index 19277acc22..f82b2ce45e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt @@ -141,7 +141,7 @@ object Stories { if (record.hasLinkPreview() && record.linkPreviews[0].attachmentId != null) { ApplicationDependencies.getJobManager().add( - AttachmentDownloadJob(record.id, record.linkPreviews[0].attachmentId, true) + AttachmentDownloadJob(record.id, record.linkPreviews[0].attachmentId!!, true) ) } } diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index 875020948f..ffa49fdc59 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -381,6 +381,7 @@ message MessageAttachment { FilePointer pointer = 1; Flag flag = 2; + bool wasDownloaded = 3; } message FilePointer { @@ -388,6 +389,9 @@ message FilePointer { message BackupLocator { string mediaName = 1; uint32 cdnNumber = 2; + bytes key = 3; + bytes digest = 4; + uint32 size = 5; } // References attachments in the transit storage tier. @@ -398,37 +402,33 @@ message FilePointer { string cdnKey = 1; uint32 cdnNumber = 2; uint64 uploadTimestamp = 3; + bytes key = 4; + bytes digest = 5; + uint32 size = 6; } - // An attachment that was copied from the transit storage tier - // to the backup (media) storage tier up without being downloaded. - // Its MediaName should be generated as “{sender_aci}_{cdn_attachment_key}”, - // but should eventually transition to a BackupLocator with mediaName - // being the content hash once it is downloaded. - message UndownloadedBackupLocator { - bytes senderAci = 1; - string cdnKey = 2; - uint32 cdnNumber = 3; + // References attachments that are invalid in such a way where download + // cannot be attempted. Could range from missing digests to missing + // CDN keys or anything else that makes download attempts impossible. + // This serves as a 'tombstone' so that the UX can show that an attachment + // did exist, but for whatever reason it's not retrievable. + message InvalidAttachmentLocator { } oneof locator { BackupLocator backupLocator = 1; AttachmentLocator attachmentLocator= 2; - UndownloadedBackupLocator undownloadedBackupLocator = 3; - } - - optional bytes key = 5; - optional string contentType = 6; - // Size of fullsize decrypted media blob in bytes. - // Can be ignored if unset/unavailable. - optional uint32 size = 7; - optional bytes incrementalMac = 8; - optional uint32 incrementalMacChunkSize = 9; - optional string fileName = 10; - optional uint32 width = 11; - optional uint32 height = 12; - optional string caption = 13; - optional string blurHash = 14; + InvalidAttachmentLocator invalidAttachmentLocator = 3; + } + + optional string contentType = 4; + optional bytes incrementalMac = 5; + optional uint32 incrementalMacChunkSize = 6; + optional string fileName = 7; + optional uint32 width = 8; + optional uint32 height = 9; + optional string caption = 10; + optional string blurHash = 11; } message Quote { diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index 7e2f51847d..94b1a1dfd1 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -46,4 +46,8 @@ message AttachmentUploadJobData { message PreKeysSyncJobData { bool forceRefreshRequested = 1; +} + +message ArchiveAttachmentJobData { + uint64 attachmentId = 1; } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a7e933bb8..968334bb71 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6707,5 +6707,11 @@ Edit note + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt b/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt index c2614d0547..2f78a5272f 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt @@ -236,7 +236,7 @@ class UploadDependencyGraphTest { transferProgress = AttachmentTable.TRANSFER_PROGRESS_PENDING, size = attachment.size, fileName = attachment.fileName, - cdnNumber = attachment.cdnNumber, + cdn = attachment.cdn, location = attachment.remoteLocation, key = attachment.remoteKey, digest = attachment.remoteDigest, @@ -256,7 +256,10 @@ class UploadDependencyGraphTest { transformProperties = attachment.transformProperties, displayOrder = 0, uploadTimestamp = attachment.uploadTimestamp, - dataHash = null + dataHash = null, + archiveMediaId = null, + archiveMediaName = null, + archiveCdn = 0 ) } diff --git a/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt b/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt index ebaeba6e9b..a3597070c8 100644 --- a/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt +++ b/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.database import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.audio.AudioHash import org.thoughtcrime.securesms.blurhash.BlurHash @@ -35,7 +36,7 @@ object FakeMessageRecords { transferProgress: Int = AttachmentTable.TRANSFER_PROGRESS_DONE, size: Long = 0L, fileName: String = "", - cdnNumber: Int = 1, + cdnNumber: Int = 3, location: String = "", key: String = "", relay: String = "", @@ -56,7 +57,10 @@ object FakeMessageRecords { transformProperties: AttachmentTable.TransformProperties? = null, displayOrder: Int = 0, uploadTimestamp: Long = 200, - dataHash: String? = null + dataHash: String? = null, + archiveCdn: Int = 0, + archiveMediaName: String? = null, + archiveMediaId: String? = null ): DatabaseAttachment { return DatabaseAttachment( attachmentId, @@ -67,7 +71,7 @@ object FakeMessageRecords { transferProgress, size, fileName, - cdnNumber, + Cdn.fromCdnNumber(cdnNumber), location, key, digest, @@ -87,7 +91,10 @@ object FakeMessageRecords { transformProperties, displayOrder, uploadTimestamp, - dataHash + dataHash, + archiveCdn, + archiveMediaId, + archiveMediaName ) } diff --git a/core-util-jvm/src/main/java/org/signal/core/util/logging/Log.kt b/core-util-jvm/src/main/java/org/signal/core/util/logging/Log.kt index dd957ac75b..1748be82ee 100644 --- a/core-util-jvm/src/main/java/org/signal/core/util/logging/Log.kt +++ b/core-util-jvm/src/main/java/org/signal/core/util/logging/Log.kt @@ -5,6 +5,8 @@ package org.signal.core.util.logging +import kotlin.reflect.KClass + object Log { private val NOOP_LOGGER: Logger = NoopLogger() private var internalCheck: InternalCheck? = null @@ -102,6 +104,11 @@ object Log { @JvmStatic fun e(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = logger.e(tag, message, t, keepLonger) + @JvmStatic + fun tag(clazz: KClass<*>): String { + return tag(clazz.java) + } + @JvmStatic fun tag(clazz: Class<*>): String { val simpleName = clazz.simpleName diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java index d1399f6af9..4c37b55cb7 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java @@ -6,13 +6,17 @@ package org.whispersystems.signalservice.api; +import org.signal.core.util.StreamUtil; import org.signal.core.util.concurrent.FutureTransformers; import org.signal.core.util.concurrent.ListenableFuture; import org.signal.core.util.concurrent.SettableFuture; import org.signal.libsignal.protocol.InvalidMessageException; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.whispersystems.signalservice.api.backup.BackupKey; +import org.whispersystems.signalservice.api.backup.MediaId; import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream; +import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.api.crypto.ProfileCipherInputStream; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; @@ -27,6 +31,7 @@ import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.internal.ServiceResponse; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.internal.push.IdentityCheckRequest; import org.whispersystems.signalservice.internal.push.IdentityCheckResponse; import org.whispersystems.signalservice.internal.push.PushServiceSocket; @@ -36,14 +41,18 @@ import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import io.reactivex.rxjava3.core.Single; @@ -159,10 +168,60 @@ public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, Fi throws IOException, InvalidMessageException, MissingConfigurationException { if (!pointer.getDigest().isPresent()) throw new InvalidMessageException("No attachment digest!"); - socket.retrieveAttachment(pointer.getCdnNumber(), pointer.getRemoteId(), destination, maxSizeBytes, listener); + socket.retrieveAttachment(pointer.getCdnNumber(), Collections.emptyMap(), pointer.getRemoteId(), destination, maxSizeBytes, listener); return AttachmentCipherInputStream.createForAttachment(destination, pointer.getSize().orElse(0), pointer.getKey(), pointer.getDigest().get(), null, 0); } + /** + * Retrieves an archived media attachment. + * + * @param archivedMediaKeyMaterial Decryption key material for decrypting outer layer of archived media. + * @param readCredentialHeaders Headers to pass to the backup CDN to authorize the download + * @param archiveDestination The download destination for archived attachment. If this file exists, download will resume. + * @param pointer The {@link SignalServiceAttachmentPointer} received in a {@link SignalServiceDataMessage}. + * @param attachmentDestination The download destination for this attachment. If this file exists, it is assumed that this is previously-downloaded content that can be resumed. + * @param listener An optional listener (may be null) to receive callbacks on download progress. + * + * @return An InputStream that streams the plaintext attachment contents. + */ + public InputStream retrieveArchivedAttachment(@Nonnull BackupKey.KeyMaterial archivedMediaKeyMaterial, + @Nonnull Map readCredentialHeaders, + @Nonnull File archiveDestination, + @Nonnull SignalServiceAttachmentPointer pointer, + @Nonnull File attachmentDestination, + long maxSizeBytes, + @Nullable ProgressListener listener) + throws IOException, InvalidMessageException, MissingConfigurationException + { + if (pointer.getDigest().isEmpty()) { + throw new InvalidMessageException("No attachment digest!"); + } + + socket.retrieveAttachment(pointer.getCdnNumber(), readCredentialHeaders, pointer.getRemoteId(), archiveDestination, maxSizeBytes, listener); + + long originalCipherLength = pointer.getSize() + .filter(s -> s > 0) + .map(s -> AttachmentCipherStreamUtil.getCiphertextLength(PaddingInputStream.getPaddedSize(s))) + .orElse(0L); + + try (InputStream backupDecrypted = AttachmentCipherInputStream.createForArchivedMedia(archivedMediaKeyMaterial, archiveDestination, originalCipherLength)) { + try (FileOutputStream fos = new FileOutputStream(attachmentDestination)) { + StreamUtil.copy(backupDecrypted, fos); + } + } + + return AttachmentCipherInputStream.createForAttachment(attachmentDestination, + pointer.getSize().orElse(0), + pointer.getKey(), + pointer.getDigest().get(), + null, + 0); + } + + public void retrieveBackup(int cdnNumber, Map headers, String cdnPath, File destination, ProgressListener listener) throws MissingConfigurationException, IOException { + socket.retrieveBackup(cdnNumber, headers, cdnPath, destination, 1_000_000_000L, listener); + } + public InputStream retrieveSticker(byte[] packId, byte[] packKey, int stickerId) throws IOException, InvalidMessageException { 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 dea2ed7ed5..0656a612f3 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 @@ -841,7 +841,7 @@ private SignalServiceAttachmentPointer uploadAttachmentV2(SignalServiceAttachmen Pair attachmentIdAndDigest = socket.uploadAttachment(attachmentData, v2UploadAttributes); return new SignalServiceAttachmentPointer(0, - new SignalServiceAttachmentRemoteId(attachmentIdAndDigest.first()), + new SignalServiceAttachmentRemoteId.V2(attachmentIdAndDigest.first()), attachment.getContentType(), attachmentKey, Optional.of(Util.toIntExact(attachment.getLength())), @@ -882,7 +882,7 @@ public ResumableUploadSpec getResumableUploadSpec() throws IOException { private SignalServiceAttachmentPointer uploadAttachmentV4(SignalServiceAttachmentStream attachment, byte[] attachmentKey, PushAttachmentData attachmentData) throws IOException { AttachmentDigest digest = socket.uploadAttachment(attachmentData); return new SignalServiceAttachmentPointer(attachmentData.getResumableUploadSpec().getCdnNumber(), - new SignalServiceAttachmentRemoteId(attachmentData.getResumableUploadSpec().getCdnKey()), + new SignalServiceAttachmentRemoteId.V4(attachmentData.getResumableUploadSpec().getCdnKey()), attachment.getContentType(), attachmentKey, Optional.of(Util.toIntExact(attachment.getLength())), diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt index 1e43a67feb..14482285f7 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt @@ -55,6 +55,15 @@ class ArchiveApi( } } + fun getCdnReadCredentials(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential): NetworkResult { + return NetworkResult.fromFetch { + val zkCredential = getZkCredential(backupKey, serviceCredential) + val presentationData = CredentialPresentationData.from(backupKey, zkCredential, backupServerPublicParams) + + pushServiceSocket.getArchiveCdnReadCredentials(presentationData.toArchiveCredentialPresentation()) + } + } + /** * Ensures that you reserve a backupId on the service. This must be done before any other * backup-related calls. You only need to do it once, but repeated calls are safe. diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetBackupInfoResponse.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetBackupInfoResponse.kt index 399e89e757..7774fd1d12 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetBackupInfoResponse.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetBackupInfoResponse.kt @@ -16,6 +16,8 @@ data class ArchiveGetBackupInfoResponse( @JsonProperty val backupDir: String?, @JsonProperty + val mediaDir: String?, + @JsonProperty val backupName: String?, @JsonProperty val usedSpace: Long? diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/GetArchiveCdnCredentialsResponse.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/GetArchiveCdnCredentialsResponse.kt new file mode 100644 index 0000000000..f9decda3ee --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/GetArchiveCdnCredentialsResponse.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.archive + +import com.fasterxml.jackson.annotation.JsonProperty + +/** + * Get response with headers to use to read from archive cdn. + */ +class GetArchiveCdnCredentialsResponse( + @JsonProperty val headers: Map +) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupId.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupId.kt index 6d6a386614..5d6ef9cf78 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupId.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupId.kt @@ -5,6 +5,9 @@ package org.whispersystems.signalservice.api.backup +import org.signal.core.util.Base64 +import java.security.MessageDigest + /** * Safe typing around a backupId, which is a 16-byte array. */ @@ -14,4 +17,9 @@ value class BackupId(val value: ByteArray) { init { require(value.size == 16) { "BackupId must be 16 bytes!" } } + + /** Encode backup-id for use in a URL/request */ + fun encode(): String { + return Base64.encodeUrlSafeWithPadding(MessageDigest.getInstance("SHA-256").digest(value).copyOfRange(0, 16)) + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt index 1939cc5e1b..aed7e88af9 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt @@ -16,10 +16,14 @@ class BackupKey(val value: ByteArray) { require(value.size == 32) { "Backup key must be 32 bytes!" } } - fun deriveSecrets(aci: ACI): KeyMaterial { - val backupId = BackupId( + fun deriveBackupId(aci: ACI): BackupId { + return BackupId( HKDF.deriveSecrets(this.value, aci.toByteArray(), "20231003_Signal_Backups_GenerateBackupId".toByteArray(), 16) ) + } + + fun deriveSecrets(aci: ACI): KeyMaterial { + val backupId = deriveBackupId(aci) val extendedKey = HKDF.deriveSecrets(this.value, backupId.value, "20231003_Signal_Backups_EncryptMessageBackup".toByteArray(), 80) @@ -31,13 +35,15 @@ class BackupKey(val value: ByteArray) { ) } - fun deriveMediaId(dataHash: ByteArray): MediaId { - return MediaId(HKDF.deriveSecrets(value, dataHash, "Media ID".toByteArray(), 15)) + fun deriveMediaId(mediaName: MediaName): MediaId { + return MediaId(HKDF.deriveSecrets(value, mediaName.toByteArray(), "Media ID".toByteArray(), 15)) } - fun deriveMediaSecrets(dataHash: ByteArray): KeyMaterial { - val mediaId = deriveMediaId(dataHash) + fun deriveMediaSecrets(mediaName: MediaName): KeyMaterial { + return deriveMediaSecrets(deriveMediaId(mediaName)) + } + fun deriveMediaSecrets(mediaId: MediaId): KeyMaterial { val extendedKey = HKDF.deriveSecrets(this.value, mediaId.value, "20231003_Signal_Backups_EncryptMedia".toByteArray(), 80) return KeyMaterial( @@ -53,5 +59,17 @@ class BackupKey(val value: ByteArray) { val macKey: ByteArray, val cipherKey: ByteArray, val iv: ByteArray - ) + ) { + companion object { + @JvmStatic + fun forMedia(id: ByteArray, keyMac: ByteArray, iv: ByteArray): KeyMaterial { + return KeyMaterial( + MediaId(id), + keyMac.copyOfRange(32, 64), + keyMac.copyOfRange(0, 32), + iv + ) + } + } + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaId.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaId.kt index 6575e99118..5ebb66caa7 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaId.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaId.kt @@ -13,11 +13,14 @@ import org.signal.core.util.Base64 @JvmInline value class MediaId(val value: ByteArray) { + constructor(mediaId: String) : this(Base64.decode(mediaId)) + init { require(value.size == 15) { "MediaId must be 15 bytes!" } } - override fun toString(): String { + /** Encode media-id for use in a URL/request */ + fun encode(): String { return Base64.encodeUrlSafeWithPadding(value) } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaName.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaName.kt new file mode 100644 index 0000000000..e0bc242aaf --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/MediaName.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.backup + +import org.signal.core.util.Base64 + +/** + * Represent a media name for the various types of media that can be archived. + */ +@JvmInline +value class MediaName(val name: String) { + + companion object { + fun fromDigest(digest: ByteArray) = MediaName(Base64.encodeWithoutPadding(digest)) + fun fromDigestForThumbnail(digest: ByteArray) = MediaName("${Base64.encodeWithoutPadding(digest)}_thumbnail") + } + + fun toByteArray(): ByteArray { + return name.toByteArray() + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java index b07d66b95d..66857a07aa 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java @@ -10,7 +10,9 @@ import org.signal.libsignal.protocol.InvalidMessageException; import org.signal.libsignal.protocol.incrementalmac.ChunkSizeChoice; import org.signal.libsignal.protocol.incrementalmac.IncrementalMacInputStream; -import org.signal.libsignal.protocol.kdf.HKDFv3; +import org.signal.libsignal.protocol.kdf.HKDF; +import org.whispersystems.signalservice.api.backup.BackupKey; +import org.whispersystems.signalservice.api.backup.MediaId; import org.whispersystems.signalservice.internal.util.ContentLengthInputStream; import org.whispersystems.signalservice.internal.util.Util; @@ -26,6 +28,8 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -47,9 +51,10 @@ public class AttachmentCipherInputStream extends FilterInputStream { private static final int CIPHER_KEY_SIZE = 32; private static final int MAC_KEY_SIZE = 32; - private Cipher cipher; + private final Cipher cipher; + private final long totalDataSize; + private boolean done; - private long totalDataSize; private long totalRead; private byte[] overflowBuffer; @@ -102,11 +107,43 @@ public static InputStream createForAttachment(File file, long plaintextLength, b } } + /** + * Decrypt archived media to it's original attachment encrypted blob. + */ + public static InputStream createForArchivedMedia(BackupKey.KeyMaterial archivedMediaKeyMaterial, File file, long originalCipherTextLength) + throws InvalidMessageException, IOException + { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(archivedMediaKeyMaterial.getMacKey(), "HmacSHA256")); + + if (file.length() <= BLOCK_SIZE + mac.getMacLength()) { + throw new InvalidMessageException("Message shorter than crypto overhead!"); + } + + try (FileInputStream macVerificationStream = new FileInputStream(file)) { + verifyMac(macVerificationStream, file.length(), mac, null); + } + + InputStream inputStream = new AttachmentCipherInputStream(new FileInputStream(file), archivedMediaKeyMaterial.getCipherKey(), file.length() - BLOCK_SIZE - mac.getMacLength()); + + if (originalCipherTextLength != 0) { + inputStream = new ContentLengthInputStream(inputStream, originalCipherTextLength); + } + + return inputStream; + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new AssertionError(e); + } catch (InvalidMacException e) { + throw new InvalidMessageException(e); + } + } + public static InputStream createForStickerData(byte[] data, byte[] packKey) throws InvalidMessageException, IOException { try { - byte[] combinedKeyMaterial = new HKDFv3().deriveSecrets(packKey, "Sticker Pack".getBytes(), 64); + byte[] combinedKeyMaterial = HKDF.deriveSecrets(packKey, "Sticker Pack".getBytes(), 64); byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(parts[1], "HmacSHA256")); @@ -159,12 +196,12 @@ public int read() throws IOException { } @Override - public int read(byte[] buffer) throws IOException { + public int read(@Nonnull byte[] buffer) throws IOException { return read(buffer, 0, buffer.length); } @Override - public int read(byte[] buffer, int offset, int length) throws IOException { + public int read(@Nonnull byte[] buffer, int offset, int length) throws IOException { if (totalRead != totalDataSize) { return readIncremental(buffer, offset, length); } else if (!done) { @@ -256,7 +293,7 @@ private int readIncremental(byte[] buffer, int offset, int length) throws IOExce } } - private static void verifyMac(InputStream inputStream, long length, Mac mac, byte[] theirDigest) + private static void verifyMac(@Nonnull InputStream inputStream, long length, @Nonnull Mac mac, @Nullable byte[] theirDigest) throws InvalidMacException { try { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.java deleted file mode 100644 index 5b5af120f7..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.signalservice.api.InvalidMessageStructureException; -import org.whispersystems.signalservice.internal.push.AttachmentPointer; - -import java.util.Optional; - -/** - * Represents a signal service attachment identifier. This can be either a CDN key or a long, but - * not both at once. Attachments V2 used a long as an attachment identifier. This lacks sufficient - * entropy to reduce the likelihood of any two uploads going to the same location within a 30-day - * window. Attachments V3 uses an opaque string as an attachment identifier which provides more - * flexibility in the amount of entropy present. - */ -public final class SignalServiceAttachmentRemoteId { - private final Optional v2; - private final Optional v3; - - public SignalServiceAttachmentRemoteId(long v2) { - this.v2 = Optional.of(v2); - this.v3 = Optional.empty(); - } - - public SignalServiceAttachmentRemoteId(String v3) { - this.v2 = Optional.empty(); - this.v3 = Optional.of(v3); - } - - public Optional getV2() { - return v2; - } - - public Optional getV3() { - return v3; - } - - @Override - public String toString() { - if (v2.isPresent()) { - return v2.get().toString(); - } else { - return v3.get(); - } - } - - public static SignalServiceAttachmentRemoteId from(AttachmentPointer attachmentPointer) throws InvalidMessageStructureException { - if (attachmentPointer.cdnKey != null) { - return new SignalServiceAttachmentRemoteId(attachmentPointer.cdnKey); - } else if (attachmentPointer.cdnId != null && attachmentPointer.cdnId > 0) { - return new SignalServiceAttachmentRemoteId(attachmentPointer.cdnId); - } else { - throw new InvalidMessageStructureException("AttachmentPointer CDN location not set"); - } - } - - /** - * Guesses that strings which contain values parseable to {@code long} should use an id-based - * CDN path. Otherwise, use key-based CDN path. - */ - public static SignalServiceAttachmentRemoteId from(String string) { - try { - return new SignalServiceAttachmentRemoteId(Long.parseLong(string)); - } catch (NumberFormatException e) { - return new SignalServiceAttachmentRemoteId(string); - } - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.kt new file mode 100644 index 0000000000..7dd8bf0f1d --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId.kt @@ -0,0 +1,54 @@ +package org.whispersystems.signalservice.api.messages + +import org.whispersystems.signalservice.api.InvalidMessageStructureException +import org.whispersystems.signalservice.internal.push.AttachmentPointer + +/** + * Represents a signal service attachment identifier. This can be either a CDN key or a long, but + * not both at once. Attachments V2 used a long as an attachment identifier. This lacks sufficient + * entropy to reduce the likelihood of any two uploads going to the same location within a 30-day + * window. Attachments V4 (backwards compatible with V3) uses an opaque string as an attachment + * identifier which provides more flexibility in the amount of entropy present. + */ +sealed interface SignalServiceAttachmentRemoteId { + + object S3 : SignalServiceAttachmentRemoteId { + override fun toString() = "" + } + + data class V2(val cdnId: Long) : SignalServiceAttachmentRemoteId { + override fun toString() = cdnId.toString() + } + + data class V4(val cdnKey: String) : SignalServiceAttachmentRemoteId { + override fun toString() = cdnKey + } + + data class Backup(val backupDir: String, val mediaDir: String, val mediaId: String) : SignalServiceAttachmentRemoteId { + override fun toString() = mediaId + } + + companion object { + + @JvmStatic + @Throws(InvalidMessageStructureException::class) + fun from(attachmentPointer: AttachmentPointer): SignalServiceAttachmentRemoteId { + return if (attachmentPointer.cdnKey != null) { + V4(attachmentPointer.cdnKey) + } else if (attachmentPointer.cdnId != null && attachmentPointer.cdnId > 0) { + V2(attachmentPointer.cdnId) + } else { + throw InvalidMessageStructureException("AttachmentPointer CDN location not set") + } + } + + /** + * Guesses that strings which contain values parseable to `long` should use an id-based + * CDN path. Otherwise, use key-based CDN path. + */ + @JvmStatic + fun from(string: String): SignalServiceAttachmentRemoteId { + return string.toLongOrNull()?.let { V2(it) } ?: V4(string) + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java index b8d5522614..8ec9b7db75 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java @@ -56,12 +56,12 @@ public static AttachmentPointer createAttachmentPointer(SignalServiceAttachmentP builder.incrementalMacChunkSize(attachment.getIncrementalMacChunkSize()); } - if (attachment.getRemoteId().getV2().isPresent()) { - builder.cdnId(attachment.getRemoteId().getV2().get()); + if (attachment.getRemoteId() instanceof SignalServiceAttachmentRemoteId.V2) { + builder.cdnId(((SignalServiceAttachmentRemoteId.V2) attachment.getRemoteId()).getCdnId()); } - if (attachment.getRemoteId().getV3().isPresent()) { - builder.cdnKey(attachment.getRemoteId().getV3().get()); + if (attachment.getRemoteId() instanceof SignalServiceAttachmentRemoteId.V4) { + builder.cdnKey(((SignalServiceAttachmentRemoteId.V4) attachment.getRemoteId()).getCdnKey()); } if (attachment.getFileName().isPresent()) { 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 f4d13021f3..1883ad16e0 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 @@ -58,6 +58,7 @@ import org.whispersystems.signalservice.api.archive.BatchArchiveMediaRequest; import org.whispersystems.signalservice.api.archive.BatchArchiveMediaResponse; import org.whispersystems.signalservice.api.archive.DeleteArchivedMediaRequest; +import org.whispersystems.signalservice.api.archive.GetArchiveCdnCredentialsResponse; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.groupsv2.CredentialResponse; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; @@ -319,6 +320,7 @@ public class PushServiceSocket { private static final String ARCHIVE_MEDIA_LIST = "/v1/archives/media?limit=%d"; private static final String ARCHIVE_MEDIA_BATCH = "/v1/archives/media/batch"; private static final String ARCHIVE_MEDIA_DELETE = "/v1/archives/media/delete"; + private static final String ARCHIVE_MEDIA_DOWNLOAD_PATH = "backups/%s/%s/%s"; private static final String CALL_LINK_CREATION_AUTH = "/v1/call-link/create-auth"; private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp"; @@ -585,6 +587,16 @@ public ArchiveMessageBackupUploadFormResponse getArchiveMessageBackupUploadForm( return JsonUtil.fromJson(response, ArchiveMessageBackupUploadFormResponse.class); } + /** + * Copy and re-encrypt media from the attachments cdn into the backup cdn. + */ + public GetArchiveCdnCredentialsResponse getArchiveCdnReadCredentials(@Nonnull ArchiveCredentialPresentation credentialPresentation) throws IOException { + Map headers = credentialPresentation.toHeaders(); + + String response = makeServiceRequestWithoutAuthentication(ARCHIVE_READ_CREDENTIALS, "GET", null, headers, NO_HANDLER); + + return JsonUtil.fromJson(response, GetArchiveCdnCredentialsResponse.class); + } public VerifyAccountResponse changeNumber(@Nonnull ChangePhoneNumberRequest changePhoneNumberRequest) throws IOException @@ -919,16 +931,27 @@ public void checkRepeatedUsePreKeys(ServiceIdType serviceIdType, byte[] digest) }, Optional.empty()); } - public void retrieveAttachment(int cdnNumber, SignalServiceAttachmentRemoteId cdnPath, File destination, long maxSizeBytes, ProgressListener listener) + public void retrieveBackup(int cdnNumber, Map headers, String cdnPath, File destination, long maxSizeBytes, ProgressListener listener) + throws MissingConfigurationException, IOException + { + downloadFromCdn(destination, cdnNumber, headers, cdnPath, maxSizeBytes, listener); + } + + public void retrieveAttachment(int cdnNumber, Map headers, SignalServiceAttachmentRemoteId cdnPath, File destination, long maxSizeBytes, ProgressListener listener) throws IOException, MissingConfigurationException { final String path; - if (cdnPath.getV2().isPresent()) { - path = String.format(Locale.US, ATTACHMENT_ID_DOWNLOAD_PATH, cdnPath.getV2().get()); + 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()); + } 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()); } else { - path = String.format(Locale.US, ATTACHMENT_KEY_DOWNLOAD_PATH, cdnPath.getV3().get()); + throw new IllegalArgumentException("Invalid cdnPath type: " + cdnPath.getClass().getSimpleName()); } - downloadFromCdn(destination, cdnNumber, path, maxSizeBytes, listener); + downloadFromCdn(destination, cdnNumber, headers, path, maxSizeBytes, listener); } public byte[] retrieveSticker(byte[] packId, int stickerId) @@ -937,7 +960,7 @@ public byte[] retrieveSticker(byte[] packId, int stickerId) ByteArrayOutputStream output = new ByteArrayOutputStream(); try { - downloadFromCdn(output, 0, 0, String.format(Locale.US, STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null); + downloadFromCdn(output, 0, 0, Collections.emptyMap(), String.format(Locale.US, STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null); } catch (MissingConfigurationException e) { throw new AssertionError(e); } @@ -951,7 +974,7 @@ public byte[] retrieveStickerManifest(byte[] packId) ByteArrayOutputStream output = new ByteArrayOutputStream(); try { - downloadFromCdn(output, 0, 0, String.format(STICKER_MANIFEST_PATH, hexPackId), 1024 * 1024, null); + downloadFromCdn(output, 0, 0, Collections.emptyMap(), String.format(STICKER_MANIFEST_PATH, hexPackId), 1024 * 1024, null); } catch (MissingConfigurationException e) { throw new AssertionError(e); } @@ -1029,7 +1052,7 @@ public void retrieveProfileAvatar(String path, File destination, long maxSizeByt throws IOException { try { - downloadFromCdn(destination, 0, path, maxSizeBytes, null); + downloadFromCdn(destination, 0, Collections.emptyMap(), path, maxSizeBytes, null); } catch (MissingConfigurationException e) { throw new AssertionError(e); } @@ -1577,15 +1600,15 @@ public AttachmentDigest uploadAttachment(PushAttachmentData attachment) throws I } } - private void downloadFromCdn(File destination, int cdnNumber, String path, long maxSizeBytes, ProgressListener listener) + private void downloadFromCdn(File destination, int cdnNumber, Map headers, String path, long maxSizeBytes, ProgressListener listener) throws IOException, MissingConfigurationException { try (FileOutputStream outputStream = new FileOutputStream(destination, true)) { - downloadFromCdn(outputStream, destination.length(), cdnNumber, path, maxSizeBytes, listener); + downloadFromCdn(outputStream, destination.length(), cdnNumber, headers, path, maxSizeBytes, listener); } } - private void downloadFromCdn(OutputStream outputStream, long offset, int cdnNumber, String path, long maxSizeBytes, ProgressListener listener) + private void downloadFromCdn(OutputStream outputStream, long offset, int cdnNumber, Map headers, String path, long maxSizeBytes, ProgressListener listener) throws PushNetworkException, NonSuccessfulResponseCodeException, MissingConfigurationException { ConnectionHolder[] cdnNumberClients = cdnClientsMap.get(cdnNumber); if (cdnNumberClients == null) { @@ -1604,6 +1627,10 @@ private void downloadFromCdn(OutputStream outputStream, long offset, int cdnNumb request.addHeader("Host", connectionHolder.getHostHeader().get()); } + for (Map.Entry header : headers.entrySet()) { + request.addHeader(header.getKey(), header.getValue()); + } + if (offset > 0) { Log.i(TAG, "Starting download from CDN with offset " + offset); request.addHeader("Range", "bytes=" + offset + "-"); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java index 243da0afef..e39142812e 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java @@ -300,7 +300,7 @@ public synchronized void onMessage(WebSocket webSocket, ByteString payload) { OutgoingRequest listener = outgoingRequests.remove(message.response.id); if (listener != null) { listener.onSuccess(new WebsocketResponse(message.response.status, - new String(message.response.body.toByteArray()), + message.response.body == null ? "" : new String(message.response.body.toByteArray()), message.response.headers, !credentialsProvider.isPresent())); if (message.response.status >= 400) { diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java index 72a05ea7d0..9fa91212ee 100644 --- a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java @@ -6,6 +6,8 @@ import org.signal.libsignal.protocol.incrementalmac.ChunkSizeChoice; import org.signal.libsignal.protocol.incrementalmac.InvalidMacException; import org.signal.libsignal.protocol.kdf.HKDFv3; +import org.whispersystems.signalservice.api.backup.BackupKey; +import org.whispersystems.signalservice.api.backup.MediaId; import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.internal.push.http.AttachmentCipherOutputStreamFactory; import org.whispersystems.signalservice.internal.util.Util; @@ -88,6 +90,62 @@ public void attachment_decryptFailOnBadKey() throws IOException { assertTrue(hitCorrectException); } + @Test + public void archive_encryptDecrypt() throws IOException, InvalidMessageException { + byte[] key = Util.getSecretBytes(64); + BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + byte[] plaintextInput = "Peter Parker".getBytes(); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); + File cipherFile = writeToFile(encryptResult.ciphertext); + InputStream inputStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); + byte[] plaintextOutput = readInputStreamFully(inputStream); + + assertArrayEquals(plaintextInput, plaintextOutput); + + cipherFile.delete(); + } + + @Test + public void archive_encryptDecryptEmpty() throws IOException, InvalidMessageException { + byte[] key = Util.getSecretBytes(64); + BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + byte[] plaintextInput = "".getBytes(); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); + File cipherFile = writeToFile(encryptResult.ciphertext); + InputStream inputStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); + byte[] plaintextOutput = readInputStreamFully(inputStream); + + assertArrayEquals(plaintextInput, plaintextOutput); + + cipherFile.delete(); + } + + @Test + public void archive_decryptFailOnBadKey() throws IOException { + File cipherFile = null; + boolean hitCorrectException = false; + + try { + byte[] key = Util.getSecretBytes(64); + byte[] badKey = Util.getSecretBytes(64); + BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), badKey, Util.getSecretBytes(16)); + byte[] plaintextInput = "Gwen Stacy".getBytes(); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); + + cipherFile = writeToFile(encryptResult.ciphertext); + + AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); + } catch (InvalidMessageException e) { + hitCorrectException = true; + } finally { + if (cipherFile != null) { + cipherFile.delete(); + } + } + + assertTrue(hitCorrectException); + } + @Test public void attachment_decryptFailOnBadDigest() throws IOException { File cipherFile = null; @@ -184,6 +242,44 @@ public void attachment_encryptDecryptPaddedContent() throws IOException, Invalid } } + @Test + public void archive_encryptDecryptPaddedContent() throws IOException, InvalidMessageException { + int[] lengths = { 531, 600, 724, 1019, 1024 }; + + for (int length : lengths) { + byte[] plaintextInput = new byte[length]; + + for (int i = 0; i < length; i++) { + plaintextInput[i] = (byte) 0x97; + } + + byte[] key = Util.getSecretBytes(64); + byte[] iv = Util.getSecretBytes(16); + ByteArrayInputStream inputStream = new ByteArrayInputStream(plaintextInput); + InputStream paddedInputStream = new PaddingInputStream(inputStream, length); + ByteArrayOutputStream destinationOutputStream = new ByteArrayOutputStream(); + + DigestingOutputStream encryptingOutputStream = new AttachmentCipherOutputStreamFactory(key, iv).createFor(destinationOutputStream); + + Util.copy(paddedInputStream, encryptingOutputStream); + + encryptingOutputStream.flush(); + encryptingOutputStream.close(); + + byte[] encryptedData = destinationOutputStream.toByteArray(); + + File cipherFile = writeToFile(encryptedData); + + BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + InputStream decryptedStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, length); + byte[] plaintextOutput = readInputStreamFully(decryptedStream); + + assertArrayEquals(plaintextInput, plaintextOutput); + + cipherFile.delete(); + } + } + @Test public void attachment_decryptFailOnNullDigest() throws IOException { File cipherFile = null; @@ -237,6 +333,35 @@ public void attachment_decryptFailOnBadMac() throws IOException { assertTrue(hitCorrectException); } + @Test + public void archive_decryptFailOnBadMac() throws IOException { + File cipherFile = null; + boolean hitCorrectException = false; + + try { + byte[] key = Util.getSecretBytes(64); + byte[] plaintextInput = "Uncle Ben".getBytes(); + EncryptResult encryptResult = encryptData(plaintextInput, key, true); + byte[] badMacCiphertext = Arrays.copyOf(encryptResult.ciphertext, encryptResult.ciphertext.length); + + badMacCiphertext[badMacCiphertext.length - 1] += 1; + + cipherFile = writeToFile(badMacCiphertext); + + BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); + fail(); + } catch (InvalidMessageException e) { + hitCorrectException = true; + } finally { + if (cipherFile != null) { + cipherFile.delete(); + } + } + + assertTrue(hitCorrectException); + } + @Test public void sticker_encryptDecrypt() throws IOException, InvalidMessageException { assumeLibSignalSupportedOnOS(); From a2e0468cd951ead8632c0de775af5a3a34734cf8 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 12:07:08 -0400 Subject: [PATCH 004/113] Remove "lower hand" confirmation dialog. --- .../webrtc/controls/RaiseHandSnackbar.kt | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt index c4648fd7da..9f64fa0f9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt @@ -5,7 +5,6 @@ package org.thoughtcrime.securesms.components.webrtc.controls -import android.content.Context import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.animation.expandIn @@ -44,7 +43,6 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.google.android.material.dialog.MaterialAlertDialogBuilder import io.reactivex.rxjava3.core.BackpressureStrategy import kotlinx.coroutines.delay import org.signal.core.ui.theme.SignalTheme @@ -160,7 +158,7 @@ private fun RaiseHand( val context = LocalContext.current TextButton( onClick = { - showLowerHandDialog(context) + ApplicationDependencies.getSignalCallManager().raiseHand(false) }, modifier = Modifier.wrapContentWidth(Alignment.End) ) { @@ -182,16 +180,6 @@ private fun RaiseHand( } } -private fun showLowerHandDialog(context: Context) { - MaterialAlertDialogBuilder(context) - .setTitle(R.string.CallOverflowPopupWindow__lower_your_hand) - .setPositiveButton( - R.string.CallOverflowPopupWindow__lower_hand - ) { _, _ -> ApplicationDependencies.getSignalCallManager().raiseHand(false) } - .setNegativeButton(R.string.CallOverflowPopupWindow__cancel, null) - .show() -} - @Composable private fun getSnackbarText(state: RaiseHandState): String { if (state.isEmpty) { From 318b59a6b27a63484fe002f1326241d14961c068 Mon Sep 17 00:00:00 2001 From: Clark Date: Fri, 12 Apr 2024 12:46:06 -0400 Subject: [PATCH 005/113] Do not fallback to REST for resumable upload spec on ratelimit. --- .../signalservice/api/SignalServiceMessageSender.java | 3 +++ 1 file changed, 3 insertions(+) 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 0656a612f3..9de3591be5 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 @@ -868,6 +868,9 @@ public ResumableUploadSpec getResumableUploadSpec() throws IOException { } catch (WebSocketUnavailableException e) { Log.w(TAG, "[getResumableUploadSpec] Pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")"); } catch (IOException e) { + if (e instanceof RateLimitException) { + throw e; + } Log.w(TAG, "Failed to retrieve attachment upload attributes using pipe. Falling back..."); } From eec2685e67ec5d5c3e6ac79f43aa8053834bdbc2 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 12:47:27 -0400 Subject: [PATCH 006/113] Registration refactor initial scaffolding. --- app/src/main/AndroidManifest.xml | 7 + .../securesms/PassphraseRequiredActivity.java | 8 +- .../registration/VerificationCodeView.kt | 2 +- .../registration/PushChallengeRequest.java | 2 +- .../registration/RegistrationData.kt | 1 + .../compose/GrantPermissionsScreen.kt | 212 ++++++++ .../fragments/EnterPhoneNumberFragment.java | 9 +- .../fragments/GrantPermissionsFragment.kt | 202 +------ .../SignalStrengthPhoneStateListener.java | 13 +- .../v2/data/RegistrationRepository.kt | 392 ++++++++++++++ .../v2/ui/RegistrationV2Activity.kt | 40 ++ .../v2/ui/RegistrationV2Extensions.kt | 13 + .../v2/ui/entercode/EnterCodeV2Fragment.kt | 128 +++++ .../GrantPermissionsV2Fragment.kt | 113 ++++ .../phonenumber/EnterPhoneNumberV2Fragment.kt | 338 ++++++++++++ .../ui/phonenumber/EnterPhoneNumberV2State.kt | 25 + .../EnterPhoneNumberV2ViewModel.kt | 93 ++++ .../v2/ui/shared/RegistrationCheckpoint.kt | 27 + .../v2/ui/shared/RegistrationV2State.kt | 24 + .../v2/ui/shared/RegistrationV2ViewModel.kt | 221 ++++++++ .../v2/ui/welcome/WelcomeV2Fragment.kt | 90 ++++ .../securesms/util/FeatureFlags.java | 26 +- .../util/livedata/LiveDataObserverCallback.kt | 31 ++ .../activity_registration_navigation_v2.xml | 22 + .../fragment_registration_enter_code_v2.xml | 141 +++++ ...ent_registration_enter_phone_number_v2.xml | 133 +++++ .../fragment_registration_welcome_v2.xml | 71 +++ .../main/res/navigation/registration_v2.xml | 492 ++++++++++++++++++ .../api/SignalServiceAccountManager.java | 12 +- .../api/registration/RegistrationApi.kt | 70 +++ .../video/videoconverter/MediaConverter.java | 2 +- 31 files changed, 2732 insertions(+), 228 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/compose/GrantPermissionsScreen.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Extensions.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationCheckpoint.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2State.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2ViewModel.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/livedata/LiveDataObserverCallback.kt create mode 100644 app/src/main/res/layout/activity_registration_navigation_v2.xml create mode 100644 app/src/main/res/layout/fragment_registration_enter_code_v2.xml create mode 100644 app/src/main/res/layout/fragment_registration_enter_phone_number_v2.xml create mode 100644 app/src/main/res/layout/fragment_registration_welcome_v2.xml create mode 100644 app/src/main/res/navigation/registration_v2.xml create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b292a8337c..56b494aa38 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -837,6 +837,13 @@ android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:exported="false"/> + + Unit, + onNotNowClicked: () -> Unit +) { + Surface { + Column( + modifier = Modifier + .padding(horizontal = 24.dp) + .padding(top = 40.dp, bottom = 24.dp) + ) { + LazyColumn( + modifier = Modifier.weight(1f) + ) { + item { + Text( + text = stringResource(id = R.string.GrantPermissionsFragment__allow_permissions), + style = MaterialTheme.typography.headlineMedium + ) + } + + item { + Text( + text = stringResource(id = R.string.GrantPermissionsFragment__to_help_you_message_people_you_know), + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(top = 12.dp, bottom = 41.dp) + ) + } + + if (deviceBuildVersion >= 33) { + item { + PermissionRow( + imageVector = ImageVector.vectorResource(id = R.drawable.permission_notification), + title = stringResource(id = R.string.GrantPermissionsFragment__notifications), + subtitle = stringResource(id = R.string.GrantPermissionsFragment__get_notified_when) + ) + } + } + + item { + PermissionRow( + imageVector = ImageVector.vectorResource(id = R.drawable.permission_contact), + title = stringResource(id = R.string.GrantPermissionsFragment__contacts), + subtitle = stringResource(id = R.string.GrantPermissionsFragment__find_people_you_know) + ) + } + + if (deviceBuildVersion < 29 || !isBackupSelectionRequired) { + item { + PermissionRow( + imageVector = ImageVector.vectorResource(id = R.drawable.permission_file), + title = stringResource(id = R.string.GrantPermissionsFragment__storage), + subtitle = stringResource(id = R.string.GrantPermissionsFragment__send_photos_videos_and_files) + ) + } + } + + item { + PermissionRow( + imageVector = ImageVector.vectorResource(id = R.drawable.permission_phone), + title = stringResource(id = R.string.GrantPermissionsFragment__phone_calls), + subtitle = stringResource(id = R.string.GrantPermissionsFragment__make_registering_easier) + ) + } + } + + Row { + TextButton(onClick = onNotNowClicked) { + Text( + text = stringResource(id = R.string.GrantPermissionsFragment__not_now) + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + if (isSearchingForBackup) { + Box { + NextButton( + isSearchingForBackup = true, + onNextClicked = onNextClicked + ) + + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center) + ) + } + } else { + NextButton( + isSearchingForBackup = false, + onNextClicked = onNextClicked + ) + } + } + } + } +} + +@Preview +@Composable +fun PermissionRowPreview() { + PermissionRow( + imageVector = ImageVector.vectorResource(id = R.drawable.permission_notification), + title = stringResource(id = R.string.GrantPermissionsFragment__notifications), + subtitle = stringResource(id = R.string.GrantPermissionsFragment__get_notified_when) + ) +} + +@Composable +fun PermissionRow( + imageVector: ImageVector, + title: String, + subtitle: String +) { + Row(modifier = Modifier.padding(bottom = 32.dp)) { + Image( + imageVector = imageVector, + contentDescription = null, + modifier = Modifier.size(48.dp) + ) + + Spacer(modifier = Modifier.size(16.dp)) + + Column { + Text( + text = title, + style = MaterialTheme.typography.titleSmall + ) + + Text( + text = subtitle, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + + Spacer(modifier = Modifier.size(32.dp)) + } +} + +@Composable +fun NextButton( + isSearchingForBackup: Boolean, + onNextClicked: () -> Unit +) { + val alpha = if (isSearchingForBackup) { + 0f + } else { + 1f + } + + Buttons.LargeTonal( + onClick = onNextClicked, + enabled = !isSearchingForBackup, + modifier = Modifier.alpha(alpha) + ) { + Text( + text = stringResource(id = R.string.GrantPermissionsFragment__next) + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java index 0855f90e5c..aa5c9a0864 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterPhoneNumberFragment.java @@ -10,7 +10,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ScrollView; @@ -138,7 +137,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat } controller.setNumberAndCountryCode(viewModelNumber); - showKeyboard(number.getEditText()); + ViewUtil.focusAndShowKeyboard(number.getEditText()); if (viewModel.hasUserSkippedReRegisterFlow() && viewModel.shouldAutoShowSmsConfirmDialog()) { viewModel.setAutoShowSmsConfirmDialog(false); @@ -146,12 +145,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat } } - private void showKeyboard(View viewToFocus) { - viewToFocus.requestFocus(); - InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(viewToFocus, InputMethodManager.SHOW_IMPLICIT); - } - @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { inflater.inflate(R.menu.enter_phone_number, menu); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/GrantPermissionsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/GrantPermissionsFragment.kt index 94ccdbdab8..c37801b045 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/GrantPermissionsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/GrantPermissionsFragment.kt @@ -6,38 +6,15 @@ package org.thoughtcrime.securesms.registration.fragments import android.os.Build -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.CircularProgressIndicator -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.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.navArgs -import org.signal.core.ui.Buttons -import org.signal.core.ui.theme.SignalTheme -import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.permissions.Permissions +import org.thoughtcrime.securesms.registration.compose.GrantPermissionsScreen import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel import org.thoughtcrime.securesms.util.BackupUtil @@ -124,180 +101,3 @@ class GrantPermissionsFragment : ComposeFragment() { RESTORE_BACKUP } } - -@Preview -@Composable -fun GrantPermissionsScreenPreview() { - SignalTheme(isDarkMode = false) { - GrantPermissionsScreen( - deviceBuildVersion = 33, - isBackupSelectionRequired = true, - isSearchingForBackup = true, - {}, - {} - ) - } -} - -@Composable -fun GrantPermissionsScreen( - deviceBuildVersion: Int, - isBackupSelectionRequired: Boolean, - isSearchingForBackup: Boolean, - onNextClicked: () -> Unit, - onNotNowClicked: () -> Unit -) { - Surface { - Column( - modifier = Modifier - .padding(horizontal = 24.dp) - .padding(top = 40.dp, bottom = 24.dp) - ) { - LazyColumn( - modifier = Modifier.weight(1f) - ) { - item { - Text( - text = stringResource(id = R.string.GrantPermissionsFragment__allow_permissions), - style = MaterialTheme.typography.headlineMedium - ) - } - - item { - Text( - text = stringResource(id = R.string.GrantPermissionsFragment__to_help_you_message_people_you_know), - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(top = 12.dp, bottom = 41.dp) - ) - } - - if (deviceBuildVersion >= 33) { - item { - PermissionRow( - imageVector = ImageVector.vectorResource(id = R.drawable.permission_notification), - title = stringResource(id = R.string.GrantPermissionsFragment__notifications), - subtitle = stringResource(id = R.string.GrantPermissionsFragment__get_notified_when) - ) - } - } - - item { - PermissionRow( - imageVector = ImageVector.vectorResource(id = R.drawable.permission_contact), - title = stringResource(id = R.string.GrantPermissionsFragment__contacts), - subtitle = stringResource(id = R.string.GrantPermissionsFragment__find_people_you_know) - ) - } - - if (deviceBuildVersion < 29 || !isBackupSelectionRequired) { - item { - PermissionRow( - imageVector = ImageVector.vectorResource(id = R.drawable.permission_file), - title = stringResource(id = R.string.GrantPermissionsFragment__storage), - subtitle = stringResource(id = R.string.GrantPermissionsFragment__send_photos_videos_and_files) - ) - } - } - - item { - PermissionRow( - imageVector = ImageVector.vectorResource(id = R.drawable.permission_phone), - title = stringResource(id = R.string.GrantPermissionsFragment__phone_calls), - subtitle = stringResource(id = R.string.GrantPermissionsFragment__make_registering_easier) - ) - } - } - - Row { - TextButton(onClick = onNotNowClicked) { - Text( - text = stringResource(id = R.string.GrantPermissionsFragment__not_now) - ) - } - - Spacer(modifier = Modifier.weight(1f)) - - if (isSearchingForBackup) { - Box { - NextButton( - isSearchingForBackup = true, - onNextClicked = onNextClicked - ) - - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) - ) - } - } else { - NextButton( - isSearchingForBackup = false, - onNextClicked = onNextClicked - ) - } - } - } - } -} - -@Preview -@Composable -fun PermissionRowPreview() { - PermissionRow( - imageVector = ImageVector.vectorResource(id = R.drawable.permission_notification), - title = stringResource(id = R.string.GrantPermissionsFragment__notifications), - subtitle = stringResource(id = R.string.GrantPermissionsFragment__get_notified_when) - ) -} - -@Composable -fun PermissionRow( - imageVector: ImageVector, - title: String, - subtitle: String -) { - Row(modifier = Modifier.padding(bottom = 32.dp)) { - Image( - imageVector = imageVector, - contentDescription = null, - modifier = Modifier.size(48.dp) - ) - - Spacer(modifier = Modifier.size(16.dp)) - - Column { - Text( - text = title, - style = MaterialTheme.typography.titleSmall - ) - - Text( - text = subtitle, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - - Spacer(modifier = Modifier.size(32.dp)) - } -} - -@Composable -fun NextButton( - isSearchingForBackup: Boolean, - onNextClicked: () -> Unit -) { - val alpha = if (isSearchingForBackup) { - 0f - } else { - 1f - } - - Buttons.LargeTonal( - onClick = onNextClicked, - enabled = !isSearchingForBackup, - modifier = Modifier.alpha(alpha) - ) { - Text( - text = stringResource(id = R.string.GrantPermissionsFragment__next) - ) - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/SignalStrengthPhoneStateListener.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/SignalStrengthPhoneStateListener.java index fded5f0aaf..a751313473 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/SignalStrengthPhoneStateListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/SignalStrengthPhoneStateListener.java @@ -1,3 +1,8 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + package org.thoughtcrime.securesms.registration.fragments; import android.content.Context; @@ -14,7 +19,8 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.util.Debouncer; -final class SignalStrengthPhoneStateListener extends PhoneStateListener +// TODO [nicholas]: move to v2 package and make package-private. convert to Kotlin +public final class SignalStrengthPhoneStateListener extends PhoneStateListener implements DefaultLifecycleObserver { private static final String TAG = Log.tag(SignalStrengthPhoneStateListener.class); @@ -22,7 +28,8 @@ final class SignalStrengthPhoneStateListener extends PhoneStateListener private final Callback callback; private final Debouncer debouncer = new Debouncer(1000); - SignalStrengthPhoneStateListener(@NonNull LifecycleOwner lifecycleOwner, @NonNull Callback callback) { + @SuppressWarnings("deprecation") + public SignalStrengthPhoneStateListener(@NonNull LifecycleOwner lifecycleOwner, @NonNull Callback callback) { this.callback = callback; lifecycleOwner.getLifecycle().addObserver(this); @@ -51,7 +58,7 @@ private boolean isLowLevel(@NonNull SignalStrength signalStrength) { } } - interface Callback { + public interface Callback { void onNoCellSignalPresent(); void onCellSignalPresent(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt new file mode 100644 index 0000000000..a1f583103e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt @@ -0,0 +1,392 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.data + +import android.content.Context +import androidx.annotation.WorkerThread +import androidx.core.app.NotificationManagerCompat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.signal.core.util.logging.Log +import org.signal.libsignal.protocol.IdentityKeyPair +import org.signal.libsignal.protocol.util.KeyHelper +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.AppCapabilities +import org.thoughtcrime.securesms.crypto.PreKeyUtil +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.crypto.SenderKeyUtil +import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore +import org.thoughtcrime.securesms.crypto.storage.SignalServiceAccountDataStoreImpl +import org.thoughtcrime.securesms.database.IdentityTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.gcm.FcmUtil +import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob +import org.thoughtcrime.securesms.jobs.PreKeysSyncJob +import org.thoughtcrime.securesms.jobs.RotateCertificateJob +import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.notifications.NotificationIds +import org.thoughtcrime.securesms.pin.SvrRepository.onRegistrationComplete +import org.thoughtcrime.securesms.push.AccountManagerFactory +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.registration.PushChallengeRequest +import org.thoughtcrime.securesms.registration.RegistrationData +import org.thoughtcrime.securesms.registration.VerifyAccountRepository +import org.thoughtcrime.securesms.service.DirectoryRefreshListener +import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.account.AccountAttributes +import org.whispersystems.signalservice.api.account.PreKeyCollection +import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess +import org.whispersystems.signalservice.api.kbs.MasterKey +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.push.SignalServiceAddress +import org.whispersystems.signalservice.api.registration.RegistrationApi +import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataHeaders +import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse +import java.util.Locale +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import kotlin.time.Duration.Companion.seconds + +/** + * A repository that deals with disk I/O during account registration. + */ +object RegistrationRepository { + + private val TAG = Log.tag(RegistrationRepository::class.java) + + private val PUSH_REQUEST_TIMEOUT = 5.seconds.inWholeMilliseconds + + /** + * Retrieve the FCM token from the Firebase service. + */ + suspend fun getFcmToken(context: Context): String? = + withContext(Dispatchers.Default) { + FcmUtil.getToken(context).orElse(null) + } + + /** + * Queries the local store for whether a PIN is set. + */ + @JvmStatic + fun hasPin(): Boolean { + return SignalStore.svr().hasPin() + } + + /** + * Queries, and creates if needed, the local registration ID. + */ + @JvmStatic + fun getRegistrationId(): Int { + // TODO [regv2]: make creation more explicit instead of hiding it in this getter + var registrationId = SignalStore.account().registrationId + if (registrationId == 0) { + registrationId = KeyHelper.generateRegistrationId(false) + SignalStore.account().registrationId = registrationId + } + return registrationId + } + + /** + * Queries, and creates if needed, the local PNI registration ID. + */ + @JvmStatic + fun getPniRegistrationId(): Int { + // TODO [regv2]: make creation more explicit instead of hiding it in this getter + var pniRegistrationId = SignalStore.account().pniRegistrationId + if (pniRegistrationId == 0) { + pniRegistrationId = KeyHelper.generateRegistrationId(false) + SignalStore.account().pniRegistrationId = pniRegistrationId + } + return pniRegistrationId + } + + /** + * Queries, and creates if needed, the local profile key. + */ + @JvmStatic + suspend fun getProfileKey(e164: String): ProfileKey = + withContext(Dispatchers.IO) { + // TODO [regv2]: make creation more explicit instead of hiding it in this getter + val recipientTable = SignalDatabase.recipients + val recipient = recipientTable.getByE164(e164) + var profileKey = if (recipient.isPresent) { + ProfileKeyUtil.profileKeyOrNull(Recipient.resolved(recipient.get()).profileKey) + } else { + null + } + if (profileKey == null) { + profileKey = ProfileKeyUtil.createNew() + Log.i(TAG, "No profile key found, created a new one") + } + profileKey + } + + /** + * Takes a server response from a successful registration and persists the relevant data. + */ + @WorkerThread + @JvmStatic + suspend fun registerAccountLocally(context: Context, registrationData: RegistrationData, response: AccountRegistrationResult, reglockEnabled: Boolean) = + withContext(Dispatchers.IO) { + val aciPreKeyCollection: PreKeyCollection = response.aciPreKeyCollection + val pniPreKeyCollection: PreKeyCollection = response.pniPreKeyCollection + val aci: ACI = ACI.parseOrThrow(response.uuid) + val pni: PNI = PNI.parseOrThrow(response.pni) + val hasPin: Boolean = response.storageCapable + + SignalStore.account().setAci(aci) + SignalStore.account().setPni(pni) + + ApplicationDependencies.resetProtocolStores() + + ApplicationDependencies.getProtocolStore().aci().sessions().archiveAllSessions() + ApplicationDependencies.getProtocolStore().pni().sessions().archiveAllSessions() + SenderKeyUtil.clearAllState() + + val aciProtocolStore = ApplicationDependencies.getProtocolStore().aci() + val aciMetadataStore = SignalStore.account().aciPreKeys + + val pniProtocolStore = ApplicationDependencies.getProtocolStore().pni() + val pniMetadataStore = SignalStore.account().pniPreKeys + + storeSignedAndLastResortPreKeys(aciProtocolStore, aciMetadataStore, aciPreKeyCollection) + storeSignedAndLastResortPreKeys(pniProtocolStore, pniMetadataStore, pniPreKeyCollection) + + val recipientTable = SignalDatabase.recipients + val selfId = Recipient.trustedPush(aci, pni, registrationData.e164).id + + recipientTable.setProfileSharing(selfId, true) + recipientTable.markRegisteredOrThrow(selfId, aci) + recipientTable.linkIdsForSelf(aci, pni, registrationData.e164) + recipientTable.setProfileKey(selfId, registrationData.profileKey) + + ApplicationDependencies.getRecipientCache().clearSelf() + + SignalStore.account().setE164(registrationData.e164) + SignalStore.account().fcmToken = registrationData.fcmToken + SignalStore.account().fcmEnabled = registrationData.isFcm + + val now = System.currentTimeMillis() + saveOwnIdentityKey(selfId, aci, aciProtocolStore, now) + saveOwnIdentityKey(selfId, pni, pniProtocolStore, now) + + SignalStore.account().setServicePassword(registrationData.password) + SignalStore.account().setRegistered(true) + TextSecurePreferences.setPromptedPushRegistration(context, true) + TextSecurePreferences.setUnauthorizedReceived(context, false) + NotificationManagerCompat.from(context).cancel(NotificationIds.UNREGISTERED_NOTIFICATION_ID) + + onRegistrationComplete(response.masterKey, response.pin, hasPin, reglockEnabled) + + ApplicationDependencies.closeConnections() + ApplicationDependencies.getIncomingMessageObserver() + PreKeysSyncJob.enqueue() + + val jobManager = ApplicationDependencies.getJobManager() + jobManager.add(DirectoryRefreshJob(false)) + jobManager.add(RotateCertificateJob()) + + DirectoryRefreshListener.schedule(context) + RotateSignedPreKeyListener.schedule(context) + } + + @JvmStatic + private fun saveOwnIdentityKey(selfId: RecipientId, serviceId: ServiceId, protocolStore: SignalServiceAccountDataStoreImpl, now: Long) { + protocolStore.identities().saveIdentityWithoutSideEffects( + selfId, + serviceId, + protocolStore.identityKeyPair.publicKey, + IdentityTable.VerifiedStatus.VERIFIED, + true, + now, + true + ) + } + + @JvmStatic + private fun storeSignedAndLastResortPreKeys(protocolStore: SignalServiceAccountDataStoreImpl, metadataStore: PreKeyMetadataStore, preKeyCollection: PreKeyCollection) { + PreKeyUtil.storeSignedPreKey(protocolStore, metadataStore, preKeyCollection.signedPreKey) + metadataStore.isSignedPreKeyRegistered = true + metadataStore.activeSignedPreKeyId = preKeyCollection.signedPreKey.id + metadataStore.lastSignedPreKeyRotationTime = System.currentTimeMillis() + + PreKeyUtil.storeLastResortKyberPreKey(protocolStore, metadataStore, preKeyCollection.lastResortKyberPreKey) + metadataStore.lastResortKyberPreKeyId = preKeyCollection.lastResortKyberPreKey.id + metadataStore.lastResortKyberPreKeyRotationTime = System.currentTimeMillis() + } + + /** + * Asks the service to send a verification code through one of our supported channels (SMS, phone call). + * This requires two or more network calls: + * 1. Create (or reuse) a session. + * 2. (Optional) If the session has any proof requirements ("challenges"), the user must solve them and submit the proof. + * 3. Once the service responds we are allowed to, we request the verification code. + */ + suspend fun requestSmsCode(context: Context, e164: String, password: String, mcc: String?, mnc: String?, mode: Mode = Mode.SMS_WITHOUT_LISTENER): NetworkResult = + withContext(Dispatchers.IO) { + val fcmToken: String? = FcmUtil.getToken(context).orElse(null) + val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password).registrationApi + val activeSession = if (fcmToken == null) { + // TODO [regv2] + val notImplementedError = NotImplementedError() + Log.w(TAG, "Not yet implemented!", notImplementedError) + NetworkResult.ApplicationError(notImplementedError) + } else { + createSessionAndBlockForPushChallenge(api, fcmToken, mcc, mnc) + } + + activeSession.then { session -> + val sessionId = session.body.id + SignalStore.registrationValues().sessionId = sessionId + SignalStore.registrationValues().sessionE164 = e164 + if (!session.body.allowedToRequestCode) { + val challenges = session.body.requestedInformation.joinToString() + Log.w(TAG, "Not allowed to request code! Remaining challenges: $challenges") + // TODO [regv2]: actually handle challenges + } + // TODO [regv2]: support other verification code [Mode] options + if (mode == Mode.PHONE_CALL) { + // TODO [regv2] + val notImplementedError = NotImplementedError() + Log.w(TAG, "Not yet implemented!", notImplementedError) + NetworkResult.ApplicationError(notImplementedError) + } else { + api.requestSmsVerificationCode(sessionId, Locale.getDefault(), mode.isSmsRetrieverSupported) + } + } + } + + /** + * Submits the user-entered verification code to the service. + */ + suspend fun submitVerificationCode(context: Context, e164: String, password: String, sessionId: String, registrationData: RegistrationData): NetworkResult = + withContext(Dispatchers.IO) { + val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password).registrationApi + api.verifyAccount(registrationData.code, sessionId) + } + + /** + * Submit the necessary assets as a verified account so that the user can actually use the service. + */ + suspend fun registerAccount(context: Context, e164: String, password: String, sessionId: String, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: VerifyAccountRepository.MasterKeyProducer? = null): NetworkResult = + withContext(Dispatchers.IO) { + val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password).registrationApi + + val universalUnidentifiedAccess: Boolean = TextSecurePreferences.isUniversalUnidentifiedAccess(context) + val unidentifiedAccessKey: ByteArray = UnidentifiedAccess.deriveAccessKeyFrom(registrationData.profileKey) + + val masterKey: MasterKey? = masterKeyProducer?.produceMasterKey() + val registrationLock: String? = masterKey?.deriveRegistrationLock() + + val accountAttributes = AccountAttributes( + signalingKey = null, + registrationId = registrationData.registrationId, + fetchesMessages = registrationData.isNotFcm, + registrationLock = registrationLock, + unidentifiedAccessKey = unidentifiedAccessKey, + unrestrictedUnidentifiedAccess = universalUnidentifiedAccess, + capabilities = AppCapabilities.getCapabilities(true), + discoverableByPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberDiscoverabilityMode == PhoneNumberPrivacyValues.PhoneNumberDiscoverabilityMode.DISCOVERABLE, + name = null, + pniRegistrationId = registrationData.pniRegistrationId, + recoveryPassword = registrationData.recoveryPassword + ) + + SignalStore.account().generateAciIdentityKeyIfNecessary() + val aciIdentity: IdentityKeyPair = SignalStore.account().aciIdentityKey + + SignalStore.account().generatePniIdentityKeyIfNecessary() + val pniIdentity: IdentityKeyPair = SignalStore.account().pniIdentityKey + + val aciPreKeyCollection = org.thoughtcrime.securesms.registration.RegistrationRepository.generateSignedAndLastResortPreKeys(aciIdentity, SignalStore.account().aciPreKeys) + val pniPreKeyCollection = org.thoughtcrime.securesms.registration.RegistrationRepository.generateSignedAndLastResortPreKeys(pniIdentity, SignalStore.account().pniPreKeys) + + api.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true) + .map { accountRegistrationResponse -> + AccountRegistrationResult( + uuid = accountRegistrationResponse.uuid, + pni = accountRegistrationResponse.pni, + storageCapable = accountRegistrationResponse.storageCapable, + number = accountRegistrationResponse.number, + masterKey = masterKey, + pin = pin, + aciPreKeyCollection = aciPreKeyCollection, + pniPreKeyCollection = pniPreKeyCollection + ) + } + } + + private suspend fun createSessionAndBlockForPushChallenge(accountManager: RegistrationApi, fcmToken: String, mcc: String?, mnc: String?): NetworkResult = + withContext(Dispatchers.IO) { + // TODO [regv2]: do not use event bus nor latch + val subscriber = PushTokenChallengeSubscriber() + val eventBus = EventBus.getDefault() + eventBus.register(subscriber) + + val sessionCreationResponse = accountManager.createRegistrationSession(fcmToken, mcc, mnc).successOrThrow() // TODO: error handling + val receivedPush = subscriber.latch.await(PUSH_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS) + eventBus.unregister(subscriber) + + if (receivedPush) { + val challenge = subscriber.challenge + if (challenge != null) { + Log.w(TAG, "Push challenge token received.") + return@withContext accountManager.submitPushChallengeToken(sessionCreationResponse.body.id, challenge) + } else { + Log.w(TAG, "Push received but challenge token was null.") + } + } else { + Log.i(TAG, "Push challenge timed out.") + } + Log.i(TAG, "Push challenge unsuccessful. Updating registration state accordingly.") + return@withContext NetworkResult.ApplicationError(NullPointerException()) + } + + @JvmStatic + fun deriveTimestamp(headers: RegistrationSessionMetadataHeaders, deltaSeconds: Int?): Long { + if (deltaSeconds == null) { + return 0L + } + + val timestamp: Long = headers.timestamp + return timestamp + deltaSeconds.seconds.inWholeMilliseconds + } + + enum class Mode(val isSmsRetrieverSupported: Boolean) { + SMS_WITH_LISTENER(true), SMS_WITHOUT_LISTENER(false), PHONE_CALL(false) + } + + private class PushTokenChallengeSubscriber { + var challenge: String? = null + val latch = CountDownLatch(1) + + @Subscribe + fun onChallengeEvent(pushChallengeEvent: PushChallengeRequest.PushChallengeEvent) { + challenge = pushChallengeEvent.challenge + latch.countDown() + } + } + + data class AccountRegistrationResult( + val uuid: String, + val pni: String, + val storageCapable: Boolean, + val number: String, + val masterKey: MasterKey?, + val pin: String?, + val aciPreKeyCollection: PreKeyCollection, + val pniPreKeyCollection: PreKeyCollection + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt new file mode 100644 index 0000000000..1271a2133b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel + +/** + * Activity to hold the entire registration process. + */ +class RegistrationV2Activity : AppCompatActivity() { + + private val TAG = Log.tag(RegistrationV2Activity::class.java) + + val sharedViewModel: RegistrationV2ViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_registration_navigation_v2) + } + + companion object { + + @JvmStatic + fun newIntentForNewRegistration(context: Context, originalIntent: Intent): Intent { + return Intent(context, RegistrationV2Activity::class.java).apply { + setData(originalIntent.data) + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Extensions.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Extensions.kt new file mode 100644 index 0000000000..9b76f73561 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Extensions.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui + +import com.google.i18n.phonenumbers.PhoneNumberUtil +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber + +fun PhoneNumber.toE164(): String { + return PhoneNumberUtil.getInstance().format(this, PhoneNumberUtil.PhoneNumberFormat.E164) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt new file mode 100644 index 0000000000..225504ae13 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.entercode + +import android.os.Bundle +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.activityViewModels +import androidx.navigation.ActivityNavigator +import androidx.navigation.fragment.NavHostFragment +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.LoggingFragment +import org.thoughtcrime.securesms.MainActivity +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.databinding.FragmentRegistrationEnterCodeV2Binding +import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity +import org.thoughtcrime.securesms.profiles.AvatarHelper +import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView +import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel + +/** + * The final screen of account registration, where the user enters their verification code. + */ +class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter_code_v2) { + + private val TAG = Log.tag(EnterCodeV2Fragment::class.java) + + private val sharedViewModel by activityViewModels() + private val binding: FragmentRegistrationEnterCodeV2Binding by ViewBinderDelegate(FragmentRegistrationEnterCodeV2Binding::bind) + + private lateinit var phoneStateListener: SignalStrengthPhoneStateListener + + private var autopilotCodeEntryActive = false + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setDebugLogSubmitMultiTapView(binding.verifyHeader) + + phoneStateListener = SignalStrengthPhoneStateListener(this, PhoneStateCallback()) + + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + popBackStack() + } + } + ) + + binding.wrongNumber.setOnClickListener { + popBackStack() + } + + binding.code.setOnCompleteListener { + sharedViewModel.verifyCodeWithoutRegistrationLock(requireContext(), it) + } + + binding.keyboard.setOnKeyPressListener { key -> + if (!autopilotCodeEntryActive) { + if (key >= 0) { + binding.code.append(key) + } else { + binding.code.delete() + } + } + } + + sharedViewModel.uiState.observe(viewLifecycleOwner) { + if (it.registrationCheckpoint == RegistrationCheckpoint.SERVICE_REGISTRATION_COMPLETED) { + handleSuccessfulVerify() + } + } + } + + private fun handleSuccessfulVerify() { + // TODO [regv2]: add functionality of [RegistrationCompleteFragment] + val activity = requireActivity() + val isProfileNameEmpty = Recipient.self().profileName.isEmpty + val isAvatarEmpty = !AvatarHelper.hasAvatar(activity, Recipient.self().id) + val needsProfile = isProfileNameEmpty || isAvatarEmpty + val needsPin = !sharedViewModel.hasPin() + + Log.i(TAG, "Pin restore flow not required. Profile name: $isProfileNameEmpty | Profile avatar: $isAvatarEmpty | Needs PIN: $needsPin") + + if (!needsProfile && !needsPin) { + sharedViewModel.completeRegistration() + } + + val startIntent = MainActivity.clearTop(activity).apply { + if (needsPin) { + putExtra("next_intent", CreateSvrPinActivity.getIntentForPinCreate(activity)) + } + + if (needsProfile) { + putExtra("next_intent", CreateProfileActivity.getIntentForUserProfile(activity)) + } + } + + activity.startActivity(startIntent) + sharedViewModel.setInProgress(false) + activity.finish() + ActivityNavigator.applyPopAnimationsToPendingTransition(activity) + } + + private fun popBackStack() { + sharedViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.PUSH_NETWORK_AUDITED) + NavHostFragment.findNavController(this).popBackStack() + } + + private class PhoneStateCallback : SignalStrengthPhoneStateListener.Callback { + override fun onNoCellSignalPresent() { + // TODO [regv2]: animate in bottom sheet + } + + override fun onCellSignalPresent() { + // TODO [regv2]: animate in bottom sheet + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt new file mode 100644 index 0000000000..e61987787c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.grantpermissions + +import android.os.Build +import android.os.Bundle +import android.view.View +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.platform.LocalContext +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.navArgs +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.registration.compose.GrantPermissionsScreen +import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2State +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel +import org.thoughtcrime.securesms.util.BackupUtil +import org.thoughtcrime.securesms.util.navigation.safeNavigate + +/** + * Screen in account registration that provides rationales for the suggested runtime permissions. + */ +@RequiresApi(23) +class GrantPermissionsV2Fragment : ComposeFragment() { + + private val TAG = Log.tag(GrantPermissionsV2Fragment::class.java) + + private val sharedViewModel by activityViewModels() + private val args by navArgs() + private val isSearchingForBackup = mutableStateOf(false) + + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions(), + ::permissionsGranted + ) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + sharedViewModel.uiState.observe(viewLifecycleOwner) { + if (it.registrationCheckpoint >= RegistrationCheckpoint.PERMISSIONS_GRANTED) { + proceedToNextScreen(it) + } + } + } + + private fun proceedToNextScreen(it: RegistrationV2State) { + // TODO [nicholas]: conditionally go to backup flow + NavHostFragment.findNavController(this).safeNavigate(GrantPermissionsV2FragmentDirections.actionSkipRestore()) + } + + @Composable + override fun FragmentContent() { + val isSearchingForBackup by this.isSearchingForBackup + + GrantPermissionsScreen( + deviceBuildVersion = Build.VERSION.SDK_INT, + isSearchingForBackup = isSearchingForBackup, + isBackupSelectionRequired = BackupUtil.isUserSelectionRequired(LocalContext.current), + onNextClicked = this::onNextClicked, + onNotNowClicked = this::onNotNowClicked + ) + } + + private fun onNextClicked() { + when (args.welcomeAction) { + WelcomeAction.CONTINUE -> continueNext() + WelcomeAction.RESTORE_BACKUP -> Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + } + } + + private fun continueNext() { + val isUserSelectionRequired = BackupUtil.isUserSelectionRequired(requireContext()) + val requiredPermissions = WelcomePermissions.getWelcomePermissions(isUserSelectionRequired) + requestPermissionLauncher.launch(requiredPermissions) + } + + private fun onNotNowClicked() { + when (args.welcomeAction) { + WelcomeAction.CONTINUE -> continueNotNow() + WelcomeAction.RESTORE_BACKUP -> Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + } + } + + private fun continueNotNow() { + NavHostFragment.findNavController(this).popBackStack() + } + + private fun permissionsGranted(permissions: Map) { + permissions.forEach { + Log.d(TAG, "${it.key} = ${it.value}") + } + sharedViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.PERMISSIONS_GRANTED) + } + + /** + * Which welcome action the user selected which prompted this + * screen. + */ + enum class WelcomeAction { + CONTINUE, + RESTORE_BACKUP + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt new file mode 100644 index 0000000000..cca66bb7e7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt @@ -0,0 +1,338 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.phonenumber + +import android.content.Context +import android.os.Bundle +import android.text.SpannableStringBuilder +import android.view.KeyEvent +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.ArrayAdapter +import android.widget.TextView +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.MenuProvider +import androidx.core.widget.addTextChangedListener +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.NavHostFragment +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.GoogleApiAvailability +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.textfield.MaterialAutoCompleteTextView +import com.google.android.material.textfield.TextInputEditText +import com.google.i18n.phonenumbers.PhoneNumberUtil +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.LoggingFragment +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.databinding.FragmentRegistrationEnterPhoneNumberV2Binding +import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter +import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView +import org.thoughtcrime.securesms.registration.util.CountryPrefix +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2State +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel +import org.thoughtcrime.securesms.registration.v2.ui.toE164 +import org.thoughtcrime.securesms.util.PlayServicesUtil +import org.thoughtcrime.securesms.util.SpanUtil +import org.thoughtcrime.securesms.util.ViewUtil +import org.thoughtcrime.securesms.util.livedata.LiveDataObserverCallback +import org.thoughtcrime.securesms.util.navigation.safeNavigate +import org.thoughtcrime.securesms.util.visible + +/** + * Screen in registration where the user enters their phone number. + */ +class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registration_enter_phone_number_v2) { + + private val TAG = Log.tag(EnterPhoneNumberV2Fragment::class.java) + private val sharedViewModel by activityViewModels() + private val fragmentViewModel by viewModels() + private val binding: FragmentRegistrationEnterPhoneNumberV2Binding by ViewBinderDelegate(FragmentRegistrationEnterPhoneNumberV2Binding::bind) + + private lateinit var spinnerAdapter: ArrayAdapter + private lateinit var phoneNumberInputLayout: TextInputEditText + private lateinit var spinnerView: MaterialAutoCompleteTextView + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + popBackStack() + } + } + ) + phoneNumberInputLayout = binding.number.editText as TextInputEditText + spinnerView = binding.countryCode.editText as MaterialAutoCompleteTextView + spinnerAdapter = ArrayAdapter( + requireContext(), + R.layout.registration_country_code_dropdown_item, + fragmentViewModel.supportedCountryPrefixes + ) + setDebugLogSubmitMultiTapView(binding.verifyHeader) + binding.registerButton.setOnClickListener { onRegistrationButtonClicked() } + + binding.toolbar.title = null + val activity = requireActivity() as AppCompatActivity + activity.setSupportActionBar(binding.toolbar) + + requireActivity().addMenuProvider(UseProxyMenuProvider(), viewLifecycleOwner) + + val existingPhoneNumber = sharedViewModel.uiState.value?.phoneNumber + if (existingPhoneNumber != null) { + fragmentViewModel.restoreState(existingPhoneNumber) + fragmentViewModel.phoneNumber()?.let { + phoneNumberInputLayout.setText(it.nationalNumber.toString()) + } + } else if (spinnerView.editableText.isBlank()) { + spinnerView.setText(fragmentViewModel.countryPrefix().toString()) + } + + sharedViewModel.uiState.observe(viewLifecycleOwner) { sharedState -> + presentRegisterButton(sharedState) + presentProgressBar(sharedState.inProgress, sharedState.isReRegister) + if (sharedState.registrationCheckpoint >= RegistrationCheckpoint.VERIFICATION_CODE_REQUESTED) { + moveToVerificationEntryScreen() + } + } + + fragmentViewModel.uiState.observe(viewLifecycleOwner) { fragmentState -> + if (fragmentViewModel.isEnteredNumberValid(fragmentState)) { + sharedViewModel.setPhoneNumber(fragmentViewModel.parsePhoneNumber(fragmentState)) + } else { + sharedViewModel.setPhoneNumber(null) + } + + if (fragmentState.error != EnterPhoneNumberV2State.Error.NONE) { + presentError(fragmentState) + } + } + + initializeInputFields() + + ViewUtil.focusAndShowKeyboard(phoneNumberInputLayout) + } + + private fun initializeInputFields() { + phoneNumberInputLayout.addTextChangedListener { + // TODO [regv2]: country code as you type formatter + fragmentViewModel.setPhoneNumber(it?.toString()) + } + phoneNumberInputLayout.onFocusChangeListener = View.OnFocusChangeListener { _: View?, hasFocus: Boolean -> + if (hasFocus) { + binding.scrollView.postDelayed({ binding.scrollView.smoothScrollTo(0, binding.registerButton.bottom) }, 250) + } + } + phoneNumberInputLayout.imeOptions = EditorInfo.IME_ACTION_DONE + phoneNumberInputLayout.setOnEditorActionListener { v: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE && v != null) { + onRegistrationButtonClicked() + return@setOnEditorActionListener true + } + false + } + + spinnerView.threshold = 100 + spinnerView.setAdapter(spinnerAdapter) + spinnerView.addTextChangedListener { s -> + if (s.isNullOrEmpty()) { + return@addTextChangedListener + } + + if (s[0] != '+') { + s.insert(0, "+") + } + + fragmentViewModel.supportedCountryPrefixes.firstOrNull { it.toString() == s.toString() }?.let { + // TODO [regv2]: setCountryFormatter(it.regionCode) + fragmentViewModel.setCountry(it.digits) + val numberLength: Int = phoneNumberInputLayout.text?.length ?: 0 + phoneNumberInputLayout.setSelection(numberLength, numberLength) + } + } + } + + private fun presentRegisterButton(sharedState: RegistrationV2State) { + binding.registerButton.isEnabled = sharedState.phoneNumber != null && PhoneNumberUtil.getInstance().isValidNumber(sharedState.phoneNumber) && !sharedState.inProgress + // TODO [regv2]: always enable the button but display error dialogs if the entered phone number is invalid + } + + private fun presentError(state: EnterPhoneNumberV2State) { + when (state.error) { + EnterPhoneNumberV2State.Error.NONE -> { + Unit + } + + EnterPhoneNumberV2State.Error.INVALID_PHONE_NUMBER -> { + MaterialAlertDialogBuilder(requireContext()).apply { + setTitle(getString(R.string.RegistrationActivity_invalid_number)) + setMessage( + String.format( + getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), + state.phoneNumber + ) + ) + setPositiveButton(android.R.string.ok) { _, _ -> fragmentViewModel.clearError() } + setOnCancelListener { fragmentViewModel.clearError() } + setOnDismissListener { fragmentViewModel.clearError() } + show() + } + } + + EnterPhoneNumberV2State.Error.PLAY_SERVICES_MISSING -> { + Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + } + + EnterPhoneNumberV2State.Error.PLAY_SERVICES_NEEDS_UPDATE -> { + GoogleApiAvailability.getInstance().getErrorDialog(requireActivity(), ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED, 0)?.show() + } + + EnterPhoneNumberV2State.Error.PLAY_SERVICES_TRANSIENT -> { + Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + } + } + } + + private fun onRegistrationButtonClicked() { + ViewUtil.hideKeyboard(requireContext(), phoneNumberInputLayout) + sharedViewModel.setInProgress(true) + val hasFcm = validateFcmStatus(requireContext()) + if (hasFcm) { + sharedViewModel.uiState.observe(viewLifecycleOwner, FcmTokenRetrievedObserver()) + sharedViewModel.fetchFcmToken(requireContext()) + } else { + sharedViewModel.setInProgress(false) + // TODO [regv2]: handle if FCM isn't available + } + } + + private fun onFcmTokenRetrieved(value: RegistrationV2State) { + if (value.phoneNumber == null) { + fragmentViewModel.setError(EnterPhoneNumberV2State.Error.INVALID_PHONE_NUMBER) + sharedViewModel.setInProgress(false) + } else { + presentConfirmNumberDialog(value.phoneNumber, value.isReRegister, value.canSkipSms) + } + } + + private fun presentProgressBar(showProgress: Boolean, isReRegister: Boolean) { + if (showProgress) { + binding.registerButton.setSpinning() + } else { + binding.registerButton.cancelSpinning() + } + binding.countryCode.isEnabled = !showProgress + binding.number.isEnabled = !showProgress + binding.cancelButton.visible = !showProgress && isReRegister + } + + private fun validateFcmStatus(context: Context): Boolean { + val fcmStatus = PlayServicesUtil.getPlayServicesStatus(context) + Log.d(TAG, "Got $fcmStatus for Play Services status.") + when (fcmStatus) { + PlayServicesUtil.PlayServicesStatus.SUCCESS -> { + return true + } + + PlayServicesUtil.PlayServicesStatus.MISSING -> { + fragmentViewModel.setError(EnterPhoneNumberV2State.Error.PLAY_SERVICES_MISSING) + return false + } + + PlayServicesUtil.PlayServicesStatus.NEEDS_UPDATE -> { + fragmentViewModel.setError(EnterPhoneNumberV2State.Error.PLAY_SERVICES_NEEDS_UPDATE) + return false + } + + PlayServicesUtil.PlayServicesStatus.TRANSIENT_ERROR -> { + fragmentViewModel.setError(EnterPhoneNumberV2State.Error.PLAY_SERVICES_TRANSIENT) + return false + } + + null -> { + Log.w(TAG, "Null result received from PlayServicesUtil, marking Play Services as missing.") + fragmentViewModel.setError(EnterPhoneNumberV2State.Error.PLAY_SERVICES_MISSING) + return false + } + } + } + + private fun onConfirmNumberDialogCanceled() { + Log.d(TAG, "User canceled confirm number, returning to edit number.") + sharedViewModel.setInProgress(false) + ViewUtil.focusAndMoveCursorToEndAndOpenKeyboard(phoneNumberInputLayout) + } + + private fun presentConfirmNumberDialog(phoneNumber: PhoneNumber, isReRegister: Boolean, canSkipSms: Boolean) { + val title = if (isReRegister) { + R.string.RegistrationActivity_additional_verification_required + } else { + R.string.RegistrationActivity_phone_number_verification_dialog_title + } + + val message: CharSequence = SpannableStringBuilder().apply { + append(SpanUtil.bold(PhoneNumberFormatter.prettyPrint(phoneNumber.toE164()))) + if (!canSkipSms) { + append("\n\n") + append(getString(R.string.RegistrationActivity_a_verification_code_will_be_sent_to_this_number)) + } + } + + MaterialAlertDialogBuilder(requireContext()).apply { + setTitle(title) + setMessage(message) + setPositiveButton(android.R.string.ok) { _, _ -> + Log.d(TAG, "User confirmed number.") + sharedViewModel.onUserConfirmedPhoneNumber(requireContext()) + } + setNegativeButton(R.string.RegistrationActivity_edit_number) { _, _ -> onConfirmNumberDialogCanceled() } + setOnCancelListener { _ -> onConfirmNumberDialogCanceled() } + }.show() + } + + private fun moveToVerificationEntryScreen() { + NavHostFragment.findNavController(this).safeNavigate(EnterPhoneNumberV2FragmentDirections.actionEnterVerificationCode()) + sharedViewModel.setInProgress(false) + } + + private fun popBackStack() { + sharedViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.INITIALIZATION) + NavHostFragment.findNavController(this).popBackStack() + } + + private inner class FcmTokenRetrievedObserver : LiveDataObserverCallback(sharedViewModel.uiState) { + override fun onValue(value: RegistrationV2State): Boolean { + val fcmRetrieved = value.isFcmSupported + if (fcmRetrieved) { + onFcmTokenRetrieved(value) + } + return fcmRetrieved + } + } + + private inner class UseProxyMenuProvider : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(R.menu.enter_phone_number, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return if (menuItem.itemId == R.id.phone_menu_use_proxy) { + NavHostFragment.findNavController(this@EnterPhoneNumberV2Fragment).safeNavigate(EnterPhoneNumberV2FragmentDirections.actionEditProxy()) + true + } else { + false + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt new file mode 100644 index 0000000000..6f399443f6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.phonenumber + +/** + * State holder for the phone number entry screen, including phone number and Play Services errors. + */ +data class EnterPhoneNumberV2State(val countryPrefixIndex: Int, val phoneNumber: String, val error: Error = Error.NONE) { + + companion object { + @JvmStatic + val INIT = EnterPhoneNumberV2State(0, "") + } + + enum class Error { + NONE, + INVALID_PHONE_NUMBER, + PLAY_SERVICES_MISSING, + PLAY_SERVICES_NEEDS_UPDATE, + PLAY_SERVICES_TRANSIENT + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt new file mode 100644 index 0000000000..4a7804f67e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.phonenumber + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import com.google.i18n.phonenumbers.NumberParseException +import com.google.i18n.phonenumbers.PhoneNumberUtil +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.registration.util.CountryPrefix + +/** + * ViewModel for the phone number entry screen. + */ +class EnterPhoneNumberV2ViewModel : ViewModel() { + + private val TAG = Log.tag(EnterPhoneNumberV2ViewModel::class.java) + + private val store = MutableStateFlow(EnterPhoneNumberV2State.INIT) + val uiState = store.asLiveData() + + val supportedCountryPrefixes: List = PhoneNumberUtil.getInstance().supportedCallingCodes + .map { CountryPrefix(it, PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(it)) } + .sortedBy { it.digits.toString() } + + fun countryPrefix(): CountryPrefix { + return supportedCountryPrefixes[store.value.countryPrefixIndex] + } + + fun phoneNumber(): PhoneNumber? { + return try { + parsePhoneNumber(store.value) + } catch (ex: NumberParseException) { + Log.w(TAG, "Could not parse phone number in current state.", ex) + null + } + } + + fun setPhoneNumber(phoneNumber: String?) { + store.update { it.copy(phoneNumber = phoneNumber ?: "") } + } + + fun setCountry(digits: Int) { + val matchingIndex = countryCodeToAdapterIndex(digits) + store.update { + it.copy(countryPrefixIndex = matchingIndex) + } + } + + fun parsePhoneNumber(state: EnterPhoneNumberV2State): PhoneNumber { + return PhoneNumberUtil.getInstance().parse(state.phoneNumber, supportedCountryPrefixes[state.countryPrefixIndex].regionCode) + } + + fun isEnteredNumberValid(state: EnterPhoneNumberV2State): Boolean { + return try { + PhoneNumberUtil.getInstance().isValidNumber(parsePhoneNumber(state)) + } catch (ex: NumberParseException) { + false + } + } + + fun restoreState(value: PhoneNumber) { + val prefixIndex = countryCodeToAdapterIndex(value.countryCode) + if (prefixIndex != -1) { + store.update { + it.copy( + countryPrefixIndex = prefixIndex, + phoneNumber = value.nationalNumber.toString() + ) + } + } + } + + private fun countryCodeToAdapterIndex(countryCode: Int): Int { + return supportedCountryPrefixes.indexOfFirst { prefix -> prefix.digits == countryCode } + } + + fun clearError() { + setError(EnterPhoneNumberV2State.Error.NONE) + } + + fun setError(error: EnterPhoneNumberV2State.Error) { + store.update { + it.copy(error = error) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationCheckpoint.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationCheckpoint.kt new file mode 100644 index 0000000000..744268df98 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationCheckpoint.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.shared + +/** + * An ordered list of checkpoints of the registration process. + * This is used for screens to know when to advance, as well as restoring state after process death. + */ +enum class RegistrationCheckpoint { + INITIALIZATION, + PERMISSIONS_GRANTED, + BACKUP_DETECTED, + BACKUP_SELECTED, + BACKUP_RESTORED, + PUSH_NETWORK_AUDITED, + PHONE_NUMBER_CONFIRMED, + VERIFICATION_CODE_REQUESTED, + CHALLENGE_RECEIVED, + CHALLENGE_COMPLETED, + VERIFICATION_CODE_ENTERED, + VERIFICATION_CODE_VALIDATED, + SERVICE_REGISTRATION_COMPLETED, + LOCAL_REGISTRATION_COMPLETE +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2State.kt new file mode 100644 index 0000000000..45511acf0b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2State.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.shared + +import com.google.i18n.phonenumbers.Phonenumber + +/** + * State holder shared across all of registration. + */ +data class RegistrationV2State( + val sessionId: String? = null, + val phoneNumber: Phonenumber.PhoneNumber? = null, + val inProgress: Boolean = false, + val isReRegister: Boolean = false, + val canSkipSms: Boolean = false, + val isFcmSupported: Boolean = false, + val fcmToken: String? = null, + val nextSms: Long = 0L, + val nextCall: Long = 0L, + val registrationCheckpoint: RegistrationCheckpoint = RegistrationCheckpoint.INITIALIZATION +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2ViewModel.kt new file mode 100644 index 0000000000..e20449fe92 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2ViewModel.kt @@ -0,0 +1,221 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.shared + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import com.google.i18n.phonenumbers.NumberParseException +import com.google.i18n.phonenumbers.PhoneNumberUtil +import com.google.i18n.phonenumbers.Phonenumber +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob +import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob +import org.thoughtcrime.securesms.jobs.ProfileUploadJob +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.registration.RegistrationData +import org.thoughtcrime.securesms.registration.RegistrationUtil +import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository +import org.thoughtcrime.securesms.registration.v2.ui.toE164 +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.Util +import org.thoughtcrime.securesms.util.dualsim.MccMncProducer +import java.io.IOException + +/** + * ViewModel shared across all of registration. + */ +class RegistrationV2ViewModel : ViewModel() { + + private val store = MutableStateFlow(RegistrationV2State()) + + private val password = Util.getSecret(18) // TODO [regv2]: persist this + + val uiState = store.asLiveData() + + init { + val existingE164 = SignalStore.registrationValues().sessionE164 + if (existingE164 != null) { + try { + val existingPhoneNumber = PhoneNumberUtil.getInstance().parse(existingE164, null) + if (existingPhoneNumber != null) { + setPhoneNumber(existingPhoneNumber) + } + } catch (ex: NumberParseException) { + Log.w(TAG, "Could not parse stored E164.", ex) + } + } + } + + fun setInProgress(inProgress: Boolean) { + store.update { + it.copy(inProgress = inProgress) + } + } + + fun setRegistrationCheckpoint(checkpoint: RegistrationCheckpoint) { + store.update { + it.copy(registrationCheckpoint = checkpoint) + } + } + + fun setPhoneNumber(phoneNumber: Phonenumber.PhoneNumber?) { + store.update { + it.copy(phoneNumber = phoneNumber) + } + } + + fun fetchFcmToken(context: Context) { + viewModelScope.launch { + val fcmToken = RegistrationRepository.getFcmToken(context) + store.update { + it.copy( + registrationCheckpoint = RegistrationCheckpoint.PUSH_NETWORK_AUDITED, + isFcmSupported = true, + fcmToken = fcmToken + ) + } + } + } + + fun onUserConfirmedPhoneNumber(context: Context) { + setRegistrationCheckpoint(RegistrationCheckpoint.PHONE_NUMBER_CONFIRMED) + // TODO [regv2]: check if can skip sms flow + val state = store.value + if (state.phoneNumber == null) { + Log.w(TAG, "Phone number was null after confirmation.") + onErrorOccurred() + return + } + if (state.canSkipSms) { + Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + } else { + // TODO [regv2]: initialize Play Services sms retriever + val mccMncProducer = MccMncProducer(context) + val e164 = state.phoneNumber.toE164() + viewModelScope.launch { + val codeRequestResponse = RegistrationRepository.requestSmsCode(context, e164, password, mccMncProducer.mcc, mccMncProducer.mnc).successOrThrow() + store.update { + it.copy( + sessionId = codeRequestResponse.body.id, + nextSms = RegistrationRepository.deriveTimestamp(codeRequestResponse.headers, codeRequestResponse.body.nextSms), + nextCall = RegistrationRepository.deriveTimestamp(codeRequestResponse.headers, codeRequestResponse.body.nextCall), + registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_REQUESTED + ) + } + } + } + } + + fun verifyCodeWithoutRegistrationLock(context: Context, code: String) { + store.update { + it.copy( + inProgress = true, + registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_ENTERED + ) + } + + val sessionId = store.value.sessionId + if (sessionId == null) { + Log.w(TAG, "Session ID was null. TODO: handle this better in the UI.") + return + } + val e164: String = getCurrentE164() ?: throw IllegalStateException() + + viewModelScope.launch { + val registrationData = getRegistrationData(code) + val verificationResponse = RegistrationRepository.submitVerificationCode(context, e164, password, sessionId, registrationData).successOrThrow() + + if (!verificationResponse.body.verified) { + Log.w(TAG, "Could not verify code!") + // TODO [regv2]: error handling + return@launch + } + + setRegistrationCheckpoint(RegistrationCheckpoint.VERIFICATION_CODE_VALIDATED) + + val registrationResponse = RegistrationRepository.registerAccount(context, e164, password, sessionId, registrationData).successOrThrow() + + localRegisterAccount(context, registrationData, registrationResponse, false) + + refreshFeatureFlags() + + store.update { + it.copy( + registrationCheckpoint = RegistrationCheckpoint.SERVICE_REGISTRATION_COMPLETED + ) + } + } + } + + fun hasPin(): Boolean { + return RegistrationRepository.hasPin() || store.value.isReRegister + } + + fun completeRegistration() { + ApplicationDependencies.getJobManager() + .startChain(ProfileUploadJob()) + .then(listOf(MultiDeviceProfileKeyUpdateJob(), MultiDeviceProfileContentUpdateJob())) + .enqueue() + RegistrationUtil.maybeMarkRegistrationComplete() + } + + private fun getCurrentE164(): String? { + return store.value.phoneNumber?.toE164() + } + + private suspend fun localRegisterAccount( + context: Context, + registrationData: RegistrationData, + remoteResult: RegistrationRepository.AccountRegistrationResult, + reglockEnabled: Boolean + ) { + RegistrationRepository.registerAccountLocally(context, registrationData, remoteResult, reglockEnabled) + } + + private suspend fun getRegistrationData(code: String): RegistrationData { + val e164: String = getCurrentE164() ?: throw IllegalStateException() + return RegistrationData( + code, + e164, + password, + RegistrationRepository.getRegistrationId(), + RegistrationRepository.getProfileKey(e164), + store.value.fcmToken, + RegistrationRepository.getPniRegistrationId(), + null // TODO [regv2]: recovery password + ) + } + + /** + * This is a generic error UI handler that re-enables the UI so that the user can recover from errors. + * Do not forget to log any errors when calling this method! + */ + private fun onErrorOccurred() { + setInProgress(false) + } + + companion object { + private val TAG = Log.tag(RegistrationV2ViewModel::class.java) + + private suspend fun refreshFeatureFlags() = withContext(Dispatchers.IO) { + val startTime = System.currentTimeMillis() + try { + FeatureFlags.refreshSync() + Log.i(TAG, "Took " + (System.currentTimeMillis() - startTime) + " ms to get feature flags.") + } catch (e: IOException) { + Log.w(TAG, "Failed to refresh flags after " + (System.currentTimeMillis() - startTime) + " ms.", e) + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt new file mode 100644 index 0000000000..1abb0512ba --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.welcome + +import android.Manifest +import android.content.pm.PackageManager +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.core.content.ContextCompat +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.NavHostFragment +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.LoggingFragment +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.databinding.FragmentRegistrationWelcomeV2Binding +import org.thoughtcrime.securesms.permissions.Permissions +import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView +import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions +import org.thoughtcrime.securesms.registration.v2.ui.grantpermissions.GrantPermissionsV2Fragment +import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel +import org.thoughtcrime.securesms.util.BackupUtil +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.Util +import org.thoughtcrime.securesms.util.navigation.safeNavigate +import kotlin.jvm.optionals.getOrNull + +/** + * First screen that is displayed on the very first app launch. + */ +class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome_v2) { + private val TAG = Log.tag(WelcomeV2Fragment::class.java) + private val sharedViewModel by activityViewModels() + private val binding: FragmentRegistrationWelcomeV2Binding by ViewBinderDelegate(FragmentRegistrationWelcomeV2Binding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + maybePrefillE164() + setDebugLogSubmitMultiTapView(binding.image) + setDebugLogSubmitMultiTapView(binding.title) + binding.welcomeContinueButton.setOnClickListener { onContinueClicked() } + binding.welcomeTermsButton.setOnClickListener { onTermsClicked() } + binding.welcomeTransferOrRestore.setOnClickListener { onRestoreFromBackupClicked() } + } + + private fun onContinueClicked() { + TextSecurePreferences.setHasSeenWelcomeScreen(requireContext(), true) + if (Permissions.isRuntimePermissionsRequired() && !hasAllPermissions()) { + NavHostFragment.findNavController(this).safeNavigate(WelcomeV2FragmentDirections.actionWelcomeFragmentToGrantPermissionsV2Fragment(GrantPermissionsV2Fragment.WelcomeAction.CONTINUE)) + } else { + skipRestore() + } + } + + private fun hasAllPermissions(): Boolean { + val isUserSelectionRequired = BackupUtil.isUserSelectionRequired(requireContext()) + return WelcomePermissions.getWelcomePermissions(isUserSelectionRequired).all { ContextCompat.checkSelfPermission(requireContext(), it) == PackageManager.PERMISSION_GRANTED } + } + + private fun skipRestore() { + NavHostFragment.findNavController(this).safeNavigate(WelcomeV2FragmentDirections.actionSkipRestore()) + } + + private fun onRestoreFromBackupClicked() { + Toast.makeText(requireContext(), "Not yet implemented.", Toast.LENGTH_SHORT).show() + } + + private fun onTermsClicked() { + Toast.makeText(requireContext(), "Not yet implemented.", Toast.LENGTH_SHORT).show() + } + + private fun maybePrefillE164() { + if (Permissions.hasAll(requireContext(), Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_PHONE_NUMBERS)) { + val localNumber = Util.getDeviceNumber(requireContext()).getOrNull() + + if (localNumber != null) { + Log.v(TAG, "Phone number detected.") + sharedViewModel.setPhoneNumber(localNumber) + } else { + Log.i(TAG, "Could not read phone number.") + } + } else { + Log.i(TAG, "No phone permission.") + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 08f450dff4..b5baf4fb4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -90,9 +90,9 @@ public final class FeatureFlags { private static final String CAMERAX_MODEL_BLOCKLIST = "android.cameraXModelBlockList"; private static final String CAMERAX_MIXED_MODEL_BLOCKLIST = "android.cameraXMixedModelBlockList"; private static final String PAYMENTS_REQUEST_ACTIVATE_FLOW = "android.payments.requestActivateFlow"; - public static final String GOOGLE_PAY_DISABLED_REGIONS = "global.donations.gpayDisabledRegions"; - public static final String CREDIT_CARD_DISABLED_REGIONS = "global.donations.ccDisabledRegions"; - public static final String PAYPAL_DISABLED_REGIONS = "global.donations.paypalDisabledRegions"; + public static final String GOOGLE_PAY_DISABLED_REGIONS = "global.donations.gpayDisabledRegions"; + public static final String CREDIT_CARD_DISABLED_REGIONS = "global.donations.ccDisabledRegions"; + public static final String PAYPAL_DISABLED_REGIONS = "global.donations.paypalDisabledRegions"; private static final String CDS_HARD_LIMIT = "android.cds.hardLimit"; private static final String PAYPAL_ONE_TIME_DONATIONS = "android.oneTimePayPalDonations.2"; private static final String PAYPAL_RECURRING_DONATIONS = "android.recurringPayPalDonations.3"; @@ -104,15 +104,15 @@ public final class FeatureFlags { private static final String SVR2_KILLSWITCH = "android.svr2.killSwitch"; private static final String CDS_DISABLE_COMPAT_MODE = "cds.disableCompatibilityMode"; private static final String FCM_MAY_HAVE_MESSAGES_KILL_SWITCH = "android.fcmNotificationFallbackKillSwitch"; - public static final String PROMPT_FOR_NOTIFICATION_LOGS = "android.logs.promptNotifications"; + public static final String PROMPT_FOR_NOTIFICATION_LOGS = "android.logs.promptNotifications"; private static final String PROMPT_FOR_NOTIFICATION_CONFIG = "android.logs.promptNotificationsConfig"; - public static final String PROMPT_BATTERY_SAVER = "android.promptBatterySaver"; - public static final String INSTANT_VIDEO_PLAYBACK = "android.instantVideoPlayback.1"; - public static final String CRASH_PROMPT_CONFIG = "android.crashPromptConfig"; + public static final String PROMPT_BATTERY_SAVER = "android.promptBatterySaver"; + public static final String INSTANT_VIDEO_PLAYBACK = "android.instantVideoPlayback.1"; + public static final String CRASH_PROMPT_CONFIG = "android.crashPromptConfig"; private static final String SEPA_DEBIT_DONATIONS = "android.sepa.debit.donations.5"; private static final String IDEAL_DONATIONS = "android.ideal.donations.5"; - public static final String IDEAL_ENABLED_REGIONS = "global.donations.idealEnabledRegions"; - public static final String SEPA_ENABLED_REGIONS = "global.donations.sepaEnabledRegions"; + public static final String IDEAL_ENABLED_REGIONS = "global.donations.idealEnabledRegions"; + public static final String SEPA_ENABLED_REGIONS = "global.donations.sepaEnabledRegions"; private static final String CALLING_REACTIONS = "android.calling.reactions"; private static final String NOTIFICATION_THUMBNAIL_BLOCKLIST = "android.notificationThumbnailProductBlocklist"; private static final String CALLING_RAISE_HAND = "android.calling.raiseHand"; @@ -127,6 +127,7 @@ public final class FeatureFlags { private static final String LINKED_DEVICE_LIFESPAN_SECONDS = "android.linkedDeviceLifespanSeconds"; private static final String MESSAGE_BACKUPS = "android.messageBackups"; private static final String CAMERAX_CUSTOM_CONTROLLER = "android.cameraXCustomController"; + private static final String REGISTRATION_V2 = "android.registration.v2"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -209,7 +210,7 @@ public final class FeatureFlags { ); @VisibleForTesting - static final Set NOT_REMOTE_CAPABLE = SetUtil.newHashSet(MESSAGE_BACKUPS); + static final Set NOT_REMOTE_CAPABLE = SetUtil.newHashSet(MESSAGE_BACKUPS, REGISTRATION_V2); /** * Values in this map will take precedence over any value. This should only be used for local @@ -741,6 +742,11 @@ public static boolean customCameraXController() { return getBoolean(CAMERAX_CUSTOM_CONTROLLER, false); } + /** Whether or not to use the V2 refactor of registration. */ + public static boolean registrationV2() { + return getBoolean(REGISTRATION_V2, false); + } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/livedata/LiveDataObserverCallback.kt b/app/src/main/java/org/thoughtcrime/securesms/util/livedata/LiveDataObserverCallback.kt new file mode 100644 index 0000000000..8f6caff24b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/livedata/LiveDataObserverCallback.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.util.livedata + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer + +/** + * A wrapper class that can be implemented in order to create a [LiveData] [Observer] that cleans up after itself. + * + * Useful for one-shot observers that can be executed as a callback on an asynchronous call that updates a [LiveData] upon completion. + */ +abstract class LiveDataObserverCallback(private val liveData: LiveData) : Observer { + final override fun onChanged(value: T) { + val shouldRemove = onValue(value) + if (shouldRemove) { + liveData.removeObserver(this) + } + } + + /** + * The body of the observer that gets executed when the value is changed. + * Recommended usage is to check some condition in the [LiveData] to determine whether the data has been handled and therefore can be removed. + * + * @return should remove this observer from the [LiveData] + */ + abstract fun onValue(value: T): Boolean +} diff --git a/app/src/main/res/layout/activity_registration_navigation_v2.xml b/app/src/main/res/layout/activity_registration_navigation_v2.xml new file mode 100644 index 0000000000..37a71416cd --- /dev/null +++ b/app/src/main/res/layout/activity_registration_navigation_v2.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_registration_enter_code_v2.xml b/app/src/main/res/layout/fragment_registration_enter_code_v2.xml new file mode 100644 index 0000000000..3d30f729dd --- /dev/null +++ b/app/src/main/res/layout/fragment_registration_enter_code_v2.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_registration_enter_phone_number_v2.xml b/app/src/main/res/layout/fragment_registration_enter_phone_number_v2.xml new file mode 100644 index 0000000000..fea95edc37 --- /dev/null +++ b/app/src/main/res/layout/fragment_registration_enter_phone_number_v2.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_registration_welcome_v2.xml b/app/src/main/res/layout/fragment_registration_welcome_v2.xml new file mode 100644 index 0000000000..5f5a350be4 --- /dev/null +++ b/app/src/main/res/layout/fragment_registration_welcome_v2.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/registration_v2.xml b/app/src/main/res/navigation/registration_v2.xml new file mode 100644 index 0000000000..9d363338d1 --- /dev/null +++ b/app/src/main/res/navigation/registration_v2.xml @@ -0,0 +1,492 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java index 9e2e8ef7f1..801615e3d5 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java @@ -13,7 +13,6 @@ import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.signal.libsignal.protocol.logging.Log; -import org.signal.libsignal.protocol.state.SignedPreKeyRecord; import org.signal.libsignal.usernames.BaseUsernameException; import org.signal.libsignal.usernames.Username; import org.signal.libsignal.usernames.Username.UsernameLink; @@ -47,6 +46,7 @@ import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; +import org.whispersystems.signalservice.api.registration.RegistrationApi; import org.whispersystems.signalservice.api.services.CdsiV2Service; import org.whispersystems.signalservice.api.storage.SignalStorageCipher; import org.whispersystems.signalservice.api.storage.SignalStorageManifest; @@ -219,7 +219,7 @@ public void requestRegistrationPushChallenge(String sessionId, String gcmRegistr public ServiceResponse createRegistrationSession(@Nullable String fcmToken, @Nullable String mcc, @Nullable String mnc) { try { - final RegistrationSessionMetadataResponse response = pushServiceSocket.createVerificationSession(fcmToken, mcc, mnc); + final RegistrationSessionMetadataResponse response = pushServiceSocket.createVerificationSession(fcmToken, mcc, mnc); return ServiceResponse.forResult(response, 200, null); } catch (IOException e) { return ServiceResponse.forUnknownError(e); @@ -311,6 +311,10 @@ public ServiceResponse verifyAccount(@Nonnu } } + public @Nonnull VerifyAccountResponse registerAccountV2(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollection aciPreKeys, PreKeyCollection pniPreKeys, String fcmToken, boolean skipDeviceTransfer) throws IOException { + return pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, aciPreKeys, pniPreKeys, fcmToken, skipDeviceTransfer); + } + public @Nonnull ServiceResponse changeNumber(@Nonnull ChangePhoneNumberRequest changePhoneNumberRequest) { try { VerifyAccountResponse response = this.pushServiceSocket.changeNumber(changePhoneNumberRequest); @@ -870,6 +874,10 @@ public KeysApi getKeysApi() { return KeysApi.create(pushServiceSocket); } + public RegistrationApi getRegistrationApi() { + return new RegistrationApi(pushServiceSocket); + } + public AuthCredentials getPaymentsAuthorization() throws IOException { return pushServiceSocket.getPaymentsAuthorization(); } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt new file mode 100644 index 0000000000..80dce4bc2e --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.registration + +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.account.AccountAttributes +import org.whispersystems.signalservice.api.account.PreKeyCollection +import org.whispersystems.signalservice.internal.push.PushServiceSocket +import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse +import org.whispersystems.signalservice.internal.push.VerifyAccountResponse +import java.util.Locale + +/** + * Class to interact with various registration-related endpoints. + */ +class RegistrationApi( + private val pushServiceSocket: PushServiceSocket +) { + + /** + * Request that the service initialize a new registration session. + */ + fun createRegistrationSession(fcmToken: String?, mcc: String?, mnc: String?): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.createVerificationSession(fcmToken, mcc, mnc) + } + } + + /** + * Submit an FCM token to the service as proof that this is an honest user attempting to register. + */ + fun submitPushChallengeToken(sessionId: String?, pushChallengeToken: String?): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.patchVerificationSession(sessionId, null, null, null, null, pushChallengeToken) + } + } + + /** + * Request an SMS verification code. On success, the server will send + * an SMS verification code to this Signal user. + * + * @param androidSmsRetrieverSupported whether the system framework will automatically parse the incoming verification message. + */ + fun requestSmsVerificationCode(sessionId: String?, locale: Locale?, androidSmsRetrieverSupported: Boolean): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.requestVerificationCode(sessionId, locale, androidSmsRetrieverSupported, PushServiceSocket.VerificationCodeTransport.SMS) + } + } + + /** + * Submit a verification code sent by the service via one of the supported channels (SMS, phone call) to prove the registrant's control of the phone number. + */ + fun verifyAccount(verificationCode: String, sessionId: String): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.submitVerificationCode(sessionId, verificationCode) + } + } + + /** + * Submit the cryptographic assets required for an account to use the service. + */ + fun registerAccount(sessionId: String?, recoveryPassword: String?, attributes: AccountAttributes?, aciPreKeys: PreKeyCollection?, pniPreKeys: PreKeyCollection?, fcmToken: String?, skipDeviceTransfer: Boolean): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, aciPreKeys, pniPreKeys, fcmToken, skipDeviceTransfer) + } + } +} diff --git a/video/lib/src/main/java/org/thoughtcrime/securesms/video/videoconverter/MediaConverter.java b/video/lib/src/main/java/org/thoughtcrime/securesms/video/videoconverter/MediaConverter.java index 7d780d50ea..0b41c96c40 100644 --- a/video/lib/src/main/java/org/thoughtcrime/securesms/video/videoconverter/MediaConverter.java +++ b/video/lib/src/main/java/org/thoughtcrime/securesms/video/videoconverter/MediaConverter.java @@ -45,7 +45,7 @@ @SuppressWarnings("WeakerAccess") public final class MediaConverter { private static final String TAG = "media-converter"; - private static final boolean VERBOSE = false; // lots of logging + private static final boolean VERBOSE = true; // lots of logging // Describes when the annotation will be discarded @Retention(RetentionPolicy.SOURCE) From 89eeae36c47997c04b87cac078a47846938e3c18 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 16:28:45 -0400 Subject: [PATCH 007/113] Fix signed int overflow in disappearing timer UI message. --- .../securesms/database/model/GroupsV2UpdateMessageConverter.kt | 2 +- .../securesms/database/model/GroupsV2UpdateMessageProducer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt index a619abe627..fba1a8f4a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt @@ -384,7 +384,7 @@ object GroupsV2UpdateMessageConverter { updates.add( GroupChangeChatUpdate.Update( groupExpirationTimerUpdate = GroupExpirationTimerUpdate( - expiresInMs = change.newTimer!!.duration * 1000, + expiresInMs = (change.newTimer!!.duration * 1000L).toUInt().toInt(), updaterAci = if (editorUnknown) null else change.editorServiceIdBytes ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java index 55f2bdf93f..dd47adc0e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java @@ -234,7 +234,7 @@ private void describeGroupSelfInvitationRevokedUpdate(@NonNull GroupSelfInvitati } } private void describeGroupExpirationTimerUpdate(@NonNull GroupExpirationTimerUpdate update, @NonNull List updates) { - final int duration = update.expiresInMs / 1000; + final int duration = Math.toIntExact(Integer.toUnsignedLong(update.expiresInMs) / 1000); String time = ExpirationUtil.getExpirationDisplayValue(context, duration); if (update.updaterAci == null) { updates.add(updateDescription(context.getString(R.string.MessageRecord_disappearing_message_time_set_to_s, time), R.drawable.ic_update_timer_16)); From 5416c3b8aafee802494b9708f81108c669095085 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 17:08:01 -0400 Subject: [PATCH 008/113] Improve play button display logic on video editor fragment. --- .../mediasend/VideoEditorFragment.kt | 49 +++++++++---------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt index ae87d4241b..aaad75d11d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt @@ -13,13 +13,13 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionState import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel import org.thoughtcrime.securesms.mediasend.v2.videos.VideoTrimData import org.thoughtcrime.securesms.mms.MediaConstraints import org.thoughtcrime.securesms.mms.VideoSlide import org.thoughtcrime.securesms.scribbles.VideoEditorPlayButtonLayout import org.thoughtcrime.securesms.util.Throttler +import org.thoughtcrime.securesms.util.visible import org.thoughtcrime.securesms.video.VideoPlayer import org.thoughtcrime.securesms.video.VideoPlayer.PlayerCallback import org.thoughtcrime.securesms.video.videoconverter.VideoThumbnailsRangeSelectorView @@ -32,7 +32,6 @@ class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragm private val videoScanThrottle = Throttler(150) private val handler = Handler(Looper.getMainLooper()) - private var canEdit = false private var isVideoGif = false private var isInEdit = false private var isFocused = false @@ -87,6 +86,8 @@ class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragm player.setWindow(requireActivity().window) player.setVideoSource(slide, isVideoGif, TAG) + hud.visible = !slide.isVideoGif + if (slide.isVideoGif) { player.setPlayerCallback(object : PlayerCallback { override fun onPlaying() { @@ -101,14 +102,8 @@ class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragm }) player.hideControls() player.loopForever() + player.play() } else { - if (MediaConstraints.isVideoTranscodeAvailable()) { - sharedViewModel.state.value?.let { state -> - bindVideoTimeline(state) - } - } else { - hud.visibility = View.VISIBLE - } hud.setPlayClickListener { player.play() } @@ -139,17 +134,22 @@ class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragm sharedViewModel.state.observe(viewLifecycleOwner) { incomingState -> val focusedUri = incomingState.focusedMedia?.uri val currentlyFocused = focusedUri != null && focusedUri == uri - if (MediaConstraints.isVideoTranscodeAvailable() && canEdit) { + if (MediaConstraints.isVideoTranscodeAvailable()) { if (currentlyFocused) { - if (!isFocused) { - bindVideoTimeline(incomingState) + if (isVideoGif) { + player.play() } else { - val videoTrimData = if (focusedUri != null) { - incomingState.getOrCreateVideoTrimData(focusedUri) + if (!isFocused) { + bindVideoTimeline(incomingState.getOrCreateVideoTrimData(uri)) } else { - VideoTrimData() + val videoTrimData = if (focusedUri != null) { + incomingState.getOrCreateVideoTrimData(focusedUri) + } else { + VideoTrimData() + } + hud.visible = incomingState.isTouchEnabled && !isVideoGif + onEditVideoDuration(videoTrimData, incomingState.isTouchEnabled) } - onEditVideoDuration(videoTrimData, incomingState.isTouchEnabled) } } else { stopPositionUpdates() @@ -161,22 +161,15 @@ class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragm } @RequiresApi(23) - private fun bindVideoTimeline(state: MediaSelectionState) { - val uri = state.focusedMedia?.uri ?: return - if (uri != this.uri) { - return - } - + private fun bindVideoTimeline(data: VideoTrimData) { val autoplay = isVideoGif val slide = VideoSlide(requireContext(), uri, 0, autoplay) - val data = state.getOrCreateVideoTrimData(uri) if (data.isDurationEdited) { player.clip(data.startTimeUs, data.endTimeUs, autoplay) } - if (slide.hasVideo()) { - canEdit = true + if (slide.hasVideo() && !autoplay) { try { videoTimeLine.registerPlayerDragListener(this) @@ -258,8 +251,6 @@ class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragm @RequiresApi(23) private fun onEditVideoDuration(data: VideoTrimData, editingComplete: Boolean) { - hud.hidePlayButton() - if (editingComplete) { isInEdit = false videoScanThrottle.clear() @@ -268,6 +259,10 @@ class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragm wasPlayingBeforeEdit = player.isPlaying } + if (wasPlayingBeforeEdit) { + hud.hidePlayButton() + } + videoScanThrottle.publish { player.pause() if (!editingComplete) { From 8f1722c718492db13f887a7b19c33944c004a8a4 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 17:11:35 -0400 Subject: [PATCH 009/113] Update placeholder label for view once media. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 968334bb71..864f8c561e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4897,7 +4897,7 @@ Add a message Add a reply Send to - View once message + View once media One or more items were too large One or more items were invalid Too many items selected From d36b2a23f57d6b40ce70eee36051d44193e28c4b Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 17:18:03 -0400 Subject: [PATCH 010/113] Hide irrelevant rows in self about sheet. --- .../recipients/ui/about/AboutSheet.kt | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) 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 ac30b404a9..ee0494f507 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 @@ -218,7 +218,7 @@ private fun Content( ) } - if (model.verified) { + if (!model.isSelf && model.verified) { AboutRow( startIcon = painterResource(id = R.drawable.check), text = stringResource(id = R.string.AboutSheet__verified), @@ -227,24 +227,26 @@ private fun Content( ) } - if (model.profileSharing || model.systemContact) { - AboutRow( - startIcon = painterResource(id = R.drawable.symbol_connections_24), - text = stringResource(id = R.string.AboutSheet__signal_connection), - endIcon = painterResource(id = R.drawable.symbol_chevron_right_compact_bold_16), - modifier = Modifier.align(alignment = Alignment.Start), - onClick = onClickSignalConnections - ) - } else { - AboutRow( - startIcon = painterResource(id = R.drawable.chat_x), - text = stringResource(id = R.string.AboutSheet__no_direct_message, model.shortName), - modifier = Modifier.align(alignment = Alignment.Start), - onClick = onClickSignalConnections - ) + if (!model.isSelf) { + if (model.profileSharing || model.systemContact) { + AboutRow( + startIcon = painterResource(id = R.drawable.symbol_connections_24), + text = stringResource(id = R.string.AboutSheet__signal_connection), + endIcon = painterResource(id = R.drawable.symbol_chevron_right_compact_bold_16), + modifier = Modifier.align(alignment = Alignment.Start), + onClick = onClickSignalConnections + ) + } else { + AboutRow( + startIcon = painterResource(id = R.drawable.chat_x), + text = stringResource(id = R.string.AboutSheet__no_direct_message, model.shortName), + modifier = Modifier.align(alignment = Alignment.Start), + onClick = onClickSignalConnections + ) + } } - if (model.systemContact) { + if (!model.isSelf && model.systemContact) { AboutRow( startIcon = painterResource(id = R.drawable.symbol_person_circle_24), text = stringResource(id = R.string.AboutSheet__s_is_in_your_system_contacts, model.shortName), @@ -260,23 +262,25 @@ private fun Content( ) } - val groupsInCommonText = if (model.groupsInCommon > 0) { - pluralStringResource(id = R.plurals.AboutSheet__d_groups_in, model.groupsInCommon, model.groupsInCommon) - } else { - stringResource(id = R.string.AboutSheet__you_have_no_groups_in_common) - } + if (!model.isSelf) { + val groupsInCommonText = if (model.groupsInCommon > 0) { + pluralStringResource(id = R.plurals.AboutSheet__d_groups_in, model.groupsInCommon, model.groupsInCommon) + } else { + stringResource(id = R.string.AboutSheet__you_have_no_groups_in_common) + } - val groupsInCommonIcon = if (!model.profileSharing && model.groupsInCommon == 0) { - painterResource(R.drawable.symbol_error_circle_24) - } else { - painterResource(R.drawable.symbol_group_24) - } + val groupsInCommonIcon = if (!model.profileSharing && model.groupsInCommon == 0) { + painterResource(R.drawable.symbol_error_circle_24) + } else { + painterResource(R.drawable.symbol_group_24) + } - AboutRow( - startIcon = groupsInCommonIcon, - text = groupsInCommonText, - modifier = Modifier.fillMaxWidth() - ) + AboutRow( + startIcon = groupsInCommonIcon, + text = groupsInCommonText, + modifier = Modifier.fillMaxWidth() + ) + } if (model.note.isNotBlank()) { AboutRow( From 29b3f09d8a31baa6b560de0f8287b495f86e8f39 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 17:49:37 -0400 Subject: [PATCH 011/113] Catch possible ISE at end of re-registration. --- .../fragments/ReRegisterWithPinFragment.kt | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/ReRegisterWithPinFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/ReRegisterWithPinFragment.kt index 6069288b7c..bab50b5ccb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/ReRegisterWithPinFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/ReRegisterWithPinFragment.kt @@ -7,6 +7,7 @@ import android.view.inputmethod.EditorInfo import android.widget.Toast import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.navigation.Navigation import androidx.navigation.fragment.findNavController import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.signal.core.util.concurrent.LifecycleDisposable @@ -119,7 +120,30 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.pin_restore_entry_fra .subscribe { processor -> if (processor.hasResult()) { Log.i(TAG, "Successfully re-registered via skip flow") - findNavController().safeNavigate(R.id.action_reRegisterWithPinFragment_to_registrationCompletePlaceHolderFragment) + try { + findNavController().safeNavigate(R.id.action_reRegisterWithPinFragment_to_registrationCompletePlaceHolderFragment) + return@subscribe + } catch (ise: IllegalStateException) { + Log.w(TAG, "Could not get parent activity fragment manager!") + } + + try { + val hostActivity = activity + if (hostActivity != null) { + Navigation.findNavController(hostActivity, R.id.nav_host_fragment).safeNavigate(R.id.action_reRegisterWithPinFragment_to_registrationCompletePlaceHolderFragment) + return@subscribe + } else { + Log.w(TAG, "Could not get parent activity!") + } + } catch (ise: IllegalStateException) { + Log.w(TAG, "Could not find navigation host fragment!") + } + + activity?.let { + Log.w(TAG, "Could not navigate to registration complete. Finishing activity gracefully.") + it.finish() + } + return@subscribe } From a83abaca1dc0ec08c729f7762305b3461620778a Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 12 Apr 2024 18:13:45 -0400 Subject: [PATCH 012/113] Order story viewer names alphabetically. --- .../securesms/stories/viewer/views/StoryViewsFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt index f04c6bd4d4..5fcdbca46f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt @@ -75,7 +75,7 @@ class StoryViewsFragment : private fun getConfiguration(state: StoryViewsState): DSLConfiguration { return configure { - state.views.forEach { storyViewItemData -> + state.views.sortedBy { it.recipient.getDisplayName(requireContext()) }.forEach { storyViewItemData -> customPref( StoryViewItem.Model( storyViewItemData = storyViewItemData, From 1b7784b01ff3eb0168930dad06880a957989fc20 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 15 Apr 2024 13:56:20 -0300 Subject: [PATCH 013/113] Update call strings to align with new designs. --- .../securesms/backup/v2/ImportExportTest.kt | 71 ++++++- .../v2/database/ChatItemExportIterator.kt | 14 +- .../v2/database/ChatItemImportInserter.kt | 5 + .../securesms/calls/log/CallLogAdapter.kt | 16 +- .../preferences/CallPreference.kt | 33 +++- .../conversation/ConversationUpdateItem.java | 12 +- .../securesms/database/CallTable.kt | 183 +++++++++++++++--- .../securesms/database/MessageTable.kt | 56 +++++- .../securesms/database/ThreadBodyUtil.java | 11 +- .../helpers/SignalDatabaseMigrations.kt | 6 +- ...lUserJoinedStateAndGroupCallActiveState.kt | 30 +++ .../model/GroupCallUpdateDetailsUtil.java | 67 ++++++- .../model/GroupCallUpdateMessageFactory.java | 47 +++-- .../database/model/MmsMessageRecord.java | 12 +- app/src/main/protowire/Backup.proto | 8 + app/src/main/protowire/Database.proto | 13 +- app/src/main/res/values/strings.xml | 73 ++++--- 17 files changed, 534 insertions(+), 123 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V225_AddLocalUserJoinedStateAndGroupCallActiveState.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt index 12acc4f3f9..59134e6eca 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt @@ -9,6 +9,7 @@ import android.Manifest import android.app.UiAutomation import android.os.Environment import androidx.test.platform.app.InstrumentationRegistry +import io.mockk.InternalPlatformDsl.toArray import okio.ByteString.Companion.toByteString import org.junit.Assert import org.junit.Before @@ -23,6 +24,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.AccountData import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo import org.thoughtcrime.securesms.backup.v2.proto.BodyRange import org.thoughtcrime.securesms.backup.v2.proto.Call +import org.thoughtcrime.securesms.backup.v2.proto.CallChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.Chat import org.thoughtcrime.securesms.backup.v2.proto.ChatItem import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage @@ -32,6 +34,8 @@ import org.thoughtcrime.securesms.backup.v2.proto.ExpirationTimerChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.FilePointer import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.proto.Group +import org.thoughtcrime.securesms.backup.v2.proto.GroupCallChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.IndividualCallChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.MessageAttachment import org.thoughtcrime.securesms.backup.v2.proto.ProfileChangeChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.Quote @@ -668,12 +672,62 @@ class ImportExportTest { ) } + var sentTime = 0L + val individualCallChatItems = individualCalls.map { call -> + ChatItem( + chatId = 1, + authorId = selfRecipient.id, + dateSent = sentTime++, + sms = false, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = sentTime + 1, + dateServerSent = sentTime, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + callingMessage = CallChatUpdate( + callMessage = IndividualCallChatUpdate( + type = IndividualCallChatUpdate.Type.INCOMING_AUDIO_CALL + ) + ) + ) + ) + }.toTypedArray() + + val startedAci = TestRecipientUtils.nextAci().toByteString() + val groupCallChatItems = groupCalls.map { call -> + ChatItem( + chatId = 1, + authorId = selfRecipient.id, + dateSent = sentTime++, + sms = false, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = sentTime + 1, + dateServerSent = sentTime, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + callingMessage = CallChatUpdate( + groupCall = GroupCallChatUpdate( + startedCallAci = startedAci, + startedCallTimestamp = 0, + endedCallTimestamp = 0, + localUserJoined = GroupCallChatUpdate.LocalUserJoined.JOINED, + inCallAcis = emptyList() + ) + ) + ) + ) + }.toTypedArray() + importExport( *standardFrames, Recipient( id = 3, contact = Contact( - aci = TestRecipientUtils.nextAci().toByteString(), + aci = startedAci, pni = TestRecipientUtils.nextPni().toByteString(), username = "cool.01", e164 = 141255501234, @@ -698,8 +752,21 @@ class ImportExportTest { name = "Cool test group" ) ), + Chat( + id = 1, + recipientId = 3, + archived = true, + pinnedOrder = 1, + expirationTimerMs = 1.days.inWholeMilliseconds, + muteUntilMs = System.currentTimeMillis(), + markedUnread = true, + dontNotifyForMentionsIfMuted = true, + wallpaper = null + ), *individualCalls.toArray(), - *groupCalls.toArray() + *groupCalls.toArray(), + *individualCallChatItems, + *groupCallChatItems ) } 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 b1cd0e8f25..f6646be505 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 @@ -200,6 +200,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } } MessageTypes.isCallLog(record.type) -> { + builder.sms = false val call = calls.getCallByMessageId(record.id) if (call != null) { builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callId = call.callId)) @@ -232,12 +233,23 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: .withoutNulls() .map { obj: UUID? -> ACI.from(obj!!).toByteString() } .toList() + + val localUserJoined: GroupCallChatUpdate.LocalUserJoined = if (groupCallUpdateDetails.localUserJoined) { + GroupCallChatUpdate.LocalUserJoined.JOINED + } else if (groupCallUpdateDetails.endedCallTimestamp == 0L) { + GroupCallChatUpdate.LocalUserJoined.UNKNOWN + } else { + GroupCallChatUpdate.LocalUserJoined.DID_NOT_JOIN + } + builder.updateMessage = ChatUpdateMessage( callingMessage = CallChatUpdate( groupCall = GroupCallChatUpdate( startedCallAci = ACI.from(UuidUtil.parseOrThrow(groupCallUpdateDetails.startedCallUuid)).toByteString(), startedCallTimestamp = groupCallUpdateDetails.startedCallTimestamp, - inCallAcis = joinedMembers + inCallAcis = joinedMembers, + localUserJoined = localUserJoined, + endedCallTimestamp = groupCallUpdateDetails.endedCallTimestamp ) ) ) 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 0e74e35538..12337a2920 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 @@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet import org.thoughtcrime.securesms.database.documents.NetworkFailure import org.thoughtcrime.securesms.database.documents.NetworkFailureSet +import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil import org.thoughtcrime.securesms.database.model.Mention import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList import org.thoughtcrime.securesms.database.model.databaseprotos.GV2UpdateDescription @@ -460,6 +461,10 @@ class ChatItemImportInserter( IndividualCallChatUpdate.Type.UNKNOWN -> typeFlags } } + updateMessage.callingMessage.groupCall != null -> { + typeFlags = MessageTypes.GROUP_CALL_TYPE + this.put(MessageTable.BODY, GroupCallUpdateDetailsUtil.createBodyFromBackup(updateMessage.callingMessage.groupCall)) + } } // Calls don't use the incoming/outgoing flags, so we overwrite the flags here this.put(MessageTable.TYPE, typeFlags) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt index da4557f780..488ce8ed0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt @@ -305,7 +305,7 @@ class CallLogAdapter( val color = ContextCompat.getColor( context, - if (call.record.event.isMissedCall()) { + if (call.record.isDisplayedAsMissedCallInUi) { R.color.signal_colorError } else { R.color.signal_colorOnSurfaceVariant @@ -371,11 +371,11 @@ class CallLogAdapter( private fun getCallStateDrawableRes(call: CallTable.Call): Int { return when (call.messageType) { MessageTypes.MISSED_VIDEO_CALL_TYPE, MessageTypes.MISSED_AUDIO_CALL_TYPE -> R.drawable.symbol_missed_incoming_compact_16 - MessageTypes.INCOMING_AUDIO_CALL_TYPE, MessageTypes.INCOMING_VIDEO_CALL_TYPE -> R.drawable.symbol_arrow_downleft_compact_16 + MessageTypes.INCOMING_AUDIO_CALL_TYPE, MessageTypes.INCOMING_VIDEO_CALL_TYPE -> if (call.isDisplayedAsMissedCallInUi) R.drawable.symbol_missed_incoming_compact_16 else R.drawable.symbol_arrow_downleft_compact_16 MessageTypes.OUTGOING_AUDIO_CALL_TYPE, MessageTypes.OUTGOING_VIDEO_CALL_TYPE -> R.drawable.symbol_arrow_upright_compact_16 MessageTypes.GROUP_CALL_TYPE -> when { call.type == CallTable.Type.AD_HOC_CALL -> R.drawable.symbol_link_compact_16 - call.event.isMissedCall() -> R.drawable.symbol_missed_incoming_compact_16 + call.isDisplayedAsMissedCallInUi -> R.drawable.symbol_missed_incoming_compact_16 call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.drawable.symbol_group_compact_16 call.direction == CallTable.Direction.INCOMING -> R.drawable.symbol_arrow_downleft_compact_16 call.direction == CallTable.Direction.OUTGOING -> R.drawable.symbol_arrow_upright_compact_16 @@ -389,23 +389,19 @@ class CallLogAdapter( @StringRes private fun getCallStateStringRes(call: CallTable.Call): Int { return when (call.messageType) { - MessageTypes.MISSED_VIDEO_CALL_TYPE, - MessageTypes.MISSED_AUDIO_CALL_TYPE -> if (call.event == CallTable.Event.MISSED) R.string.CallLogAdapter__missed else R.string.CallLogAdapter__missed_notification_profile - MessageTypes.INCOMING_AUDIO_CALL_TYPE -> R.string.CallLogAdapter__incoming - MessageTypes.INCOMING_VIDEO_CALL_TYPE -> R.string.CallLogAdapter__incoming + MessageTypes.MISSED_VIDEO_CALL_TYPE, MessageTypes.MISSED_AUDIO_CALL_TYPE -> if (call.event == CallTable.Event.MISSED) R.string.CallLogAdapter__missed else R.string.CallLogAdapter__missed_notification_profile MessageTypes.OUTGOING_AUDIO_CALL_TYPE -> R.string.CallLogAdapter__outgoing MessageTypes.OUTGOING_VIDEO_CALL_TYPE -> R.string.CallLogAdapter__outgoing MessageTypes.GROUP_CALL_TYPE -> when { call.type == CallTable.Type.AD_HOC_CALL -> R.string.CallLogAdapter__call_link - call.event == CallTable.Event.MISSED -> R.string.CallLogAdapter__missed call.event == CallTable.Event.MISSED_NOTIFICATION_PROFILE -> R.string.CallLogAdapter__missed_notification_profile + call.isDisplayedAsMissedCallInUi -> R.string.CallLogAdapter__missed call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.string.CallPreference__group_call call.direction == CallTable.Direction.INCOMING -> R.string.CallLogAdapter__incoming call.direction == CallTable.Direction.OUTGOING -> R.string.CallLogAdapter__outgoing else -> throw AssertionError() } - - else -> error("Unexpected type ${call.messageType}") + else -> if (call.isDisplayedAsMissedCallInUi) R.string.CallLogAdapter__missed else R.string.CallLogAdapter__incoming } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt index 28df51273f..42159fafdb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.components.settings.conversation.preferences import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.database.CallTable import org.thoughtcrime.securesms.database.MessageTypes @@ -46,10 +47,10 @@ object CallPreference { private fun getCallIcon(call: CallTable.Call): Int { return when (call.messageType) { MessageTypes.MISSED_VIDEO_CALL_TYPE, MessageTypes.MISSED_AUDIO_CALL_TYPE -> R.drawable.symbol_missed_incoming_24 - MessageTypes.INCOMING_AUDIO_CALL_TYPE, MessageTypes.INCOMING_VIDEO_CALL_TYPE -> R.drawable.symbol_arrow_downleft_24 + MessageTypes.INCOMING_AUDIO_CALL_TYPE, MessageTypes.INCOMING_VIDEO_CALL_TYPE -> if (call.isDisplayedAsMissedCallInUi) R.drawable.symbol_missed_incoming_24 else R.drawable.symbol_arrow_downleft_24 MessageTypes.OUTGOING_AUDIO_CALL_TYPE, MessageTypes.OUTGOING_VIDEO_CALL_TYPE -> R.drawable.symbol_arrow_upright_24 MessageTypes.GROUP_CALL_TYPE -> when { - call.event.isMissedCall() -> R.drawable.symbol_missed_incoming_24 + call.isDisplayedAsMissedCallInUi -> R.drawable.symbol_missed_incoming_24 call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.drawable.symbol_group_24 call.direction == CallTable.Direction.INCOMING -> R.drawable.symbol_arrow_downleft_24 call.direction == CallTable.Direction.OUTGOING -> R.drawable.symbol_arrow_upright_24 @@ -61,15 +62,14 @@ object CallPreference { private fun getCallType(call: CallTable.Call): String { val id = when (call.messageType) { - MessageTypes.MISSED_AUDIO_CALL_TYPE -> if (call.event == CallTable.Event.MISSED) R.string.MessageRecord_missed_voice_call else R.string.MessageRecord_missed_voice_call_notification_profile - MessageTypes.MISSED_VIDEO_CALL_TYPE -> if (call.event == CallTable.Event.MISSED) R.string.MessageRecord_missed_video_call else R.string.MessageRecord_missed_video_call_notification_profile - MessageTypes.INCOMING_AUDIO_CALL_TYPE -> R.string.MessageRecord_incoming_voice_call - MessageTypes.INCOMING_VIDEO_CALL_TYPE -> R.string.MessageRecord_incoming_video_call + MessageTypes.MISSED_AUDIO_CALL_TYPE -> getMissedCallString(false, call.event) + MessageTypes.MISSED_VIDEO_CALL_TYPE -> getMissedCallString(true, call.event) + MessageTypes.INCOMING_AUDIO_CALL_TYPE -> if (call.isDisplayedAsMissedCallInUi) getMissedCallString(false, call.event) else R.string.MessageRecord_incoming_voice_call + MessageTypes.INCOMING_VIDEO_CALL_TYPE -> if (call.isDisplayedAsMissedCallInUi) getMissedCallString(true, call.event) else R.string.MessageRecord_incoming_video_call MessageTypes.OUTGOING_AUDIO_CALL_TYPE -> R.string.MessageRecord_outgoing_voice_call MessageTypes.OUTGOING_VIDEO_CALL_TYPE -> R.string.MessageRecord_outgoing_video_call MessageTypes.GROUP_CALL_TYPE -> when { - call.event == CallTable.Event.MISSED -> R.string.CallPreference__missed_group_call - call.event == CallTable.Event.MISSED_NOTIFICATION_PROFILE -> R.string.CallPreference__missed_group_call_notification_profile + call.isDisplayedAsMissedCallInUi -> if (call.event == CallTable.Event.MISSED_NOTIFICATION_PROFILE) R.string.CallPreference__missed_group_call_notification_profile else R.string.CallPreference__missed_group_call call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.string.CallPreference__group_call call.direction == CallTable.Direction.INCOMING -> R.string.CallPreference__incoming_group_call call.direction == CallTable.Direction.OUTGOING -> R.string.CallPreference__outgoing_group_call @@ -81,6 +81,23 @@ object CallPreference { return context.getString(id) } + @StringRes + private fun getMissedCallString(isVideo: Boolean, callEvent: CallTable.Event): Int { + return if (callEvent == CallTable.Event.MISSED_NOTIFICATION_PROFILE) { + if (isVideo) { + R.string.MessageRecord_missed_video_call_notification_profile + } else { + R.string.MessageRecord_missed_voice_call_notification_profile + } + } else { + if (isVideo) { + R.string.MessageRecord_missed_video_call + } else { + R.string.MessageRecord_missed_voice_call + } + } + } + private fun getCallTime(messageRecord: MessageRecord): String { return DateUtils.getOnlyTimeString(context, messageRecord.timestamp) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 84bb5180ee..1b323f4ad6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.database.model.LiveUpdateMessage; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.UpdateDescription; +import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails; import org.thoughtcrime.securesms.groups.LiveGroup; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.recipients.LiveRecipient; @@ -447,11 +448,14 @@ private void present(@NonNull ConversationMessage conversationMessage, } }); } else if (conversationMessage.getMessageRecord().isGroupCall()) { - UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true); - Collection acis = updateDescription.getMentioned(); + GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()); + boolean isRingingOnLocalDevice = groupCallUpdateDetails.isRingingOnLocalDevice; + boolean endedRecently = GroupCallUpdateDetailsUtil.checkCallEndedRecently(groupCallUpdateDetails); + UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true); + Collection acis = updateDescription.getMentioned(); int text = 0; - if (Util.hasItems(acis)) { + if (Util.hasItems(acis) || isRingingOnLocalDevice) { if (acis.contains(SignalStore.account().requireAci())) { text = R.string.ConversationUpdateItem_return_to_call; } else if (GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()).isCallFull) { @@ -459,6 +463,8 @@ private void present(@NonNull ConversationMessage conversationMessage, } else { text = R.string.ConversationUpdateItem_join_call; } + } else if (endedRecently) { + text = R.string.ConversationUpdateItem_call_back; } if (text != 0 && conversationRecipient.isGroup() && conversationRecipient.isActiveGroup()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt index 48d8e96154..ec3a21e9e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt @@ -18,11 +18,13 @@ import org.signal.core.util.readToList import org.signal.core.util.readToMap import org.signal.core.util.readToSingleLong import org.signal.core.util.readToSingleObject +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.requireString import org.signal.core.util.select +import org.signal.core.util.toInt import org.signal.core.util.toSingleLine import org.signal.core.util.update import org.signal.core.util.withinTransaction @@ -64,6 +66,22 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl const val DELETION_TIMESTAMP = "deletion_timestamp" const val READ = "read" + /** + * Whether a given call event was joined by the local user + * + * Used to determine if a group call in the "GENERIC_GROUP_CALL" state is to be + * displayed as a missed call in the ui + */ + const val LOCAL_JOINED = "local_joined" + + /** + * Whether a given call event is currently considered active. + * + * Used to determine if a group call in the "GENERIC_GROUP_CALL" state is to be + * displayed as a missed call in the ui + */ + const val GROUP_CALL_ACTIVE = "group_call_active" + //language=sql const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( @@ -78,6 +96,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl $RINGER INTEGER DEFAULT NULL, $DELETION_TIMESTAMP INTEGER DEFAULT 0, $READ INTEGER DEFAULT 1, + $LOCAL_JOINED INTEGER DEFAULT 0, + $GROUP_CALL_ACTIVE INTEGER DEFAULT 0, UNIQUE ($CALL_ID, $PEER) ON CONFLICT FAIL ) """ @@ -468,6 +488,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl timestamp, "", emptyList(), + false, false ) } else { @@ -484,7 +505,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl TYPE to Type.serialize(type), DIRECTION to Direction.serialize(direction), TIMESTAMP to timestamp, - RINGER to ringer + RINGER to ringer, + LOCAL_JOINED to true ) .run(SQLiteDatabase.CONFLICT_ABORT) } @@ -508,6 +530,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl timestamp, "", emptyList(), + false, false ) } else { @@ -524,7 +547,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl TYPE to Type.serialize(type), DIRECTION to Direction.serialize(Direction.INCOMING), TIMESTAMP to timestamp, - RINGER to null + RINGER to null, + LOCAL_JOINED to false ) .run(SQLiteDatabase.CONFLICT_ABORT) } @@ -601,7 +625,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl call.messageId, peekGroupCallEraId, peekJoinedUuids, - isCallFull + isCallFull, + call.event == Event.RINGING ) } else { SignalDatabase.messages.insertGroupCall( @@ -610,16 +635,19 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl timestamp, peekGroupCallEraId, peekJoinedUuids, - isCallFull + isCallFull, + false ) } insertCallEventFromGroupUpdate( - callId, - messageId, - sender, - groupRecipient.id, - timestamp + callId = callId, + messageId = messageId, + sender = sender, + groupRecipientId = groupRecipient.id, + timestamp = timestamp, + didLocalUserJoin = peekJoinedUuids.contains(Recipient.self().requireServiceId().rawUuid), + isGroupCallActive = peekJoinedUuids.isNotEmpty() ) } } @@ -660,7 +688,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl messageId: MessageId?, sender: RecipientId, groupRecipientId: RecipientId, - timestamp: Long + timestamp: Long, + didLocalUserJoin: Boolean, + isGroupCallActive: Boolean ) { if (messageId != null) { val call = getCallById(callId, groupRecipientId) @@ -677,7 +707,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl TYPE to Type.serialize(Type.GROUP_CALL), DIRECTION to Direction.serialize(direction), TIMESTAMP to timestamp, - RINGER to null + RINGER to null, + LOCAL_JOINED to didLocalUserJoin, + GROUP_CALL_ACTIVE to isGroupCallActive ) .run(SQLiteDatabase.CONFLICT_ABORT) @@ -692,6 +724,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl setMessageId(callId, messageId) Log.d(TAG, "Updated call event message id for newly inserted group call state: $callId") } + + updateGroupCallState(call, didLocalUserJoin, isGroupCallActive) } } else { Log.d(TAG, "Skipping call event processing for null era id.") @@ -701,7 +735,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } /** - * Since this does not alter the call table, we can simply pass this directly through to the old handler. + * Update necessary call info from peek */ fun updateGroupCallFromPeek( threadId: Long, @@ -709,7 +743,26 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl peekJoinedUuids: Collection, isCallFull: Boolean ): Boolean { - val sameEraId = SignalDatabase.messages.updatePreviousGroupCall(threadId, peekGroupCallEraId, peekJoinedUuids, isCallFull) + val callId = peekGroupCallEraId?.let { CallId.fromEra(it) } + val recipientId = SignalDatabase.threads.getRecipientIdForThreadId(threadId) + val call = if (callId != null && recipientId != null) { + getCallById(callId.longValue(), recipientId) + } else { + null + } + + val sameEraId = SignalDatabase.messages.updatePreviousGroupCall( + threadId = threadId, + peekGroupCallEraId = peekGroupCallEraId, + peekJoinedUuids = peekJoinedUuids, + isCallFull = isCallFull, + isRingingOnLocalDevice = call?.event == Event.RINGING + ) + + if (call != null) { + updateGroupCallState(call, peekJoinedUuids) + } + ApplicationDependencies.getDatabaseObserver().notifyCallUpdateObservers() return sameEraId } @@ -742,6 +795,35 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl return call.event != Event.RINGING && call.event != Event.GENERIC_GROUP_CALL } + private fun updateGroupCallState( + call: Call, + peekJoinedUuids: Collection + ) { + updateGroupCallState( + call, + peekJoinedUuids.contains(Recipient.self().requireServiceId().rawUuid), + peekJoinedUuids.isNotEmpty() + ) + } + + private fun updateGroupCallState( + call: Call, + hasLocalUserJoined: Boolean, + isGroupCallActive: Boolean + ) { + writableDatabase.update(TABLE_NAME) + .values( + LOCAL_JOINED to (call.didLocalUserJoin || hasLocalUserJoined), + GROUP_CALL_ACTIVE to isGroupCallActive + ) + .where( + "$CALL_ID = ? AND $PEER = ?", + call.callId, + call.peer.toLong() + ) + .run() + } + private fun handleGroupRingState( ringId: Long, groupRecipientId: RecipientId, @@ -893,7 +975,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl timestamp = timestamp, eraId = "", joinedUuids = emptyList(), - isCallFull = false + isCallFull = false, + isIncomingGroupCallRingingOnLocalDevice = event == Event.RINGING ) db @@ -1074,9 +1157,10 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl // endregion private fun getCallsCursor(isCount: Boolean, offset: Int, limit: Int, searchTerm: String?, filter: CallLogFilter): Cursor { + val isMissedGenericGroupCall = "$EVENT = ${Event.serialize(Event.GENERIC_GROUP_CALL)} AND $LOCAL_JOINED = ${false.toInt()} AND $GROUP_CALL_ACTIVE = ${false.toInt()}" val filterClause: SqlUtil.Query = when (filter) { CallLogFilter.ALL -> SqlUtil.buildQuery("$DELETION_TIMESTAMP = 0") - CallLogFilter.MISSED -> SqlUtil.buildQuery("($EVENT = ${Event.serialize(Event.MISSED)} OR $EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)}) AND $DELETION_TIMESTAMP = 0") + CallLogFilter.MISSED -> SqlUtil.buildQuery("($EVENT = ${Event.serialize(Event.MISSED)} OR $EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} OR $EVENT = ${Event.serialize(Event.NOT_ACCEPTED)} OR $EVENT = ${Event.serialize(Event.DECLINED)} OR ($isMissedGenericGroupCall)) AND $DELETION_TIMESTAMP = 0") CallLogFilter.AD_HOC -> SqlUtil.buildQuery("$TYPE = ${Type.serialize(Type.AD_HOC_CALL)} AND $DELETION_TIMESTAMP = 0") } @@ -1112,16 +1196,28 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl val projection = if (isCount) { "COUNT(*)," } else { - "p.$ID, p.$TIMESTAMP, $EVENT, $DIRECTION, $PEER, p.$TYPE, $CALL_ID, $MESSAGE_ID, $RINGER, children, in_period, ${MessageTable.BODY}," + "p.$ID, p.$TIMESTAMP, $EVENT, $DIRECTION, $PEER, p.$TYPE, $CALL_ID, $MESSAGE_ID, $RINGER, $LOCAL_JOINED, $GROUP_CALL_ACTIVE, children, in_period, ${MessageTable.BODY}," } + // Group call events by those we consider missed or not missed to build out our call log aggregation. val eventTypeSubQuery = """ - ($TABLE_NAME.$EVENT = c.$EVENT AND ($TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED)} OR $TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)})) OR - ( + ($TABLE_NAME.$EVENT = c.$EVENT AND ( + $TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED)} OR + $TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} OR + $TABLE_NAME.$EVENT = ${Event.serialize(Event.NOT_ACCEPTED)} OR + $TABLE_NAME.$EVENT = ${Event.serialize(Event.DECLINED)} OR + ($TABLE_NAME.$isMissedGenericGroupCall) + )) OR ( $TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED)} AND c.$EVENT != ${Event.serialize(Event.MISSED)} AND $TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} AND - c.$EVENT != ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} + c.$EVENT != ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} AND + $TABLE_NAME.$EVENT != ${Event.serialize(Event.NOT_ACCEPTED)} AND + c.$EVENT != ${Event.serialize(Event.NOT_ACCEPTED)} AND + $TABLE_NAME.$EVENT != ${Event.serialize(Event.DECLINED)} AND + c.$EVENT != ${Event.serialize(Event.DECLINED)} AND + (NOT ($TABLE_NAME.$isMissedGenericGroupCall)) AND + (NOT (c.$isMissedGenericGroupCall)) ) """ @@ -1131,6 +1227,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl LOWER( COALESCE( NULLIF(${GroupTable.TABLE_NAME}.${GroupTable.TITLE}, ''), + NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.NICKNAME_JOINED_NAME}, ''), + NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.NICKNAME_GIVEN_NAME}, ''), NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.SYSTEM_JOINED_NAME}, ''), NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.SYSTEM_GIVEN_NAME}, ''), NULLIF(${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_JOINED_NAME}, ''), @@ -1141,7 +1239,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl FROM ( WITH cte AS ( SELECT - $ID, $TIMESTAMP, $EVENT, $DIRECTION, $PEER, $TYPE, $CALL_ID, $MESSAGE_ID, $RINGER, + $ID, $TIMESTAMP, $EVENT, $DIRECTION, $PEER, $TYPE, $CALL_ID, $MESSAGE_ID, $RINGER, $LOCAL_JOINED, $GROUP_CALL_ACTIVE, ( SELECT $ID @@ -1225,10 +1323,20 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } fun markRingingCallsAsMissed() { - writableDatabase.update(TABLE_NAME) - .values(EVENT to Event.serialize(Event.MISSED)) - .where("$EVENT = ?", Event.serialize(Event.RINGING)) - .run() + writableDatabase.withinTransaction { db -> + val messageIds: List = db.select(MESSAGE_ID) + .from(TABLE_NAME) + .where("$EVENT = ? AND $MESSAGE_ID != NULL", Event.serialize(Event.RINGING)) + .run() + .readToList { it.requireLong(MESSAGE_ID) } + + db.update(TABLE_NAME) + .values(EVENT to Event.serialize(Event.MISSED)) + .where("$EVENT = ?", Event.serialize(Event.RINGING)) + .run() + + SignalDatabase.messages.clearIsRingingOnLocalDeviceFlag(messageIds) + } } fun getCallsCount(searchTerm: String?, filter: CallLogFilter): Int { @@ -1288,6 +1396,10 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl .run() } + /** + * @param isGroupCallActive - Whether the group call currently contains users. Only valid for group calls. + * @param didLocalUserJoin - Determines whether the local user joined this call. Only valid for group calls. + */ data class Call( val callId: Long, val peer: RecipientId, @@ -1296,11 +1408,20 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl val event: Event, val messageId: Long?, val timestamp: Long, - val ringerRecipient: RecipientId? + val ringerRecipient: RecipientId?, + val isGroupCallActive: Boolean, + val didLocalUserJoin: Boolean ) { val messageType: Long = getMessageType(type, direction, event) + val isDisplayedAsMissedCallInUi = isDisplayedAsMissedCallInUi(this) + companion object Deserializer : Serializer { + + private fun isDisplayedAsMissedCallInUi(call: Call): Boolean { + return call.event in Event.DISPLAY_AS_MISSED_CALL || (call.event == Event.GENERIC_GROUP_CALL && !call.didLocalUserJoin && !call.isGroupCallActive) + } + fun getMessageType(type: Type, direction: Direction, event: Event): Long { if (type == Type.GROUP_CALL || type == Type.AD_HOC_CALL) { return MessageTypes.GROUP_CALL_TYPE @@ -1334,7 +1455,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } else { null } - } + }, + isGroupCallActive = data.requireBoolean(GROUP_CALL_ACTIVE), + didLocalUserJoin = data.requireBoolean(LOCAL_JOINED) ) } } @@ -1482,6 +1605,14 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } companion object Serializer : IntSerializer { + + val DISPLAY_AS_MISSED_CALL = listOf( + MISSED, + MISSED_NOTIFICATION_PROFILE, + DECLINED, + NOT_ACCEPTED + ) + override fun serialize(data: Event): Int = data.code override fun deserialize(data: Int): Event { 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 3e3cc862d7..485e488a6a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -828,7 +828,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat timestamp: Long, eraId: String, joinedUuids: Collection, - isCallFull: Boolean + isCallFull: Boolean, + isIncomingGroupCallRingingOnLocalDevice: Boolean ): MessageId { val recipient = Recipient.resolved(groupRecipientId) val threadId = threads.getOrCreateThreadIdFor(recipient) @@ -840,7 +841,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat startedCallUuid = Recipient.resolved(sender).requireServiceId().toString(), startedCallTimestamp = timestamp, inCallUuids = joinedUuids.map { it.toString() }, - isCallFull = isCallFull + isCallFull = isCallFull, + localUserJoined = joinedUuids.contains(Recipient.self().requireServiceId().rawUuid), + isRingingOnLocalDevice = isIncomingGroupCallRingingOnLocalDevice ).encode() val values = contentValuesOf( @@ -893,11 +896,46 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat } } + /** + * Clears the flag in GroupCallUpdateDetailsUtil that specifies that the call is ringing on the local device. + * Called when cleaning up the call ringing state (which can get out of sync in the case of an application crash) + */ + fun clearIsRingingOnLocalDeviceFlag(messageIds: Collection) { + writableDatabase.withinTransaction { db -> + val queries = SqlUtil.buildCollectionQuery(ID, messageIds) + + for (query in queries) { + val messageIdBodyPairs = db.select(ID, BODY) + .from(TABLE_NAME) + .where(query.where, query.whereArgs) + .run() + .readToList { cursor -> + cursor.requireLong(ID) to cursor.requireString(BODY) + } + + for ((messageId, body) in messageIdBodyPairs) { + val oldBody = GroupCallUpdateDetailsUtil.parse(body) + if (!oldBody.isRingingOnLocalDevice) { + continue + } + + val newBody = GroupCallUpdateDetailsUtil.createUpdatedBody(oldBody, oldBody.inCallUuids, oldBody.isCallFull, false) + + db.update(TABLE_NAME) + .values(BODY to newBody) + .where(ID_WHERE, messageId) + .run() + } + } + } + } + fun updateGroupCall( messageId: Long, eraId: String, joinedUuids: Collection, - isCallFull: Boolean + isCallFull: Boolean, + isRingingOnLocalDevice: Boolean ): MessageId { writableDatabase.withinTransaction { db -> val message = try { @@ -911,7 +949,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val sameEraId = updateDetail.eraId == eraId && !Util.isEmpty(eraId) val inCallUuids = if (sameEraId) joinedUuids.map { it.toString() } else emptyList() val contentValues = contentValuesOf( - BODY to GroupCallUpdateDetailsUtil.createUpdatedBody(updateDetail, inCallUuids, isCallFull) + BODY to GroupCallUpdateDetailsUtil.createUpdatedBody(updateDetail, inCallUuids, isCallFull, isRingingOnLocalDevice) ) if (sameEraId && containsSelf) { @@ -929,7 +967,13 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat return MessageId(messageId) } - fun updatePreviousGroupCall(threadId: Long, peekGroupCallEraId: String?, peekJoinedUuids: Collection, isCallFull: Boolean): Boolean { + fun updatePreviousGroupCall( + threadId: Long, + peekGroupCallEraId: String?, + peekJoinedUuids: Collection, + isCallFull: Boolean, + isRingingOnLocalDevice: Boolean + ): Boolean { return writableDatabase.withinTransaction { db -> val cursor = db .select(*MMS_PROJECTION) @@ -952,7 +996,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat } val contentValues = contentValuesOf( - BODY to GroupCallUpdateDetailsUtil.createUpdatedBody(groupCallUpdateDetails, inCallUuids, isCallFull) + BODY to GroupCallUpdateDetailsUtil.createUpdatedBody(groupCallUpdateDetails, inCallUuids, isCallFull, isRingingOnLocalDevice) ) if (sameEraId && containsSelf) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java index 577e5a98b3..5ffa864c02 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java @@ -136,24 +136,21 @@ private ThreadBodyUtil() { boolean accepted = call.getEvent() == CallTable.Event.ACCEPTED; if (call.getDirection() == CallTable.Direction.OUTGOING) { if (call.getType() == CallTable.Type.AUDIO_CALL) { - return context.getString(accepted ? R.string.MessageRecord_outgoing_voice_call : R.string.MessageRecord_unanswered_voice_call); + return context.getString(R.string.MessageRecord_outgoing_voice_call); } else { - return context.getString(accepted ? R.string.MessageRecord_outgoing_video_call : R.string.MessageRecord_unanswered_video_call); + return context.getString(R.string.MessageRecord_outgoing_video_call); } } else { boolean isVideoCall = call.getType() == CallTable.Type.VIDEO_CALL; - boolean isMissed = call.getEvent().isMissedCall(); - if (accepted) { + if (accepted || !call.isDisplayedAsMissedCallInUi()) { return context.getString(isVideoCall ? R.string.MessageRecord_incoming_video_call : R.string.MessageRecord_incoming_voice_call); - } else if (isMissed) { + } else { if (call.getEvent() == CallTable.Event.MISSED_NOTIFICATION_PROFILE) { return isVideoCall ? context.getString(R.string.MessageRecord_missed_video_call_notification_profile) : context.getString(R.string.MessageRecord_missed_voice_call_notification_profile); } else { return isVideoCall ? context.getString(R.string.MessageRecord_missed_video_call) : context.getString(R.string.MessageRecord_missed_voice_call); } - } else { - return isVideoCall ? context.getString(R.string.MessageRecord_you_declined_a_video_call) : context.getString(R.string.MessageRecord_you_declined_a_voice_call); } } } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 27a2903153..cc1d3741aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -82,6 +82,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V221_AddReadColumnT import org.thoughtcrime.securesms.database.helpers.migration.V222_DataHashRefactor import org.thoughtcrime.securesms.database.helpers.migration.V223_AddNicknameAndNoteFieldsToRecipientTable import org.thoughtcrime.securesms.database.helpers.migration.V224_AddAttachmentArchiveColumns +import org.thoughtcrime.securesms.database.helpers.migration.V225_AddLocalUserJoinedStateAndGroupCallActiveState /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -166,10 +167,11 @@ object SignalDatabaseMigrations { 221 to V221_AddReadColumnToCallEventsTable, 222 to V222_DataHashRefactor, 223 to V223_AddNicknameAndNoteFieldsToRecipientTable, - 224 to V224_AddAttachmentArchiveColumns + 224 to V224_AddAttachmentArchiveColumns, + 225 to V225_AddLocalUserJoinedStateAndGroupCallActiveState ) - const val DATABASE_VERSION = 224 + const val DATABASE_VERSION = 225 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V225_AddLocalUserJoinedStateAndGroupCallActiveState.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V225_AddLocalUserJoinedStateAndGroupCallActiveState.kt new file mode 100644 index 0000000000..ec1703e0da --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V225_AddLocalUserJoinedStateAndGroupCallActiveState.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds local user joined state and group call active state to the + * call events table for proper representation of missed non-ringing + * group calls. + * + * Pre-migration call events will display as if the user joined the call. + */ +@Suppress("ClassName") +object V225_AddLocalUserJoinedStateAndGroupCallActiveState : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE call ADD COLUMN local_joined INTEGER DEFAULT 0") + db.execSQL("ALTER TABLE call ADD COLUMN group_call_active INTEGER DEFAULT 0") + + /** + * Assume for pre-migration calls that we've joined them all. This avoids + * erroneously marking calls as missed. + */ + db.execSQL("UPDATE call SET local_joined = 1") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateDetailsUtil.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateDetailsUtil.java index d549cc2eb3..56c58e397f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateDetailsUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateDetailsUtil.java @@ -3,20 +3,53 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.core.util.Base64; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.backup.v2.proto.GroupCallChatUpdate; import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails; -import org.signal.core.util.Base64; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.whispersystems.signalservice.api.push.ServiceId; import java.io.IOException; import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; public final class GroupCallUpdateDetailsUtil { private static final String TAG = Log.tag(GroupCallUpdateDetailsUtil.class); + private static final long CALL_RECENCY_TIMEOUT = TimeUnit.MINUTES.toMillis(5); + private GroupCallUpdateDetailsUtil() { } + /** + * Generates a group chat update message body from backup data + */ + public static @NonNull String createBodyFromBackup(@NonNull GroupCallChatUpdate groupCallChatUpdate) { + ServiceId.ACI startedCall = groupCallChatUpdate.startedCallAci != null ? ServiceId.ACI.parseOrNull(groupCallChatUpdate.startedCallAci) : null; + + GroupCallUpdateDetails details = new GroupCallUpdateDetails.Builder() + .startedCallUuid(Objects.toString(startedCall, null)) + .startedCallTimestamp(groupCallChatUpdate.startedCallTimestamp) + .endedCallTimestamp(groupCallChatUpdate.endedCallTimestamp) + .isCallFull(false) + .inCallUuids(groupCallChatUpdate.inCallAcis.stream() + .filter(Objects::nonNull) + .map(ServiceId.ACI::parseOrNull) + .filter(Objects::nonNull) + .map(ServiceId.ACI::toString) + .collect(Collectors.toList()) + ) + .isRingingOnLocalDevice(false) + .localUserJoined(groupCallChatUpdate.localUserJoined != GroupCallChatUpdate.LocalUserJoined.DID_NOT_JOIN) + .build(); + + return Base64.encodeWithPadding(details.encode()); + } + public static @NonNull GroupCallUpdateDetails parse(@Nullable String body) { GroupCallUpdateDetails groupCallUpdateDetails = new GroupCallUpdateDetails(); @@ -33,10 +66,38 @@ private GroupCallUpdateDetailsUtil() { return groupCallUpdateDetails; } - public static @NonNull String createUpdatedBody(@NonNull GroupCallUpdateDetails groupCallUpdateDetails, @NonNull List inCallUuids, boolean isCallFull) { + public static boolean checkCallEndedRecently(@NonNull GroupCallUpdateDetails groupCallUpdateDetails) { + if (groupCallUpdateDetails.endedCallTimestamp == 0) { + return false; + } + + long now = System.currentTimeMillis(); + if (now > groupCallUpdateDetails.endedCallTimestamp) { + return false; + } + + return now - groupCallUpdateDetails.endedCallTimestamp < CALL_RECENCY_TIMEOUT; + } + + public static @NonNull String createUpdatedBody(@NonNull GroupCallUpdateDetails groupCallUpdateDetails, @NonNull List inCallUuids, boolean isCallFull, boolean isRingingOnLocalDevice) + { + boolean localUserJoined = groupCallUpdateDetails.localUserJoined || inCallUuids.contains(Recipient.self().requireServiceId().getRawUuid().toString()); + long endedTimestamp = groupCallUpdateDetails.endedCallTimestamp; + boolean callBecameEmpty = !groupCallUpdateDetails.inCallUuids.isEmpty() && inCallUuids.isEmpty() && !isRingingOnLocalDevice; + boolean ringTerminatedWithNoUsers = groupCallUpdateDetails.isRingingOnLocalDevice && !isRingingOnLocalDevice && inCallUuids.isEmpty(); + + if (callBecameEmpty || ringTerminatedWithNoUsers) { + endedTimestamp = System.currentTimeMillis(); + } else if (!inCallUuids.isEmpty()) { + endedTimestamp = 0; + } + GroupCallUpdateDetails.Builder builder = groupCallUpdateDetails.newBuilder() .isCallFull(isCallFull) - .inCallUuids(inCallUuids); + .inCallUuids(inCallUuids) + .localUserJoined(localUserJoined) + .endedCallTimestamp(endedTimestamp) + .isRingingOnLocalDevice(isRingingOnLocalDevice); return Base64.encodeWithPadding(builder.build().encode()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java index 2e268d4acb..9c1fbb0cd7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.concurrent.TimeUnit; /** * Create a group call update message based on time and joined members. @@ -53,45 +54,61 @@ public GroupCallUpdateMessageFactory(@NonNull Context context, } private @NonNull String createString() { - String time = DateUtils.getTimeString(context, Locale.getDefault(), groupCallUpdateDetails.startedCallTimestamp); + long endedTimestamp = groupCallUpdateDetails.endedCallTimestamp; + boolean isWithinTimeout = GroupCallUpdateDetailsUtil.checkCallEndedRecently(groupCallUpdateDetails); + String time = DateUtils.getTimeString(context, Locale.getDefault(), groupCallUpdateDetails.startedCallTimestamp); + boolean isOutgoing = Objects.equals(selfAci.toString(), groupCallUpdateDetails.startedCallUuid); switch (joinedMembers.size()) { case 0: - return withTime ? context.getString(R.string.MessageRecord_group_call_s, time) - : context.getString(R.string.MessageRecord_group_call); + if (isWithinTimeout) { + return withTime ? context.getString(R.string.MessageRecord__the_video_call_has_ended_s, time) + : context.getString(R.string.MessageRecord__the_video_call_has_ended); + } else if (endedTimestamp == 0 || groupCallUpdateDetails.localUserJoined) { + if (isOutgoing) { + return withTime ? context.getString(R.string.MessageRecord__outgoing_video_call_s, time) + : context.getString(R.string.MessageRecord__outgoing_video_call); + } else { + return withTime ? context.getString(R.string.MessageRecord__incoming_video_call_s, time) + : context.getString(R.string.MessageRecord__incoming_video_call); + } + } else { + return withTime ? context.getString(R.string.MessageRecord__missed_video_call_s, time) + : context.getString(R.string.MessageRecord__missed_video_call); + } case 1: if (joinedMembers.get(0).toString().equals(groupCallUpdateDetails.startedCallUuid)) { if (Objects.equals(joinedMembers.get(0), selfAci)) { - return withTime ? context.getString(R.string.MessageRecord_you_started_a_group_call_s, time) - : context.getString(R.string.MessageRecord_you_started_a_group_call); + return withTime ? context.getString(R.string.MessageRecord__you_started_a_video_call_s, time) + : context.getString(R.string.MessageRecord__you_started_a_video_call); } else { - return withTime ? context.getString(R.string.MessageRecord_s_started_a_group_call_s, describe(joinedMembers.get(0)), time) - : context.getString(R.string.MessageRecord_s_started_a_group_call, describe(joinedMembers.get(0))); + return withTime ? context.getString(R.string.MessageRecord__s_started_a_video_call_s, describe(joinedMembers.get(0)), time) + : context.getString(R.string.MessageRecord__s_started_a_video_call, describe(joinedMembers.get(0))); } } else if (Objects.equals(joinedMembers.get(0), selfAci)) { - return withTime ? context.getString(R.string.MessageRecord_you_are_in_the_group_call_s1, time) - : context.getString(R.string.MessageRecord_you_are_in_the_group_call); + return withTime ? context.getString(R.string.MessageRecord_you_are_in_the_call_s1, time) + : context.getString(R.string.MessageRecord_you_are_in_the_call); } else { - return withTime ? context.getString(R.string.MessageRecord_s_is_in_the_group_call_s, describe(joinedMembers.get(0)), time) - : context.getString(R.string.MessageRecord_s_is_in_the_group_call, describe(joinedMembers.get(0))); + return withTime ? context.getString(R.string.MessageRecord_s_is_in_the_call_s, describe(joinedMembers.get(0)), time) + : context.getString(R.string.MessageRecord_s_is_in_the_call, describe(joinedMembers.get(0))); } case 2: - return withTime ? context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call_s1, + return withTime ? context.getString(R.string.MessageRecord_s_and_s_are_in_the_call_s1, describe(joinedMembers.get(0)), describe(joinedMembers.get(1)), time) - : context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call, + : context.getString(R.string.MessageRecord_s_and_s_are_in_the_call, describe(joinedMembers.get(0)), describe(joinedMembers.get(1))); default: int others = joinedMembers.size() - 2; - return withTime ? context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_group_call_s, + return withTime ? context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_call_s, others, describe(joinedMembers.get(0)), describe(joinedMembers.get(1)), others, time) - : context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_group_call, + : context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_call, others, describe(joinedMembers.get(0)), describe(joinedMembers.get(1)), diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java index febd0af229..ff41ee8e08 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java @@ -232,21 +232,20 @@ public SpannableString getDisplayBody(@NonNull Context context) { if (call.getDirection() == CallTable.Direction.OUTGOING) { if (call.getType() == CallTable.Type.AUDIO_CALL) { - int updateString = accepted ? R.string.MessageRecord_outgoing_voice_call : R.string.MessageRecord_unanswered_voice_call; + int updateString = R.string.MessageRecord_outgoing_voice_call; return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), R.drawable.ic_update_audio_call_outgoing_16); } else { - int updateString = accepted ? R.string.MessageRecord_outgoing_video_call : R.string.MessageRecord_unanswered_video_call; + int updateString = R.string.MessageRecord_outgoing_video_call; return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), R.drawable.ic_update_video_call_outgoing_16); } } else { boolean isVideoCall = call.getType() == CallTable.Type.VIDEO_CALL; - boolean isMissed = call.getEvent().isMissedCall(); - if (accepted) { + if (accepted || !call.isDisplayedAsMissedCallInUi()) { int updateString = isVideoCall ? R.string.MessageRecord_incoming_video_call : R.string.MessageRecord_incoming_voice_call; int icon = isVideoCall ? R.drawable.ic_update_video_call_incoming_16 : R.drawable.ic_update_audio_call_incoming_16; return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), icon); - } else if (isMissed) { + } else { int icon = isVideoCall ? R.drawable.ic_update_video_call_missed_16 : R.drawable.ic_update_audio_call_missed_16; int message; if (call.getEvent() == CallTable.Event.MISSED_NOTIFICATION_PROFILE) { @@ -261,9 +260,6 @@ public SpannableString getDisplayBody(@NonNull Context context) { icon, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red)); - } else { - return isVideoCall ? staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_you_declined_a_video_call), callDateString), R.drawable.ic_update_video_call_incoming_16) - : staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_you_declined_a_voice_call), callDateString), R.drawable.ic_update_audio_call_incoming_16); } } } diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index ffa49fdc59..aa3208a861 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -516,9 +516,17 @@ message IndividualCallChatUpdate { } message GroupCallChatUpdate { + enum LocalUserJoined { + UNKNOWN = 0; + JOINED = 1; + DID_NOT_JOIN = 2; + } + optional bytes startedCallAci = 1; uint64 startedCallTimestamp = 2; repeated bytes inCallAcis = 3; + uint64 endedCallTimestamp = 4; // 0 indicates we do not know + LocalUserJoined localUserJoined = 5; } message SimpleChatUpdate { diff --git a/app/src/main/protowire/Database.proto b/app/src/main/protowire/Database.proto index f5ffc95987..c5800aeb4f 100644 --- a/app/src/main/protowire/Database.proto +++ b/app/src/main/protowire/Database.proto @@ -116,11 +116,14 @@ message CryptoValue { } message GroupCallUpdateDetails { - string eraId = 1; - string startedCallUuid = 2; - int64 startedCallTimestamp = 3; - repeated string inCallUuids = 4; - bool isCallFull = 5; + string eraId = 1; + string startedCallUuid = 2; + int64 startedCallTimestamp = 3; + repeated string inCallUuids = 4; + bool isCallFull = 5; + bool localUserJoined = 6; + int64 endedCallTimestamp = 7; + bool isRingingOnLocalDevice = 8; } message ExpiringProfileKeyCredentialColumnData { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 864f8c561e..db213e0b24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1335,17 +1335,13 @@ You have left the group. You updated the group. The group was updated. - + Outgoing voice call - + Outgoing video call - - Unanswered voice call - - Unanswered video call - + Incoming voice call - + Incoming video call Missed voice call @@ -1355,10 +1351,6 @@ Missed voice call while notification profile on Missed video call while notification profile on - - You declined a voice call - - You declined a video call %1$s · %2$s %s updated the group. @@ -1567,28 +1559,53 @@ %s can now accept Payments - %1$s started a group call · %2$s - You started a group call · %1$s - %1$s is in the group call · %2$s - You are in the group call · %1$s - %1$s and %2$s are in the group call · %3$s - Group call · %1$s - - %1$s started a group call - You started a group call - %1$s is in the group call - You are in the group call - %1$s and %2$s are in the group call - Group call + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s You - + + %1$s, %2$s, and %3$d other are in the group call · %4$s %1$s, %2$s, and %3$d others are in the group call · %4$s - + + %1$s, %2$s, and %3$d other are in the group call %1$s, %2$s, and %3$d others are in the group call @@ -2712,6 +2729,8 @@ Loading Learn more Join call + + Call back Return to call Call is full Invite friends From 9703a868e5ddd8a99225245d8f21d71dc20ef3b5 Mon Sep 17 00:00:00 2001 From: Alex Konradi Date: Mon, 15 Apr 2024 13:48:16 -0400 Subject: [PATCH 014/113] Request new ZKC-based auth credential. --- .../signalservice/internal/push/PushServiceSocket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1883ad16e0..2f7b0a9a2a 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 @@ -267,7 +267,7 @@ public class PushServiceSocket { private static final String STICKER_MANIFEST_PATH = "stickers/%s/manifest.proto"; private static final String STICKER_PATH = "stickers/%s/full/%d"; - private static final String GROUPSV2_CREDENTIAL = "/v1/certificate/auth/group?redemptionStartSeconds=%d&redemptionEndSeconds=%d&pniAsServiceId=true"; + private static final String GROUPSV2_CREDENTIAL = "/v1/certificate/auth/group?redemptionStartSeconds=%d&redemptionEndSeconds=%d&zkcCredential=true"; private static final String GROUPSV2_GROUP = "/v1/groups/"; private static final String GROUPSV2_GROUP_PASSWORD = "/v1/groups/?inviteLinkPassword=%s"; private static final String GROUPSV2_GROUP_CHANGES = "/v1/groups/logs/%s?maxSupportedChangeEpoch=%d&includeFirstState=%s&includeLastState=false"; From 7811e51b4164795bec761544b0685c448ee8cf43 Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 16 Apr 2024 13:29:56 -0400 Subject: [PATCH 015/113] Add CDN number as parameter for read credential call. --- .../securesms/backup/v2/BackupRepository.kt | 13 ++++++++++--- .../securesms/jobs/AttachmentDownloadJob.kt | 2 +- .../securesms/jobs/RestoreAttachmentJob.kt | 2 +- .../signalservice/api/archive/ArchiveApi.kt | 6 +++--- .../internal/push/PushServiceSocket.java | 6 +++--- 5 files changed, 18 insertions(+), 11 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 499bf9f929..367706530a 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 @@ -15,6 +15,7 @@ import org.signal.libsignal.messagebackup.MessageBackupKey import org.signal.libsignal.protocol.ServiceId.Aci import org.signal.libsignal.zkgroup.profiles.ProfileKey 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 @@ -299,7 +300,7 @@ object BackupRepository { .then { credential -> api.getBackupInfo(backupKey, credential) } - .then { info -> getCdnReadCredentials().map { it.headers to info } } + .then { info -> getCdnReadCredentials(info.cdn ?: Cdn.CDN_3.cdnNumber).map { it.headers to info } } .map { pair -> val (cdnCredentials, info) = pair val messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver() @@ -456,7 +457,7 @@ object BackupRepository { /** * Retrieve credentials for reading from the backup cdn. */ - fun getCdnReadCredentials(): NetworkResult { + fun getCdnReadCredentials(cdnNumber: Int): NetworkResult { val cached = SignalStore.backup().cdnReadCredentials if (cached != null) { return NetworkResult.Success(cached) @@ -468,6 +469,7 @@ object BackupRepository { return getAuthCredential() .then { credential -> api.getCdnReadCredentials( + cdnNumber = cdnNumber, backupKey = backupKey, serviceCredential = credential ) @@ -490,7 +492,12 @@ object BackupRepository { val cachedBackupMediaDirectory = SignalStore.backup().cachedBackupMediaDirectory if (cachedBackupDirectory != null && cachedBackupMediaDirectory != null) { - return NetworkResult.Success(BackupDirectories(cachedBackupDirectory, cachedBackupMediaDirectory)) + return NetworkResult.Success( + BackupDirectories( + backupDir = cachedBackupDirectory, + mediaDir = cachedBackupMediaDirectory + ) + ) } val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt index 5969ea6908..090564ffb4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt @@ -229,7 +229,7 @@ class AttachmentDownloadJob private constructor( val stream = if (useArchiveCdn) { archiveFile = SignalDatabase.attachments.getOrCreateArchiveTransferFile(attachmentId) - val cdnCredentials = BackupRepository.getCdnReadCredentials().successOrThrow().headers + val cdnCredentials = BackupRepository.getCdnReadCredentials(attachment.archiveCdn).successOrThrow().headers messageReceiver .retrieveArchivedAttachment( 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 1a0799fc7f..88eb206e92 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt @@ -226,7 +226,7 @@ class RestoreAttachmentJob private constructor( val stream = if (useArchiveCdn) { archiveFile = SignalDatabase.attachments.getOrCreateArchiveTransferFile(attachmentId) - val cdnCredentials = BackupRepository.getCdnReadCredentials().successOrThrow().headers + val cdnCredentials = BackupRepository.getCdnReadCredentials(attachment.archiveCdn).successOrThrow().headers messageReceiver .retrieveArchivedAttachment( diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt index 14482285f7..1928bd5d6b 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt @@ -55,12 +55,12 @@ class ArchiveApi( } } - fun getCdnReadCredentials(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential): NetworkResult { + fun getCdnReadCredentials(cdnNumber: Int, backupKey: BackupKey, serviceCredential: ArchiveServiceCredential): NetworkResult { return NetworkResult.fromFetch { val zkCredential = getZkCredential(backupKey, serviceCredential) val presentationData = CredentialPresentationData.from(backupKey, zkCredential, backupServerPublicParams) - pushServiceSocket.getArchiveCdnReadCredentials(presentationData.toArchiveCredentialPresentation()) + pushServiceSocket.getArchiveCdnReadCredentials(cdnNumber, presentationData.toArchiveCredentialPresentation()) } } @@ -225,7 +225,7 @@ class ArchiveApi( return backupRequestContext.receiveResponse( backupAuthResponse, backupServerPublicParams, - 20 + 201 ) } 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 2f7b0a9a2a..36a050d34f 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 @@ -311,7 +311,7 @@ public class PushServiceSocket { private static final String BACKUP_AUTH_CHECK = "/v2/backup/auth/check"; private static final String ARCHIVE_CREDENTIALS = "/v1/archives/auth?redemptionStartSeconds=%d&redemptionEndSeconds=%d"; - private static final String ARCHIVE_READ_CREDENTIALS = "/v1/archives/auth/read"; + private static final String ARCHIVE_READ_CREDENTIALS = "/v1/archives/auth/read?cdn=%d"; private static final String ARCHIVE_BACKUP_ID = "/v1/archives/backupid"; private static final String ARCHIVE_PUBLIC_KEY = "/v1/archives/keys"; private static final String ARCHIVE_INFO = "/v1/archives"; @@ -590,10 +590,10 @@ public ArchiveMessageBackupUploadFormResponse getArchiveMessageBackupUploadForm( /** * Copy and re-encrypt media from the attachments cdn into the backup cdn. */ - public GetArchiveCdnCredentialsResponse getArchiveCdnReadCredentials(@Nonnull ArchiveCredentialPresentation credentialPresentation) throws IOException { + public GetArchiveCdnCredentialsResponse getArchiveCdnReadCredentials(int cdnNumber, @Nonnull ArchiveCredentialPresentation credentialPresentation) throws IOException { Map headers = credentialPresentation.toHeaders(); - String response = makeServiceRequestWithoutAuthentication(ARCHIVE_READ_CREDENTIALS, "GET", null, headers, NO_HANDLER); + String response = makeServiceRequestWithoutAuthentication(String.format(Locale.US, ARCHIVE_READ_CREDENTIALS, cdnNumber), "GET", null, headers, NO_HANDLER); return JsonUtil.fromJson(response, GetArchiveCdnCredentialsResponse.class); } From 5e4dfcc65f4a2f06520b188294e58fc81ede9c59 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Tue, 16 Apr 2024 13:30:00 -0400 Subject: [PATCH 016/113] Add translator notes for some strings. --- app/src/main/res/values/strings.xml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db213e0b24..203d2c432f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -415,6 +415,7 @@ Join + Full Error sending media @@ -613,6 +614,7 @@ Unarchive Delete Select all + %d selected %d selected @@ -864,6 +866,7 @@ Invitation sent %d invitations sent + “%1$s” can’t be automatically added to this group by you.\n\nThey’ve been invited to join, and won’t see any group messages until they accept. These users can’t be automatically added to this group by you.\n\nThey’ve been invited to join the group, and won’t see any group messages until they accept. @@ -1044,7 +1047,9 @@ Edit name and picture Legacy Group + This is a Legacy Group. Features like group admins are only available for New Groups. + This is a Legacy Group. To access new features like @mentions and admins, This Legacy Group can’t be upgraded to a New Group because it is too large. The maximum group size is %1$d. upgrade this group. @@ -1271,6 +1276,7 @@ Delete + %1$d selected (%2$s) %1$d selected (%2$s) @@ -2083,6 +2089,7 @@ Unable to request a verification code. Please check network connection and try again. Non-standard number format + The number you entered (%1$s) appears to be a non-standard format.\n\nDid you mean %2$s? Signal Android - Phone Number Format @@ -2795,6 +2802,7 @@ Leave call The following people may have reinstalled or changed devices. Verify your safety number with them to ensure privacy. View + Previous verified @@ -3108,6 +3116,7 @@ Default High + Max @@ -3468,6 +3477,7 @@ Sent to %1$s You on %1$s at %2$s %1$s on %2$s at %3$s + To From Transaction details including the payment amount and time of transaction are part of the MobileCoin Ledger. @@ -3530,6 +3540,7 @@ Confirm payment Network fee Estimated %1$s + To Total amount Balance: %1$s @@ -4134,6 +4145,7 @@ Copied to clipboard Admin + Approve Deny @@ -4161,6 +4173,7 @@ Voice message · %1$s + %1$s to %2$s @@ -4207,6 +4220,7 @@ %1$s and %2$s joined %1$s, %2$s and %3$s joined %1$s, %2$s and %3$d others joined + %1$s left %1$s and %2$s left %1$s, %2$s and %3$s left @@ -4584,6 +4598,7 @@ Messaging Disappearing messages App security + Block screenshots in the recents list and inside the app Signal messages and calls, always relay calls, and sealed sender Default timer for new chats @@ -4640,11 +4655,14 @@ Media quality Sent media quality Sending high quality media will use more data. + High + Standard Calls + Auto Use custom colors Chat color @@ -4856,6 +4874,7 @@ Take a picture Choose a photo Photo + Text Save Clear avatar @@ -5509,7 +5528,7 @@ %1$s %2$s You - + %1$s to %2$s Reply From 7a2d408ca2dfe19418be3ca4b8901c5c4d4c6889 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Tue, 16 Apr 2024 14:50:37 -0400 Subject: [PATCH 017/113] Stop voice memo playback if the current item is deleted. Fixes #13502. --- .../voice/VoiceNotePlaybackService.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java index bdf5851508..a628098137 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java @@ -34,12 +34,15 @@ import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.database.DatabaseObserver; import org.thoughtcrime.securesms.database.MessageTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobs.MultiDeviceViewedUpdateJob; import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob; +import org.thoughtcrime.securesms.mms.PartUriParser; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.service.KeyCachingService; @@ -64,6 +67,8 @@ public class VoiceNotePlaybackService extends MediaSessionService { private KeyClearedReceiver keyClearedReceiver; private VoiceNotePlayerCallback voiceNotePlayerCallback; + private final DatabaseObserver.Observer attachmentDeletionObserver = this::onAttachmentDeleted; + @Override public void onCreate() { super.onCreate(); @@ -83,6 +88,7 @@ public void onCreate() { setMediaNotificationProvider(new VoiceNoteMediaNotificationProvider(this)); setListener(new MediaSessionServiceListener()); + ApplicationDependencies.getDatabaseObserver().registerAttachmentObserver(attachmentDeletionObserver); } @Override @@ -95,6 +101,7 @@ public void onTaskRemoved(Intent rootIntent) { @Override public void onDestroy() { + ApplicationDependencies.getDatabaseObserver().unregisterObserver(attachmentDeletionObserver); player.release(); mediaSession.release(); mediaSession = null; @@ -191,6 +198,28 @@ public void onAudioAttributesChanged(AudioAttributes audioAttributes) { } } + private void onAttachmentDeleted() { + Log.d(TAG, "Database attachment observer invoked."); + ContextCompat.getMainExecutor(getApplicationContext()).execute(() -> { + if (player != null) { + final MediaItem currentItem = player.getCurrentMediaItem(); + if (currentItem == null || currentItem.playbackProperties == null) { + Log.d(TAG, "Current item is null or playback properties are null."); + return; + } + final Uri currentUi = currentItem.playbackProperties.uri; + final DatabaseAttachment attachment = SignalDatabase.attachments().getAttachment(new PartUriParser(currentUi).getPartId()); + if (attachment == null) { + player.stop(); + int playingIndex = player.getCurrentMediaItemIndex(); + player.removeMediaItem(playingIndex); + Log.d(TAG, "Currently playing item removed."); + } else { + Log.d(TAG, "Attachment was not null, therefore not deleted, therefore no action taken."); + } + } + }); + } /** * Some devices, such as the ASUS Zenfone 8, erroneously report multiple broadcast receivers for {@value Intent#ACTION_MEDIA_BUTTON} in the package manager. * This triggers a failure within the {@link MediaSession} initialization and throws an {@link IllegalStateException}. From d8bbfe26783f98d5669fbee4cbddb282df0a01b1 Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 16 Apr 2024 15:01:16 -0400 Subject: [PATCH 018/113] Add archived media sync job. --- .../securesms/backup/v2/BackupRepository.kt | 46 ++++++++ .../ui/MessageBackupsTestRestoreViewModel.kt | 13 ++- .../InternalBackupPlaygroundViewModel.kt | 2 + .../securesms/database/AttachmentTable.kt | 13 ++- .../helpers/SignalDatabaseMigrations.kt | 6 +- .../V226_AddAttachmentMediaIdIndex.kt | 19 ++++ .../securesms/jobs/JobManagerFactories.java | 1 + .../securesms/jobs/SyncArchivedMediaJob.kt | 107 ++++++++++++++++++ .../signalservice/api/archive/ArchiveApi.kt | 19 +++- .../archive/ArchiveGetMediaItemsResponse.kt | 2 + 10 files changed, 215 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V226_AddAttachmentMediaIdIndex.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/SyncArchivedMediaJob.kt 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 367706530a..52cc26740f 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 @@ -224,6 +224,22 @@ object BackupRepository { Log.d(TAG, "import() ${eventTimer.stop().summary}") } + fun listRemoteMediaObjects(limit: Int, cursor: String? = null): NetworkResult { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return api + .triggerBackupIdReservation(backupKey) + .then { getAuthCredential() } + .then { credential -> + api.setPublicKey(backupKey, credential) + .map { credential } + } + .then { credential -> + api.getArchiveMediaItemsPage(backupKey, credential, limit, cursor) + } + } + /** * Returns an object with details about the remote backup state. */ @@ -420,6 +436,34 @@ object BackupRepository { .also { Log.i(TAG, "deleteArchivedMediaResult: $it") } } + fun deleteAbandonedMediaObjects(mediaObjects: Collection): NetworkResult { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + val mediaToDelete = mediaObjects + .map { + DeleteArchivedMediaRequest.ArchivedMediaObject( + cdn = it.cdn, + mediaId = it.mediaId + ) + } + + if (mediaToDelete.isEmpty()) { + Log.i(TAG, "No media to delete, quick success") + return NetworkResult.Success(Unit) + } + + return getAuthCredential() + .then { credential -> + api.deleteArchivedMedia( + backupKey = backupKey, + serviceCredential = credential, + mediaToDelete = mediaToDelete + ) + } + .also { Log.i(TAG, "deleteAbandonedMediaObjectsResult: $it") } + } + fun debugDeleteAllArchivedMedia(): NetworkResult { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() @@ -566,6 +610,8 @@ object BackupRepository { } } +data class ArchivedMediaObject(val mediaId: String, val cdn: Int) + data class BackupDirectories(val backupDir: String, val mediaDir: String) class ExportState(val backupTime: Long, val allowMediaBackup: Boolean) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt index fc3774f37c..071d0c5894 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt @@ -15,14 +15,15 @@ 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.orNull import org.signal.libsignal.zkgroup.profiles.ProfileKey import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.thoughtcrime.securesms.jobmanager.JobTracker import org.thoughtcrime.securesms.jobs.BackupRestoreJob +import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob +import org.thoughtcrime.securesms.jobs.SyncArchivedMediaJob import org.thoughtcrime.securesms.recipients.Recipient import java.io.InputStream +import kotlin.time.Duration.Companion.seconds class MessageBackupsTestRestoreViewModel : ViewModel() { val disposables = CompositeDisposable() @@ -47,8 +48,12 @@ class MessageBackupsTestRestoreViewModel : ViewModel() { fun restore() { _state.value = _state.value.copy(importState = ImportState.IN_PROGRESS) disposables += Single.fromCallable { - val jobState = ApplicationDependencies.getJobManager().runSynchronously(BackupRestoreJob(), 120_000) - jobState.orNull() == JobTracker.JobState.SUCCESS + ApplicationDependencies + .getJobManager() + .startChain(BackupRestoreJob()) + .then(SyncArchivedMediaJob()) + .then(BackupRestoreMediaJob()) + .enqueueAndBlockUntilCompletion(120.seconds.inWholeMilliseconds) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) 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 91414a28f1..63787909a0 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 @@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.jobs.AttachmentUploadJob import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.jobs.BackupRestoreJob import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob +import org.thoughtcrime.securesms.jobs.SyncArchivedMediaJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.IncomingMessage import org.thoughtcrime.securesms.recipients.Recipient @@ -168,6 +169,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { ApplicationDependencies .getJobManager() .startChain(BackupRestoreJob()) + .then(SyncArchivedMediaJob()) .then(BackupRestoreMediaJob()) .enqueueAndBlockUntilCompletion(120.seconds.inWholeMilliseconds) } 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 149bab4270..e10954a671 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -250,7 +250,8 @@ 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 attachment_data_index ON $TABLE_NAME ($DATA_FILE);", + "CREATE INDEX IF NOT EXISTS attachment_archive_media_id_index ON $TABLE_NAME ($ARCHIVE_MEDIA_ID);" ) val ATTACHMENT_POINTER_REUSE_THRESHOLD = 7.days.inWholeMilliseconds @@ -1300,6 +1301,16 @@ class AttachmentTable( .run() } + fun updateArchiveCdnByMediaId(archiveMediaId: String, archiveCdn: Int): Int { + return writableDatabase + .update(TABLE_NAME) + .values( + ARCHIVE_CDN to archiveCdn + ) + .where("$ARCHIVE_MEDIA_ID = ?", archiveMediaId) + .run() + } + fun clearArchiveData(attachmentIds: List) { SqlUtil.buildCollectionQuery(ID, attachmentIds.map { it.id }) .forEach { query -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index cc1d3741aa..9cffce8811 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -83,6 +83,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V222_DataHashRefact import org.thoughtcrime.securesms.database.helpers.migration.V223_AddNicknameAndNoteFieldsToRecipientTable import org.thoughtcrime.securesms.database.helpers.migration.V224_AddAttachmentArchiveColumns import org.thoughtcrime.securesms.database.helpers.migration.V225_AddLocalUserJoinedStateAndGroupCallActiveState +import org.thoughtcrime.securesms.database.helpers.migration.V226_AddAttachmentMediaIdIndex /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -168,10 +169,11 @@ object SignalDatabaseMigrations { 222 to V222_DataHashRefactor, 223 to V223_AddNicknameAndNoteFieldsToRecipientTable, 224 to V224_AddAttachmentArchiveColumns, - 225 to V225_AddLocalUserJoinedStateAndGroupCallActiveState + 225 to V225_AddLocalUserJoinedStateAndGroupCallActiveState, + 226 to V226_AddAttachmentMediaIdIndex ) - const val DATABASE_VERSION = 225 + const val DATABASE_VERSION = 226 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V226_AddAttachmentMediaIdIndex.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V226_AddAttachmentMediaIdIndex.kt new file mode 100644 index 0000000000..fd89b97879 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V226_AddAttachmentMediaIdIndex.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds index to archive_media_id + */ +@Suppress("ClassName") +object V226_AddAttachmentMediaIdIndex : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("CREATE INDEX IF NOT EXISTS attachment_archive_media_id_index ON attachment (archive_media_id);") + } +} 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 2e6c57546f..01553fac8f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -223,6 +223,7 @@ public static Map getJobFactories(@NonNull Application appl put(StoryOnboardingDownloadJob.KEY, new StoryOnboardingDownloadJob.Factory()); put(SubmitRateLimitPushChallengeJob.KEY, new SubmitRateLimitPushChallengeJob.Factory()); put(Svr2MirrorJob.KEY, new Svr2MirrorJob.Factory()); + put(SyncArchivedMediaJob.KEY, new SyncArchivedMediaJob.Factory()); put(ThreadUpdateJob.KEY, new ThreadUpdateJob.Factory()); put(TrimThreadJob.KEY, new TrimThreadJob.Factory()); put(TypingSendJob.KEY, new TypingSendJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SyncArchivedMediaJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/SyncArchivedMediaJob.kt new file mode 100644 index 0000000000..45fd928d09 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SyncArchivedMediaJob.kt @@ -0,0 +1,107 @@ +/* + * 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.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.backup.v2.ArchivedMediaObject +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse +import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException +import java.lang.Exception + +/** + * Job responsible for keeping remote archive media objects in sync. That is + * we make sure our CDN number aligns on all media ids, as well as deleting any + * extra media ids that we don't know about. + */ +class SyncArchivedMediaJob private constructor( + parameters: Parameters, + private var jobCursor: String? +) : BaseJob(parameters) { + + companion object { + private val TAG = Log.tag(BackupRestoreMediaJob::class.java) + + private const val KEY_CURSOR = "cursor" + + const val KEY = "SyncArchivedMediaJob" + } + + constructor(cursor: String? = null) : this( + Parameters.Builder() + .setQueue("SyncArchivedMedia") + .setMaxAttempts(Parameters.UNLIMITED) + .setMaxInstancesForQueue(2) + .build(), + cursor + ) + + override fun serialize(): ByteArray? { + return JsonJobData.Builder() + .putString(KEY_CURSOR, jobCursor) + .serialize() + } + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onRun() { + val batchSize = 100 + val attachmentsToDelete = HashSet() + var cursor: String? = jobCursor + do { + val archivedItemPage = BackupRepository.listRemoteMediaObjects(batchSize, cursor).successOrThrow() + attachmentsToDelete += syncPage(archivedItemPage) + cursor = archivedItemPage.cursor + if (attachmentsToDelete.size >= batchSize) { + BackupRepository.deleteAbandonedMediaObjects(attachmentsToDelete) + Log.i(TAG, "Deleted ${attachmentsToDelete.size} attachments off CDN") + attachmentsToDelete.clear() + } + if (attachmentsToDelete.isEmpty()) { + jobCursor = archivedItemPage.cursor + } + } while (cursor != null) + + if (attachmentsToDelete.isNotEmpty()) { + BackupRepository.deleteAbandonedMediaObjects(attachmentsToDelete) + Log.i(TAG, "Deleted ${attachmentsToDelete.size} attachments off CDN") + } + } + + /** + * Update CDNs of archived media items. Returns set of objects that don't match + * to a local attachment DB row. + */ + private fun syncPage(archivedItemPage: ArchiveGetMediaItemsResponse): Set { + val abandonedObjects = HashSet() + SignalDatabase.rawDatabase.withinTransaction { + archivedItemPage.storedMediaObjects.forEach { storedMediaObject -> + val rows = SignalDatabase.attachments.updateArchiveCdnByMediaId(archiveMediaId = storedMediaObject.mediaId, archiveCdn = storedMediaObject.cdn) + if (rows == 0) { + abandonedObjects.add(ArchivedMediaObject(storedMediaObject.mediaId, storedMediaObject.cdn)) + } + } + } + return abandonedObjects + } + + override fun onShouldRetry(e: Exception): Boolean { + return e is NetworkFailureException + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): SyncArchivedMediaJob { + val data = JsonJobData.deserialize(serializedData) + return SyncArchivedMediaJob(parameters, if (data.hasString(KEY_CURSOR)) data.getString(KEY_CURSOR) else null) + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt index 1928bd5d6b..04ac52f092 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt @@ -112,6 +112,17 @@ class ArchiveApi( } } + /** + * Lists the media objects in the backup + */ + fun listMediaObjects(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential, limit: Int, cursor: String? = null): NetworkResult { + return NetworkResult.fromFetch { + val zkCredential = getZkCredential(backupKey, serviceCredential) + val presentationData = CredentialPresentationData.from(backupKey, zkCredential, backupServerPublicParams) + pushServiceSocket.getArchiveMediaItemsPage(presentationData.toArchiveCredentialPresentation(), limit, cursor) + } + } + /** * Retrieves a resumable upload URL you can use to upload your main message backup file to cloud storage. */ @@ -136,15 +147,11 @@ class ArchiveApi( */ fun debugGetUploadedMediaItemMetadata(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential): NetworkResult> { return NetworkResult.fromFetch { - val zkCredential = getZkCredential(backupKey, serviceCredential) - val presentationData = CredentialPresentationData.from(backupKey, zkCredential, backupServerPublicParams) - val credentialPresentation = presentationData.toArchiveCredentialPresentation() - val mediaObjects: MutableList = ArrayList() var cursor: String? = null do { - val response: ArchiveGetMediaItemsResponse = pushServiceSocket.getArchiveMediaItemsPage(credentialPresentation, 512, cursor) + val response: ArchiveGetMediaItemsResponse = getArchiveMediaItemsPage(backupKey, serviceCredential, 512, cursor).successOrThrow() mediaObjects += response.storedMediaObjects cursor = response.cursor } while (cursor != null) @@ -158,7 +165,7 @@ class ArchiveApi( * @param limit The maximum number of items to return. * @param cursor A token that can be read from your previous response, telling the server where to start the next page. */ - fun getArchiveMediaItemsPage(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential, limit: Int, cursor: String): NetworkResult { + fun getArchiveMediaItemsPage(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential, limit: Int, cursor: String?): NetworkResult { return NetworkResult.fromFetch { val zkCredential = getZkCredential(backupKey, serviceCredential) val presentationData = CredentialPresentationData.from(backupKey, zkCredential, backupServerPublicParams) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetMediaItemsResponse.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetMediaItemsResponse.kt index a55b4d3bc3..a456fad647 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetMediaItemsResponse.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveGetMediaItemsResponse.kt @@ -12,6 +12,8 @@ import com.fasterxml.jackson.annotation.JsonProperty */ class ArchiveGetMediaItemsResponse( @JsonProperty val storedMediaObjects: List, + @JsonProperty val backupDir: String?, + @JsonProperty val mediaDir: String?, @JsonProperty val cursor: String? ) { class StoredMediaObject( From 5f31f5966c6e629b005a01a4c744f1282b53eeb9 Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 16 Apr 2024 15:01:32 -0400 Subject: [PATCH 019/113] Update backup locator proto. --- .../securesms/attachments/ArchivedAttachment.kt | 14 ++++++++------ .../backup/v2/database/ChatItemImportInserter.kt | 6 ++++-- app/src/main/protowire/Backup.proto | 8 +++++++- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt index 4c732d27a6..bda906d2a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt @@ -25,8 +25,10 @@ class ArchivedAttachment : Attachment { constructor( contentType: String?, size: Long, - cdn: Cdn, - cdnKey: ByteArray, + cdn: Int, + key: ByteArray, + cdnKey: String?, + archiveCdn: Int?, archiveMediaName: String, archiveMediaId: String, digest: ByteArray, @@ -46,9 +48,9 @@ class ArchivedAttachment : Attachment { transferState = AttachmentTable.TRANSFER_NEEDS_RESTORE, size = size, fileName = null, - cdn = cdn, - remoteLocation = null, - remoteKey = Base64.encodeWithoutPadding(cdnKey), + cdn = Cdn.fromCdnNumber(cdn), + remoteLocation = cdnKey, + remoteKey = Base64.encodeWithoutPadding(key), remoteDigest = digest, incrementalDigest = incrementalMac, fastPreflightId = null, @@ -65,7 +67,7 @@ class ArchivedAttachment : Attachment { audioHash = null, transformProperties = null ) { - this.archiveCdn = cdn.cdnNumber + this.archiveCdn = archiveCdn ?: Cdn.CDN_3.cdnNumber this.archiveMediaName = archiveMediaName this.archiveMediaId = archiveMediaId } 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 12337a2920..161fc3e9f0 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 @@ -618,8 +618,10 @@ class ChatItemImportInserter( return ArchivedAttachment( contentType = contentType, size = pointer.backupLocator.size.toLong(), - cdn = Cdn.fromCdnNumber(pointer.backupLocator.cdnNumber), - cdnKey = pointer.backupLocator.key.toByteArray(), + cdn = pointer.backupLocator.transitCdnNumber ?: Cdn.CDN_0.cdnNumber, + key = pointer.backupLocator.key.toByteArray(), + cdnKey = pointer.backupLocator.transitCdnKey, + archiveCdn = pointer.backupLocator.cdnNumber, archiveMediaName = pointer.backupLocator.mediaName, archiveMediaId = backupState.backupKey.deriveMediaId(MediaName(pointer.backupLocator.mediaName)).encode(), digest = pointer.backupLocator.digest.toByteArray(), diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index aa3208a861..f3a6c9213c 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -388,10 +388,16 @@ message FilePointer { // References attachments in the backup (media) storage tier. message BackupLocator { string mediaName = 1; - uint32 cdnNumber = 2; + // If present, the cdn number of the succesful upload. + // If empty/0, may still have been uploaded, and clients + // can discover the cdn number via the list endpoint. + optional uint32 cdnNumber = 2; bytes key = 3; bytes digest = 4; uint32 size = 5; + // Fallback in case backup tier upload failed. + optional string transitCdnKey = 6; + optional uint32 transitCdnNumber = 7; } // References attachments in the transit storage tier. From cd03da54d5dd67de78411ab2770f3cf463422afd Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Tue, 16 Apr 2024 15:36:46 -0400 Subject: [PATCH 020/113] Fix note to self message detail text. --- .../thoughtcrime/securesms/components/FromTextView.java | 8 +++++++- .../securesms/messagedetails/RecipientViewHolder.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java index 6202548b5a..b254011987 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java @@ -40,9 +40,15 @@ public void setText(Recipient recipient, @Nullable CharSequence fromString, @Nul } public void setText(Recipient recipient, @Nullable CharSequence fromString, @Nullable CharSequence suffix, boolean asThread) { + setText(recipient, fromString, suffix, asThread, false); + } + + public void setText(Recipient recipient, @Nullable CharSequence fromString, @Nullable CharSequence suffix, boolean asThread, boolean showSelfAsYou) { SpannableStringBuilder builder = new SpannableStringBuilder(); - if (asThread && recipient.isSelf()) { + if (asThread && recipient.isSelf() && showSelfAsYou) { + builder.append(getContext().getString(R.string.Recipient_you)); + } else if (asThread && recipient.isSelf()) { builder.append(getContext().getString(R.string.note_to_self)); } else { builder.append(fromString); diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/RecipientViewHolder.java b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/RecipientViewHolder.java index 347a09f52b..6a9cf28268 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/RecipientViewHolder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/RecipientViewHolder.java @@ -41,7 +41,7 @@ final class RecipientViewHolder extends RecyclerView.ViewHolder { void bind(RecipientDeliveryStatus data) { unidentifiedDeliveryIcon.setVisibility(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(itemView.getContext()) && data.isUnidentified() ? View.VISIBLE : View.GONE); - fromView.setText(data.getRecipient()); + fromView.setText(data.getRecipient(), data.getRecipient().getDisplayName(itemView.getContext()), null, true, true); avatar.setRecipient(data.getRecipient()); badge.setBadgeFromRecipient(data.getRecipient()); From cbb04e8f0c63a3e888b19380b83ab20f07050a18 Mon Sep 17 00:00:00 2001 From: Jim Gustafson Date: Wed, 17 Apr 2024 07:32:29 -0700 Subject: [PATCH 021/113] Update to RingRTC v2.40.0 --- .../service/webrtc/SignalCallManager.java | 20 ++++++++++++++----- dependencies.gradle.kts | 2 +- gradle/verification-metadata.xml | 10 +++++----- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java index c5a3054aa7..1d35497222 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java @@ -91,6 +91,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -757,10 +758,10 @@ public void onSendBusy(@NonNull CallId callId, @Nullable Remote remote, @NonNull } @Override - public void onSendCallMessage(@NonNull UUID aciUuid, @NonNull byte[] bytes, @NonNull CallManager.CallMessageUrgency urgency) { + public void onSendCallMessage(@NonNull UUID aciUuid, @NonNull byte[] message, @NonNull CallManager.CallMessageUrgency urgency) { Log.i(TAG, "onSendCallMessage():"); - OpaqueMessage opaqueMessage = new OpaqueMessage(bytes, getUrgencyFromCallUrgency(urgency)); + OpaqueMessage opaqueMessage = new OpaqueMessage(message, getUrgencyFromCallUrgency(urgency)); SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOpaque(opaqueMessage, null); networkExecutor.execute(() -> { @@ -774,27 +775,36 @@ public void onSendCallMessage(@NonNull UUID aciUuid, @NonNull byte[] bytes, @Non recipient.isSelf() ? Optional.empty() : UnidentifiedAccessUtil.getAccessFor(context, recipient), callMessage); } catch (UntrustedIdentityException e) { - Log.i(TAG, "sendOpaqueCallMessage onFailure: ", e); + Log.i(TAG, "onSendCallMessage onFailure: ", e); RetrieveProfileJob.enqueue(recipient.getId()); process((s, p) -> p.handleGroupMessageSentError(s, Collections.singletonList(recipient.getId()), UNTRUSTED_IDENTITY)); } catch (IOException e) { - Log.i(TAG, "sendOpaqueCallMessage onFailure: ", e); + Log.i(TAG, "onSendCallMessage onFailure: ", e); process((s, p) -> p.handleGroupMessageSentError(s, Collections.singletonList(recipient.getId()), NETWORK_FAILURE)); } }); } @Override - public void onSendCallMessageToGroup(@NonNull byte[] groupIdBytes, @NonNull byte[] message, @NonNull CallManager.CallMessageUrgency urgency) { + public void onSendCallMessageToGroup(@NonNull byte[] groupIdBytes, @NonNull byte[] message, @NonNull CallManager.CallMessageUrgency urgency, @NonNull List overrideRecipients) { Log.i(TAG, "onSendCallMessageToGroup():"); networkExecutor.execute(() -> { try { GroupId groupId = GroupId.v2(new GroupIdentifier(groupIdBytes)); List recipients = SignalDatabase.groups().getGroupMembers(groupId, GroupTable.MemberSet.FULL_MEMBERS_EXCLUDING_SELF); + Set toInclude = new HashSet<>(overrideRecipients.size()); + + toInclude.addAll(overrideRecipients); recipients = RecipientUtil.getEligibleForSending((recipients.stream() .map(Recipient::resolve) + .filter(r -> { + if (toInclude.isEmpty()) { + return true; + } + return r.getHasServiceId() && toInclude.contains(r.requireServiceId().getRawUuid()); + }) .collect(Collectors.toList()))); OpaqueMessage opaqueMessage = new OpaqueMessage(message, getUrgencyFromCallUrgency(urgency)); diff --git a/dependencies.gradle.kts b/dependencies.gradle.kts index 7425b277c4..23258f7a48 100644 --- a/dependencies.gradle.kts +++ b/dependencies.gradle.kts @@ -120,7 +120,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.39.3") + library("signal-ringrtc", "org.signal:ringrtc-android:2.40.0") library("signal-android-database-sqlcipher", "org.signal:sqlcipher-android:4.5.4-S2") // Third Party diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index ac7b8f07b8..4b88b89e2f 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5719,12 +5719,12 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + From f673c4eb835dfae910261c0b25a581a5b02c900c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 17 Apr 2024 11:50:03 -0400 Subject: [PATCH 022/113] Remove sql language annotation (for now). It's broken in newer versions of Android Studio. It doesn't seem to allow partial-sql anymore, only fully-formed statements. Same with roomsql. --- .../signal/core/util/SQLiteDatabaseExtensions.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/core-util/src/main/java/org/signal/core/util/SQLiteDatabaseExtensions.kt b/core-util/src/main/java/org/signal/core/util/SQLiteDatabaseExtensions.kt index dc67a323f4..4f41107b39 100644 --- a/core-util/src/main/java/org/signal/core/util/SQLiteDatabaseExtensions.kt +++ b/core-util/src/main/java/org/signal/core/util/SQLiteDatabaseExtensions.kt @@ -6,7 +6,6 @@ import android.database.sqlite.SQLiteDatabase import androidx.core.content.contentValuesOf import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteQueryBuilder -import org.intellij.lang.annotations.Language /** * Begins a transaction on the `this` database, runs the provided [block] providing the `this` value as it's argument @@ -168,7 +167,7 @@ class SelectBuilderPart2( private val columns: Array, private val tableName: String ) { - fun where(@Language("sql") where: String, vararg whereArgs: Any): SelectBuilderPart3 { + fun where(where: String, vararg whereArgs: Any): SelectBuilderPart3 { return SelectBuilderPart3(db, columns, tableName, where, SqlUtil.buildArgs(*whereArgs)) } @@ -316,12 +315,12 @@ class UpdateBuilderPart2( private val tableName: String, private val values: ContentValues ) { - fun where(@Language("sql") where: String, vararg whereArgs: Any): UpdateBuilderPart3 { + fun where(where: String, vararg whereArgs: Any): UpdateBuilderPart3 { require(where.isNotBlank()) return UpdateBuilderPart3(db, tableName, values, where, SqlUtil.buildArgs(*whereArgs)) } - fun where(@Language("sql") where: String, whereArgs: Array): UpdateBuilderPart3 { + fun where(where: String, whereArgs: Array): UpdateBuilderPart3 { require(where.isNotBlank()) return UpdateBuilderPart3(db, tableName, values, where, whereArgs) } @@ -368,12 +367,12 @@ class DeleteBuilderPart1( private val db: SupportSQLiteDatabase, private val tableName: String ) { - fun where(@Language("sql") where: String, vararg whereArgs: Any): DeleteBuilderPart2 { + fun where(where: String, vararg whereArgs: Any): DeleteBuilderPart2 { require(where.isNotBlank()) return DeleteBuilderPart2(db, tableName, where, SqlUtil.buildArgs(*whereArgs)) } - fun where(@Language("sql") where: String, whereArgs: Array): DeleteBuilderPart2 { + fun where(where: String, whereArgs: Array): DeleteBuilderPart2 { require(where.isNotBlank()) return DeleteBuilderPart2(db, tableName, where, whereArgs) } @@ -395,11 +394,11 @@ class ExistsBuilderPart1( private val tableName: String ) { - fun where(@Language("sql") where: String, vararg whereArgs: Any): ExistsBuilderPart2 { + fun where(where: String, vararg whereArgs: Any): ExistsBuilderPart2 { return ExistsBuilderPart2(db, tableName, where, SqlUtil.buildArgs(*whereArgs)) } - fun where(@Language("sql") where: String, whereArgs: Array): ExistsBuilderPart2 { + fun where(where: String, whereArgs: Array): ExistsBuilderPart2 { return ExistsBuilderPart2(db, tableName, where, whereArgs) } From 7a6bd0e1f2fa60d18b6df0cdf7f4b548b3b520a9 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 17 Apr 2024 12:53:09 -0400 Subject: [PATCH 023/113] Revert "Remove vestigial call camera toggle button." This reverts commit 7a9c01e6e5f317234def04fc9de33507c1387361. --- .../components/webrtc/WebRtcCallView.java | 6 ++++++ .../controls/ControlsAndInfoController.kt | 3 ++- .../main/res/layout/webrtc_call_controls.xml | 20 +++++++++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java index 01565857a6..ea190d0996 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java @@ -96,6 +96,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { private RecipientId recipientId; private ImageView answer; private TextView answerWithoutVideoLabel; + private ImageView cameraDirectionToggle; private AccessibleToggleButton ringToggle; private PictureInPictureGestureHelper pictureInPictureGestureHelper; private ImageView overflow; @@ -177,6 +178,7 @@ protected void onFinishInflate() { incomingRingStatus = findViewById(R.id.call_screen_incoming_ring_status); answer = findViewById(R.id.call_screen_answer_call); answerWithoutVideoLabel = findViewById(R.id.call_screen_answer_without_video_label); + cameraDirectionToggle = findViewById(R.id.call_screen_camera_direction_toggle); ringToggle = findViewById(R.id.call_screen_audio_ring_toggle); overflow = findViewById(R.id.call_screen_overflow_button); hangup = findViewById(R.id.call_screen_end_call); @@ -271,6 +273,7 @@ public void onPageSelected(int position) { runIfNonNull(controlsListener, listener -> listener.onRingGroupChanged(isOn, ringToggle.isActivated())); }); + cameraDirectionToggle.setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onCameraDirectionChanged)); smallLocalRender.findViewById(R.id.call_participant_switch_camera).setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onCameraDirectionChanged)); overflow.setOnClickListener(v -> { @@ -352,6 +355,7 @@ public void onPageSelected(int position) { rotatableControls.add(audioToggle); rotatableControls.add(micToggle); rotatableControls.add(videoToggle); + rotatableControls.add(cameraDirectionToggle); rotatableControls.add(decline); rotatableControls.add(smallLocalAudioIndicator); rotatableControls.add(ringToggle); @@ -917,6 +921,7 @@ private static void runIfNonNull(@Nullable T listener, @NonNull Consumer } private void updateButtonStateForLargeButtons() { + cameraDirectionToggle.setImageResource(R.drawable.webrtc_call_screen_camera_toggle); hangup.setImageResource(R.drawable.webrtc_call_screen_hangup); overflow.setImageResource(R.drawable.webrtc_call_screen_overflow_menu); micToggle.setBackgroundResource(R.drawable.webrtc_call_screen_mic_toggle); @@ -927,6 +932,7 @@ private void updateButtonStateForLargeButtons() { } private void updateButtonStateForSmallButtons() { + cameraDirectionToggle.setImageResource(R.drawable.webrtc_call_screen_camera_toggle_small); hangup.setImageResource(R.drawable.webrtc_call_screen_hangup_small); overflow.setImageResource(R.drawable.webrtc_call_screen_overflow_menu_small); micToggle.setBackgroundResource(R.drawable.webrtc_call_screen_mic_toggle_small); 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 9d64922890..4a3fed6ee7 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 @@ -209,7 +209,7 @@ class ControlsAndInfoController( } } - private fun onControlTopChanged() { + fun onControlTopChanged() { val guidelineTop = max(frame.top, coordinator.height - behavior.peekHeight) aboveControlsGuideline.setGuidelineBegin(guidelineTop) webRtcCallView.onControlTopChanged() @@ -321,6 +321,7 @@ class ControlsAndInfoController( val margin = if (controlState.displaySmallCallButtons()) 4.dp else 8.dp setControlConstraints(R.id.call_screen_speaker_toggle, controlState.displayAudioToggle(), margin) + setControlConstraints(R.id.call_screen_camera_direction_toggle, controlState.displayCameraToggle(), margin) setControlConstraints(R.id.call_screen_video_toggle, controlState.displayVideoToggle(), margin) setControlConstraints(R.id.call_screen_audio_mic_toggle, controlState.displayMuteAudio(), margin) setControlConstraints(R.id.call_screen_audio_ring_toggle, controlState.displayRingToggle(), margin) diff --git a/app/src/main/res/layout/webrtc_call_controls.xml b/app/src/main/res/layout/webrtc_call_controls.xml index 76ccefabd2..1b62cad7e9 100644 --- a/app/src/main/res/layout/webrtc_call_controls.xml +++ b/app/src/main/res/layout/webrtc_call_controls.xml @@ -56,12 +56,28 @@ android:scaleType="fitXY" android:visibility="gone" app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls" - app:layout_constraintEnd_toStartOf="@id/call_screen_video_toggle" + app:layout_constraintEnd_toStartOf="@id/call_screen_camera_direction_toggle" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:srcCompat="@drawable/webrtc_call_screen_speaker_toggle" tools:visibility="visible" /> + + Date: Wed, 17 Apr 2024 13:30:17 -0400 Subject: [PATCH 024/113] Fix missing send button for voice notes. --- .../securesms/conversation/v2/ConversationFragment.kt | 4 ++++ 1 file changed, 4 insertions(+) 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 81a2c43750..2fd6cd0ded 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 @@ -648,6 +648,10 @@ class ConversationFragment : conversationGroupViewModel.updateGroupStateIfNeeded() + if (inputPanel.voiceNoteDraft != null) { + updateToggleButtonState() + } + if (SignalStore.rateLimit().needsRecaptcha()) { RecaptchaProofBottomSheetFragment.show(childFragmentManager) } From 58282e589b366737831a3c5c12ad5633d8e9d173 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 17 Apr 2024 14:49:06 -0300 Subject: [PATCH 025/113] Implement backups settings fragment. --- .../backup/v2/ui/MessageBackupsFrequency.kt | 16 + .../backup/v2/ui/MessageBackupsTypeFeature.kt | 16 + .../ui/MessageBackupsTypeSelectionScreen.kt | 2 + ...CreateCallLinkBottomSheetDialogFragment.kt | 9 +- .../links/details/CallLinkDetailsFragment.kt | 10 +- .../app/chats/ChatsSettingsFragment.kt | 19 +- .../settings/app/chats/ChatsSettingsState.kt | 3 +- .../app/chats/ChatsSettingsViewModel.kt | 7 +- .../backups/RemoteBackupsSettingsFragment.kt | 570 ++++++++++++++++++ .../backups/RemoteBackupsSettingsState.kt | 33 + .../backups/RemoteBackupsSettingsViewModel.kt | 43 ++ .../webrtc/controls/CallInfoView.kt | 13 +- .../requests/CallLinkIncomingRequestSheet.kt | 9 +- .../securesms/keyvalue/BackupValues.kt | 7 + app/src/main/res/navigation/app_settings.xml | 11 + .../src/main/java/org/signal/core/ui/Rows.kt | 82 ++- .../java/org/signal/core/ui/SignalPreview.kt | 17 + 17 files changed, 806 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFrequency.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsViewModel.kt create mode 100644 core-ui/src/main/java/org/signal/core/ui/SignalPreview.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFrequency.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFrequency.kt new file mode 100644 index 0000000000..777238b225 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFrequency.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.ui + +/** + * Describes how often a users messages are backed up. + */ +enum class MessageBackupsFrequency { + DAILY, + WEEKLY, + MONTHLY, + NEVER +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeFeature.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeFeature.kt index da608ad624..fb037d0e0a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeFeature.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeFeature.kt @@ -18,6 +18,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import org.signal.core.ui.Previews +import org.signal.core.ui.SignalPreview +import org.thoughtcrime.securesms.R /** * Represents a "Feature" included for a specify tier of message backups @@ -53,3 +56,16 @@ fun MessageBackupsTypeFeatureRow( ) } } + +@SignalPreview +@Composable +private fun MessageBackupsTypeFeatureRowPreview() { + Previews.Preview { + MessageBackupsTypeFeatureRow( + messageBackupsTypeFeature = MessageBackupsTypeFeature( + iconResourceId = R.drawable.symbol_edit_24, + label = "Content Label" + ) + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeSelectionScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeSelectionScreen.kt index e69242525f..c1b38f299a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeSelectionScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeSelectionScreen.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -269,6 +270,7 @@ private fun formatCostPerMonth(pricePerMonth: FiatMoney): String { } } +@Stable data class MessageBackupsType( val pricePerMonth: FiatMoney, val title: String, diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt index 1be045338f..6d9a9f3a32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt @@ -24,10 +24,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.core.app.ShareCompat @@ -128,19 +127,19 @@ class CreateCallLinkBottomSheetDialogFragment : ComposeBottomSheetDialogFragment Rows.TextRow( text = stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__share_link_via_signal), - icon = ImageVector.vectorResource(id = R.drawable.symbol_forward_24), + icon = painterResource(id = R.drawable.symbol_forward_24), onClick = this@CreateCallLinkBottomSheetDialogFragment::onShareViaSignalClicked ) Rows.TextRow( text = stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__copy_link), - icon = ImageVector.vectorResource(id = R.drawable.symbol_copy_android_24), + icon = painterResource(id = R.drawable.symbol_copy_android_24), onClick = this@CreateCallLinkBottomSheetDialogFragment::onCopyLinkClicked ) Rows.TextRow( text = stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__share_link), - icon = ImageVector.vectorResource(id = R.drawable.symbol_share_android_24), + icon = painterResource(id = R.drawable.symbol_share_android_24), onClick = this@CreateCallLinkBottomSheetDialogFragment::onShareLinkClicked ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt index f0689ac88a..023f7b54f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt @@ -17,10 +17,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat @@ -287,25 +285,25 @@ private fun CallLinkDetails( Rows.TextRow( text = stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__share_link_via_signal), - icon = ImageVector.vectorResource(id = R.drawable.symbol_forward_24), + icon = painterResource(id = R.drawable.symbol_forward_24), onClick = callback::onShareLinkViaSignalClicked ) Rows.TextRow( text = stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__copy_link), - icon = ImageVector.vectorResource(id = R.drawable.symbol_copy_android_24), + icon = painterResource(id = R.drawable.symbol_copy_android_24), onClick = callback::onCopyClicked ) Rows.TextRow( text = stringResource(id = R.string.CallLinkDetailsFragment__share_link), - icon = ImageVector.vectorResource(id = R.drawable.symbol_link_24), + icon = painterResource(id = R.drawable.symbol_link_24), onClick = callback::onShareClicked ) Rows.TextRow( text = stringResource(id = R.string.CallLinkDetailsFragment__delete_call_link), - icon = ImageVector.vectorResource(id = R.drawable.symbol_trash_24), + icon = painterResource(id = R.drawable.symbol_trash_24), foregroundTint = MaterialTheme.colorScheme.error, onClick = callback::onDeleteClicked ) 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 216bba9715..bab62d48ca 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,12 +1,15 @@ package org.thoughtcrime.securesms.components.settings.app.chats +import android.content.Intent import androidx.lifecycle.ViewModelProvider import androidx.navigation.Navigation import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFlowActivity 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.configure +import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -81,9 +84,23 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch sectionHeaderPref(R.string.preferences_chats__backups) + if (FeatureFlags.messageBackups() || state.remoteBackupsEnabled) { + clickPref( + title = DSLSettingsText.from("Signal Backups"), // TODO [message-backups] -- Finalized copy + summary = DSLSettingsText.from(if (state.remoteBackupsEnabled) R.string.arrays__enabled else R.string.arrays__disabled), + onClick = { + if (state.remoteBackupsEnabled) { + Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_remoteBackupsSettingsFragment) + } else { + startActivity(Intent(requireContext(), MessageBackupsFlowActivity::class.java)) + } + } + ) + } + clickPref( title = DSLSettingsText.from(R.string.preferences_chats__chat_backups), - summary = DSLSettingsText.from(if (state.chatBackupsEnabled) R.string.arrays__enabled else R.string.arrays__disabled), + 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 f47f48d11a..8429a69eb8 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,5 +6,6 @@ data class ChatsSettingsState( val keepMutedChatsArchived: Boolean, val useSystemEmoji: Boolean, val enterKeySends: Boolean, - val chatBackupsEnabled: Boolean + val localBackupsEnabled: Boolean, + val remoteBackupsEnabled: 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 2062181367..87ea0a647f 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 @@ -22,7 +22,8 @@ class ChatsSettingsViewModel @JvmOverloads constructor( keepMutedChatsArchived = SignalStore.settings().shouldKeepMutedChatsArchived(), useSystemEmoji = SignalStore.settings().isPreferSystemEmoji, enterKeySends = SignalStore.settings().isEnterKeySends, - chatBackupsEnabled = SignalStore.settings().isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(ApplicationDependencies.getApplication()) + localBackupsEnabled = SignalStore.settings().isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(ApplicationDependencies.getApplication()), + remoteBackupsEnabled = SignalStore.backup().areBackupsEnabled ) ) @@ -59,8 +60,8 @@ class ChatsSettingsViewModel @JvmOverloads constructor( fun refresh() { val backupsEnabled = SignalStore.settings().isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(ApplicationDependencies.getApplication()) - if (store.state.chatBackupsEnabled != backupsEnabled) { - store.update { it.copy(chatBackupsEnabled = backupsEnabled) } + if (store.state.localBackupsEnabled != backupsEnabled) { + store.update { it.copy(localBackupsEnabled = backupsEnabled) } } } } 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/chats/backups/RemoteBackupsSettingsFragment.kt new file mode 100644 index 0000000000..f8d2294640 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt @@ -0,0 +1,570 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups + +import android.content.Intent +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.Stable +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.unit.dp +import androidx.navigation.fragment.findNavController +import kotlinx.collections.immutable.persistentListOf +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.Snackbars +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.MessageBackupsFlowActivity +import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFrequency +import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsType +import org.thoughtcrime.securesms.compose.ComposeFragment +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.Currency +import java.util.Locale + +/** + * Remote backups settings fragment. + * + * TODO [message-backups] -- All copy in this file is non-final + */ +class RemoteBackupsSettingsFragment : ComposeFragment() { + + private val viewModel by viewModel { + RemoteBackupsSettingsViewModel() + } + + @Composable + override fun FragmentContent() { + val state by viewModel.state + val callbacks = remember { Callbacks() } + + RemoteBackupsSettingsContent( + messageBackupsType = state.messageBackupsType, + lastBackupTimestamp = state.lastBackupTimestamp, + canBackUpUsingCellular = state.canBackUpUsingCellular, + backupsFrequency = state.backupsFrequency, + requestedDialog = state.dialog, + requestedSnackbar = state.snackbar, + contentCallbacks = callbacks + ) + } + + @Stable + private inner class Callbacks : ContentCallbacks { + override fun onNavigationClick() { + findNavController().popBackStack() + } + + override fun onEnableBackupsClick() { + startActivity(Intent(requireContext(), MessageBackupsFlowActivity::class.java)) + } + + override fun onBackUpUsingCellularClick(canUseCellular: Boolean) { + viewModel.setCanBackUpUsingCellular(canUseCellular) + } + + override fun onViewPaymentHistory() { + // TODO [message-backups] Navigate to payment history + } + + override fun onBackupNowClick() { + // TODO [message-backups] Enqueue immediate backup + } + + override fun onTurnOffAndDeleteBackupsClick() { + viewModel.requestDialog(RemoteBackupsSettingsState.Dialog.TURN_OFF_AND_DELETE_BACKUPS) + } + + override fun onChangeBackupFrequencyClick() { + viewModel.requestDialog(RemoteBackupsSettingsState.Dialog.BACKUP_FREQUENCY) + } + + override fun onDialogDismissed() { + viewModel.requestDialog(RemoteBackupsSettingsState.Dialog.NONE) + } + + override fun onSnackbarDismissed() { + viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.NONE) + } + + override fun onSelectBackupsFrequencyChange(newFrequency: MessageBackupsFrequency) { + viewModel.setBackupsFrequency(newFrequency) + } + + override fun onTurnOffAndDeleteBackupsConfirm() { + viewModel.turnOffAndDeleteBackups() + } + + override fun onChangeBackupsTypeClick() { + // TODO - launch flow at appropriate point + } + } +} + +/** + * Callback interface for RemoteBackupsSettingsContent composable. + */ +private interface ContentCallbacks { + fun onNavigationClick() = Unit + fun onEnableBackupsClick() = Unit + fun onChangeBackupsTypeClick() = Unit + fun onBackUpUsingCellularClick(canUseCellular: Boolean) = Unit + fun onViewPaymentHistory() = Unit + fun onBackupNowClick() = Unit + fun onTurnOffAndDeleteBackupsClick() = Unit + fun onChangeBackupFrequencyClick() = Unit + fun onDialogDismissed() = Unit + fun onSnackbarDismissed() = Unit + fun onSelectBackupsFrequencyChange(newFrequency: MessageBackupsFrequency) = Unit + fun onTurnOffAndDeleteBackupsConfirm() = Unit +} + +@Composable +private fun RemoteBackupsSettingsContent( + messageBackupsType: MessageBackupsType?, + lastBackupTimestamp: Long, + canBackUpUsingCellular: Boolean, + backupsFrequency: MessageBackupsFrequency, + requestedDialog: RemoteBackupsSettingsState.Dialog, + requestedSnackbar: RemoteBackupsSettingsState.Snackbar, + contentCallbacks: ContentCallbacks +) { + val snackbarHostState = remember { + SnackbarHostState() + } + + Scaffolds.Settings( + title = "Signal Backups", + onNavigationClick = contentCallbacks::onNavigationClick, + navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24), + snackbarHost = { + Snackbars.Host(snackbarHostState = snackbarHostState) + } + ) { + LazyColumn( + modifier = Modifier + .padding(it) + ) { + item { + BackupTypeRow( + messageBackupsType = messageBackupsType, + onEnableBackupsClick = contentCallbacks::onEnableBackupsClick, + onChangeBackupsTypeClick = contentCallbacks::onChangeBackupsTypeClick + ) + } + + if (messageBackupsType == null) { + item { + Rows.TextRow( + text = "Payment history", + onClick = contentCallbacks::onViewPaymentHistory + ) + } + } else { + item { + Dividers.Default() + } + + item { + Texts.SectionHeader(text = "Backup Details") + } + + item { + LastBackupRow( + lastBackupTimestamp = lastBackupTimestamp, + onBackupNowClick = {} + ) + } + + item { + Rows.TextRow(text = { + Column { + Text( + text = "Backup size", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = "2.3GB", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + }) + } + + item { + Rows.TextRow( + text = { + Column { + Text( + text = "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 + ) + } + + item { + Rows.ToggleRow( + checked = canBackUpUsingCellular, + text = "Back up using cellular", + onCheckChanged = contentCallbacks::onBackUpUsingCellularClick + ) + } + + item { + Dividers.Default() + } + + item { + Rows.TextRow( + text = "Turn off and delete backup", + foregroundTint = MaterialTheme.colorScheme.error, + onClick = contentCallbacks::onTurnOffAndDeleteBackupsClick + ) + } + } + } + } + + when (requestedDialog) { + RemoteBackupsSettingsState.Dialog.NONE -> {} + RemoteBackupsSettingsState.Dialog.TURN_OFF_AND_DELETE_BACKUPS -> { + TurnOffAndDeleteBackupsDialog( + onConfirm = contentCallbacks::onTurnOffAndDeleteBackupsConfirm, + onDismiss = contentCallbacks::onDialogDismissed + ) + } + + RemoteBackupsSettingsState.Dialog.BACKUP_FREQUENCY -> { + BackupFrequencyDialog( + selected = backupsFrequency, + onSelected = contentCallbacks::onSelectBackupsFrequencyChange, + onDismiss = contentCallbacks::onDialogDismissed + ) + } + } + + LaunchedEffect(requestedSnackbar) { + when (requestedSnackbar) { + RemoteBackupsSettingsState.Snackbar.NONE -> { + snackbarHostState.currentSnackbarData?.dismiss() + } + + RemoteBackupsSettingsState.Snackbar.BACKUP_DELETED_AND_TURNED_OFF -> { + snackbarHostState.showSnackbar( + "Backup deleted and turned off" + ) + } + + RemoteBackupsSettingsState.Snackbar.BACKUP_TYPE_CHANGED_AND_SUBSCRIPTION_CANCELLED -> { + snackbarHostState.showSnackbar( + "Backup type changed and subscription cancelled" + ) + } + + RemoteBackupsSettingsState.Snackbar.SUBSCRIPTION_CANCELLED -> { + snackbarHostState.showSnackbar( + "Subscription cancelled" + ) + } + + RemoteBackupsSettingsState.Snackbar.DOWNLOAD_COMPLETE -> { + snackbarHostState.showSnackbar( + "Download complete" + ) + } + } + contentCallbacks.onSnackbarDismissed() + } +} + +@Composable +private fun BackupTypeRow( + messageBackupsType: MessageBackupsType?, + onEnableBackupsClick: () -> Unit, + onChangeBackupsTypeClick: () -> Unit +) { + Row( + 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) + ) { + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = "Backup type", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface + ) + + if (messageBackupsType == null) { + Text( + text = "Backups disabled", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } else { + val localResources = LocalContext.current.resources + val formattedCurrency = remember(messageBackupsType.pricePerMonth) { + FiatMoneyUtil.format(localResources, messageBackupsType.pricePerMonth, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) + } + + Text( + text = "${messageBackupsType.title} · $formattedCurrency/month" + ) + } + } + + if (messageBackupsType == null) { + Buttons.Small(onClick = onEnableBackupsClick) { + Text(text = "Enable backups") + } + } + } +} + +@Composable +private fun LastBackupRow( + lastBackupTimestamp: Long, + onBackupNowClick: () -> Unit +) { + Row( + modifier = Modifier + .padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter)) + .padding(top = 16.dp, bottom = 14.dp) + ) { + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = "Last backup", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface + ) + + if (lastBackupTimestamp > 0) { + val context = LocalContext.current + + val day = remember(lastBackupTimestamp) { + DateUtils.getDayPrecisionTimeString(context, Locale.getDefault(), lastBackupTimestamp) + } + + val time = remember(lastBackupTimestamp) { + DateUtils.getOnlyTimeString(context, lastBackupTimestamp) + } + + Text( + text = "$day at $time", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } else { + Text( + text = "Never", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + + Buttons.Small(onClick = onBackupNowClick) { + Text(text = "Back up now") + } + } +} + +@Composable +private fun TurnOffAndDeleteBackupsDialog( + onConfirm: () -> Unit, + onDismiss: () -> Unit +) { + Dialogs.SimpleAlertDialog( + title = "Turn off and delete backups?", + body = "You will not be charged again. Your backup will be deleted and no new backups will be created.", + confirm = "Turn off and delete", + dismiss = stringResource(id = android.R.string.cancel), + confirmColor = MaterialTheme.colorScheme.error, + onConfirm = onConfirm, + onDismiss = onDismiss + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun BackupFrequencyDialog( + selected: MessageBackupsFrequency, + onSelected: (MessageBackupsFrequency) -> Unit, + onDismiss: () -> Unit +) { + AlertDialog( + onDismissRequest = onDismiss + ) { + Column( + modifier = Modifier + .background( + color = AlertDialogDefaults.containerColor, + shape = AlertDialogDefaults.shape + ) + .fillMaxWidth() + ) { + Text( + text = "Backup frequency", + style = MaterialTheme.typography.headlineMedium, + modifier = Modifier.padding(24.dp) + ) + + MessageBackupsFrequency.values().forEach { + Rows.RadioRow( + selected = selected == it, + text = getTextForFrequency(backupsFrequency = it), + label = when (it) { + MessageBackupsFrequency.NEVER -> "By tapping \"Back up now\"" + else -> null + }, + modifier = Modifier + .padding(end = 24.dp) + .clickable(onClick = { + onSelected(it) + onDismiss() + }) + ) + } + + Box( + contentAlignment = Alignment.CenterEnd, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(bottom = 24.dp) + ) { + TextButton(onClick = onDismiss) { + Text(text = stringResource(id = android.R.string.cancel)) + } + } + } + } +} + +@Composable +private fun getTextForFrequency(backupsFrequency: MessageBackupsFrequency): String { + return when (backupsFrequency) { + MessageBackupsFrequency.DAILY -> "Daily" + MessageBackupsFrequency.WEEKLY -> "Weekly" + MessageBackupsFrequency.MONTHLY -> "Monthly" + MessageBackupsFrequency.NEVER -> "Manually back up" + } +} + +@SignalPreview +@Composable +private fun RemoteBackupsSettingsContentPreview() { + Previews.Preview { + RemoteBackupsSettingsContent( + messageBackupsType = null, + lastBackupTimestamp = -1, + canBackUpUsingCellular = false, + backupsFrequency = MessageBackupsFrequency.NEVER, + requestedDialog = RemoteBackupsSettingsState.Dialog.NONE, + requestedSnackbar = RemoteBackupsSettingsState.Snackbar.NONE, + contentCallbacks = object : ContentCallbacks {} + ) + } +} + +@SignalPreview +@Composable +private fun BackupTypeRowPreview() { + Previews.Preview { + BackupTypeRow( + messageBackupsType = MessageBackupsType( + title = "Text + all media", + pricePerMonth = FiatMoney(BigDecimal.valueOf(3L), Currency.getInstance(Locale.US)), + features = persistentListOf() + ), + onChangeBackupsTypeClick = {}, + onEnableBackupsClick = {} + ) + } +} + +@SignalPreview +@Composable +private fun LastBackupRowPreview() { + Previews.Preview { + LastBackupRow( + lastBackupTimestamp = -1, + onBackupNowClick = {} + ) + } +} + +@SignalPreview +@Composable +private fun TurnOffAndDeleteBackupsDialogPreview() { + Previews.Preview { + TurnOffAndDeleteBackupsDialog( + onConfirm = {}, + onDismiss = {} + ) + } +} + +@SignalPreview +@Composable +private fun BackupFrequencyDialogPreview() { + Previews.Preview { + BackupFrequencyDialog( + selected = MessageBackupsFrequency.DAILY, + onSelected = {}, + onDismiss = {} + ) + } +} 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/chats/backups/RemoteBackupsSettingsState.kt new file mode 100644 index 0000000000..5320318d41 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups + +import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFrequency +import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsType + +data class RemoteBackupsSettingsState( + val messageBackupsType: MessageBackupsType? = null, + val canBackUpUsingCellular: Boolean = false, + val backupSize: Long = 0, + val backupsFrequency: MessageBackupsFrequency = MessageBackupsFrequency.DAILY, + val lastBackupTimestamp: Long = 0, + val dialog: Dialog = Dialog.NONE, + val snackbar: Snackbar = Snackbar.NONE +) { + enum class Dialog { + NONE, + TURN_OFF_AND_DELETE_BACKUPS, + BACKUP_FREQUENCY + } + + enum class Snackbar { + NONE, + BACKUP_DELETED_AND_TURNED_OFF, + BACKUP_TYPE_CHANGED_AND_SUBSCRIPTION_CANCELLED, + SUBSCRIPTION_CANCELLED, + DOWNLOAD_COMPLETE + } +} 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/chats/backups/RemoteBackupsSettingsViewModel.kt new file mode 100644 index 0000000000..ed2cd30201 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsViewModel.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFrequency + +/** + * ViewModel for state management of RemoteBackupsSettingsFragment + */ +class RemoteBackupsSettingsViewModel : ViewModel() { + private val internalState = mutableStateOf(RemoteBackupsSettingsState()) + + val state: State = internalState + + fun setCanBackUpUsingCellular(canBackUpUsingCellular: Boolean) { + // TODO [message-backups] -- Update via repository? + internalState.value = state.value.copy(canBackUpUsingCellular = canBackUpUsingCellular) + } + + fun setBackupsFrequency(backupsFrequency: MessageBackupsFrequency) { + // TODO [message-backups] -- Update via repository? + internalState.value = state.value.copy(backupsFrequency = backupsFrequency) + } + + fun requestDialog(dialog: RemoteBackupsSettingsState.Dialog) { + internalState.value = state.value.copy(dialog = dialog) + } + + fun requestSnackbar(snackbar: RemoteBackupsSettingsState.Snackbar) { + internalState.value = state.value.copy(snackbar = snackbar) + } + + fun turnOffAndDeleteBackups() { + // TODO [message-backups] -- Delete. + internalState.value = state.value.copy(snackbar = RemoteBackupsSettingsState.Snackbar.BACKUP_DELETED_AND_TURNED_OFF) + } +} 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 ed924592c6..8b8dfd8f21 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 @@ -34,12 +34,11 @@ import androidx.compose.runtime.rxjava3.subscribeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -183,7 +182,7 @@ private fun CallInfo( item { Rows.TextRow( text = stringResource(id = R.string.CallLinkDetailsFragment__share_link), - icon = ImageVector.vectorResource(id = R.drawable.symbol_link_24), + icon = painterResource(id = R.drawable.symbol_link_24), iconModifier = Modifier .background( color = MaterialTheme.colorScheme.surfaceVariant, @@ -446,7 +445,7 @@ private fun CallParticipantRow( if (showIcons && showHandRaised) { Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.symbol_raise_hand_24), + painter = painterResource(id = R.drawable.symbol_raise_hand_24), contentDescription = null, modifier = Modifier.align(Alignment.CenterVertically) ) @@ -454,7 +453,7 @@ private fun CallParticipantRow( if (showIcons && !isVideoEnabled) { Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.symbol_video_slash_24), + painter = painterResource(id = R.drawable.symbol_video_slash_24), contentDescription = null, modifier = Modifier.align(Alignment.CenterVertically) ) @@ -466,7 +465,7 @@ private fun CallParticipantRow( } Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.symbol_mic_slash_24), + painter = painterResource(id = R.drawable.symbol_mic_slash_24), contentDescription = null, modifier = Modifier.align(Alignment.CenterVertically) ) @@ -478,7 +477,7 @@ private fun CallParticipantRow( } Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.symbol_minus_circle_24), + painter = painterResource(id = R.drawable.symbol_minus_circle_24), contentDescription = null, modifier = Modifier .clickable(onClick = onBlockClicked) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/requests/CallLinkIncomingRequestSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/requests/CallLinkIncomingRequestSheet.kt index 5911cacedb..10aea686a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/requests/CallLinkIncomingRequestSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/requests/CallLinkIncomingRequestSheet.kt @@ -23,11 +23,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -165,7 +164,7 @@ private fun CallLinkIncomingRequestSheetContent( item { Rows.TextRow( text = stringResource(id = R.string.CallLinkIncomingRequestSheet__approve_entry), - icon = ImageVector.vectorResource(R.drawable.symbol_check_circle_24), + icon = painterResource(R.drawable.symbol_check_circle_24), onClick = onApproveEntry ) } @@ -173,7 +172,7 @@ private fun CallLinkIncomingRequestSheetContent( item { Rows.TextRow( text = stringResource(id = R.string.CallLinkIncomingRequestSheet__deny_entry), - icon = ImageVector.vectorResource(R.drawable.symbol_x_circle_24), + icon = painterResource(R.drawable.symbol_x_circle_24), onClick = onDenyEntry ) } @@ -219,7 +218,7 @@ private fun Title( style = MaterialTheme.typography.headlineMedium ) Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.symbol_person_circle_24), + painter = painterResource(id = R.drawable.symbol_person_circle_24), contentDescription = null, modifier = Modifier .padding(start = 6.dp) 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 4e371b7cc4..57d0e91bc4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -25,6 +25,11 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { private const val KEY_OPTIMIZE_STORAGE = "backup.optimizeStorage" + /** + * Specifies whether remote backups are enabled on this device. + */ + private const val KEY_BACKUPS_ENABLED = "backup.enabled" + private val cachedCdnCredentialsExpiresIn: Duration = 12.hours } @@ -40,6 +45,8 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { var restoreState: RestoreState by enumValue(KEY_RESTORE_STATE, RestoreState.NONE, RestoreState.serializer) var optimizeStorage: Boolean by booleanValue(KEY_OPTIMIZE_STORAGE, false) + var areBackupsEnabled: Boolean by booleanValue(KEY_BACKUPS_ENABLED, false) + /** * Retrieves the stored credentials, mapped by the day they're valid. The day is represented as * the unix time (in seconds) of the start of the day. Wrapped in a [ArchiveServiceCredentials] diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 211097fed9..38005862fb 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -300,6 +300,13 @@ app:exitAnim="@anim/fragment_open_exit" app:popEnterAnim="@anim/fragment_close_enter" app:popExitAnim="@anim/fragment_close_exit" /> + + + diff --git a/core-ui/src/main/java/org/signal/core/ui/Rows.kt b/core-ui/src/main/java/org/signal/core/ui/Rows.kt index 35fd45da4a..bd9368a092 100644 --- a/core-ui/src/main/java/org/signal/core/ui/Rows.kt +++ b/core-ui/src/main/java/org/signal/core/ui/Rows.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -22,8 +23,9 @@ import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -110,41 +112,53 @@ object Rows { text: String, modifier: Modifier = Modifier, iconModifier: Modifier = Modifier, - icon: ImageVector? = null, + icon: Painter? = null, foregroundTint: Color = MaterialTheme.colorScheme.onSurface, onClick: (() -> Unit)? = null ) { - if (icon != null) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(enabled = onClick != null, onClick = onClick ?: {}) - .padding(defaultPadding()) - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = foregroundTint, - modifier = iconModifier - ) - - Spacer(modifier = Modifier.width(24.dp)) - + TextRow( + text = { Text( text = text, - modifier = Modifier.weight(1f).align(CenterVertically), - color = foregroundTint + color = foregroundTint, + modifier = Modifier.align(CenterVertically) ) + }, + icon = if (icon != null) { + { + Icon( + painter = icon, + contentDescription = null, + tint = foregroundTint, + modifier = iconModifier + ) + } + } else { + null + }, + modifier = modifier, + onClick = onClick + ) + } + + @Composable + fun TextRow( + text: @Composable RowScope.() -> Unit, + modifier: Modifier = Modifier, + icon: (@Composable RowScope.() -> Unit)? = null, + onClick: (() -> Unit)? = null + ) { + Row( + modifier = modifier + .fillMaxWidth() + .clickable(enabled = onClick != null, onClick = onClick ?: {}) + .padding(defaultPadding()) + ) { + if (icon != null) { + icon() + Spacer(modifier = Modifier.width(24.dp)) } - } else { - Text( - text = text, - color = foregroundTint, - modifier = modifier - .fillMaxWidth() - .clickable(enabled = onClick != null, onClick = onClick ?: {}) - .padding(defaultPadding()) - ) + text() } } @@ -190,11 +204,13 @@ private fun ToggleRowPreview() { } } -@Preview +@SignalPreview @Composable private fun TextRowPreview() { - SignalTheme(isDarkMode = false) { - Rows.TextRow(text = "TextRow") - Rows.TextRow(text = "TextRow") + Previews.Preview { + Rows.TextRow( + text = "TextRow", + icon = painterResource(id = android.R.drawable.ic_menu_camera) + ) } } diff --git a/core-ui/src/main/java/org/signal/core/ui/SignalPreview.kt b/core-ui/src/main/java/org/signal/core/ui/SignalPreview.kt new file mode 100644 index 0000000000..ec457432cd --- /dev/null +++ b/core-ui/src/main/java/org/signal/core/ui/SignalPreview.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.ui + +import android.content.res.Configuration +import androidx.compose.ui.tooling.preview.Preview + +/** + * Our very own preview that will generate light and dark previews for + * composables + */ +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +annotation class SignalPreview() From ce1b73970c1157eb3e10383d3376bd59d9e08d87 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 17 Apr 2024 14:49:28 -0300 Subject: [PATCH 026/113] Implement BackupStatus widget. --- .../backup/v2/ui/status/BackupStatus.kt | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatus.kt 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 new file mode 100644 index 0000000000..d71de68a82 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatus.kt @@ -0,0 +1,253 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.ui.status + +import android.content.res.Configuration +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.signal.core.ui.Buttons +import org.signal.core.ui.Previews +import org.thoughtcrime.securesms.R +import kotlin.math.max +import kotlin.math.min + +private const val NONE = -1 + +/** + * Displays a "heads up" widget containing information about the current + * status of the user's backup. + */ +@Composable +fun BackupStatus( + data: BackupStatusData, + onActionClick: () -> Unit = {} +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .border(1.dp, color = MaterialTheme.colorScheme.outline.copy(alpha = 0.38f), shape = RoundedCornerShape(12.dp)) + .fillMaxWidth() + .padding(14.dp) + ) { + val foreground: Brush = data.iconColors.foreground + Icon( + painter = painterResource(id = data.iconRes), + contentDescription = null, + modifier = Modifier + .background(color = data.iconColors.background, shape = CircleShape) + .padding(8.dp) + .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } + .drawWithCache { + onDrawWithContent { + drawContent() + drawRect(foreground, blendMode = BlendMode.SrcAtop) + } + } + ) + + Column( + modifier = Modifier + .padding(start = 12.dp) + .weight(1f) + ) { + Text( + text = stringResource(id = data.titleRes), + style = MaterialTheme.typography.bodyMedium + ) + + if (data.progress >= 0f) { + LinearProgressIndicator( + progress = data.progress, + strokeCap = StrokeCap.Round, + modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp) + ) + } + + if (data.statusRes != NONE) { + Text( + text = stringResource(id = data.statusRes), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + + if (data.actionRes != NONE) { + Buttons.Small( + onClick = onActionClick, + modifier = Modifier.padding(start = 8.dp) + ) { + Text(text = stringResource(id = data.actionRes)) + } + } + } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun BackupStatusPreview() { + Previews.Preview { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + BackupStatus( + data = BackupStatusData.CouldNotCompleteBackup + ) + + BackupStatus( + data = BackupStatusData.NotEnoughFreeSpace + ) + + BackupStatus( + data = BackupStatusData.RestoringMedia(50, 100) + ) + } + } +} + +/** + * Sealed interface describing status data to display in BackupStatus widget. + * + * TODO [message-requests] - Finalize assets and text + */ +sealed interface BackupStatusData { + + @get:DrawableRes + val iconRes: Int + + @get:StringRes + val titleRes: Int + + val iconColors: IconColors + + @get:StringRes + val actionRes: Int get() = NONE + + @get:StringRes + val statusRes: Int get() = NONE + + val progress: Float get() = NONE.toFloat() + + /** + * Generic failure + */ + object CouldNotCompleteBackup : BackupStatusData { + override val iconRes: Int = R.drawable.symbol_backup_light + override val titleRes: Int = R.string.default_error_msg + override val iconColors: IconColors = IconColors.Warning + } + + /** + * User does not have enough space on their device to complete backup restoration + */ + object NotEnoughFreeSpace : BackupStatusData { + override val iconRes: Int = R.drawable.symbol_backup_light + override val titleRes: Int = R.string.default_error_msg + override val iconColors: IconColors = IconColors.Warning + override val actionRes: Int = R.string.registration_activity__skip + } + + /** + * Restoring media, finished, and paused states. + */ + data class RestoringMedia( + val bytesDownloaded: Long, + val bytesTotal: Long, + val status: Status = Status.NONE + ) : BackupStatusData { + override val iconRes: Int = R.drawable.symbol_backup_light + override val iconColors: IconColors = IconColors.Normal + + override val titleRes: Int = 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 + } + + override val statusRes: Int = 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 + } + + override val progress: Float = if (bytesTotal > 0) { + min(1f, max(0f, bytesDownloaded.toFloat() / bytesTotal)) + } else { + 0f + } + } + + sealed interface IconColors { + @get:Composable + val foreground: Brush + + @get:Composable + val background: Color + + object Normal : IconColors { + 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 background: Color @Composable get() = MaterialTheme.colorScheme.primaryContainer + } + + object Warning : IconColors { + override val foreground: Brush @Composable get() = SolidColor(Color(0xFFC86600)) + override val background: Color @Composable get() = Color(0xFFF9E4B6) + } + } + + /** + * Describes the status of an in-progress media download session. + */ + enum class Status { + NONE, + LOW_BATTERY, + WAITING_FOR_INTERNET, + WAITING_FOR_WIFI, + FINISHED + } +} From 9762899272deb9b92ac0dc87a26acce35b3866f1 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 17 Apr 2024 15:04:22 -0400 Subject: [PATCH 027/113] Remove old thread remappings. --- .../securesms/backup/FullBackupExporter.java | 5 ++- .../database/RemappedRecordTables.kt | 34 +++++++++++++++++-- .../securesms/database/RemappedRecords.java | 23 ++++--------- .../securesms/database/ThreadTable.kt | 22 ++++++++---- 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java index c62a0fae85..1b3d40bf59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.database.OneTimePreKeyTable; import org.thoughtcrime.securesms.database.PendingRetryReceiptTable; import org.thoughtcrime.securesms.database.ReactionTable; +import org.thoughtcrime.securesms.database.RemappedRecordTables; import org.thoughtcrime.securesms.database.SearchTable; import org.thoughtcrime.securesms.database.SenderKeyTable; import org.thoughtcrime.securesms.database.SenderKeySharedTable; @@ -92,7 +93,9 @@ public class FullBackupExporter extends FullBackupBase { SenderKeyTable.TABLE_NAME, SenderKeySharedTable.TABLE_NAME, PendingRetryReceiptTable.TABLE_NAME, - AvatarPickerDatabase.TABLE_NAME + AvatarPickerDatabase.TABLE_NAME, + RemappedRecordTables.Recipients.TABLE_NAME, + RemappedRecordTables.Threads.TABLE_NAME ); public static BackupEvent export(@NonNull Context context, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt index 3859c9761b..1eb4bc806c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database import android.content.Context import android.database.Cursor import androidx.core.content.contentValuesOf +import org.signal.core.util.delete import org.signal.core.util.logging.Log import org.signal.core.util.readToList import org.signal.core.util.requireLong @@ -30,7 +31,7 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe const val NEW_ID = "new_id" } - private object Recipients { + object Recipients { const val TABLE_NAME = "remapped_recipients" const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( @@ -41,7 +42,7 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe """ } - private object Threads { + object Threads { const val TABLE_NAME = "remapped_threads" const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( @@ -56,6 +57,9 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe val recipientMap: MutableMap = HashMap() readableDatabase.withinTransaction { db -> + trimInvalidRecipientEntries(db) + trimInvalidThreadEntries(db) + val mappings = getAllMappings(db, Recipients.TABLE_NAME) for (mapping in mappings) { val oldId = RecipientId.from(mapping.oldId) @@ -102,6 +106,32 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe .run() } + fun deleteThreadMapping(oldId: Long) { + writableDatabase.delete(Threads.TABLE_NAME) + .where("$OLD_ID = ?", oldId) + .run() + } + + private fun trimInvalidRecipientEntries(db: SQLiteDatabase) { + val count = db.delete(Recipients.TABLE_NAME) + .where("$OLD_ID IN (SELECT $ID FROM ${RecipientTable.TABLE_NAME})") + .run() + + if (count > 0) { + Log.w(TAG, "Trimmed $count invalid recipient entries.", true) + } + } + + private fun trimInvalidThreadEntries(db: SQLiteDatabase) { + val count = db.delete(Threads.TABLE_NAME) + .where("$OLD_ID IN (SELECT $ID FROM ${ThreadTable.TABLE_NAME})") + .run() + + if (count > 0) { + Log.w(TAG, "Trimmed $count invalid thread entries.", true) + } + } + private fun getAllMappings(db: SQLiteDatabase, table: String): List { return db.select() .from(table) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java index cbd5737c82..fb97049171 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java @@ -49,25 +49,16 @@ static RemappedRecords getInstance() { return Optional.ofNullable(threadMap.get(oldId)); } - boolean areAnyRemapped(@NonNull Collection recipientIds) { - ensureRecipientMapIsPopulated(); - return recipientIds.stream().anyMatch(id -> recipientMap.containsKey(id)); + void deleteThread(long oldId) { + ensureInTransaction(); + ensureThreadMapIsPopulated(); + threadMap.remove(oldId); + SignalDatabase.remappedRecords().deleteThreadMapping(oldId); } - @NonNull Set remap(@NonNull Collection recipientIds) { + boolean areAnyRemapped(@NonNull Collection recipientIds) { ensureRecipientMapIsPopulated(); - - Set remapped = new LinkedHashSet<>(); - - for (RecipientId original : recipientIds) { - if (recipientMap.containsKey(original)) { - remapped.add(recipientMap.get(original)); - } else { - remapped.add(original); - } - } - - return remapped; + return recipientIds.stream().anyMatch(id -> recipientMap.containsKey(id)); } @NonNull String buildRemapDescription(@NonNull Collection recipientIds) { 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 a304546eec..05b458c023 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -1141,15 +1141,23 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa fun getOrCreateValidThreadId(recipient: Recipient, candidateId: Long, distributionType: Int): Long { return if (candidateId != -1L) { - val remapped = RemappedRecords.getInstance().getThread(candidateId) - if (remapped.isPresent) { - Log.i(TAG, "Using remapped threadId: " + candidateId + " -> " + remapped.get()) - remapped.get() + if (areThreadIdAndRecipientAssociated(candidateId, recipient)) { + candidateId } else { - if (areThreadIdAndRecipientAssociated(candidateId, recipient)) { - candidateId + val remapped = RemappedRecords.getInstance().getThread(candidateId) + if (remapped.isPresent) { + if (areThreadIdAndRecipientAssociated(remapped.get(), recipient)) { + Log.i(TAG, "Using remapped threadId: $candidateId -> ${remapped.get()}") + remapped.get() + } else { + Log.i(TAG, "There's a remap for $candidateId -> ${remapped.get()}, but it's not associated with $recipient. Deleting old remap and throwing.") + writableDatabase.withinTransaction { + RemappedRecords.getInstance().deleteThread(candidateId) + } + throw IllegalArgumentException("Candidate threadId ($candidateId) is not associated with recipient ($recipient)") + } } else { - throw IllegalArgumentException() + throw IllegalArgumentException("Candidate threadId ($candidateId) is not associated with recipient ($recipient)") } } } else { From 4bcd1df4f85045657fa624fd0972b3654036fc3c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 17 Apr 2024 16:58:44 -0400 Subject: [PATCH 028/113] Expand account consistency checks. --- .../jobs/AccountConsistencyWorkerJob.kt | 27 ++++++++++++++----- .../securesms/util/ProfileUtil.java | 17 ++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob.kt index 2e68d7b3e9..59dc62346f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob.kt @@ -56,19 +56,34 @@ class AccountConsistencyWorkerJob private constructor(parameters: Parameters) : return } - val profile: SignalServiceProfile = ProfileUtil.retrieveProfileSync(context, Recipient.self(), SignalServiceProfile.RequestType.PROFILE, false).profile - val encodedPublicKey = Base64.encodeWithPadding(SignalStore.account().aciIdentityKey.publicKey.serialize()) + val aciProfile: SignalServiceProfile = ProfileUtil.retrieveProfileSync(context, Recipient.self(), SignalServiceProfile.RequestType.PROFILE, false).profile + val encodedAciPublicKey = Base64.encodeWithPadding(SignalStore.account().aciIdentityKey.publicKey.serialize()) - if (profile.identityKey != encodedPublicKey) { - Log.w(TAG, "Identity key on profile differed from the one we have locally! Marking ourselves unregistered.") + if (aciProfile.identityKey != encodedAciPublicKey) { + Log.w(TAG, "ACI identity key on profile differed from the one we have locally! Marking ourselves unregistered.") SignalStore.account().setRegistered(false) SignalStore.registrationValues().clearRegistrationComplete() SignalStore.registrationValues().clearHasUploadedProfile() - } else { - Log.i(TAG, "Everything matched.") + + SignalStore.misc().lastConsistencyCheckTime = System.currentTimeMillis() + return } + val pniProfile: SignalServiceProfile = ProfileUtil.retrieveProfileSync(SignalStore.account().pni!!, SignalServiceProfile.RequestType.PROFILE).profile + val encodedPniPublicKey = Base64.encodeWithPadding(SignalStore.account().pniIdentityKey.publicKey.serialize()) + + if (pniProfile.identityKey != encodedPniPublicKey) { + Log.w(TAG, "PNI identity key on profile differed from the one we have locally!") + + SignalStore.account().setRegistered(false) + SignalStore.registrationValues().clearRegistrationComplete() + SignalStore.registrationValues().clearHasUploadedProfile() + return + } + + Log.i(TAG, "Everything matched.") + SignalStore.misc().lastConsistencyCheckTime = System.currentTimeMillis() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java index 869b3fd6b1..b5024b7177 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java @@ -43,6 +43,7 @@ import org.whispersystems.signalservice.api.profiles.AvatarUploadParams; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.services.ProfileService; import org.whispersystems.signalservice.api.util.StreamDetails; @@ -108,6 +109,22 @@ public static void handleSelfProfileKeyChange() { return new ProfileService.ProfileResponseProcessor(response.second()).getResultOrThrow(); } + @WorkerThread + public static @NonNull ProfileAndCredential retrieveProfileSync(@NonNull ServiceId.PNI pni, + @NonNull SignalServiceProfile.RequestType requestType) + throws IOException + { + ProfileService profileService = ApplicationDependencies.getProfileService(); + + ServiceResponse response = Single + .fromCallable(() -> new SignalServiceAddress(pni)) + .flatMap(address -> profileService.getProfile(address, Optional.empty(), Optional.empty(), requestType, Locale.getDefault())) + .onErrorReturn(t -> ServiceResponse.forUnknownError(t)) + .blockingGet(); + + return new ProfileService.ProfileResponseProcessor(response).getResultOrThrow(); + } + public static Single>> retrieveProfile(@NonNull Context context, @NonNull Recipient recipient, @NonNull SignalServiceProfile.RequestType requestType) From d9e9fe1d6ad30adc49350f2e3f4c04fc25280799 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 18 Apr 2024 10:00:24 -0300 Subject: [PATCH 029/113] Move backups selection code to its own package. --- app/src/main/AndroidManifest.xml | 4 ++-- .../backup/v2/ui/restore/RestoreFromBackupFragment.kt | 4 ++-- .../v2/ui/{ => subscription}/MessageBackupsCheckoutSheet.kt | 2 +- .../ui/{ => subscription}/MessageBackupsEducationScreen.kt | 2 +- .../v2/ui/{ => subscription}/MessageBackupsFlowActivity.kt | 2 +- .../ui/{ => subscription}/MessageBackupsFlowRepository.kt | 2 +- .../v2/ui/{ => subscription}/MessageBackupsFlowState.kt | 2 +- .../v2/ui/{ => subscription}/MessageBackupsFlowViewModel.kt | 2 +- .../v2/ui/{ => subscription}/MessageBackupsFrequency.kt | 2 +- .../MessageBackupsPinConfirmationScreen.kt | 2 +- .../{ => subscription}/MessageBackupsPinEducationScreen.kt | 2 +- .../backup/v2/ui/{ => subscription}/MessageBackupsScreen.kt | 2 +- .../{ => subscription}/MessageBackupsTestRestoreActivity.kt | 2 +- .../MessageBackupsTestRestoreViewModel.kt | 2 +- .../v2/ui/{ => subscription}/MessageBackupsTypeFeature.kt | 2 +- .../{ => subscription}/MessageBackupsTypeSelectionScreen.kt | 2 +- .../components/settings/app/chats/ChatsSettingsFragment.kt | 2 +- .../app/chats/backups/RemoteBackupsSettingsFragment.kt | 6 +++--- .../app/chats/backups/RemoteBackupsSettingsState.kt | 4 ++-- .../app/chats/backups/RemoteBackupsSettingsViewModel.kt | 2 +- .../thoughtcrime/securesms/pin/PinRestoreEntryFragment.java | 2 +- 21 files changed, 26 insertions(+), 26 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsCheckoutSheet.kt (99%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsEducationScreen.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsFlowActivity.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsFlowRepository.kt (67%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsFlowState.kt (92%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsFlowViewModel.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsFrequency.kt (79%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsPinConfirmationScreen.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsPinEducationScreen.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsScreen.kt (80%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsTestRestoreActivity.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsTestRestoreViewModel.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsTypeFeature.kt (96%) rename app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/{ => subscription}/MessageBackupsTypeSelectionScreen.kt (99%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 56b494aa38..63b922b710 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -749,7 +749,7 @@ android:exported="false"/> - diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragment.kt index c44e63e45e..f92b2e0b9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragment.kt @@ -32,8 +32,8 @@ import org.signal.core.ui.Buttons import org.signal.core.ui.Previews import org.signal.core.ui.theme.SignalTheme import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsTypeFeature -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsTypeFeatureRow +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFeature +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFeatureRow import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.devicetransfer.moreoptions.MoreTransferOrRestoreOptionsMode import org.thoughtcrime.securesms.util.navigation.safeNavigate diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsCheckoutSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutSheet.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsCheckoutSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutSheet.kt index e32f13f9e0..9937537671 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsCheckoutSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsCheckoutSheet.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import android.view.LayoutInflater import android.view.ViewGroup diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsEducationScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsEducationScreen.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsEducationScreen.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsEducationScreen.kt index bb22849d32..9b132bf4f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsEducationScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsEducationScreen.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.foundation.Image import androidx.compose.foundation.background diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowActivity.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowActivity.kt index d8c8685a47..e787c48b04 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowActivity.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import android.os.Bundle import androidx.activity.compose.setContent diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowRepository.kt similarity index 67% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowRepository.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowRepository.kt index 19e28b439c..791b352f3f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowRepository.kt @@ -3,6 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription class MessageBackupsFlowRepository diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowState.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowState.kt similarity index 92% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowState.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowState.kt index d844df38c0..738c5c4e63 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowState.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewayResponse import org.thoughtcrime.securesms.keyvalue.SignalStore diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowViewModel.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt index 4865ead980..222359eb8a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFlowViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFrequency.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFrequency.kt similarity index 79% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFrequency.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFrequency.kt index 777238b225..3adb6d9e58 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsFrequency.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFrequency.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription /** * Describes how often a users messages are backed up. diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsPinConfirmationScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsPinConfirmationScreen.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsPinConfirmationScreen.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsPinConfirmationScreen.kt index c0ab92e70d..7468a53880 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsPinConfirmationScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsPinConfirmationScreen.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsPinEducationScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsPinEducationScreen.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsPinEducationScreen.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsPinEducationScreen.kt index d474440912..b5cb5a4636 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsPinEducationScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsPinEducationScreen.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsScreen.kt similarity index 80% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsScreen.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsScreen.kt index 6025778299..994f180ffd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsScreen.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription enum class MessageBackupsScreen { EDUCATION, diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTestRestoreActivity.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTestRestoreActivity.kt index 956f5997a8..065af0c3ec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTestRestoreActivity.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import android.content.Context import android.content.Intent diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTestRestoreViewModel.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTestRestoreViewModel.kt index 071d0c5894..66578fea19 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTestRestoreViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTestRestoreViewModel.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.runtime.MutableState import androidx.compose.runtime.State diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeFeature.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeFeature.kt similarity index 96% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeFeature.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeFeature.kt index fb037d0e0a..adf33e4eff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeFeature.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeFeature.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeSelectionScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeSelectionScreen.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt index c1b38f299a..4d92d152bc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/MessageBackupsTypeSelectionScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt @@ -2,7 +2,7 @@ * Copyright 2024 Signal Messenger, LLC * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.backup.v2.ui +package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.foundation.Image import androidx.compose.foundation.background 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 bab62d48ca..56bfba179e 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 @@ -4,7 +4,7 @@ import android.content.Intent import androidx.lifecycle.ViewModelProvider import androidx.navigation.Navigation import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFlowActivity +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFlowActivity import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment import org.thoughtcrime.securesms.components.settings.DSLSettingsText 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/chats/backups/RemoteBackupsSettingsFragment.kt index f8d2294640..ddb7bd3f84 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/chats/backups/RemoteBackupsSettingsFragment.kt @@ -46,9 +46,9 @@ import org.signal.core.ui.Snackbars 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.MessageBackupsFlowActivity -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFrequency -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsType +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFlowActivity +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.util.DateUtils 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/chats/backups/RemoteBackupsSettingsState.kt index 5320318d41..fedf62ab1e 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/chats/backups/RemoteBackupsSettingsState.kt @@ -5,8 +5,8 @@ package org.thoughtcrime.securesms.components.settings.app.chats.backups -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFrequency -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsType +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType data class RemoteBackupsSettingsState( val messageBackupsType: MessageBackupsType? = null, 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/chats/backups/RemoteBackupsSettingsViewModel.kt index ed2cd30201..d4b6fc4967 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/chats/backups/RemoteBackupsSettingsViewModel.kt @@ -8,7 +8,7 @@ package org.thoughtcrime.securesms.components.settings.app.chats.backups import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsFrequency +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency /** * ViewModel for state management of RemoteBackupsSettingsFragment diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java index 5486fced5c..41f3c5ef6d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java @@ -27,7 +27,7 @@ import org.thoughtcrime.securesms.LoggingFragment; import org.thoughtcrime.securesms.MainActivity; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.backup.v2.ui.MessageBackupsTestRestoreActivity; +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTestRestoreActivity; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobs.ProfileUploadJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; From 735a8e680c07cd71de4c920d945c86b49e4327c7 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 18 Apr 2024 11:12:01 -0300 Subject: [PATCH 030/113] Add backupSubscription field to configuration object. --- .../badges/gifts/flow/GiftFlowRepository.kt | 4 +-- .../DonationsConfigurationExtensions.kt | 30 +++++++++---------- .../gateway/GatewaySelectorRepository.kt | 10 +++---- ...criptionsConfigurationExtensionsKtTest.kt} | 20 ++++++------- .../api/services/DonationsService.java | 8 ++--- .../internal/push/PushServiceSocket.java | 4 +-- ...n.java => SubscriptionsConfiguration.java} | 9 +++++- 7 files changed, 46 insertions(+), 39 deletions(-) rename app/src/test/java/org/thoughtcrime/securesms/components/settings/app/subscription/{DonationsConfigurationExtensionsKtTest.kt => SubscriptionsConfigurationExtensionsKtTest.kt} (91%) rename libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/{DonationsConfiguration.java => SubscriptionsConfiguration.java} (90%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt index d066cd5ad4..faa0650606 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt @@ -8,7 +8,7 @@ import org.thoughtcrime.securesms.badges.models.Badge import org.thoughtcrime.securesms.components.settings.app.subscription.getGiftBadgeAmounts import org.thoughtcrime.securesms.components.settings.app.subscription.getGiftBadges import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.whispersystems.signalservice.internal.push.DonationsConfiguration +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration import java.util.Currency import java.util.Locale @@ -28,7 +28,7 @@ class GiftFlowRepository { .getDonationsConfiguration(Locale.getDefault()) } .flatMap { it.flattenResult() } - .map { DonationsConfiguration.GIFT_LEVEL to it.getGiftBadges().first() } + .map { SubscriptionsConfiguration.GIFT_LEVEL to it.getGiftBadges().first() } .subscribeOn(Schedulers.io()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt index 85aa56820a..54f1f27265 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt @@ -4,11 +4,11 @@ import org.signal.core.util.money.FiatMoney import org.signal.core.util.money.PlatformCurrencyUtil import org.thoughtcrime.securesms.badges.Badges import org.thoughtcrime.securesms.badges.models.Badge -import org.whispersystems.signalservice.internal.push.DonationsConfiguration -import org.whispersystems.signalservice.internal.push.DonationsConfiguration.BOOST_LEVEL -import org.whispersystems.signalservice.internal.push.DonationsConfiguration.GIFT_LEVEL -import org.whispersystems.signalservice.internal.push.DonationsConfiguration.LevelConfiguration -import org.whispersystems.signalservice.internal.push.DonationsConfiguration.SUBSCRIPTION_LEVELS +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.BOOST_LEVEL +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.GIFT_LEVEL +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.LevelConfiguration +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.SUBSCRIPTION_LEVELS import java.math.BigDecimal import java.util.Currency @@ -26,7 +26,7 @@ private const val SEPA_DEBIT = "SEPA_DEBIT" * @param level The subscription level to get amounts for * @param paymentMethodAvailability Predicate object which checks whether different payment methods are availble. */ -fun DonationsConfiguration.getSubscriptionAmounts( +fun SubscriptionsConfiguration.getSubscriptionAmounts( level: Int, paymentMethodAvailability: PaymentMethodAvailability = DefaultPaymentMethodAvailability ): Set { @@ -41,7 +41,7 @@ fun DonationsConfiguration.getSubscriptionAmounts( /** * Currently, we only support a single gift badge at level GIFT_LEVEL */ -fun DonationsConfiguration.getGiftBadges(): List { +fun SubscriptionsConfiguration.getGiftBadges(): List { val configuration = levels[GIFT_LEVEL] return listOfNotNull(configuration?.badge?.let { Badges.fromServiceBadge(it) }) } @@ -49,7 +49,7 @@ fun DonationsConfiguration.getGiftBadges(): List { /** * Currently, we only support a single gift badge amount per currency */ -fun DonationsConfiguration.getGiftBadgeAmounts(paymentMethodAvailability: PaymentMethodAvailability = DefaultPaymentMethodAvailability): Map { +fun SubscriptionsConfiguration.getGiftBadgeAmounts(paymentMethodAvailability: PaymentMethodAvailability = DefaultPaymentMethodAvailability): Map { return getFilteredCurrencies(paymentMethodAvailability).filter { it.value.oneTime[GIFT_LEVEL]?.isNotEmpty() == true }.mapKeys { @@ -62,12 +62,12 @@ fun DonationsConfiguration.getGiftBadgeAmounts(paymentMethodAvailability: Paymen /** * Currently, we only support a single boost badge at level BOOST_LEVEL */ -fun DonationsConfiguration.getBoostBadges(): List { +fun SubscriptionsConfiguration.getBoostBadges(): List { val configuration = levels[BOOST_LEVEL] return listOfNotNull(configuration?.badge?.let { Badges.fromServiceBadge(it) }) } -fun DonationsConfiguration.getBoostAmounts(paymentMethodAvailability: PaymentMethodAvailability = DefaultPaymentMethodAvailability): Map> { +fun SubscriptionsConfiguration.getBoostAmounts(paymentMethodAvailability: PaymentMethodAvailability = DefaultPaymentMethodAvailability): Map> { return getFilteredCurrencies(paymentMethodAvailability).filter { it.value.oneTime[BOOST_LEVEL]?.isNotEmpty() == true }.mapKeys { @@ -77,12 +77,12 @@ fun DonationsConfiguration.getBoostAmounts(paymentMethodAvailability: PaymentMet } } -fun DonationsConfiguration.getBadge(level: Int): Badge { +fun SubscriptionsConfiguration.getBadge(level: Int): Badge { require(level == GIFT_LEVEL || level == BOOST_LEVEL || SUBSCRIPTION_LEVELS.contains(level)) return Badges.fromServiceBadge(levels[level]!!.badge) } -fun DonationsConfiguration.getSubscriptionLevels(): Map { +fun SubscriptionsConfiguration.getSubscriptionLevels(): Map { return levels.filterKeys { SUBSCRIPTION_LEVELS.contains(it) }.toSortedMap() } @@ -90,17 +90,17 @@ fun DonationsConfiguration.getSubscriptionLevels(): Map * Get a map describing the minimum donation amounts per currency. * This returns only the currencies available to the user. */ -fun DonationsConfiguration.getMinimumDonationAmounts(paymentMethodAvailability: PaymentMethodAvailability = DefaultPaymentMethodAvailability): Map { +fun SubscriptionsConfiguration.getMinimumDonationAmounts(paymentMethodAvailability: PaymentMethodAvailability = DefaultPaymentMethodAvailability): Map { return getFilteredCurrencies(paymentMethodAvailability) .mapKeys { Currency.getInstance(it.key.uppercase()) } .mapValues { FiatMoney(it.value.minimum, it.key) } } -fun DonationsConfiguration.getAvailablePaymentMethods(currencyCode: String): Set { +fun SubscriptionsConfiguration.getAvailablePaymentMethods(currencyCode: String): Set { return currencies[currencyCode.lowercase()]?.supportedPaymentMethods ?: emptySet() } -private fun DonationsConfiguration.getFilteredCurrencies(paymentMethodAvailability: PaymentMethodAvailability): Map { +private fun SubscriptionsConfiguration.getFilteredCurrencies(paymentMethodAvailability: PaymentMethodAvailability): Map { val userPaymentMethods = paymentMethodAvailability.toSet() val availableCurrencyCodes = PlatformCurrencyUtil.getAvailableCurrencyCodes() return currencies.filter { (code, config) -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt index 981098c416..5244d3cc95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt @@ -5,7 +5,7 @@ import org.signal.core.util.money.FiatMoney import org.thoughtcrime.securesms.components.settings.app.subscription.getAvailablePaymentMethods import org.thoughtcrime.securesms.payments.currency.CurrencyUtil import org.whispersystems.signalservice.api.services.DonationsService -import org.whispersystems.signalservice.internal.push.DonationsConfiguration +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration import java.util.Locale class GatewaySelectorRepository( @@ -18,10 +18,10 @@ class GatewaySelectorRepository( .map { configuration -> val available = configuration.getAvailablePaymentMethods(currencyCode).map { when (it) { - DonationsConfiguration.PAYPAL -> listOf(GatewayResponse.Gateway.PAYPAL) - DonationsConfiguration.CARD -> listOf(GatewayResponse.Gateway.CREDIT_CARD, GatewayResponse.Gateway.GOOGLE_PAY) - DonationsConfiguration.SEPA_DEBIT -> listOf(GatewayResponse.Gateway.SEPA_DEBIT) - DonationsConfiguration.IDEAL -> listOf(GatewayResponse.Gateway.IDEAL) + SubscriptionsConfiguration.PAYPAL -> listOf(GatewayResponse.Gateway.PAYPAL) + SubscriptionsConfiguration.CARD -> listOf(GatewayResponse.Gateway.CREDIT_CARD, GatewayResponse.Gateway.GOOGLE_PAY) + SubscriptionsConfiguration.SEPA_DEBIT -> listOf(GatewayResponse.Gateway.SEPA_DEBIT) + SubscriptionsConfiguration.IDEAL -> listOf(GatewayResponse.Gateway.IDEAL) else -> listOf() } }.flatten().toSet() diff --git a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensionsKtTest.kt b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/subscription/SubscriptionsConfigurationExtensionsKtTest.kt similarity index 91% rename from app/src/test/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensionsKtTest.kt rename to app/src/test/java/org/thoughtcrime/securesms/components/settings/app/subscription/SubscriptionsConfigurationExtensionsKtTest.kt index 01f2cfa9f7..6b0902ea89 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensionsKtTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/subscription/SubscriptionsConfigurationExtensionsKtTest.kt @@ -12,20 +12,20 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.whispersystems.signalservice.internal.push.DonationsConfiguration +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration import org.whispersystems.signalservice.internal.util.JsonUtil import java.util.Currency @RunWith(RobolectricTestRunner::class) @Config(application = Application::class) -class DonationsConfigurationExtensionsKtTest { +class SubscriptionsConfigurationExtensionsKtTest { private val testData: String = javaClass.classLoader!!.getResourceAsStream("donations_configuration_test_data.json").bufferedReader().readText() - private val testSubject = JsonUtil.fromJson(testData, DonationsConfiguration::class.java) + private val testSubject = JsonUtil.fromJson(testData, SubscriptionsConfiguration::class.java) @Test fun `Given all methods are available, when I getSubscriptionAmounts, then I expect all currencies`() { - val subscriptionPrices = testSubject.getSubscriptionAmounts(DonationsConfiguration.SUBSCRIPTION_LEVELS.first(), AllPaymentMethodsAvailability) + val subscriptionPrices = testSubject.getSubscriptionAmounts(SubscriptionsConfiguration.SUBSCRIPTION_LEVELS.first(), AllPaymentMethodsAvailability) assertEquals(3, subscriptionPrices.size) assertTrue(subscriptionPrices.map { it.currency.currencyCode }.containsAll(setOf("JPY", "BIF", "USD"))) @@ -33,7 +33,7 @@ class DonationsConfigurationExtensionsKtTest { @Test fun `Given only PayPal available, when I getSubscriptionAmounts, then I expect BIF and JPY`() { - val subscriptionPrices = testSubject.getSubscriptionAmounts(DonationsConfiguration.SUBSCRIPTION_LEVELS.first(), PayPalOnly) + val subscriptionPrices = testSubject.getSubscriptionAmounts(SubscriptionsConfiguration.SUBSCRIPTION_LEVELS.first(), PayPalOnly) assertEquals(2, subscriptionPrices.size) assertTrue(subscriptionPrices.map { it.currency.currencyCode }.containsAll(setOf("JPY", "BIF"))) @@ -41,7 +41,7 @@ class DonationsConfigurationExtensionsKtTest { @Test fun `Given only Card available, when I getSubscriptionAmounts, then I expect BIF and USD`() { - val subscriptionPrices = testSubject.getSubscriptionAmounts(DonationsConfiguration.SUBSCRIPTION_LEVELS.first(), CardOnly) + val subscriptionPrices = testSubject.getSubscriptionAmounts(SubscriptionsConfiguration.SUBSCRIPTION_LEVELS.first(), CardOnly) assertEquals(2, subscriptionPrices.size) assertTrue(subscriptionPrices.map { it.currency.currencyCode }.containsAll(setOf("USD", "BIF"))) @@ -76,7 +76,7 @@ class DonationsConfigurationExtensionsKtTest { val subscriptionLevels = testSubject.getSubscriptionLevels() assertEquals(3, subscriptionLevels.size) - assertEquals(DonationsConfiguration.SUBSCRIPTION_LEVELS, subscriptionLevels.keys) + assertEquals(SubscriptionsConfiguration.SUBSCRIPTION_LEVELS, subscriptionLevels.keys) subscriptionLevels.keys.fold(0) { acc, i -> assertTrue(acc < i) i @@ -165,7 +165,7 @@ class DonationsConfigurationExtensionsKtTest { fun `Given GIFT_LEVEL, When I getBadge, then I expect the gift badge`() { mockkStatic(ApplicationDependencies::class) { every { ApplicationDependencies.getApplication() } returns ApplicationProvider.getApplicationContext() - val badge = testSubject.getBadge(DonationsConfiguration.GIFT_LEVEL) + val badge = testSubject.getBadge(SubscriptionsConfiguration.GIFT_LEVEL) assertTrue(badge.isGift()) } @@ -175,7 +175,7 @@ class DonationsConfigurationExtensionsKtTest { fun `Given BOOST_LEVEL, When I getBadge, then I expect the boost badge`() { mockkStatic(ApplicationDependencies::class) { every { ApplicationDependencies.getApplication() } returns ApplicationProvider.getApplicationContext() - val badge = testSubject.getBadge(DonationsConfiguration.BOOST_LEVEL) + val badge = testSubject.getBadge(SubscriptionsConfiguration.BOOST_LEVEL) assertTrue(badge.isBoost()) } @@ -185,7 +185,7 @@ class DonationsConfigurationExtensionsKtTest { fun `Given a sub level, When I getBadge, then I expect a sub badge`() { mockkStatic(ApplicationDependencies::class) { every { ApplicationDependencies.getApplication() } returns ApplicationProvider.getApplicationContext() - val badge = testSubject.getBadge(DonationsConfiguration.SUBSCRIPTION_LEVELS.first()) + val badge = testSubject.getBadge(SubscriptionsConfiguration.SUBSCRIPTION_LEVELS.first()) assertTrue(badge.isSubscription()) } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java index 922c168dac..09721453d4 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java @@ -19,7 +19,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.push.BankMandate; import org.whispersystems.signalservice.internal.push.DonationProcessor; -import org.whispersystems.signalservice.internal.push.DonationsConfiguration; +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration; import org.whispersystems.signalservice.internal.push.PushServiceSocket; import java.io.IOException; @@ -42,8 +42,8 @@ public class DonationsService { private final PushServiceSocket pushServiceSocket; - private final AtomicReference> donationsConfigurationCache = new AtomicReference<>(null); - private final AtomicReference> sepaBankMandateCache = new AtomicReference<>(null); + private final AtomicReference> donationsConfigurationCache = new AtomicReference<>(null); + private final AtomicReference> sepaBankMandateCache = new AtomicReference<>(null); private static class CacheEntry { private final T cachedValue; @@ -111,7 +111,7 @@ public ServiceResponse submitBoostReceiptCredentialRe return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.submitBoostReceiptCredentials(paymentIntentId, receiptCredentialRequest, processor), 200)); } - public ServiceResponse getDonationsConfiguration(Locale locale) { + public ServiceResponse getDonationsConfiguration(Locale locale) { return getCachedValue( locale, donationsConfigurationCache, 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 36a050d34f..1f274f8edf 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 @@ -1348,11 +1348,11 @@ public ReceiptCredentialResponse submitBoostReceiptCredentials(String paymentInt /** * Get the DonationsConfiguration pointed at by /v1/subscriptions/configuration */ - public DonationsConfiguration getDonationsConfiguration(Locale locale) throws IOException { + public SubscriptionsConfiguration getDonationsConfiguration(Locale locale) throws IOException { Map headers = Collections.singletonMap("Accept-Language", locale.getLanguage() + "-" + locale.getCountry()); String result = makeServiceRequestWithoutAuthentication(DONATIONS_CONFIGURATION, "GET", null, headers, NO_HANDLER); - return JsonUtil.fromJson(result, DonationsConfiguration.class); + return JsonUtil.fromJson(result, SubscriptionsConfiguration.class); } /** diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationsConfiguration.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java similarity index 90% rename from libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationsConfiguration.java rename to libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java index 52a5fc81b2..8cc0d6792b 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationsConfiguration.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java @@ -14,7 +14,7 @@ /** * Response JSON for a call to /v1/subscriptions/configuration */ -public class DonationsConfiguration { +public class SubscriptionsConfiguration { public static final String PAYPAL = "PAYPAL"; public static final String CARD = "CARD"; @@ -44,6 +44,9 @@ public static class CurrencyConfiguration { @JsonProperty("subscription") private Map subscription; + @JsonProperty("backupSubscription") + private Map backupSubscription; + @JsonProperty("supportedPaymentMethods") private Set supportedPaymentMethods; @@ -59,6 +62,10 @@ public Map getSubscription() { return subscription; } + public Map getBackupSubscription() { + return backupSubscription; + } + public Set getSupportedPaymentMethods() { return supportedPaymentMethods; } From 1e4d96b7c40f135dca441d09bc6a075107b3781f Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Thu, 18 Apr 2024 11:23:43 -0400 Subject: [PATCH 031/113] Add camera permission check to group stories. --- .../conversation/ConversationSettingsFragment.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index d79f32aa29..72e6123036 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.components.settings.conversation +import android.Manifest import android.app.ActivityOptions import android.content.ActivityNotFoundException import android.content.Context @@ -79,6 +80,7 @@ import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository import org.thoughtcrime.securesms.nicknames.NicknameActivity +import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientExporter @@ -197,6 +199,10 @@ class ConversationSettingsFragment : DSLSettingsFragment( } } + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults) + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return if (item.itemId == R.id.action_edit) { val args = ConversationSettingsFragmentArgs.fromBundle(requireArguments()) @@ -415,7 +421,14 @@ class ConversationSettingsFragment : DSLSettingsFragment( .setPositiveButton(android.R.string.ok) { d, _ -> d.dismiss() } .show() } else { - addToGroupStoryDelegate.addToStory(state.recipient.id) + Permissions.with(this@ConversationSettingsFragment) + .request(Manifest.permission.CAMERA) + .ifNecessary() + .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.symbol_camera_24) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) + .onAllGranted { addToGroupStoryDelegate.addToStory(state.recipient.id) } + .onAnyDenied { Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show() } + .execute() } }, onVideoClick = { From a82b9ee25f1a148b546da7264bb725dbb17a8b8a Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 18 Apr 2024 11:23:58 -0400 Subject: [PATCH 032/113] Add a job to backfill attachment uploads to the archive service. --- .../attachments/AttachmentUploadUtil.kt | 105 ++++++++ .../securesms/backup/v2/BackupRepository.kt | 4 + .../securesms/database/AttachmentTable.kt | 122 +++++++++- .../helpers/SignalDatabaseMigrations.kt | 6 +- .../V227_AddAttachmentArchiveTransferState.kt | 19 ++ .../securesms/jobmanager/JobExtensions.kt | 14 -- .../jobs/ArchiveAttachmentBackfillJob.kt | 226 ++++++++++++++++++ .../jobs/AttachmentHashBackfillJob.kt | 3 +- .../securesms/jobs/AttachmentUploadJob.kt | 80 +------ .../securesms/jobs/JobManagerFactories.java | 1 + .../securesms/jobs/PreKeysSyncJob.kt | 2 +- app/src/main/protowire/JobData.proto | 5 + .../signalservice/api/NetworkResult.kt | 19 +- .../signalservice/api/archive/ArchiveApi.kt | 7 + .../api/archive/ArchiveMediaResponse.kt | 17 +- .../NonSuccessfulResponseCodeException.java | 15 +- .../internal/push/PushServiceSocket.java | 35 ++- 17 files changed, 567 insertions(+), 113 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V227_AddAttachmentArchiveTransferState.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobExtensions.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt new file mode 100644 index 0000000000..3423a8150a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.attachments + +import android.content.Context +import android.graphics.Bitmap +import android.os.Build +import org.signal.core.util.logging.Log +import org.signal.protos.resumableuploads.ResumableUpload +import org.thoughtcrime.securesms.blurhash.BlurHashEncoder +import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.util.MediaUtil +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream +import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec +import java.io.IOException +import java.util.Objects + +/** + * A place collect common attachment upload operations to allow for code reuse. + */ +object AttachmentUploadUtil { + + private val TAG = Log.tag(AttachmentUploadUtil::class.java) + + /** + * Builds a [SignalServiceAttachmentStream] from the provided data, which can then be provided to various upload methods. + */ + @Throws(IOException::class) + fun buildSignalServiceAttachmentStream( + context: Context, + attachment: Attachment, + uploadSpec: ResumableUpload, + cancellationSignal: (() -> Boolean)? = null, + progressListener: ProgressListener? = null + ): SignalServiceAttachmentStream { + val inputStream = PartAuthority.getAttachmentStream(context, attachment.uri!!) + val builder = SignalServiceAttachment.newStreamBuilder() + .withStream(inputStream) + .withContentType(attachment.contentType) + .withLength(attachment.size) + .withFileName(attachment.fileName) + .withVoiceNote(attachment.voiceNote) + .withBorderless(attachment.borderless) + .withGif(attachment.videoGif) + .withFaststart(attachment.transformProperties?.mp4FastStart ?: false) + .withWidth(attachment.width) + .withHeight(attachment.height) + .withUploadTimestamp(System.currentTimeMillis()) + .withCaption(attachment.caption) + .withResumableUploadSpec(ResumableUploadSpec.from(uploadSpec)) + .withCancelationSignal(cancellationSignal) + .withListener(progressListener) + + if (MediaUtil.isImageType(attachment.contentType)) { + builder.withBlurHash(getImageBlurHash(context, attachment)) + } else if (MediaUtil.isVideoType(attachment.contentType)) { + builder.withBlurHash(getVideoBlurHash(context, attachment)) + } + + return builder.build() + } + + @Throws(IOException::class) + private fun getImageBlurHash(context: Context, attachment: Attachment): String? { + if (attachment.blurHash != null) { + return attachment.blurHash!!.hash + } + + if (attachment.uri == null) { + return null + } + + return PartAuthority.getAttachmentStream(context, attachment.uri!!).use { inputStream -> + BlurHashEncoder.encode(inputStream) + } + } + + @Throws(IOException::class) + private fun getVideoBlurHash(context: Context, attachment: Attachment): String? { + if (attachment.blurHash != null) { + return attachment.blurHash.hash + } + + if (Build.VERSION.SDK_INT < 23) { + Log.w(TAG, "Video thumbnails not supported...") + return null + } + + return MediaUtil.getVideoThumbnail(context, Objects.requireNonNull(attachment.uri), 1000)?.let { bitmap -> + val thumb = Bitmap.createScaledBitmap(bitmap, 100, 100, false) + bitmap.recycle() + + Log.i(TAG, "Generated video thumbnail...") + val hash = BlurHashEncoder.encode(thumb) + thumb.recycle() + + hash + } + } +} 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 52cc26740f..8a1377abc8 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 @@ -346,6 +346,10 @@ object BackupRepository { return api .triggerBackupIdReservation(backupKey) .then { getAuthCredential() } + .then { credential -> + api.setPublicKey(backupKey, credential) + .map { credential } + } .then { credential -> val mediaName = attachment.getMediaName() val request = attachment.toArchiveMediaRequest(mediaName, backupKey) 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 e10954a671..f10387e5f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -98,7 +98,6 @@ import java.security.NoSuchAlgorithmException import java.util.LinkedList import java.util.Optional import java.util.UUID -import kotlin.time.Duration.Companion.days class AttachmentTable( context: Context, @@ -147,6 +146,7 @@ class AttachmentTable( const val ARCHIVE_MEDIA_NAME = "archive_media_name" const val ARCHIVE_MEDIA_ID = "archive_media_id" const val ARCHIVE_TRANSFER_FILE = "archive_transfer_file" + const val ARCHIVE_TRANSFER_STATE = "archive_transfer_state" const val ATTACHMENT_JSON_ALIAS = "attachment_json" @@ -201,7 +201,8 @@ class AttachmentTable( ARCHIVE_TRANSFER_FILE ) - const val CREATE_TABLE = """ + @JvmField + val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( $ID INTEGER PRIMARY KEY AUTOINCREMENT, $MESSAGE_ID INTEGER, @@ -239,7 +240,8 @@ class AttachmentTable( $ARCHIVE_CDN INTEGER DEFAULT 0, $ARCHIVE_MEDIA_NAME TEXT DEFAULT NULL, $ARCHIVE_MEDIA_ID TEXT DEFAULT NULL, - $ARCHIVE_TRANSFER_FILE TEXT DEFAULT NULL + $ARCHIVE_TRANSFER_FILE TEXT DEFAULT NULL, + $ARCHIVE_TRANSFER_STATE INTEGER DEFAULT ${ArchiveTransferState.NONE.value} ) """ @@ -254,8 +256,6 @@ class AttachmentTable( "CREATE INDEX IF NOT EXISTS attachment_archive_media_id_index ON $TABLE_NAME ($ARCHIVE_MEDIA_ID);" ) - val ATTACHMENT_POINTER_REUSE_THRESHOLD = 7.days.inWholeMilliseconds - @JvmStatic @Throws(IOException::class) fun newDataFile(context: Context): File { @@ -426,6 +426,78 @@ class AttachmentTable( }.flatten() } + /** + * Finds the next eligible attachment that needs to be uploaded to the archive service. + * If it exists, it'll also atomically be marked as [ArchiveTransferState.BACKFILL_UPLOAD_IN_PROGRESS]. + */ + fun getNextAttachmentToArchiveAndMarkUploadInProgress(): DatabaseAttachment? { + return writableDatabase.withinTransaction { + val record: DatabaseAttachment? = readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$ARCHIVE_TRANSFER_STATE = ? AND $DATA_FILE NOT NULL AND $TRANSFER_STATE = $TRANSFER_PROGRESS_DONE", ArchiveTransferState.NONE.value) + .orderBy("$ID DESC") + .limit(1) + .run() + .readToSingleObject { it.readAttachment() } + + if (record != null) { + writableDatabase + .update(TABLE_NAME) + .values(ARCHIVE_TRANSFER_STATE to ArchiveTransferState.BACKFILL_UPLOAD_IN_PROGRESS.value) + .where("$ID = ?", record.attachmentId) + .run() + } + + record + } + } + + /** + * Returns the current archive transfer state, if the attachment can be found. + */ + fun getArchiveTransferState(id: AttachmentId): ArchiveTransferState? { + return readableDatabase + .select(ARCHIVE_TRANSFER_STATE) + .from(TABLE_NAME) + .where("$ID = ?", id.id) + .run() + .readToSingleObject { ArchiveTransferState.deserialize(it.requireInt(ARCHIVE_TRANSFER_STATE)) } + } + + /** + * Sets the archive transfer state for the given attachment and all other attachments that share the same data file. + */ + fun setArchiveTransferState(id: AttachmentId, state: ArchiveTransferState) { + writableDatabase.withinTransaction { + val dataFile: String = readableDatabase + .select(DATA_FILE) + .from(TABLE_NAME) + .where("$ID = ?", id.id) + .run() + .readToSingleObject { it.requireString(DATA_FILE) } ?: return@withinTransaction + + writableDatabase + .update(TABLE_NAME) + .values(ARCHIVE_TRANSFER_STATE to state.value) + .where("$DATA_FILE = ?", dataFile) + .run() + } + } + + /** + * Resets any in-progress archive backfill states to [ArchiveTransferState.NONE], returning the number that had to be reset. + * This should only be called if you believe the backfill process has finished. In this case, if this returns a value > 0, + * it indicates that state was mis-tracked and you should try uploading again. + */ + fun resetPendingArchiveBackfills(): Int { + return writableDatabase + .update(TABLE_NAME) + .values(ARCHIVE_TRANSFER_STATE to ArchiveTransferState.NONE.value) + .where("$ARCHIVE_TRANSFER_STATE == ${ArchiveTransferState.BACKFILL_UPLOAD_IN_PROGRESS.value} || $ARCHIVE_TRANSFER_STATE == ${ArchiveTransferState.BACKFILL_UPLOADED.value}") + .run() + } + fun deleteAttachmentsForMessage(mmsId: Long): Boolean { Log.d(TAG, "[deleteAttachmentsForMessage] mmsId: $mmsId") @@ -1992,4 +2064,44 @@ class AttachmentTable( } } } + + /** + * This maintains two different state paths for uploading attachments to the archive. + * + * The first is the backfill process, which will happen after newly-enabling backups. That process will go: + * 1. [NONE] + * 2. [BACKFILL_UPLOAD_IN_PROGRESS] + * 3. [BACKFILL_UPLOADED] + * 4. [FINISHED] or [PERMANENT_FAILURE] + * + * The second is when newly sending/receiving an attachment after enabling backups. That process will go: + * 1. [NONE] + * 2. [ATTACHMENT_TRANSFER_PENDING] + * 3. [FINISHED] or [PERMANENT_FAILURE] + */ + enum class ArchiveTransferState(val value: Int) { + /** Not backed up at all. */ + NONE(0), + + /** The upload to the attachment service is in progress. */ + BACKFILL_UPLOAD_IN_PROGRESS(1), + + /** Successfully uploaded to the attachment service during the backfill process. Still need to tell the service to move the file over to the archive service. */ + BACKFILL_UPLOADED(2), + + /** Completely finished backing up the attachment. */ + FINISHED(3), + + /** It is impossible to upload this attachment. */ + PERMANENT_FAILURE(4), + + /** We sent/received this attachment after enabling backups, but still need to transfer the file to the archive service. */ + ATTACHMENT_TRANSFER_PENDING(5); + + companion object { + fun deserialize(value: Int): ArchiveTransferState { + return values().firstOrNull { it.value == value } ?: NONE + } + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 9cffce8811..17dabc9d4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -84,6 +84,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V223_AddNicknameAnd import org.thoughtcrime.securesms.database.helpers.migration.V224_AddAttachmentArchiveColumns import org.thoughtcrime.securesms.database.helpers.migration.V225_AddLocalUserJoinedStateAndGroupCallActiveState import org.thoughtcrime.securesms.database.helpers.migration.V226_AddAttachmentMediaIdIndex +import org.thoughtcrime.securesms.database.helpers.migration.V227_AddAttachmentArchiveTransferState /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -170,10 +171,11 @@ object SignalDatabaseMigrations { 223 to V223_AddNicknameAndNoteFieldsToRecipientTable, 224 to V224_AddAttachmentArchiveColumns, 225 to V225_AddLocalUserJoinedStateAndGroupCallActiveState, - 226 to V226_AddAttachmentMediaIdIndex + 226 to V226_AddAttachmentMediaIdIndex, + 227 to V227_AddAttachmentArchiveTransferState ) - const val DATABASE_VERSION = 226 + const val DATABASE_VERSION = 227 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V227_AddAttachmentArchiveTransferState.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V227_AddAttachmentArchiveTransferState.kt new file mode 100644 index 0000000000..db664232e0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V227_AddAttachmentArchiveTransferState.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds a new column to track the status of transferring attachments to the archive service. + */ +object V227_AddAttachmentArchiveTransferState : SignalDatabaseMigration { + + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE attachment ADD COLUMN archive_transfer_state INTEGER DEFAULT 0") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobExtensions.kt deleted file mode 100644 index 525b7b4e9b..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobExtensions.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.jobmanager - -import org.thoughtcrime.securesms.jobmanager.impl.BackoffUtil -import org.thoughtcrime.securesms.util.FeatureFlags - -/** - * Helper to calculate the default backoff interval for a [Job] given it's run attempt count. - */ -fun Job.defaultBackoffInterval(): Long = BackoffUtil.exponentialBackoff(runAttempt + 1, FeatureFlags.getDefaultMaxBackoff()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt new file mode 100644 index 0000000000..71436b32f6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt @@ -0,0 +1,226 @@ +/* + * 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.signal.protos.resumableuploads.ResumableUpload +import org.thoughtcrime.securesms.attachments.Attachment +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.attachments.AttachmentUploadUtil +import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.attachments.PointerAttachment +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.protos.ArchiveAttachmentBackfillJobData +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.archive.ArchiveMediaResponse +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer +import java.io.IOException +import java.util.Optional +import kotlin.time.Duration.Companion.days + +/** + * When run, this will find the next attachment that needs to be uploaded to the archive service and upload it. + * It will enqueue a copy of itself if it thinks there is more work to be done, and that copy will continue the upload process. + */ +class ArchiveAttachmentBackfillJob private constructor( + parameters: Parameters, + private var attachmentId: AttachmentId?, + private var uploadSpec: ResumableUpload? +) : Job(parameters) { + companion object { + private val TAG = Log.tag(ArchiveAttachmentBackfillJob::class.java) + + const val KEY = "ArchiveAttachmentBackfillJob" + } + + constructor() : this( + parameters = Parameters.Builder() + .setQueue("ArchiveAttachmentBackfillJob") + .setMaxInstancesForQueue(2) + .setLifespan(30.days.inWholeMilliseconds) + .setMaxAttempts(Parameters.UNLIMITED) + .addConstraint(NetworkConstraint.KEY) + .build(), + attachmentId = null, + uploadSpec = null + ) + + override fun serialize(): ByteArray { + return ArchiveAttachmentBackfillJobData( + attachmentId = attachmentId?.id, + uploadSpec = uploadSpec + ).encode() + } + + override fun getFactoryKey(): String = KEY + + override fun run(): Result { + var attachmentRecord: DatabaseAttachment? = if (attachmentId != null) { + Log.i(TAG, "Retrying $attachmentId") + SignalDatabase.attachments.getAttachment(attachmentId!!) + } else { + SignalDatabase.attachments.getNextAttachmentToArchiveAndMarkUploadInProgress() + } + + if (attachmentRecord == null && attachmentId != null) { + Log.w(TAG, "Attachment $attachmentId was not found! Was likely deleted during the process of archiving. Re-enqueuing job with no ID.") + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + return Result.success() + } + + // TODO [backup] If we ever wanted to allow multiple instances of this job to run in parallel, this would have to be done somewhere else + if (attachmentRecord == null) { + Log.i(TAG, "No more attachments to backfill! Ensuring there's no dangling state.") + + val resetCount = SignalDatabase.attachments.resetPendingArchiveBackfills() + if (resetCount > 0) { + Log.w(TAG, "We thought we were done, but $resetCount items were still in progress! Need to run again to retry.") + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + } else { + Log.i(TAG, "All good! Should be done.") + } + + return Result.success() + } + + attachmentId = attachmentRecord.attachmentId + + val transferState: AttachmentTable.ArchiveTransferState? = SignalDatabase.attachments.getArchiveTransferState(attachmentRecord.attachmentId) + if (transferState == null) { + Log.w(TAG, "Attachment $attachmentId was not found when looking for the transfer state! Was likely just deleted. Re-enqueuing job with no ID.") + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + return Result.success() + } + + Log.i(TAG, "Current state: $transferState") + + if (transferState == AttachmentTable.ArchiveTransferState.FINISHED) { + Log.i(TAG, "Attachment $attachmentId is already finished. Skipping.") + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + return Result.success() + } + + if (transferState == AttachmentTable.ArchiveTransferState.PERMANENT_FAILURE) { + Log.i(TAG, "Attachment $attachmentId is already marked as a permanent failure. Skipping.") + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + return Result.success() + } + + if (transferState == AttachmentTable.ArchiveTransferState.ATTACHMENT_TRANSFER_PENDING) { + Log.i(TAG, "Attachment $attachmentId is already marked as pending transfer, meaning it's a send attachment that will be uploaded on it's own. Skipping.") + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + return Result.success() + } + + if (transferState == AttachmentTable.ArchiveTransferState.BACKFILL_UPLOAD_IN_PROGRESS) { + if (uploadSpec == null || System.currentTimeMillis() > uploadSpec!!.timeout) { + Log.d(TAG, "Need an upload spec. Fetching...") + uploadSpec = ApplicationDependencies.getSignalServiceMessageSender().getResumableUploadSpec().toProto() + } else { + Log.d(TAG, "Already have an upload spec. Continuing...") + } + + val attachmentStream = try { + AttachmentUploadUtil.buildSignalServiceAttachmentStream( + context = context, + attachment = attachmentRecord, + uploadSpec = uploadSpec!!, + cancellationSignal = { this.isCanceled } + ) + } catch (e: IOException) { + Log.e(TAG, "Failed to get attachment stream for $attachmentId", e) + return Result.retry(defaultBackoff()) + } + + Log.d(TAG, "Beginning upload...") + val remoteAttachment: SignalServiceAttachmentPointer = try { + ApplicationDependencies.getSignalServiceMessageSender().uploadAttachment(attachmentStream) + } catch (e: IOException) { + Log.w(TAG, "Failed to upload $attachmentId", e) + return Result.retry(defaultBackoff()) + } + Log.d(TAG, "Upload complete!") + + val pointerAttachment: Attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, attachmentRecord.fastPreflightId).get() + SignalDatabase.attachments.finalizeAttachmentAfterUpload(attachmentRecord.attachmentId, pointerAttachment, remoteAttachment.uploadTimestamp) + SignalDatabase.attachments.setArchiveTransferState(attachmentRecord.attachmentId, AttachmentTable.ArchiveTransferState.BACKFILL_UPLOADED) + + attachmentRecord = SignalDatabase.attachments.getAttachment(attachmentRecord.attachmentId) + } + + if (attachmentRecord == null) { + Log.w(TAG, "$attachmentId was not found after uploading! Possibly deleted in a narrow race condition. Re-enqueuing job with no ID.") + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + return Result.success() + } + + Log.d(TAG, "Moving attachment to archive...") + return when (val result = BackupRepository.archiveMedia(attachmentRecord)) { + is NetworkResult.Success -> { + Log.d(TAG, "Move complete!") + + SignalDatabase.attachments.setArchiveTransferState(attachmentRecord.attachmentId, AttachmentTable.ArchiveTransferState.FINISHED) + ApplicationDependencies.getJobManager().add(ArchiveAttachmentBackfillJob()) + Result.success() + } + + is NetworkResult.ApplicationError -> { + Log.w(TAG, "Failed to archive ${attachmentRecord.attachmentId} due to an application error. Retrying.", result.throwable) + Result.retry(defaultBackoff()) + } + + is NetworkResult.NetworkError -> { + Log.w(TAG, "Encountered a transient network error. Retrying.") + Result.retry(defaultBackoff()) + } + + is NetworkResult.StatusCodeError -> { + Log.w(TAG, "Failed request with status code ${result.code} for ${attachmentRecord.attachmentId}") + + when (ArchiveMediaResponse.StatusCodes.from(result.code)) { + ArchiveMediaResponse.StatusCodes.BadArguments, + ArchiveMediaResponse.StatusCodes.InvalidPresentationOrSignature, + ArchiveMediaResponse.StatusCodes.InsufficientPermissions, + ArchiveMediaResponse.StatusCodes.RateLimited -> { + Result.retry(defaultBackoff()) + } + + ArchiveMediaResponse.StatusCodes.NoMediaSpaceRemaining -> { + // TODO [backup] This will end the process right away. We need to integrate this with client-driven retry UX. + Result.failure() + } + + ArchiveMediaResponse.StatusCodes.Unknown -> { + Result.retry(defaultBackoff()) + } + } + } + } + } + + override fun onFailure() { + attachmentId?.let { id -> + Log.w(TAG, "Failed to archive $id!") + } + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): ArchiveAttachmentBackfillJob { + val data = serializedData?.let { ArchiveAttachmentBackfillJobData.ADAPTER.decode(it) } + + return ArchiveAttachmentBackfillJob( + parameters = parameters, + attachmentId = data?.attachmentId?.let { AttachmentId(it) }, + uploadSpec = data?.uploadSpec + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentHashBackfillJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentHashBackfillJob.kt index a4178f470c..be32017fe2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentHashBackfillJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentHashBackfillJob.kt @@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobmanager.Job -import org.thoughtcrime.securesms.jobmanager.defaultBackoffInterval import java.io.File import java.io.FileNotFoundException import java.io.IOException @@ -84,7 +83,7 @@ class AttachmentHashBackfillJob private constructor(parameters: Parameters) : Jo Log.w(TAG, "Underlying cause was a FileNotFoundException. Clearing all usages.", true) SignalDatabase.attachments.clearUsagesOfDataFile(file) } else { - return Result.retry(defaultBackoffInterval()) + return Result.retry(defaultBackoff()) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.kt index e8af9f7247..c6097ef5fe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.kt @@ -4,8 +4,6 @@ */ package org.thoughtcrime.securesms.jobs -import android.graphics.Bitmap -import android.os.Build import android.text.TextUtils import org.greenrobot.eventbus.EventBus import org.signal.core.util.inRoundedDays @@ -15,8 +13,8 @@ import org.signal.protos.resumableuploads.ResumableUpload import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.attachments.AttachmentUploadUtil import org.thoughtcrime.securesms.attachments.PointerAttachment -import org.thoughtcrime.securesms.blurhash.BlurHashEncoder import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies @@ -26,20 +24,16 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec import org.thoughtcrime.securesms.jobs.protos.AttachmentUploadJobData import org.thoughtcrime.securesms.mms.MmsException -import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.net.NotPushRegisteredException import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.service.AttachmentProgressService import org.thoughtcrime.securesms.util.FeatureFlags -import org.thoughtcrime.securesms.util.MediaUtil import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil import org.whispersystems.signalservice.api.messages.SignalServiceAttachment import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResumableUploadResponseCodeException import org.whispersystems.signalservice.internal.crypto.PaddingInputStream -import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec import java.io.IOException -import java.util.Objects import java.util.Optional import java.util.concurrent.TimeUnit import kotlin.time.Duration.Companion.days @@ -210,23 +204,12 @@ class AttachmentUploadJob private constructor( } return try { - val inputStream = PartAuthority.getAttachmentStream(context, attachment.uri!!) - val builder = SignalServiceAttachment.newStreamBuilder() - .withStream(inputStream) - .withContentType(attachment.contentType) - .withLength(attachment.size) - .withFileName(attachment.fileName) - .withVoiceNote(attachment.voiceNote) - .withBorderless(attachment.borderless) - .withGif(attachment.videoGif) - .withFaststart(attachment.transformProperties?.mp4FastStart ?: false) - .withWidth(attachment.width) - .withHeight(attachment.height) - .withUploadTimestamp(System.currentTimeMillis()) - .withCaption(attachment.caption) - .withResumableUploadSpec(ResumableUploadSpec.from(resumableUploadSpec)) - .withCancelationSignal { this.isCanceled } - .withListener(object : SignalServiceAttachment.ProgressListener { + AttachmentUploadUtil.buildSignalServiceAttachmentStream( + context = context, + attachment = attachment, + uploadSpec = resumableUploadSpec, + cancellationSignal = { isCanceled }, + progressListener = object : SignalServiceAttachment.ProgressListener { override fun onAttachmentProgress(total: Long, progress: Long) { EventBus.getDefault().postSticky(PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)) notification?.progress = (progress.toFloat() / total) @@ -235,58 +218,13 @@ class AttachmentUploadJob private constructor( override fun shouldCancel(): Boolean { return isCanceled } - }) - - if (MediaUtil.isImageType(attachment.contentType)) { - builder.withBlurHash(getImageBlurHash(attachment)).build() - } else if (MediaUtil.isVideoType(attachment.contentType)) { - builder.withBlurHash(getVideoBlurHash(attachment)).build() - } else { - builder.build() - } + } + ) } catch (e: IOException) { throw InvalidAttachmentException(e) } } - @Throws(IOException::class) - private fun getImageBlurHash(attachment: Attachment): String? { - if (attachment.blurHash != null) { - return attachment.blurHash!!.hash - } - - if (attachment.uri == null) { - return null - } - - return PartAuthority.getAttachmentStream(context, attachment.uri!!).use { inputStream -> - BlurHashEncoder.encode(inputStream) - } - } - - @Throws(IOException::class) - private fun getVideoBlurHash(attachment: Attachment): String? { - if (attachment.blurHash != null) { - return attachment.blurHash!!.hash - } - - if (Build.VERSION.SDK_INT < 23) { - Log.w(TAG, "Video thumbnails not supported...") - return null - } - - return MediaUtil.getVideoThumbnail(context, Objects.requireNonNull(attachment.uri), 1000)?.let { bitmap -> - val thumb = Bitmap.createScaledBitmap(bitmap, 100, 100, false) - bitmap.recycle() - - Log.i(TAG, "Generated video thumbnail...") - val hash = BlurHashEncoder.encode(thumb) - thumb.recycle() - - hash - } - } - private inner class InvalidAttachmentException : Exception { constructor(message: String?) : super(message) constructor(e: Exception?) : super(e) 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 01553fac8f..d79a476d69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -101,6 +101,7 @@ public static Map getJobFactories(@NonNull Application appl put(AccountConsistencyWorkerJob.KEY, new AccountConsistencyWorkerJob.Factory()); put(AnalyzeDatabaseJob.KEY, new AnalyzeDatabaseJob.Factory()); put(ArchiveAttachmentJob.KEY, new ArchiveAttachmentJob.Factory()); + put(ArchiveAttachmentBackfillJob.KEY, new ArchiveAttachmentBackfillJob.Factory()); put(AttachmentCompressionJob.KEY, new AttachmentCompressionJob.Factory()); put(AttachmentCopyJob.KEY, new AttachmentCopyJob.Factory()); put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt index 35a6e7cf0f..fb79eda0d5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt @@ -269,7 +269,7 @@ class PreKeysSyncJob private constructor( return when (result) { is NetworkResult.Success -> true - is NetworkResult.NetworkError -> throw result.throwable ?: PushNetworkException("Network error") + is NetworkResult.NetworkError -> throw result.exception ?: PushNetworkException("Network error") is NetworkResult.ApplicationError -> throw result.throwable is NetworkResult.StatusCodeError -> if (result.code == 409) { false diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index 94b1a1dfd1..29964bc2dc 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -50,4 +50,9 @@ message PreKeysSyncJobData { message ArchiveAttachmentJobData { uint64 attachmentId = 1; +} + +message ArchiveAttachmentBackfillJobData { + optional uint64 attachmentId = 1; + ResumableUpload uploadSpec = 2; } \ No newline at end of file diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt index 12981b17df..3d741d9295 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt @@ -6,7 +6,6 @@ package org.whispersystems.signalservice.api import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException import java.io.IOException /** @@ -33,7 +32,7 @@ sealed class NetworkResult { fun fromFetch(fetch: () -> T): NetworkResult = try { Success(fetch()) } catch (e: NonSuccessfulResponseCodeException) { - StatusCodeError(e.code, e) + StatusCodeError(e.code, e.body, e) } catch (e: IOException) { NetworkError(e) } catch (e: Throwable) { @@ -45,10 +44,10 @@ sealed class NetworkResult { data class Success(val result: T) : NetworkResult() /** Indicates a generic network error occurred before we were able to process a response. */ - data class NetworkError(val throwable: Throwable? = null) : NetworkResult() + data class NetworkError(val exception: IOException) : NetworkResult() /** Indicates we got a response, but it was a non-2xx response. */ - data class StatusCodeError(val code: Int, val throwable: Throwable? = null) : NetworkResult() + data class StatusCodeError(val code: Int, val body: String?, val exception: IOException) : NetworkResult() /** Indicates that the application somehow failed in a way unrelated to network activity. Usually a runtime crash. */ data class ApplicationError(val throwable: Throwable) : NetworkResult() @@ -59,8 +58,8 @@ sealed class NetworkResult { fun successOrThrow(): T { when (this) { is Success -> return result - is NetworkError -> throw throwable ?: PushNetworkException("Network error") - is StatusCodeError -> throw throwable ?: NonSuccessfulResponseCodeException(this.code) + is NetworkError -> throw exception + is StatusCodeError -> throw exception is ApplicationError -> throw throwable } } @@ -72,8 +71,8 @@ sealed class NetworkResult { fun map(transform: (T) -> R): NetworkResult { return when (this) { is Success -> Success(transform(this.result)) - is NetworkError -> NetworkError(throwable) - is StatusCodeError -> StatusCodeError(code, throwable) + is NetworkError -> NetworkError(exception) + is StatusCodeError -> StatusCodeError(code, body, exception) is ApplicationError -> ApplicationError(throwable) } } @@ -85,8 +84,8 @@ sealed class NetworkResult { fun then(result: (T) -> NetworkResult): NetworkResult { return when (this) { is Success -> result(this.result) - is NetworkError -> NetworkError(throwable) - is StatusCodeError -> StatusCodeError(code, throwable) + is NetworkError -> NetworkError(exception) + is StatusCodeError -> StatusCodeError(code, body, exception) is ApplicationError -> ApplicationError(throwable) } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt index 04ac52f092..e66596915b 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt @@ -176,6 +176,13 @@ class ArchiveApi( /** * Copy and re-encrypt media from the attachments cdn into the backup cdn. + * + * Possible errors: + * 400: Bad arguments, or made on an authenticated channel + * 401: Invalid presentation or signature + * 403: Insufficient permissions + * 413: No media space remaining + * 429: Rate-limited */ fun archiveAttachmentMedia( backupKey: BackupKey, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaResponse.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaResponse.kt index 7cdfa650dd..9fe8ae4427 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaResponse.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaResponse.kt @@ -12,4 +12,19 @@ import com.fasterxml.jackson.annotation.JsonProperty */ class ArchiveMediaResponse( @JsonProperty val cdn: Int -) +) { + enum class StatusCodes(val code: Int) { + BadArguments(400), + InvalidPresentationOrSignature(401), + InsufficientPermissions(403), + NoMediaSpaceRemaining(413), + RateLimited(429), + Unknown(-1); + + companion object { + fun from(code: Int): StatusCodes { + return values().firstOrNull { it.code == code } ?: Unknown + } + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException.java index df4e20cee1..c6c7090f60 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException.java @@ -13,16 +13,25 @@ */ public class NonSuccessfulResponseCodeException extends IOException { - private final int code; + private final int code; + private final String body; public NonSuccessfulResponseCodeException(int code) { super("StatusCode: " + code); this.code = code; + this.body = null; } public NonSuccessfulResponseCodeException(int code, String s) { super("[" + code + "] " + s); this.code = code; + this.body = null; + } + + public NonSuccessfulResponseCodeException(int code, String s, String body) { + super("[" + code + "] " + s); + this.code = code; + this.body = body; } public int getCode() { @@ -36,4 +45,8 @@ public boolean is4xx() { public boolean is5xx() { return code >= 500 && code < 600; } + + public String getBody() { + return body; + } } 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 1f274f8edf..c3425b118c 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 @@ -325,8 +325,9 @@ public class PushServiceSocket { private static final String CALL_LINK_CREATION_AUTH = "/v1/call-link/create-auth"; private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp"; - private static final Map NO_HEADERS = Collections.emptyMap(); - private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler(); + private static final Map NO_HEADERS = Collections.emptyMap(); + private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler(); + private static final ResponseCodeHandler UNOPINIONATED_HANDER = new UnopinionatedResponseCodeHandler(); private static final long CDN2_RESUMABLE_LINK_LIFETIME_MILLIS = TimeUnit.DAYS.toMillis(7); @@ -494,14 +495,14 @@ public ArchiveServiceCredentialsResponse getArchiveCredentials(long currentTime) long secondsRoundedToNearestDay = TimeUnit.DAYS.toSeconds(TimeUnit.MILLISECONDS.toDays(currentTime)); long endTimeInSeconds = secondsRoundedToNearestDay + TimeUnit.DAYS.toSeconds(7); - String response = makeServiceRequest(String.format(Locale.US, ARCHIVE_CREDENTIALS, secondsRoundedToNearestDay, endTimeInSeconds), "GET", null); + String response = makeServiceRequest(String.format(Locale.US, ARCHIVE_CREDENTIALS, secondsRoundedToNearestDay, endTimeInSeconds), "GET", null, NO_HEADERS, UNOPINIONATED_HANDER, Optional.empty()); return JsonUtil.fromJson(response, ArchiveServiceCredentialsResponse.class); } public void setArchiveBackupId(BackupAuthCredentialRequest request) throws IOException { String body = JsonUtil.toJson(new ArchiveSetBackupIdRequest(request)); - makeServiceRequest(ARCHIVE_BACKUP_ID, "PUT", body); + makeServiceRequest(ARCHIVE_BACKUP_ID, "PUT", body, NO_HEADERS, UNOPINIONATED_HANDER, Optional.empty()); } public void setArchivePublicKey(ECPublicKey publicKey, ArchiveCredentialPresentation credentialPresentation) throws IOException { @@ -555,7 +556,7 @@ public ArchiveGetMediaItemsResponse getArchiveMediaItemsPage(ArchiveCredentialPr public ArchiveMediaResponse archiveAttachmentMedia(@Nonnull ArchiveCredentialPresentation credentialPresentation, @Nonnull ArchiveMediaRequest request) throws IOException { Map headers = credentialPresentation.toHeaders(); - String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA, "PUT", JsonUtil.toJson(request), headers, NO_HANDLER); + String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA, "PUT", JsonUtil.toJson(request), headers, UNOPINIONATED_HANDER); return JsonUtil.fromJson(response, ArchiveMediaResponse.class); } @@ -566,7 +567,7 @@ public ArchiveMediaResponse archiveAttachmentMedia(@Nonnull ArchiveCredentialPre public BatchArchiveMediaResponse archiveAttachmentMedia(@Nonnull ArchiveCredentialPresentation credentialPresentation, @Nonnull BatchArchiveMediaRequest request) throws IOException { Map headers = credentialPresentation.toHeaders(); - String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA_BATCH, "PUT", JsonUtil.toJson(request), headers, NO_HANDLER); + String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA_BATCH, "PUT", JsonUtil.toJson(request), headers, UNOPINIONATED_HANDER); return JsonUtil.fromJson(response, BatchArchiveMediaResponse.class); } @@ -2660,6 +2661,28 @@ private static class EmptyResponseCodeHandler implements ResponseCodeHandler { public void handle(int responseCode, ResponseBody body) { } } + /** + * A {@link ResponseCodeHandler} that only throws {@link NonSuccessfulResponseCodeException} with the response body. + * Any further processing is left to the caller. + */ + private static class UnopinionatedResponseCodeHandler implements ResponseCodeHandler { + @Override + public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResponseCodeException, PushNetworkException { + if (responseCode < 200 || responseCode > 299) { + String bodyString = null; + if (body != null) { + try { + bodyString = readBodyString(body); + } catch (MalformedResponseException e) { + Log.w(TAG, "Failed to read body string", e); + } + } + + throw new NonSuccessfulResponseCodeException(responseCode, "Response: " + responseCode, bodyString); + } + } + } + public enum ClientSet { KeyBackup } public CredentialResponse retrieveGroupsV2Credentials(long todaySeconds) From 947ab7d48b18146c7207be04b48cb84aced68ef3 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 18 Apr 2024 13:11:48 -0300 Subject: [PATCH 033/113] Implement skeleton for backup sheets. --- .../backup/v2/ui/BackupAlertBottomSheet.kt | 289 ++++++++++++++++++ .../backup/v2/ui/BackupsIconColors.kt | 45 +++ .../backup/v2/ui/status/BackupStatus.kt | 58 +--- .../src/main/java/org/signal/core/ui/Icons.kt | 57 ++++ 4 files changed, 402 insertions(+), 47 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupsIconColors.kt create mode 100644 core-ui/src/main/java/org/signal/core/ui/Icons.kt 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 new file mode 100644 index 0000000000..a0bb482a7d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt @@ -0,0 +1,289 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.ui + +import android.os.Parcelable +import androidx.annotation.StringRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +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.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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.core.os.BundleCompat +import androidx.core.os.bundleOf +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 +import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment + +/** + * Notifies the user of an issue with their backup. + */ +class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() { + + companion object { + private const val ARG_ALERT = "alert" + + fun create(backupAlert: BackupAlert): BackupAlertBottomSheet { + return BackupAlertBottomSheet().apply { + arguments = bundleOf(ARG_ALERT to backupAlert) + } + } + } + + private val backupAlert: BackupAlert by lazy(LazyThreadSafetyMode.NONE) { + BundleCompat.getParcelable(requireArguments(), ARG_ALERT, BackupAlert::class.java)!! + } + + @Composable + override fun SheetContent() { + BackupAlertSheetContent( + backupAlert = backupAlert, + onPrimaryActionClick = this::performPrimaryAction, + onSecondaryActionClick = this::performSecondaryAction + ) + } + + @Stable + private fun performPrimaryAction() { + when (backupAlert) { + BackupAlert.GENERIC -> { + // TODO [message-backups] -- Back up now + } + BackupAlert.PAYMENT_PROCESSING -> { + // TODO [message-backups] -- Silence + } + BackupAlert.MEDIA_BACKUPS_ARE_OFF -> { + // TODO [message-backups] -- Download media now + } + BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> { + // TODO [message-backups] -- Download media now + } + } + + dismissAllowingStateLoss() + } + + @Stable + private fun performSecondaryAction() { + when (backupAlert) { + BackupAlert.GENERIC -> { + // TODO [message-backups] - Dismiss and notify later + } + BackupAlert.PAYMENT_PROCESSING -> error("PAYMENT_PROCESSING state does not support a secondary action.") + BackupAlert.MEDIA_BACKUPS_ARE_OFF -> { + // TODO [message-backups] - Silence and remind on last day + } + BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> { + // TODO [message-backups] - Silence forever + } + } + + dismissAllowingStateLoss() + } +} + +@Composable +private fun BackupAlertSheetContent( + backupAlert: BackupAlert, + onPrimaryActionClick: () -> Unit, + onSecondaryActionClick: () -> Unit +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter)) + ) { + BottomSheets.Handle() + + Spacer(modifier = Modifier.size(26.dp)) + + val iconColors = rememberBackupsIconColors(backupAlert = backupAlert) + Icons.BrushedForeground( + painter = painterResource(id = R.drawable.symbol_backup_light), // TODO [message-backups] final asset + contentDescription = null, + foregroundBrush = iconColors.foreground, + modifier = Modifier + .size(88.dp) + .background(color = iconColors.background, shape = CircleShape) + .padding(20.dp) + ) + + Text( + text = stringResource(id = rememberTitleResource(backupAlert = backupAlert)), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp, bottom = 6.dp) + ) + + when (backupAlert) { + BackupAlert.GENERIC -> GenericBody() + BackupAlert.PAYMENT_PROCESSING -> PaymentProcessingBody() + BackupAlert.MEDIA_BACKUPS_ARE_OFF -> MediaBackupsAreOffBody() + BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> MediaWillBeDeletedTodayBody() + } + + val secondaryActionResource = rememberSecondaryActionResource(backupAlert = backupAlert) + val padBottom = if (secondaryActionResource > 0) 16.dp else 56.dp + + Buttons.LargeTonal( + onClick = onPrimaryActionClick, + modifier = Modifier + .defaultMinSize(minWidth = 220.dp) + .padding(top = 60.dp, bottom = padBottom) + ) { + Text(text = stringResource(id = rememberPrimaryActionResource(backupAlert = backupAlert))) + } + + if (secondaryActionResource > 0) { + TextButton(onClick = onSecondaryActionClick, modifier = Modifier.padding(bottom = 32.dp)) { + Text(text = stringResource(id = secondaryActionResource)) + } + } + } +} + +@Composable +private fun GenericBody() { + Text(text = "TODO") +} + +@Composable +private fun PaymentProcessingBody() { + Text(text = "TODO") +} + +@Composable +private fun MediaBackupsAreOffBody() { + Text(text = "TODO") +} + +@Composable +private fun MediaWillBeDeletedTodayBody() { + Text(text = "TODO") +} + +@Composable +private fun rememberBackupsIconColors(backupAlert: BackupAlert): BackupsIconColors { + return remember(backupAlert) { + when (backupAlert) { + BackupAlert.GENERIC, BackupAlert.PAYMENT_PROCESSING -> BackupsIconColors.Warning + BackupAlert.MEDIA_BACKUPS_ARE_OFF, BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> BackupsIconColors.Error + } + } +} + +@Composable +@StringRes +private fun rememberTitleResource(backupAlert: BackupAlert): Int { + return remember(backupAlert) { + when (backupAlert) { + BackupAlert.GENERIC -> R.string.default_error_msg // TODO [message-backups] -- Finalized copy + BackupAlert.PAYMENT_PROCESSING -> R.string.default_error_msg // TODO [message-backups] -- Finalized copy + BackupAlert.MEDIA_BACKUPS_ARE_OFF -> R.string.default_error_msg // TODO [message-backups] -- Finalized copy + BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> R.string.default_error_msg // TODO [message-backups] -- Finalized copy + } + } +} + +@Composable +private fun rememberPrimaryActionResource(backupAlert: BackupAlert): Int { + return remember(backupAlert) { + when (backupAlert) { + BackupAlert.GENERIC -> android.R.string.ok // TODO [message-backups] -- Finalized copy + BackupAlert.PAYMENT_PROCESSING -> android.R.string.ok // TODO [message-backups] -- Finalized copy + BackupAlert.MEDIA_BACKUPS_ARE_OFF -> android.R.string.ok // TODO [message-backups] -- Finalized copy + BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> android.R.string.ok // TODO [message-backups] -- Finalized copy + } + } +} + +@Composable +private fun rememberSecondaryActionResource(backupAlert: BackupAlert): Int { + return remember(backupAlert) { + when (backupAlert) { + BackupAlert.GENERIC -> android.R.string.cancel // TODO [message-backups] -- Finalized copy + BackupAlert.PAYMENT_PROCESSING -> -1 + BackupAlert.MEDIA_BACKUPS_ARE_OFF -> android.R.string.cancel // TODO [message-backups] -- Finalized copy + BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> android.R.string.cancel // TODO [message-backups] -- Finalized copy + } + } +} + +@SignalPreview +@Composable +private fun BackupAlertSheetContentPreviewGeneric() { + Previews.BottomSheetPreview { + BackupAlertSheetContent( + backupAlert = BackupAlert.GENERIC, + onPrimaryActionClick = {}, + onSecondaryActionClick = {} + ) + } +} + +@SignalPreview +@Composable +private fun BackupAlertSheetContentPreviewPayment() { + Previews.BottomSheetPreview { + BackupAlertSheetContent( + backupAlert = BackupAlert.PAYMENT_PROCESSING, + onPrimaryActionClick = {}, + onSecondaryActionClick = {} + ) + } +} + +@SignalPreview +@Composable +private fun BackupAlertSheetContentPreviewMedia() { + Previews.BottomSheetPreview { + BackupAlertSheetContent( + backupAlert = BackupAlert.MEDIA_BACKUPS_ARE_OFF, + onPrimaryActionClick = {}, + onSecondaryActionClick = {} + ) + } +} + +@SignalPreview +@Composable +private fun BackupAlertSheetContentPreviewDelete() { + Previews.BottomSheetPreview { + BackupAlertSheetContent( + backupAlert = BackupAlert.MEDIA_WILL_BE_DELETED_TODAY, + onPrimaryActionClick = {}, + onSecondaryActionClick = {} + ) + } +} + +@Parcelize +enum class BackupAlert : Parcelable { + GENERIC, + PAYMENT_PROCESSING, + MEDIA_BACKUPS_ARE_OFF, + MEDIA_WILL_BE_DELETED_TODAY +} 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 new file mode 100644 index 0000000000..16e8396066 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupsIconColors.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +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 + + @get:Composable + val background: Color + + 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 background: Color @Composable get() = MaterialTheme.colorScheme.primaryContainer + } + + object Warning : BackupsIconColors { + override val foreground: Brush @Composable get() = SolidColor(Color(0xFFC86600)) + override val background: Color @Composable get() = Color(0xFFF9E4B6) + } + + object Error : BackupsIconColors { + override val foreground: Brush @Composable get() = SolidColor(MaterialTheme.colorScheme.error) + override val background: Color @Composable get() = Color(0xFFFFD9D9) + } +} 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 d71de68a82..023b295a46 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 @@ -17,30 +17,23 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.CompositingStrategy -import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview 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.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.v2.ui.BackupsIconColors import kotlin.math.max import kotlin.math.min @@ -63,19 +56,13 @@ fun BackupStatus( .padding(14.dp) ) { val foreground: Brush = data.iconColors.foreground - Icon( + Icons.BrushedForeground( painter = painterResource(id = data.iconRes), contentDescription = null, + foregroundBrush = foreground, modifier = Modifier .background(color = data.iconColors.background, shape = CircleShape) .padding(8.dp) - .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } - .drawWithCache { - onDrawWithContent { - drawContent() - drawRect(foreground, blendMode = BlendMode.SrcAtop) - } - } ) Column( @@ -92,7 +79,9 @@ fun BackupStatus( LinearProgressIndicator( progress = data.progress, strokeCap = StrokeCap.Round, - modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp) + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) ) } @@ -152,7 +141,7 @@ sealed interface BackupStatusData { @get:StringRes val titleRes: Int - val iconColors: IconColors + val iconColors: BackupsIconColors @get:StringRes val actionRes: Int get() = NONE @@ -168,7 +157,7 @@ sealed interface BackupStatusData { object CouldNotCompleteBackup : BackupStatusData { override val iconRes: Int = R.drawable.symbol_backup_light override val titleRes: Int = R.string.default_error_msg - override val iconColors: IconColors = IconColors.Warning + override val iconColors: BackupsIconColors = BackupsIconColors.Warning } /** @@ -177,7 +166,7 @@ sealed interface BackupStatusData { object NotEnoughFreeSpace : BackupStatusData { override val iconRes: Int = R.drawable.symbol_backup_light override val titleRes: Int = R.string.default_error_msg - override val iconColors: IconColors = IconColors.Warning + override val iconColors: BackupsIconColors = BackupsIconColors.Warning override val actionRes: Int = R.string.registration_activity__skip } @@ -190,7 +179,7 @@ sealed interface BackupStatusData { val status: Status = Status.NONE ) : BackupStatusData { override val iconRes: Int = R.drawable.symbol_backup_light - override val iconColors: IconColors = IconColors.Normal + override val iconColors: BackupsIconColors = BackupsIconColors.Normal override val titleRes: Int = when (status) { Status.NONE -> R.string.default_error_msg @@ -215,31 +204,6 @@ sealed interface BackupStatusData { } } - sealed interface IconColors { - @get:Composable - val foreground: Brush - - @get:Composable - val background: Color - - object Normal : IconColors { - 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 background: Color @Composable get() = MaterialTheme.colorScheme.primaryContainer - } - - object Warning : IconColors { - override val foreground: Brush @Composable get() = SolidColor(Color(0xFFC86600)) - override val background: Color @Composable get() = Color(0xFFF9E4B6) - } - } - /** * Describes the status of an in-progress media download session. */ diff --git a/core-ui/src/main/java/org/signal/core/ui/Icons.kt b/core-ui/src/main/java/org/signal/core/ui/Icons.kt new file mode 100644 index 0000000000..397871cd41 --- /dev/null +++ b/core-ui/src/main/java/org/signal/core/ui/Icons.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.ui + +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview + +object Icons { + /** + * Icon that takes a Brush instead of a Color for its foreground + */ + @Composable + fun BrushedForeground( + painter: Painter, + contentDescription: String?, + foregroundBrush: Brush, + modifier: Modifier = Modifier + ) { + Icon( + painter = painter, + contentDescription = contentDescription, + modifier = modifier + .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } + .drawWithCache { + onDrawWithContent { + drawContent() + drawRect(foregroundBrush, blendMode = BlendMode.SrcAtop) + } + } + ) + } +} + +@Preview +@Composable +private fun BrushedForegroundPreview() { + Previews.Preview { + Icons.BrushedForeground( + painter = painterResource(id = android.R.drawable.ic_menu_camera), + contentDescription = null, + foregroundBrush = Brush.linearGradient(listOf(Color.Red, Color.Blue)) + ) + } +} From 62cf3feeaa12f07c5eb9ee155ffb55457abab163 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Thu, 18 Apr 2024 14:44:49 -0400 Subject: [PATCH 034/113] Restore a Local Backup v2 --- app/src/main/AndroidManifest.xml | 7 + .../securesms/PassphraseRequiredActivity.java | 15 +- .../securesms/backup/FullBackupImporter.java | 8 + .../securesms/keyvalue/InternalValues.java | 9 + .../securesms/lock/v2/BaseSvrPinFragment.java | 8 +- .../v2/data/RegistrationRepository.kt | 12 +- .../v2/ui/RegistrationV2Activity.kt | 4 +- .../v2/ui/entercode/EnterCodeV2Fragment.kt | 6 +- .../securesms/restore/RestoreActivity.kt | 38 +++ .../securesms/restore/RestoreRepository.kt | 91 +++++++ .../securesms/restore/RestoreState.kt | 15 ++ .../securesms/restore/RestoreViewModel.kt | 60 +++++ .../choosebackup/ChooseBackupV2Fragment.kt | 77 ++++++ .../RestoreLocalBackupFragment.kt | 167 ++++++++++++ .../RestoreLocalBackupState.kt | 26 ++ .../RestoreLocalBackupViewModel.kt | 104 ++++++++ .../TransferOrRestoreV2Fragment.kt | 90 +++++++ app/src/main/res/layout/activity_restore.xml | 25 ++ .../res/layout/fragment_choose_backup_v2.xml | 77 ++++++ .../fragment_restore_local_backup_v2.xml | 107 ++++++++ .../layout/fragment_transfer_restore_v2.xml | 244 ++++++++++++++++++ app/src/main/res/navigation/restore.xml | 41 +++ 22 files changed, 1221 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/RestoreState.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/choosebackup/ChooseBackupV2Fragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/restore/transferorrestore/TransferOrRestoreV2Fragment.kt create mode 100644 app/src/main/res/layout/activity_restore.xml create mode 100644 app/src/main/res/layout/fragment_choose_backup_v2.xml create mode 100644 app/src/main/res/layout/fragment_restore_local_backup_v2.xml create mode 100644 app/src/main/res/layout/fragment_transfer_restore_v2.xml create mode 100644 app/src/main/res/navigation/restore.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 63b922b710..0f55e9c3a6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -844,6 +844,13 @@ android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:exported="false"/> + + T initFragment(@IdRes int target, } private void routeApplicationState(boolean locked) { - Intent intent = getIntentForState(getApplicationState(locked)); + final int applicationState = getApplicationState(locked); + Intent intent = getIntentForState(applicationState); if (intent != null) { + Log.d(TAG, "routeApplicationState(), intent: " + intent.getComponent()); startActivity(intent); finish(); } @@ -148,6 +153,7 @@ private Intent getIntentForState(int state) { case STATE_TRANSFER_ONGOING: return getOldDeviceTransferIntent(); case STATE_TRANSFER_LOCKED: return getOldDeviceTransferLockedIntent(); case STATE_CHANGE_NUMBER_LOCK: return getChangeNumberLockIntent(); + case STATE_RESTORE_BACKUP: return getRestoreIntent(); default: return null; } } @@ -161,6 +167,8 @@ private int getApplicationState(boolean locked) { return STATE_UI_BLOCKING_UPGRADE; } else if (!TextSecurePreferences.hasPromptedPushRegistration(this)) { return STATE_WELCOME_PUSH_SCREEN; + } else if (SignalStore.internalValues().enterRestoreV2Flow()) { + return STATE_RESTORE_BACKUP; } else if (SignalStore.storageService().needsAccountRestore()) { return STATE_ENTER_SIGNAL_PIN; } else if (userHasSkippedOrForgottenPin()) { @@ -233,6 +241,11 @@ private Intent getCreateSignalPinIntent() { return getRoutedIntent(CreateSvrPinActivity.class, intent); } + private Intent getRestoreIntent() { + Intent intent = RestoreActivity.getIntentForRestore(this); + return getRoutedIntent(intent, getIntent()); + } + private Intent getCreateProfileNameIntent() { Intent intent = CreateProfileActivity.getIntentForUserProfile(this); return getRoutedIntent(intent, getIntent()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index 6c7212bf8b..4fd2ef27ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.BackupUtil; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.Util; import java.io.ByteArrayOutputStream; @@ -270,6 +271,13 @@ private static void processKeyValue(KeyValue keyValue) { return; } + if (FeatureFlags.registrationV2()) { + if (SignalStore.account().getKeysToIncludeInBackup().contains(keyValue.key)) { + Log.i(TAG, "[regv2] skipping restore of " + keyValue.key); + return; + } + } + if (keyValue.blobValue != null) { dataSet.putBlob(keyValue.key, keyValue.blobValue.toByteArray()); } else if (keyValue.booleanValue != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java index eff23ac9a3..f0236ed9ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java @@ -31,6 +31,7 @@ public final class InternalValues extends SignalStoreValues { public static final String FORCE_WEBSOCKET_MODE = "internal.force_websocket_mode"; public static final String LAST_SCROLL_POSITION = "internal.last_scroll_position"; public static final String CONVERSATION_ITEM_V2_MEDIA = "internal.conversation_item_v2_media"; + public static final String FORCE_ENTER_RESTORE_V2_FLOW = "internal.force_enter_restore_v2_flow"; InternalValues(KeyValueStore store) { super(store); @@ -210,4 +211,12 @@ public void setUseConversationItemV2Media(boolean useConversationFragmentV2Media public boolean useConversationItemV2Media() { return FeatureFlags.internalUser() && getBoolean(CONVERSATION_ITEM_V2_MEDIA, false); } + + public void setForceEnterRestoreV2Flow(boolean enter) { + putBoolean(FORCE_ENTER_RESTORE_V2_FLOW, enter); + } + + public boolean enterRestoreV2Flow() { + return FeatureFlags.registrationV2() && getBoolean(FORCE_ENTER_RESTORE_V2_FLOW, false); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseSvrPinFragment.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseSvrPinFragment.java index 8069eee080..401d2cccc9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseSvrPinFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseSvrPinFragment.java @@ -23,6 +23,7 @@ import com.google.android.material.button.MaterialButton; import org.thoughtcrime.securesms.LoggingFragment; +import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.pin.PinOptOutDialog; @@ -151,8 +152,11 @@ protected CircularProgressMaterialButton getConfirm() { protected void closeNavGraphBranch() { Intent activityIntent = requireActivity().getIntent(); - if (activityIntent != null && activityIntent.hasExtra("next_intent")) { - startActivity(activityIntent.getParcelableExtra("next_intent")); + if (activityIntent != null && activityIntent.hasExtra(PassphraseRequiredActivity.NEXT_INTENT_EXTRA)) { + final Intent nextIntent = activityIntent.getParcelableExtra(PassphraseRequiredActivity.NEXT_INTENT_EXTRA); + if (nextIntent != null) { + startActivity(nextIntent); + } } requireActivity().finish(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt index a1f583103e..0307823580 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt @@ -32,7 +32,7 @@ import org.thoughtcrime.securesms.jobs.RotateCertificateJob import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.notifications.NotificationIds -import org.thoughtcrime.securesms.pin.SvrRepository.onRegistrationComplete +import org.thoughtcrime.securesms.pin.SvrRepository import org.thoughtcrime.securesms.push.AccountManagerFactory import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId @@ -188,7 +188,7 @@ object RegistrationRepository { TextSecurePreferences.setUnauthorizedReceived(context, false) NotificationManagerCompat.from(context).cancel(NotificationIds.UNREGISTERED_NOTIFICATION_ID) - onRegistrationComplete(response.masterKey, response.pin, hasPin, reglockEnabled) + SvrRepository.onRegistrationComplete(response.masterKey, response.pin, hasPin, reglockEnabled) ApplicationDependencies.closeConnections() ApplicationDependencies.getIncomingMessageObserver() @@ -335,7 +335,11 @@ object RegistrationRepository { val eventBus = EventBus.getDefault() eventBus.register(subscriber) - val sessionCreationResponse = accountManager.createRegistrationSession(fcmToken, mcc, mnc).successOrThrow() // TODO: error handling + val sessionCreationResponse = accountManager.createRegistrationSession(fcmToken, mcc, mnc) + if (sessionCreationResponse !is NetworkResult.Success) { + return@withContext sessionCreationResponse + } + val receivedPush = subscriber.latch.await(PUSH_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS) eventBus.unregister(subscriber) @@ -343,7 +347,7 @@ object RegistrationRepository { val challenge = subscriber.challenge if (challenge != null) { Log.w(TAG, "Push challenge token received.") - return@withContext accountManager.submitPushChallengeToken(sessionCreationResponse.body.id, challenge) + return@withContext accountManager.submitPushChallengeToken(sessionCreationResponse.result.body.id, challenge) } else { Log.w(TAG, "Push received but challenge token was null.") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt index 1271a2133b..8592149509 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt @@ -9,15 +9,15 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.BaseActivity import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel /** * Activity to hold the entire registration process. */ -class RegistrationV2Activity : AppCompatActivity() { +class RegistrationV2Activity : BaseActivity() { private val TAG = Log.tag(RegistrationV2Activity::class.java) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt index 225504ae13..565f0705b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt @@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.MainActivity import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.ViewBinderDelegate import org.thoughtcrime.securesms.databinding.FragmentRegistrationEnterCodeV2Binding +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity import org.thoughtcrime.securesms.profiles.AvatarHelper import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity @@ -91,9 +92,12 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter Log.i(TAG, "Pin restore flow not required. Profile name: $isProfileNameEmpty | Profile avatar: $isAvatarEmpty | Needs PIN: $needsPin") + SignalStore.internalValues().setForceEnterRestoreV2Flow(true) + if (!needsProfile && !needsPin) { sharedViewModel.completeRegistration() } + sharedViewModel.setInProgress(false) val startIntent = MainActivity.clearTop(activity).apply { if (needsPin) { @@ -105,8 +109,8 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter } } + Log.d(TAG, "Launching ${startIntent.component}") activity.startActivity(startIntent) - sharedViewModel.setInProgress(false) activity.finish() ActivityNavigator.applyPopAnimationsToPendingTransition(activity) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt new file mode 100644 index 0000000000..e28896c2df --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import org.signal.core.util.getParcelableExtraCompat +import org.thoughtcrime.securesms.BaseActivity +import org.thoughtcrime.securesms.PassphraseRequiredActivity +import org.thoughtcrime.securesms.R + +/** + * Activity to hold the restore from backup flow. + */ +class RestoreActivity : BaseActivity() { + + private val sharedViewModel: RestoreViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_restore) + intent.getParcelableExtraCompat(PassphraseRequiredActivity.NEXT_INTENT_EXTRA, Intent::class.java)?.let { + sharedViewModel.setNextIntent(it) + } + } + + companion object { + @JvmStatic + fun getIntentForRestore(context: Context): Intent { + return Intent(context, RestoreActivity::class.java) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt new file mode 100644 index 0000000000..a919d0a279 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore + +import android.content.Context +import android.net.Uri +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.AppInitialization +import org.thoughtcrime.securesms.backup.BackupPassphrase +import org.thoughtcrime.securesms.backup.FullBackupImporter +import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.service.LocalBackupListener +import org.thoughtcrime.securesms.util.BackupUtil +import java.io.IOException + +/** + * Repository to handle restoring a backup of a user's message history. + */ +object RestoreRepository { + private val TAG = Log.tag(RestoreRepository.javaClass) + + suspend fun getLocalBackupFromUri(context: Context, uri: Uri): BackupUtil.BackupInfo? = withContext(Dispatchers.IO) { + BackupUtil.getBackupInfoFromSingleUri(context, uri) + } + + suspend fun restoreBackupAsynchronously(context: Context, backupFileUri: Uri, passphrase: String): BackupImportResult = withContext(Dispatchers.IO) { + // TODO [regv2]: migrate this to a service + try { + Log.i(TAG, "Starting backup restore.") + DataRestoreConstraint.isRestoringData = true + + val database = SignalDatabase.backupDatabase + + BackupPassphrase.set(context, passphrase) + + if (!FullBackupImporter.validatePassphrase(context, backupFileUri, passphrase)) { + // TODO [regv2]: implement a specific, user-visible error for wrong passphrase. + return@withContext BackupImportResult.FAILURE_UNKNOWN + } + + FullBackupImporter.importFile( + context, + AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + database, + backupFileUri, + passphrase + ) + + SignalDatabase.runPostBackupRestoreTasks(database) + NotificationChannels.getInstance().restoreContactNotificationChannels() + + if (BackupUtil.canUserAccessBackupDirectory(context)) { + LocalBackupListener.setNextBackupTimeToIntervalFromNow(context) + SignalStore.settings().isBackupEnabled = true + LocalBackupListener.schedule(context) + } + + AppInitialization.onPostBackupRestore(context) + + Log.i(TAG, "Backup restore complete.") + return@withContext BackupImportResult.SUCCESS + } catch (e: FullBackupImporter.DatabaseDowngradeException) { + Log.w(TAG, "Failed due to the backup being from a newer version of Signal.", e) + return@withContext BackupImportResult.FAILURE_VERSION_DOWNGRADE + } catch (e: FullBackupImporter.ForeignKeyViolationException) { + Log.w(TAG, "Failed due to foreign key constraint violations.", e) + return@withContext BackupImportResult.FAILURE_FOREIGN_KEY + } catch (e: IOException) { + Log.w(TAG, e) + return@withContext BackupImportResult.FAILURE_UNKNOWN + } finally { + DataRestoreConstraint.isRestoringData = false + } + } + + enum class BackupImportResult { + SUCCESS, + FAILURE_VERSION_DOWNGRADE, + FAILURE_FOREIGN_KEY, + FAILURE_UNKNOWN + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreState.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreState.kt new file mode 100644 index 0000000000..e2df0cf003 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreState.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore + +import android.content.Intent +import android.net.Uri +import org.thoughtcrime.securesms.devicetransfer.newdevice.BackupRestorationType + +/** + * Shared state holder for the restore flow. + */ +data class RestoreState(val restorationType: BackupRestorationType = BackupRestorationType.LOCAL_BACKUP, val backupFile: Uri? = null, val nextIntent: Intent? = null) diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt new file mode 100644 index 0000000000..c75adbd296 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore + +import android.content.Intent +import android.net.Uri +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import org.thoughtcrime.securesms.devicetransfer.newdevice.BackupRestorationType + +/** + * Shared view model for the restore flow. + */ +class RestoreViewModel : ViewModel() { + private val store = MutableStateFlow(RestoreState()) + val uiState = store.asLiveData() + + fun setNextIntent(nextIntent: Intent) { + store.update { + it.copy(nextIntent = nextIntent) + } + } + + fun onTransferFromAndroidDeviceSelected() { + store.update { + it.copy(restorationType = BackupRestorationType.DEVICE_TRANSFER) + } + } + + fun onRestoreFromLocalBackupSelected() { + store.update { + it.copy(restorationType = BackupRestorationType.LOCAL_BACKUP) + } + } + + fun onRestoreFromRemoteBackupSelected() { + store.update { + it.copy(restorationType = BackupRestorationType.REMOTE_BACKUP) + } + } + + fun getBackupRestorationType(): BackupRestorationType { + return store.value.restorationType + } + + fun setBackupFileUri(backupFileUri: Uri) { + store.update { + it.copy(backupFile = backupFileUri) + } + } + + fun getBackupFileUri(): Uri? = store.value.backupFile + + fun getNextIntent(): Intent? = store.value.nextIntent +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/choosebackup/ChooseBackupV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/choosebackup/ChooseBackupV2Fragment.kt new file mode 100644 index 0000000000..51a0caa594 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/choosebackup/ChooseBackupV2Fragment.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore.choosebackup + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.DocumentsContract +import android.text.method.LinkMovementMethod +import android.view.View +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.text.HtmlCompat +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.NavHostFragment +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.LoggingFragment +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.databinding.FragmentChooseBackupV2Binding +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate +import org.thoughtcrime.securesms.restore.RestoreViewModel +import org.thoughtcrime.securesms.util.navigation.safeNavigate + +/** + * This fragment presents a button to the user to browse their local file system for a legacy backup file. + */ +class ChooseBackupV2Fragment : LoggingFragment(R.layout.fragment_choose_backup_v2) { + private val sharedViewModel by activityViewModels() + private val binding: FragmentChooseBackupV2Binding by ViewBinderDelegate(FragmentChooseBackupV2Binding::bind) + + private val pickMedia = registerForActivityResult(BackupFileContract()) { + if (it != null) { + onUserChoseBackupFile(it) + } else { + Log.i(TAG, "Null URI returned for backup file selection.") + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + RegistrationViewDelegate.setDebugLogSubmitMultiTapView(binding.chooseBackupFragmentTitle) + binding.chooseBackupFragmentButton.setOnClickListener { onChooseBackupSelected() } + + binding.chooseBackupFragmentLearnMore.text = HtmlCompat.fromHtml(String.format("%s", getString(R.string.backup_support_url), getString(R.string.ChooseBackupFragment__learn_more)), 0) + binding.chooseBackupFragmentLearnMore.movementMethod = LinkMovementMethod.getInstance() + } + + private fun onChooseBackupSelected() { + pickMedia.launch("application/octet-stream") + } + + private fun onUserChoseBackupFile(backupFileUri: Uri) { + sharedViewModel.setBackupFileUri(backupFileUri) + NavHostFragment.findNavController(this).safeNavigate(ChooseBackupV2FragmentDirections.actionChooseLocalBackupFragmentToRestoreLocalBackupFragment()) + } + + private class BackupFileContract : ActivityResultContracts.GetContent() { + override fun createIntent(context: Context, input: String): Intent { + return super.createIntent(context, input).apply { + putExtra(Intent.EXTRA_LOCAL_ONLY, true) + if (Build.VERSION.SDK_INT >= 26) { + putExtra(DocumentsContract.EXTRA_INITIAL_URI, SignalStore.settings().latestSignalBackupDirectory) + } + } + } + } + + companion object { + private val TAG = Log.tag(ChooseBackupV2Fragment::class.java) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt new file mode 100644 index 0000000000..576ff86fcb --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore.restorelocalbackup + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.widget.EditText +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.LoggingFragment +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.BackupEvent +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.databinding.FragmentRestoreLocalBackupV2Binding +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView +import org.thoughtcrime.securesms.registration.fragments.RestoreBackupFragment.PassphraseAsYouTypeFormatter +import org.thoughtcrime.securesms.restore.RestoreRepository +import org.thoughtcrime.securesms.restore.RestoreViewModel +import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.Util +import org.thoughtcrime.securesms.util.ViewModelFactory +import org.thoughtcrime.securesms.util.ViewUtil +import org.thoughtcrime.securesms.util.visible +import java.util.Locale + +/** + * This fragment is used to monitor and manage an in-progress backup restore. + */ +class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_local_backup_v2) { + private val navigationViewModel: RestoreViewModel by activityViewModels() + private val restoreLocalBackupViewModel: RestoreLocalBackupViewModel by viewModels( + factoryProducer = ViewModelFactory.factoryProducer { + val fileBackupUri = navigationViewModel.getBackupFileUri()!! + RestoreLocalBackupViewModel(fileBackupUri) + } + ) + private val binding: FragmentRestoreLocalBackupV2Binding by ViewBinderDelegate(FragmentRestoreLocalBackupV2Binding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setDebugLogSubmitMultiTapView(binding.verifyHeader) + Log.i(TAG, "Backup restore.") + + restoreLocalBackupViewModel.uiState.observe(viewLifecycleOwner) { fragmentState -> + fragmentState.backupInfo?.let { + presentBackupFileInfo(backupSize = it.size, backupTimestamp = it.timestamp) + if (fragmentState.backupPassphrase.isEmpty()) { + presentBackupPassPhrasePromptDialog() + } + } + + if (fragmentState.restoreInProgress) { + presentRestoreProgress(fragmentState.backupProgressCount) + } else { + presentProgressEnded() + } + + if (fragmentState.backupRestoreComplete) { + val importResult = fragmentState.backupImportResult + if (importResult == null) { + onBackupCompletedSuccessfully() + } else { + handleBackupImportResult(importResult) + } + } + } + + restoreLocalBackupViewModel.startRestore(requireContext()) + } + + private fun onBackupCompletedSuccessfully() { + Log.d(TAG, "onBackupCompletedSuccessfully()") + SignalStore.internalValues().setForceEnterRestoreV2Flow(false) + val activity = requireActivity() + navigationViewModel.getNextIntent()?.let { + Log.d(TAG, "Launching ${it.component}") + activity.startActivity(it) + } + activity.finish() + } + + override fun onStart() { + super.onStart() + EventBus.getDefault().register(this) + } + + override fun onStop() { + super.onStop() + EventBus.getDefault().unregister(this) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEvent(event: BackupEvent) { + restoreLocalBackupViewModel.onBackupProgressUpdate(event) + } + + private fun handleBackupImportResult(importResult: RestoreRepository.BackupImportResult) { + when (importResult) { + RestoreRepository.BackupImportResult.FAILURE_VERSION_DOWNGRADE -> Toast.makeText(requireContext(), R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show() + RestoreRepository.BackupImportResult.FAILURE_FOREIGN_KEY -> Toast.makeText(requireContext(), R.string.RegistrationActivity_backup_failure_foreign_key, Toast.LENGTH_LONG).show() + RestoreRepository.BackupImportResult.FAILURE_UNKNOWN -> Toast.makeText(requireContext(), R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show() + RestoreRepository.BackupImportResult.SUCCESS -> Log.w(TAG, "Successful backup import should not be handled here.", IllegalStateException()) + } + } + + private fun presentProgressEnded() { + binding.restoreButton.cancelSpinning() + binding.cancelLocalRestoreButton.visible = true + binding.backupProgressText.text = "" + } + + private fun presentRestoreProgress(backupProgressCount: Long) { + binding.restoreButton.setSpinning() + binding.cancelLocalRestoreButton.visibility = View.INVISIBLE + if (backupProgressCount > 0L) { + binding.backupProgressText.text = getString(R.string.RegistrationActivity_d_messages_so_far, backupProgressCount) + } else { + binding.backupProgressText.setText(R.string.RegistrationActivity_checking) + } + } + + private fun presentBackupPassPhrasePromptDialog() { + val view = LayoutInflater.from(requireContext()).inflate(R.layout.enter_backup_passphrase_dialog, null) + val prompt = view.findViewById(R.id.restore_passphrase_input) + + prompt.addTextChangedListener(PassphraseAsYouTypeFormatter()) + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.RegistrationActivity_enter_backup_passphrase) + .setView(view) + .setPositiveButton(R.string.RegistrationActivity_restore) { _, _ -> + ViewUtil.hideKeyboard(requireContext(), prompt) + + val passphrase = prompt.getText().toString() + restoreLocalBackupViewModel.confirmPassphrase(requireContext(), passphrase) + } + .setNegativeButton(android.R.string.cancel, null) + .show() + + Log.i(TAG, "Prompt for backup passphrase shown to user.") + } + + private fun presentBackupFileInfo(backupSize: Long, backupTimestamp: Long) { + if (backupSize > 0) { + binding.backupSizeText.text = getString(R.string.RegistrationActivity_backup_size_s, Util.getPrettyFileSize(backupSize)) + } + + if (backupTimestamp > 0) { + binding.backupCreatedText.text = getString(R.string.RegistrationActivity_backup_timestamp_s, DateUtils.getExtendedRelativeTimeSpanString(requireContext(), Locale.getDefault(), backupTimestamp)) + } + } + + companion object { + private val TAG = Log.tag(RestoreLocalBackupFragment::class.java) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt new file mode 100644 index 0000000000..fbf2305fe0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore.restorelocalbackup + +import android.net.Uri +import org.thoughtcrime.securesms.restore.RestoreRepository +import org.thoughtcrime.securesms.util.BackupUtil.BackupInfo + +/** + * State holder for a backup restore. + */ +data class RestoreLocalBackupState( + val uri: Uri, + val backupInfo: BackupInfo? = null, + val backupPassphrase: String = "", + val restoreInProgress: Boolean = false, + val backupVerifyingInProgress: Boolean = false, + val backupProgressCount: Long = -1, + val backupEstimatedTotalCount: Long = -1, + val backupRestoreComplete: Boolean = false, + val backupImportResult: RestoreRepository.BackupImportResult? = null, + val abort: Boolean = false +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt new file mode 100644 index 0000000000..f8c2fee5d3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore.restorelocalbackup + +import android.content.Context +import android.net.Uri +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.BackupEvent +import org.thoughtcrime.securesms.restore.RestoreRepository + +/** + * ViewModel for [RestoreLocalBackupFragment] + */ +class RestoreLocalBackupViewModel(fileBackupUri: Uri) : ViewModel() { + private val store = MutableStateFlow(RestoreLocalBackupState(fileBackupUri)) + val uiState = store.asLiveData() + + fun startRestore(context: Context) { + val backupFileUri = store.value.uri + viewModelScope.launch { + val backupInfo = RestoreRepository.getLocalBackupFromUri(context, backupFileUri) + + if (backupInfo == null) { + abort() + return@launch + } + + store.update { + it.copy( + backupInfo = backupInfo + ) + } + } + } + + private fun abort() { + store.update { + it.copy(abort = true) + } + } + + fun confirmPassphrase(context: Context, passphrase: String) { + store.update { + it.copy( + backupPassphrase = passphrase, + restoreInProgress = true + ) + } + + val backupFileUri = store.value.backupInfo?.uri + val backupPassphrase = store.value.backupPassphrase + if (backupFileUri == null) { + Log.w(TAG, "Could not begin backup import because backup file URI was null!") + abort() + return + } + + if (backupPassphrase.isEmpty()) { + Log.w(TAG, "Could not begin backup import because backup passphrase was empty!") + abort() + return + } + + viewModelScope.launch { + val importResult = RestoreRepository.restoreBackupAsynchronously(context, backupFileUri, backupPassphrase) + + store.update { + it.copy( + backupImportResult = if (importResult == RestoreRepository.BackupImportResult.SUCCESS) null else importResult, + restoreInProgress = false, + backupRestoreComplete = true, + backupEstimatedTotalCount = -1L, + backupProgressCount = -1L, + backupVerifyingInProgress = false + ) + } + } + } + + fun onBackupProgressUpdate(event: BackupEvent) { + store.update { + it.copy( + backupProgressCount = event.count, + backupEstimatedTotalCount = event.estimatedTotalCount, + backupVerifyingInProgress = event.type == BackupEvent.Type.PROGRESS_VERIFYING, + backupRestoreComplete = event.type == BackupEvent.Type.FINISHED, + restoreInProgress = event.type != BackupEvent.Type.FINISHED + ) + } + } + + companion object { + private val TAG = Log.tag(RestoreLocalBackupViewModel::class.java) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/transferorrestore/TransferOrRestoreV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/transferorrestore/TransferOrRestoreV2Fragment.kt new file mode 100644 index 0000000000..b96b888302 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/transferorrestore/TransferOrRestoreV2Fragment.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.restore.transferorrestore + +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.NavHostFragment +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.LoggingFragment +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.databinding.FragmentTransferRestoreV2Binding +import org.thoughtcrime.securesms.devicetransfer.newdevice.BackupRestorationType +import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate +import org.thoughtcrime.securesms.restore.RestoreViewModel +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.SpanUtil +import org.thoughtcrime.securesms.util.navigation.safeNavigate +import org.thoughtcrime.securesms.util.visible + +/** + * This presents a list of options for the user to restore (or skip) a backup. + */ +class TransferOrRestoreV2Fragment : LoggingFragment(R.layout.fragment_transfer_restore_v2) { + private val sharedViewModel by activityViewModels() + private val binding: FragmentTransferRestoreV2Binding by ViewBinderDelegate(FragmentTransferRestoreV2Binding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + RegistrationViewDelegate.setDebugLogSubmitMultiTapView(binding.transferOrRestoreTitle) + binding.transferOrRestoreFragmentTransfer.setOnClickListener { sharedViewModel.onTransferFromAndroidDeviceSelected() } + binding.transferOrRestoreFragmentRestore.setOnClickListener { sharedViewModel.onRestoreFromLocalBackupSelected() } + binding.transferOrRestoreFragmentRestoreRemote.setOnClickListener { sharedViewModel.onRestoreFromRemoteBackupSelected() } + binding.transferOrRestoreFragmentNext.setOnClickListener { launchSelection(sharedViewModel.getBackupRestorationType()) } + binding.transferOrRestoreFragmentMoreOptions.setOnClickListener { + Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + } + + binding.transferOrRestoreFragmentRestoreRemoteCard.visible = FeatureFlags.messageBackups() + binding.transferOrRestoreFragmentMoreOptions.visible = FeatureFlags.messageBackups() + + val description = getString(R.string.TransferOrRestoreFragment__transfer_your_account_and_messages_from_your_old_android_device) + val toBold = getString(R.string.TransferOrRestoreFragment__you_need_access_to_your_old_device) + + binding.transferOrRestoreFragmentTransferDescription.text = SpanUtil.boldSubstring(description, toBold) + + sharedViewModel.uiState.observe(viewLifecycleOwner) { state -> + updateSelection(state.restorationType) + } + + // TODO [regv2]: port backup file detection to here + } + + private fun updateSelection(restorationType: BackupRestorationType) { + binding.transferOrRestoreFragmentTransferCard.isSelected = restorationType == BackupRestorationType.DEVICE_TRANSFER + binding.transferOrRestoreFragmentRestoreCard.isSelected = restorationType == BackupRestorationType.LOCAL_BACKUP + binding.transferOrRestoreFragmentRestoreRemoteCard.isSelected = restorationType == BackupRestorationType.REMOTE_BACKUP + } + + private fun launchSelection(restorationType: BackupRestorationType) { + when (restorationType) { + BackupRestorationType.DEVICE_TRANSFER -> { + // TODO [regv2] + Log.w(TAG, "Not yet implemented!", NotImplementedError()) + Toast.makeText(requireContext(), "Not yet implemented!", Toast.LENGTH_LONG).show() + } + BackupRestorationType.LOCAL_BACKUP -> { + NavHostFragment.findNavController(this).safeNavigate(TransferOrRestoreV2FragmentDirections.actionTransferOrRestoreToRestore()) + } + BackupRestorationType.REMOTE_BACKUP -> { + // TODO [regv2] + Log.w(TAG, "Not yet implemented!", NotImplementedError()) + Toast.makeText(requireContext(), "Not yet implemented!", Toast.LENGTH_LONG).show() + } + else -> { + throw IllegalArgumentException() + } + } + } + + companion object { + private val TAG = Log.tag(TransferOrRestoreV2Fragment::class.java) + } +} diff --git a/app/src/main/res/layout/activity_restore.xml b/app/src/main/res/layout/activity_restore.xml new file mode 100644 index 0000000000..09b7fa8162 --- /dev/null +++ b/app/src/main/res/layout/activity_restore.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_choose_backup_v2.xml b/app/src/main/res/layout/fragment_choose_backup_v2.xml new file mode 100644 index 0000000000..123d54be0d --- /dev/null +++ b/app/src/main/res/layout/fragment_choose_backup_v2.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_restore_local_backup_v2.xml b/app/src/main/res/layout/fragment_restore_local_backup_v2.xml new file mode 100644 index 0000000000..e0f7121202 --- /dev/null +++ b/app/src/main/res/layout/fragment_restore_local_backup_v2.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_transfer_restore_v2.xml b/app/src/main/res/layout/fragment_transfer_restore_v2.xml new file mode 100644 index 0000000000..883b564737 --- /dev/null +++ b/app/src/main/res/layout/fragment_transfer_restore_v2.xml @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/restore.xml b/app/src/main/res/navigation/restore.xml new file mode 100644 index 0000000000..1446b50e78 --- /dev/null +++ b/app/src/main/res/navigation/restore.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file From 15d8a698c5d46049c500015428c5b195d50d7de3 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 18 Apr 2024 15:53:39 -0300 Subject: [PATCH 035/113] Add new name collision state management. --- .../database/NameCollisionTablesTest.kt | 239 +++++++++ .../conversation/v2/ConversationBannerView.kt | 2 + .../conversation/v2/ConversationFragment.kt | 6 +- .../conversation/v2/ConversationRepository.kt | 20 +- .../conversation/v2/ConversationViewModel.kt | 5 + .../v2/groups/ConversationGroupViewModel.kt | 37 +- .../securesms/database/GroupTable.kt | 14 +- .../securesms/database/MessageTable.kt | 4 + .../securesms/database/NameCollisionTables.kt | 475 ++++++++++++++++++ .../securesms/database/RecipientTable.kt | 18 +- .../securesms/database/SignalDatabase.kt | 9 + .../helpers/SignalDatabaseMigrations.kt | 6 +- .../migration/V228_AddNameCollisionTables.kt | 42 ++ .../securesms/jobs/RetrieveProfileJob.kt | 14 + .../spoofing/ReviewCardRepository.java | 35 +- .../profiles/spoofing/ReviewRecipient.java | 4 +- .../profiles/spoofing/ReviewUtil.java | 95 ---- 17 files changed, 861 insertions(+), 164 deletions(-) create mode 100644 app/src/androidTest/java/org/thoughtcrime/securesms/database/NameCollisionTablesTest.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/NameCollisionTables.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V228_AddNameCollisionTables.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/NameCollisionTablesTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/NameCollisionTablesTest.kt new file mode 100644 index 0000000000..ddb83494c5 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/NameCollisionTablesTest.kt @@ -0,0 +1,239 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.signal.storageservice.protos.groups.Member +import org.signal.storageservice.protos.groups.local.DecryptedMember +import org.thoughtcrime.securesms.mms.IncomingMessage +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.testing.GroupTestingUtils +import org.thoughtcrime.securesms.testing.SignalActivityRule +import org.thoughtcrime.securesms.testing.assertIsSize + +@RunWith(AndroidJUnit4::class) +class NameCollisionTablesTest { + + @get:Rule + val harness = SignalActivityRule(createGroup = true) + + private lateinit var alice: RecipientId + private lateinit var bob: RecipientId + private lateinit var charlie: RecipientId + + @Before + fun setUp() { + alice = setUpRecipient(harness.others[0]) + bob = setUpRecipient(harness.others[1]) + charlie = setUpRecipient(harness.others[2]) + } + + @Test + fun givenAUserWithAThreadIdButNoConflicts_whenIGetCollisionsForThreadRecipient_thenIExpectNoCollisions() { + val threadRecipientId = alice + SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(threadRecipientId)) + val actual = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(threadRecipientId) + + actual assertIsSize 0 + } + + @Test + fun givenTwoUsers_whenOneChangesTheirProfileNameToMatchTheOther_thenIExpectANameCollision() { + setProfileName(alice, ProfileName.fromParts("Alice", "Android")) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + + val actualAlice = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(alice) + val actualBob = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(bob) + + actualAlice assertIsSize 2 + actualBob assertIsSize 2 + } + + @Test + fun givenTwoUsersWithANameCollisions_whenOneChangesToADifferentName_thenIExpectNoNameCollisions() { + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + setProfileName(alice, ProfileName.fromParts("Alice", "Android")) + + val actualAlice = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(alice) + val actualBob = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(bob) + + actualAlice assertIsSize 0 + actualBob assertIsSize 0 + } + + @Test + fun givenThreeUsersWithANameCollisions_whenOneChangesToADifferentName_thenIExpectTwoNameCollisions() { + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + setProfileName(charlie, ProfileName.fromParts("Bob", "Android")) + setProfileName(alice, ProfileName.fromParts("Alice", "Android")) + + val actualAlice = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(alice) + val actualBob = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(bob) + val actualCharlie = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(charlie) + + actualAlice assertIsSize 0 + actualBob assertIsSize 2 + actualCharlie assertIsSize 2 + } + + @Test + fun givenTwoUsersWithADismissedNameCollision_whenOneChangesToADifferentNameAndBack_thenIExpectANameCollision() { + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + SignalDatabase.nameCollisions.markCollisionsForThreadRecipientDismissed(alice) + + setProfileName(alice, ProfileName.fromParts("Alice", "Android")) + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + + val actualAlice = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(alice) + + actualAlice assertIsSize 2 + } + + @Test + fun givenADismissedNameCollisionForAlice_whenIGetNameCollisionsForAlice_thenIExpectNoNameCollisions() { + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + SignalDatabase.nameCollisions.markCollisionsForThreadRecipientDismissed(alice) + + val actualCollisions = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(alice) + + actualCollisions assertIsSize 0 + } + + @Test + fun givenADismissedNameCollisionForAliceThatIUpdate_whenIGetNameCollisionsForAlice_thenIExpectNoNameCollisions() { + SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(alice)) + + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + SignalDatabase.nameCollisions.markCollisionsForThreadRecipientDismissed(alice) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + + val actualCollisions = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(alice) + + actualCollisions assertIsSize 0 + } + + @Test + fun givenADismissedNameCollisionForAlice_whenIGetNameCollisionsForBob_thenIExpectANameCollisionWithTwoEntries() { + SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(alice)) + + setProfileName(alice, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob, ProfileName.fromParts("Bob", "Android")) + SignalDatabase.nameCollisions.markCollisionsForThreadRecipientDismissed(alice) + + val actualCollisions = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(bob) + + actualCollisions assertIsSize 2 + } + + @Test + fun givenAGroupWithAliceAndBob_whenIInsertNameChangeMessageForAlice_thenIExpectAGroupNameCollision() { + val alice = Recipient.resolved(alice) + val bob = Recipient.resolved(bob) + val info = createGroup() + + setProfileName(alice.id, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob.id, ProfileName.fromParts("Bob", "Android")) + + SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(info.recipientId)) + SignalDatabase.messages.insertProfileNameChangeMessages(alice, "Bob Android", "Alice Android") + + val collisions = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(info.recipientId) + + collisions assertIsSize 2 + } + + @Test + fun givenAGroupWithAliceAndBobWithDismissedCollision_whenIInsertNameChangeMessageForAlice_thenIExpectAGroupNameCollision() { + val alice = Recipient.resolved(alice) + val bob = Recipient.resolved(bob) + val info = createGroup() + + setProfileName(alice.id, ProfileName.fromParts("Bob", "Android")) + setProfileName(bob.id, ProfileName.fromParts("Bob", "Android")) + + SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(info.recipientId)) + SignalDatabase.messages.insertProfileNameChangeMessages(alice, "Bob Android", "Alice Android") + SignalDatabase.nameCollisions.markCollisionsForThreadRecipientDismissed(info.recipientId) + SignalDatabase.messages.insertProfileNameChangeMessages(alice, "Bob Android", "Alice Android") + + val collisions = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(info.recipientId) + + collisions assertIsSize 0 + } + + @Test + fun givenAGroupWithAliceAndBob_whenIInsertNameChangeMessageForAliceWithMismatch_thenIExpectNoGroupNameCollision() { + val alice = Recipient.resolved(alice) + val bob = Recipient.resolved(bob) + val info = createGroup() + + setProfileName(alice.id, ProfileName.fromParts("Alice", "Android")) + setProfileName(bob.id, ProfileName.fromParts("Bob", "Android")) + + SignalDatabase.threads.getOrCreateThreadIdFor(Recipient.resolved(info.recipientId)) + SignalDatabase.messages.insertProfileNameChangeMessages(alice, "Alice Android", "Bob Android") + + val collisions = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(info.recipientId) + + collisions assertIsSize 0 + } + + private fun setUpRecipient(recipientId: RecipientId): RecipientId { + SignalDatabase.recipients.setProfileSharing(recipientId, false) + val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipientId, false) + + MmsHelper.insert( + threadId = threadId, + message = IncomingMessage( + type = MessageType.NORMAL, + from = recipientId, + groupId = null, + body = "hi", + sentTimeMillis = 100L, + receivedTimeMillis = 200L, + serverTimeMillis = 100L, + isUnidentified = true + ) + ) + + return recipientId + } + + private fun setProfileName(recipientId: RecipientId, name: ProfileName) { + SignalDatabase.recipients.setProfileName(recipientId, name) + SignalDatabase.nameCollisions.handleIndividualNameCollision(recipientId) + } + + private fun createGroup(): GroupTestingUtils.TestGroupInfo { + return GroupTestingUtils.insertGroup( + revision = 0, + DecryptedMember( + aciBytes = harness.self.requireAci().toByteString(), + role = Member.Role.ADMINISTRATOR + ), + DecryptedMember( + aciBytes = Recipient.resolved(alice).requireAci().toByteString(), + role = Member.Role.ADMINISTRATOR + ), + DecryptedMember( + aciBytes = Recipient.resolved(bob).requireAci().toByteString(), + role = Member.Role.ADMINISTRATOR + ) + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationBannerView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationBannerView.kt index 258783f16e..738e0d7a44 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationBannerView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationBannerView.kt @@ -120,6 +120,7 @@ class ConversationBannerView @JvmOverloads constructor( setOnHideListener { clearRequestReview() + listener?.onDismissReview() true } } @@ -194,5 +195,6 @@ class ConversationBannerView @JvmOverloads constructor( fun onUnverifiedBannerDismissed(unverifiedIdentities: List) fun onRequestReviewIndividual(recipientId: RecipientId) fun onReviewGroupMembers(groupId: GroupId.V2) + fun onDismissReview() } } 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 2fd6cd0ded..b7f55bcc5d 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 @@ -423,7 +423,7 @@ class ConversationFragment : private val conversationGroupViewModel: ConversationGroupViewModel by viewModels( factoryProducer = { - ConversationGroupViewModel.Factory(args.threadId, conversationRecipientRepository) + ConversationGroupViewModel.Factory(conversationRecipientRepository) } ) @@ -3704,6 +3704,10 @@ class ConversationFragment : override fun onReviewGroupMembers(groupId: GroupId.V2) { ReviewCardDialogFragment.createForReviewMembers(groupId).show(childFragmentManager, null) } + + override fun onDismissReview() { + viewModel.onDismissReview() + } } //endregion diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt index b0ee8e6ec1..a403faa587 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt @@ -82,7 +82,7 @@ import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.mms.QuoteModel import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.SlideDeck -import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil +import org.thoughtcrime.securesms.profiles.spoofing.ReviewRecipient import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId @@ -363,6 +363,12 @@ class ConversationRepository( }.subscribeOn(Schedulers.io()) } + fun dismissRequestReviewState(threadRecipientId: RecipientId) { + SignalExecutors.BOUNDED_IO.execute { + SignalDatabase.nameCollisions.markCollisionsForThreadRecipientDismissed(threadRecipientId) + } + } + fun getRequestReviewState(recipient: Recipient, group: GroupRecord?, messageRequest: MessageRequestState): Single { return Single.fromCallable { if (group == null && messageRequest.state != MessageRequestState.State.INDIVIDUAL) { @@ -370,12 +376,12 @@ class ConversationRepository( } if (group == null) { - val recipientsToReview = ReviewUtil.getRecipientsToPromptForReview(recipient.id) - if (recipientsToReview.size > 0) { + val recipientsToReview = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(recipient.id) + if (recipientsToReview.isNotEmpty()) { return@fromCallable RequestReviewState( individualReviewState = IndividualReviewState( target = recipient, - firstDuplicate = Recipient.resolvedList(recipientsToReview)[0] + firstDuplicate = recipientsToReview.first().recipient ) ) } @@ -383,14 +389,14 @@ class ConversationRepository( if (group != null && group.isV2Group) { val groupId = group.id.requireV2() - val duplicateRecipients: List = ReviewUtil.getDuplicatedRecipients(groupId).map { it.recipient } + val duplicateRecipients: List = SignalDatabase.nameCollisions.getCollisionsForThreadRecipientId(group.recipientId) if (duplicateRecipients.isNotEmpty()) { return@fromCallable RequestReviewState( groupReviewState = GroupReviewState( groupId, - duplicateRecipients[0], - duplicateRecipients[1], + duplicateRecipients[0].recipient, + duplicateRecipients[1].recipient, duplicateRecipients.size ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index 695744c056..0de5820bb0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -291,6 +291,11 @@ class ConversationViewModel( refreshReminder.onNext(Unit) } + fun onDismissReview() { + val recipientId = recipientSnapshot?.id ?: return + repository.dismissRequestReviewState(recipientId) + } + override fun onCleared() { disposables.clear() startExpiration.onComplete() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel.kt index ebbd92d065..4c965aaf3d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Maybe -import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.addTo @@ -25,21 +24,23 @@ import org.thoughtcrime.securesms.groups.v2.GroupManagementRepository import org.thoughtcrime.securesms.jobs.ForceUpdateGroupV2Job import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob -import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil import org.thoughtcrime.securesms.recipients.Recipient /** * Manages group state and actions for conversations. */ class ConversationGroupViewModel( - private val threadId: Long, private val groupManagementRepository: GroupManagementRepository = GroupManagementRepository(), private val recipientRepository: ConversationRecipientRepository ) : ViewModel() { private val disposables = CompositeDisposable() - private val _groupRecord: BehaviorSubject - private val _reviewState: Subject + + private val _groupRecord: BehaviorSubject = recipientRepository + .groupRecord + .filter { it.isPresent } + .map { it.get() } + .subscribeWithSubject(BehaviorSubject.create(), disposables) private val _groupActiveState: Subject = BehaviorSubject.create() private val _memberLevel: BehaviorSubject = BehaviorSubject.create() @@ -50,28 +51,6 @@ class ConversationGroupViewModel( get() = _groupRecord.value init { - _groupRecord = recipientRepository - .groupRecord - .filter { it.isPresent } - .map { it.get() } - .subscribeWithSubject(BehaviorSubject.create(), disposables) - - val duplicates = _groupRecord.map { groupRecord -> - if (groupRecord.isV2Group) { - ReviewUtil.getDuplicatedRecipients(groupRecord.id.requireV2()).map { it.recipient } - } else { - emptyList() - } - } - - _reviewState = Observable.combineLatest(_groupRecord, duplicates) { record, dupes -> - if (dupes.isEmpty()) { - ConversationGroupReviewState.EMPTY - } else { - ConversationGroupReviewState(record.id.requireV2(), dupes[0], dupes.size) - } - }.subscribeWithSubject(BehaviorSubject.create(), disposables) - disposables += _groupRecord.subscribe { groupRecord -> _groupActiveState.onNext(ConversationGroupActiveState(groupRecord.isActive, groupRecord.isV2Group)) _memberLevel.onNext(ConversationGroupMemberLevel(groupRecord.memberLevel(Recipient.self()), groupRecord.isAnnouncementGroup)) @@ -154,9 +133,9 @@ class ConversationGroupViewModel( .addTo(disposables) } - class Factory(private val threadId: Long, private val recipientRepository: ConversationRecipientRepository) : ViewModelProvider.Factory { + class Factory(private val recipientRepository: ConversationRecipientRepository) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return modelClass.cast(ConversationGroupViewModel(threadId, recipientRepository = recipientRepository)) as T + return modelClass.cast(ConversationGroupViewModel(recipientRepository = recipientRepository)) as T } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt index 7f938d6224..5164e32712 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt @@ -813,6 +813,10 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT Recipient.live(groupRecipientId).refresh() notifyConversationListListeners() + if (groupId.isV2) { + SignalDatabase.nameCollisions.handleGroupNameCollisions(groupId.requireV2(), members.toSet()) + } + return true } @@ -881,7 +885,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT val groupMembers = getV2GroupMembers(decryptedGroup, true) - if (existingGroup.isPresent && existingGroup.get().isV2Group) { + val addedMembers: List = if (existingGroup.isPresent && existingGroup.get().isV2Group) { val change = GroupChangeReconstruct.reconstructGroupChange(existingGroup.get().requireV2GroupProperties().decryptedGroup, decryptedGroup) val removed: List = DecryptedGroupUtil.removedMembersServiceIdList(change) @@ -898,6 +902,10 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT e164 = null ) } + + change.newMembers.toAciList().toRecipientIds() + } else { + groupMembers } writableDatabase.withinTransaction { database -> @@ -920,6 +928,10 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT Recipient.live(groupRecipientId).refresh() notifyConversationListListeners() + + if (groupId.isV2 && addedMembers.isNotEmpty()) { + SignalDatabase.nameCollisions.handleGroupNameCollisions(groupId.requireV2(), addedMembers.toSet()) + } } fun updateTitle(groupId: GroupId.V1, title: String?) { 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 485e488a6a..e856df7c80 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -1073,6 +1073,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat notifyConversationListeners(threadId) TrimThreadJob.enqueueAsync(threadId) } + + groupRecords.filter { it.isV2Group }.forEach { + SignalDatabase.nameCollisions.handleGroupNameCollisions(it.id.requireV2(), setOf(recipient.id)) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/NameCollisionTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/NameCollisionTables.kt new file mode 100644 index 0000000000..4430eae814 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/NameCollisionTables.kt @@ -0,0 +1,475 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database + +import android.content.Context +import androidx.annotation.WorkerThread +import androidx.core.content.contentValuesOf +import net.zetetic.database.sqlcipher.SQLiteDatabase +import org.signal.core.util.Base64 +import org.signal.core.util.Hex +import org.signal.core.util.SqlUtil +import org.signal.core.util.delete +import org.signal.core.util.exists +import org.signal.core.util.insertInto +import org.signal.core.util.orNull +import org.signal.core.util.readToList +import org.signal.core.util.readToSet +import org.signal.core.util.readToSingleLong +import org.signal.core.util.readToSingleObject +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.requireString +import org.signal.core.util.select +import org.signal.core.util.toInt +import org.signal.core.util.update +import org.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.database.model.MessageRecord +import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails +import org.thoughtcrime.securesms.groups.GroupId +import org.thoughtcrime.securesms.groups.GroupId.V2 +import org.thoughtcrime.securesms.profiles.spoofing.ReviewRecipient +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import java.io.IOException +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import kotlin.time.Duration.Companion.days + +/** + * Tables to help manage the state of name collisions. + */ +class NameCollisionTables( + context: Context, + database: SignalDatabase +) : DatabaseTable(context, database) { + + companion object { + private const val ID = "_id" + + private val PROFILE_CHANGE_TIMEOUT = 1.days + + fun createTables(db: SQLiteDatabase) { + db.execSQL(NameCollisionTable.CREATE_TABLE) + db.execSQL(NameCollisionMembershipTable.CREATE_TABLE) + } + + fun createIndexes(db: SQLiteDatabase) { + NameCollisionMembershipTable.CREATE_INDEXES.forEach { + db.execSQL(it) + } + } + } + + /** + * Represents a detected name collision which can involve one or more recipients. + */ + private object NameCollisionTable { + const val TABLE_NAME = "name_collision" + + /** + * The thread id of the conversation to display this collision for. + */ + const val THREAD_ID = "thread_id" + + /** + * Whether the user has manually dismissed the collision. + */ + const val DISMISSED = "dismissed" + + /** + * The hash representing the latest known display name state. + */ + const val HASH = "hash" + + const val CREATE_TABLE = """ + CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY AUTOINCREMENT, + $THREAD_ID INTEGER UNIQUE NOT NULL, + $DISMISSED INTEGER DEFAULT 0, + $HASH STRING DEFAULT NULL + ) + """ + } + + /** + * Represents a recipient who is involved in a name collision. + */ + private object NameCollisionMembershipTable { + const val TABLE_NAME = "name_collision_membership" + + /** + * FK Reference to a name_collision + */ + const val COLLISION_ID = "collision_id" + + /** + * FK Reference to the recipient involved + */ + const val RECIPIENT_ID = "recipient_id" + + /** + * Proto containing group profile change details. Only present for entries tied to group collisions. + */ + const val PROFILE_CHANGE_DETAILS = "profile_change_details" + + const val CREATE_TABLE = """ + CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY AUTOINCREMENT, + $COLLISION_ID INTEGER NOT NULL REFERENCES ${NameCollisionTable.TABLE_NAME} ($ID) ON DELETE CASCADE, + $RECIPIENT_ID INTEGER NOT NULL REFERENCES ${RecipientTable.TABLE_NAME} ($ID) ON DELETE CASCADE, + $PROFILE_CHANGE_DETAILS BLOB DEFAULT NULL, + UNIQUE ($COLLISION_ID, $RECIPIENT_ID) + ) + """ + + val CREATE_INDEXES = arrayOf( + "CREATE INDEX name_collision_membership_collision_id_index ON $TABLE_NAME ($COLLISION_ID)", + "CREATE INDEX name_collision_membership_recipient_id_index ON $TABLE_NAME ($RECIPIENT_ID)" + ) + } + + /** + * Marks the relevant collisions dismissed according to the given thread recipient. + */ + @WorkerThread + fun markCollisionsForThreadRecipientDismissed(threadRecipientId: RecipientId) { + writableDatabase.withinTransaction { db -> + val threadId = SignalDatabase.threads.getThreadIdFor(threadRecipientId) ?: return@withinTransaction + + db.update(NameCollisionTable.TABLE_NAME) + .values(NameCollisionTable.DISMISSED to 1) + .where("${NameCollisionTable.THREAD_ID} = ?", threadId) + .run() + } + } + + /** + * @return A flattened list of similar recipients. + */ + @WorkerThread + fun getCollisionsForThreadRecipientId(recipientId: RecipientId): List { + val threadId = SignalDatabase.threads.getThreadIdFor(recipientId) ?: return emptyList() + val collisionId = readableDatabase + .select(ID) + .from(NameCollisionTable.TABLE_NAME) + .where("${NameCollisionTable.THREAD_ID} = ? AND ${NameCollisionTable.DISMISSED} = 0", threadId) + .run() + .readToSingleLong() + + if (collisionId <= 0) { + return emptyList() + } + + val collisions = readableDatabase + .select() + .from(NameCollisionMembershipTable.TABLE_NAME) + .where("${NameCollisionMembershipTable.COLLISION_ID} = ?", collisionId) + .run() + .readToList { cursor -> + ReviewRecipient( + Recipient.resolved(RecipientId.from(cursor.requireLong(NameCollisionMembershipTable.RECIPIENT_ID))), + cursor.requireBlob(NameCollisionMembershipTable.PROFILE_CHANGE_DETAILS)?.let { ProfileChangeDetails.ADAPTER.decode(it) } + ) + }.toMutableList() + + val groups = collisions.groupBy { SqlUtil.buildCaseInsensitiveGlobPattern(it.recipient.getDisplayName(context)) } + val toDelete: List = groups.values.filter { it.size < 2 }.flatten() + val toReturn: List = groups.values.filter { it.size >= 2 }.flatten() + + if (toDelete.isNotEmpty()) { + writableDatabase.withinTransaction { db -> + val queries = SqlUtil.buildCollectionQuery( + column = NameCollisionMembershipTable.RECIPIENT_ID, + values = toDelete.map { it.recipient.id } + ) + + for (query in queries) { + db.delete(NameCollisionMembershipTable.TABLE_NAME) + .where("${NameCollisionMembershipTable.COLLISION_ID} = ? AND ${query.where}", SqlUtil.appendArgs(arrayOf(collisionId.toString()), query.whereArgs)) + .run() + } + + pruneCollisions() + } + } + + return toReturn + } + + /** + * Update the collision *only* for the given individual. + */ + @WorkerThread + fun handleIndividualNameCollision(recipientId: RecipientId) { + writableDatabase.withinTransaction { db -> + val similarRecipients = SignalDatabase.recipients.getSimilarRecipientIds(Recipient.resolved(recipientId)) + + db.delete(NameCollisionMembershipTable.TABLE_NAME) + .where("${NameCollisionMembershipTable.RECIPIENT_ID} = ?", recipientId) + .run() + + if (similarRecipients.size == 1) { + val threadId = SignalDatabase.threads.getThreadIdFor(recipientId) ?: -1 + if (threadId > 0L) { + db.delete(NameCollisionTable.TABLE_NAME) + .where("${NameCollisionTable.THREAD_ID} = ?", threadId) + .run() + } + } + + similarRecipients.forEach { threadRecipientId -> + handleNameCollisions( + threadRecipientId = threadRecipientId, + getCollisionRecipients = { + val recipients = Recipient.resolvedList(similarRecipients) + + recipients.map { ReviewRecipient(it) }.toSet() + } + ) + } + + pruneCollisions() + } + } + + /** + * Update the collisions for the given group + */ + @WorkerThread + fun handleGroupNameCollisions(groupId: GroupId.V2, changed: Set) { + writableDatabase.withinTransaction { + val threadRecipientId = SignalDatabase.recipients.getByGroupId(groupId).orNull() ?: return@withinTransaction + handleNameCollisions( + threadRecipientId = threadRecipientId, + getCollisionRecipients = { getDuplicatedGroupRecipients(groupId, changed).toSet() } + ) + + pruneCollisions() + } + } + + private fun handleNameCollisions( + threadRecipientId: RecipientId, + getCollisionRecipients: () -> Set + ) { + check(writableDatabase.inTransaction()) + + val resolved = Recipient.resolved(threadRecipientId) + val collisionRecipients: Set = getCollisionRecipients() + + if (collisionRecipients.size < 2 && !collisionExists(threadRecipientId)) { + return + } + + val collision: NameCollision = getOrCreateCollision(resolved) + val hash: String = calculateHash(collisionRecipients) + + updateCollision( + collision.copy( + members = collisionRecipients, + hash = hash, + dismissed = if (!collision.dismissed) false else collision.hash == hash + ) + ) + } + + private fun collisionExists(threadRecipientId: RecipientId): Boolean { + val threadId = SignalDatabase.threads.getThreadIdFor(threadRecipientId) ?: return false + return writableDatabase + .exists(NameCollisionTable.TABLE_NAME) + .where("${NameCollisionTable.THREAD_ID} = ?", threadId) + .run() + } + + private fun getOrCreateCollision(threadRecipient: Recipient): NameCollision { + check(writableDatabase.inTransaction()) + val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(threadRecipient) + + val collision = writableDatabase + .select() + .from(NameCollisionTable.TABLE_NAME) + .where("${NameCollisionTable.THREAD_ID} = ?", threadId) + .run() + .readToSingleObject { nameCollisionCursor -> + NameCollision( + id = nameCollisionCursor.requireLong(ID), + threadId = threadId, + members = writableDatabase + .select(NameCollisionMembershipTable.RECIPIENT_ID, NameCollisionMembershipTable.PROFILE_CHANGE_DETAILS) + .from(NameCollisionMembershipTable.TABLE_NAME) + .where("${NameCollisionMembershipTable.COLLISION_ID} = ?", nameCollisionCursor.requireInt(ID)) + .run() + .readToSet { + val id = RecipientId.from(it.requireLong(NameCollisionMembershipTable.RECIPIENT_ID)) + val rawProfileChangeDetails = it.requireBlob(NameCollisionMembershipTable.PROFILE_CHANGE_DETAILS) + val profileChangeDetails = if (rawProfileChangeDetails != null) { + ProfileChangeDetails.ADAPTER.decode(rawProfileChangeDetails) + } else { + null + } + + ReviewRecipient( + Recipient.resolved(id), + profileChangeDetails + ) + }, + dismissed = nameCollisionCursor.requireBoolean(NameCollisionTable.DISMISSED), + hash = nameCollisionCursor.requireString(NameCollisionTable.HASH) ?: "" + ) + } + + return if (collision == null) { + val rowId = writableDatabase + .insertInto(NameCollisionTable.TABLE_NAME) + .values( + contentValuesOf( + NameCollisionTable.THREAD_ID to threadId, + NameCollisionTable.DISMISSED to 0, + NameCollisionTable.HASH to null + ) + ) + .run() + + NameCollision(id = rowId, threadId = threadId, members = emptySet(), dismissed = false, hash = "") + } else { + collision + } + } + + private fun updateCollision(collision: NameCollision) { + check(writableDatabase.inTransaction()) + + writableDatabase + .update(NameCollisionTable.TABLE_NAME) + .values( + contentValuesOf( + NameCollisionTable.DISMISSED to collision.dismissed.toInt(), + NameCollisionTable.THREAD_ID to collision.threadId, + NameCollisionTable.HASH to collision.hash + ) + ) + .where("$ID = ?", collision.id) + .run() + + writableDatabase + .delete(NameCollisionMembershipTable.TABLE_NAME) + .where("${NameCollisionMembershipTable.COLLISION_ID} = ?") + .run() + + if (collision.members.size < 2) { + return + } + + collision.members.forEach { member -> + writableDatabase + .insertInto(NameCollisionMembershipTable.TABLE_NAME) + .values( + NameCollisionMembershipTable.RECIPIENT_ID to member.recipient.id.toLong(), + NameCollisionMembershipTable.COLLISION_ID to collision.id, + NameCollisionMembershipTable.PROFILE_CHANGE_DETAILS to member.profileChangeDetails?.encode() + ) + .run(conflictStrategy = org.thoughtcrime.securesms.database.SQLiteDatabase.CONFLICT_IGNORE) + } + } + + private fun calculateHash(collisionRecipients: Set): String { + if (collisionRecipients.isEmpty()) { + return "" + } + + return try { + val digest = MessageDigest.getInstance("MD5") + val names = collisionRecipients.map { it.recipient.getDisplayName(context) } + names.forEach { digest.update(it.encodeToByteArray()) } + Hex.toStringCondensed(digest.digest()) + } catch (e: NoSuchAlgorithmException) { + "" + } + } + + /** + * Remove any collision for which there is only a single member. + */ + private fun pruneCollisions() { + check(writableDatabase.inTransaction()) + + writableDatabase.execSQL( + """ + DELETE FROM ${NameCollisionTable.TABLE_NAME} + WHERE ${NameCollisionTable.TABLE_NAME}.$ID IN ( + SELECT ${NameCollisionMembershipTable.COLLISION_ID} + FROM ${NameCollisionMembershipTable.TABLE_NAME} + GROUP BY ${NameCollisionMembershipTable.COLLISION_ID} + HAVING COUNT($ID) < 2 + ) + """.trimIndent() + ) + } + + private fun getDuplicatedGroupRecipients(groupId: V2, toCheck: Set): List { + if (toCheck.isEmpty()) { + return emptyList() + } + + val profileChangeRecords: Map = getProfileChangeRecordsForGroup(groupId).associateBy { it.fromRecipient.id } + val members: MutableList = SignalDatabase.groups.getGroupMembers(groupId, GroupTable.MemberSet.FULL_MEMBERS_INCLUDING_SELF).toMutableList() + val changed: List = Recipient.resolvedList(toCheck) + .map { recipient -> ReviewRecipient(recipient.resolve(), profileChangeRecords[recipient.id]?.let { getProfileChangeDetails(it) }) } + .filter { !it.recipient.isSystemContact && it.recipient.nickname.isEmpty } + + val results = mutableListOf() + + for (reviewRecipient in changed) { + if (results.contains(reviewRecipient)) { + continue + } + + members.remove(reviewRecipient.recipient) + + for (member in members) { + if (member.getDisplayName(context) == reviewRecipient.recipient.getDisplayName(context)) { + results.add(reviewRecipient) + results.add(ReviewRecipient(member)) + } + } + } + + return results + } + + private fun getProfileChangeRecordsForGroup(groupId: V2): List { + val groupRecipientId = SignalDatabase.recipients.getByGroupId(groupId).get() + val groupThreadId = SignalDatabase.threads.getThreadIdFor(groupRecipientId) + + return if (groupThreadId == null) { + emptyList() + } else { + SignalDatabase.messages.getProfileChangeDetailsRecords( + groupThreadId, + System.currentTimeMillis() - PROFILE_CHANGE_TIMEOUT.inWholeMilliseconds + ) + } + } + + private fun getProfileChangeDetails(record: MessageRecord): ProfileChangeDetails { + try { + return ProfileChangeDetails.ADAPTER.decode(Base64.decode(record.body)) + } catch (e: IOException) { + throw IllegalArgumentException(e) + } + } + + private data class NameCollision( + val id: Long, + val threadId: Long, + val members: Set, + val dismissed: Boolean, + val hash: String + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index db956aaa84..c3cfa5b00c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -87,6 +87,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.profiles.ProfileName import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.recipients.RecipientUtil import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId import org.thoughtcrime.securesms.storage.StorageRecordUpdate import org.thoughtcrime.securesms.storage.StorageSyncHelper @@ -1715,9 +1716,20 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } fun getSimilarRecipientIds(recipient: Recipient): List { - val projection = SqlUtil.buildArgs(ID, "COALESCE(NULLIF($SYSTEM_JOINED_NAME, ''), NULLIF($PROFILE_JOINED_NAME, '')) AS checked_name") - val where = "checked_name = ? AND $HIDDEN = ?" - val arguments = SqlUtil.buildArgs(recipient.profileName.toString(), 0) + if (!recipient.nickname.isEmpty || recipient.isSystemContact) { + return emptyList() + } + + val threadId = threads.getThreadIdFor(recipient.id) + val isMessageRequestAccepted = RecipientUtil.isMessageRequestAccepted(threadId, recipient) + if (isMessageRequestAccepted) { + return emptyList() + } + + val glob = SqlUtil.buildCaseInsensitiveGlobPattern(recipient.profileName.toString()) + val projection = SqlUtil.buildArgs(ID, "COALESCE(NULLIF($NICKNAME_JOINED_NAME, ''), NULLIF($SYSTEM_JOINED_NAME, ''), NULLIF($PROFILE_JOINED_NAME, '')) AS checked_name") + val where = "checked_name GLOB ? AND $HIDDEN = ? AND $BLOCKED = ?" + val arguments = SqlUtil.buildArgs(glob, 0, 0) readableDatabase.query(TABLE_NAME, projection, where, arguments, null, null, null).use { cursor -> if (cursor == null || cursor.count == 0) { 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 e054825cd8..3fccabc29a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt @@ -73,6 +73,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data val callTable: CallTable = CallTable(context, this) val kyberPreKeyTable: KyberPreKeyTable = KyberPreKeyTable(context, this) val callLinkTable: CallLinkTable = CallLinkTable(context, this) + val nameCollisionTables: NameCollisionTables = NameCollisionTables(context, this) override fun onOpen(db: net.zetetic.database.sqlcipher.SQLiteDatabase) { db.setForeignKeyConstraintsEnabled(true) @@ -109,6 +110,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data db.execSQL(CallLinkTable.CREATE_TABLE) db.execSQL(CallTable.CREATE_TABLE) db.execSQL(KyberPreKeyTable.CREATE_TABLE) + NameCollisionTables.createTables(db) executeStatements(db, SearchTable.CREATE_TABLE) executeStatements(db, RemappedRecordTables.CREATE_TABLE) executeStatements(db, MessageSendLogTables.CREATE_TABLE) @@ -139,6 +141,8 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data executeStatements(db, SearchTable.CREATE_TRIGGERS) executeStatements(db, MessageSendLogTables.CREATE_TRIGGERS) + NameCollisionTables.createIndexes(db) + DistributionListTables.insertInitialDistributionListAtCreationTime(db) if (context.getDatabasePath(ClassicOpenHelper.NAME).exists()) { @@ -526,5 +530,10 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data @get:JvmName("callLinks") val callLinks: CallLinkTable get() = instance!!.callLinkTable + + @get:JvmStatic + @get:JvmName("nameCollisions") + val nameCollisions: NameCollisionTables + get() = instance!!.nameCollisionTables } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 17dabc9d4d..4f8fbc0f93 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -85,6 +85,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V224_AddAttachmentA import org.thoughtcrime.securesms.database.helpers.migration.V225_AddLocalUserJoinedStateAndGroupCallActiveState import org.thoughtcrime.securesms.database.helpers.migration.V226_AddAttachmentMediaIdIndex import org.thoughtcrime.securesms.database.helpers.migration.V227_AddAttachmentArchiveTransferState +import org.thoughtcrime.securesms.database.helpers.migration.V228_AddNameCollisionTables /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -172,10 +173,11 @@ object SignalDatabaseMigrations { 224 to V224_AddAttachmentArchiveColumns, 225 to V225_AddLocalUserJoinedStateAndGroupCallActiveState, 226 to V226_AddAttachmentMediaIdIndex, - 227 to V227_AddAttachmentArchiveTransferState + 227 to V227_AddAttachmentArchiveTransferState, + 228 to V228_AddNameCollisionTables ) - const val DATABASE_VERSION = 227 + const val DATABASE_VERSION = 228 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V228_AddNameCollisionTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V228_AddNameCollisionTables.kt new file mode 100644 index 0000000000..c2bc02dc10 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V228_AddNameCollisionTables.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds the tables for managing name collisions + */ +object V228_AddNameCollisionTables : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL( + """ + CREATE TABLE name_collision ( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + thread_id INTEGER UNIQUE NOT NULL, + dismissed INTEGER DEFAULT 0, + hash STRING DEFAULT NULL + ) + """ + ) + + db.execSQL( + """ + CREATE TABLE name_collision_membership ( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + collision_id INTEGER NOT NULL REFERENCES name_collision (_id) ON DELETE CASCADE, + recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE, + profile_change_details BLOB DEFAULT NULL, + UNIQUE (collision_id, recipient_id) + ) + """ + ) + + db.execSQL("CREATE INDEX name_collision_membership_collision_id_index ON name_collision_membership (collision_id)") + db.execSQL("CREATE INDEX name_collision_membership_recipient_id_index ON name_collision_membership (recipient_id)") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt index 90a3277d43..0e5dfd0ce3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt @@ -399,6 +399,20 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val Log.i(TAG, "Name changed, but wasn't relevant to write an event. blocked: ${recipient.isBlocked}, group: ${recipient.isGroup}, self: ${recipient.isSelf}, firstSet: ${localDisplayName.isEmpty()}, displayChange: ${remoteDisplayName != localDisplayName}") } + if (recipient.isIndividual && + !recipient.isSystemContact && + !recipient.nickname.isEmpty && + !recipient.isProfileSharing && + !recipient.isBlocked && + !recipient.isSelf && + !recipient.isHidden + ) { + val threadId = SignalDatabase.threads.getThreadIdFor(recipient.id) + if (threadId != null && !RecipientUtil.isMessageRequestAccepted(threadId, recipient)) { + SignalDatabase.nameCollisions.handleIndividualNameCollision(recipient.id) + } + } + if (writeChangeEvent || localDisplayName.isEmpty()) { ApplicationDependencies.getDatabaseObserver().notifyConversationListListeners() val threadId = SignalDatabase.threads.getThreadIdFor(recipient.id) diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardRepository.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardRepository.java index 232a77795d..ee832801b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardRepository.java @@ -51,7 +51,7 @@ void loadRecipients(@NonNull OnRecipientsLoadedListener onRecipientsLoadedListen if (groupId != null) { loadRecipientsForGroup(groupId, onRecipientsLoadedListener); } else if (recipientId != null) { - loadSimilarRecipients(context, recipientId, onRecipientsLoadedListener); + loadSimilarRecipients(recipientId, onRecipientsLoadedListener); } else { throw new AssertionError(); } @@ -113,34 +113,21 @@ void removeFromGroup(@NonNull ReviewCard reviewCard, @NonNull OnRemoveFromGroupL private static void loadRecipientsForGroup(@NonNull GroupId.V2 groupId, @NonNull OnRecipientsLoadedListener onRecipientsLoadedListener) { - SignalExecutors.BOUNDED.execute(() -> onRecipientsLoadedListener.onRecipientsLoaded(ReviewUtil.getDuplicatedRecipients(groupId))); + SignalExecutors.BOUNDED.execute(() -> { + RecipientId groupRecipientId = SignalDatabase.recipients().getByGroupId(groupId).orElse(null); + if (groupRecipientId != null) { + onRecipientsLoadedListener.onRecipientsLoaded(SignalDatabase.nameCollisions().getCollisionsForThreadRecipientId(groupRecipientId)); + } else { + onRecipientsLoadedListener.onRecipientsLoadFailed(); + } + }); } - private static void loadSimilarRecipients(@NonNull Context context, - @NonNull RecipientId recipientId, + private static void loadSimilarRecipients(@NonNull RecipientId recipientId, @NonNull OnRecipientsLoadedListener onRecipientsLoadedListener) { SignalExecutors.BOUNDED.execute(() -> { - Recipient resolved = Recipient.resolved(recipientId); - - List recipientIds = SignalDatabase.recipients() - .getSimilarRecipientIds(resolved); - - if (recipientIds.isEmpty()) { - onRecipientsLoadedListener.onRecipientsLoadFailed(); - return; - } - - HashSet ids = new HashSet<>(recipientIds); - ids.add(recipientId); - - List recipients = Stream.of(ids) - .map(Recipient::resolved) - .map(ReviewRecipient::new) - .sorted(new ReviewRecipient.Comparator(context, recipientId)) - .toList(); - - onRecipientsLoadedListener.onRecipientsLoaded(recipients); + onRecipientsLoadedListener.onRecipientsLoaded(SignalDatabase.nameCollisions().getCollisionsForThreadRecipientId(recipientId)); }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewRecipient.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewRecipient.java index fc6df5ca79..2dcd8c6bce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewRecipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewRecipient.java @@ -13,11 +13,11 @@ public class ReviewRecipient { private final Recipient recipient; private final ProfileChangeDetails profileChangeDetails; - ReviewRecipient(@NonNull Recipient recipient) { + public ReviewRecipient(@NonNull Recipient recipient) { this(recipient, null); } - ReviewRecipient(@NonNull Recipient recipient, @Nullable ProfileChangeDetails profileChangeDetails) { + public ReviewRecipient(@NonNull Recipient recipient, @Nullable ProfileChangeDetails profileChangeDetails) { this.recipient = recipient; this.profileChangeDetails = profileChangeDetails; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewUtil.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewUtil.java index 173ea4b773..e4f098e468 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewUtil.java @@ -7,102 +7,15 @@ import com.annimon.stream.Stream; -import org.thoughtcrime.securesms.database.GroupTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.model.GroupRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails; -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.signal.core.util.Base64; - -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.TimeUnit; public final class ReviewUtil { private ReviewUtil() { } - private static final long TIMEOUT = TimeUnit.HOURS.toMillis(24); - - /** - * Checks a single recipient against the database to see whether duplicates exist. - * This should not be used in the context of a group, due to performance reasons. - * - * @param recipientId Id of the recipient we are interested in. - * @return Whether or not multiple recipients share this profile name. - */ - @WorkerThread - public static List getRecipientsToPromptForReview(@NonNull RecipientId recipientId) - { - Recipient recipient = Recipient.resolved(recipientId); - - if (recipient.isGroup() || recipient.isSystemContact()) { - return Collections.emptyList(); - } - - return Stream.of(SignalDatabase.recipients().getSimilarRecipientIds(recipient)) - .filter(id -> !id.equals(recipientId)) - .toList(); - } - - @WorkerThread - public static @NonNull List getDuplicatedRecipients(@NonNull GroupId.V2 groupId) - { - Context context = ApplicationDependencies.getApplication(); - List profileChangeRecords = getProfileChangeRecordsForGroup(context, groupId); - - if (profileChangeRecords.isEmpty()) { - return Collections.emptyList(); - } - - List members = SignalDatabase.groups() - .getGroupMembers(groupId, GroupTable.MemberSet.FULL_MEMBERS_INCLUDING_SELF); - - List changed = Stream.of(profileChangeRecords) - .distinctBy(record -> record.getFromRecipient().getId()) - .map(record -> new ReviewRecipient(record.getFromRecipient().resolve(), getProfileChangeDetails(record))) - .filter(recipient -> !recipient.getRecipient().isSystemContact()) - .toList(); - - List results = new LinkedList<>(); - - for (ReviewRecipient recipient : changed) { - if (results.contains(recipient)) { - continue; - } - - members.remove(recipient.getRecipient()); - - for (Recipient member : members) { - if (Objects.equals(member.getDisplayName(context), recipient.getRecipient().getDisplayName(context))) { - results.add(recipient); - results.add(new ReviewRecipient(member)); - } - } - } - - return results; - } - - @WorkerThread - public static @NonNull List getProfileChangeRecordsForGroup(@NonNull Context context, @NonNull GroupId.V2 groupId) { - RecipientId recipientId = SignalDatabase.recipients().getByGroupId(groupId).get(); - Long threadId = SignalDatabase.threads().getThreadIdFor(recipientId); - - if (threadId == null) { - return Collections.emptyList(); - } else { - return SignalDatabase.messages().getProfileChangeDetailsRecords(threadId, System.currentTimeMillis() - TIMEOUT); - } - } - @WorkerThread public static int getGroupsInCommonCount(@NonNull Context context, @NonNull RecipientId recipientId) { return Stream.of(SignalDatabase.groups() @@ -112,12 +25,4 @@ public static int getGroupsInCommonCount(@NonNull Context context, @NonNull Reci .toList() .size(); } - - private static @NonNull ProfileChangeDetails getProfileChangeDetails(@NonNull MessageRecord messageRecord) { - try { - return ProfileChangeDetails.ADAPTER.decode(Base64.decode(messageRecord.getBody())); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } } From d74260b536a0f09bcf2597bfc32ad1b9b358ab12 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 18 Apr 2024 15:40:51 -0400 Subject: [PATCH 036/113] Improve network reliability. --- .../thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt | 4 ---- 1 file changed, 4 deletions(-) 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 4ed388b261..151626d190 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt @@ -69,7 +69,6 @@ open class SignalServiceNetworkAccess(context: Context) { private const val COUNTRY_CODE_IRAN = 98 private const val COUNTRY_CODE_CUBA = 53 private const val COUNTRY_CODE_UZBEKISTAN = 998 - private const val COUNTRY_CODE_UKRAINE = 380 private const val G_HOST = "reflector-nrgwuv7kwq-uc.a.run.app" private const val F_SERVICE_HOST = "chat-signal.global.ssl.fastly.net" @@ -210,9 +209,6 @@ open class SignalServiceNetworkAccess(context: Context) { COUNTRY_CODE_UZBEKISTAN to buildGConfiguration( listOf(HostConfig("https://www.google.co.uz", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs ), - COUNTRY_CODE_UKRAINE to buildGConfiguration( - listOf(HostConfig("https://www.google.com.ua", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs - ), COUNTRY_CODE_IRAN to fConfig, COUNTRY_CODE_CUBA to fConfig ) From ee58d47926a2be89d1538646c0d1678b39c5b1ff Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 18 Apr 2024 15:45:47 -0400 Subject: [PATCH 037/113] Cycle rx message sending flag. --- .../main/java/org/thoughtcrime/securesms/util/FeatureFlags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index b5baf4fb4d..82e8e028f0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -123,7 +123,7 @@ public final class FeatureFlags { private static final String RETRY_RECEIPT_MAX_COUNT_RESET_AGE = "android.retryReceipt.maxCountResetAge"; private static final String PREKEY_FORCE_REFRESH_INTERVAL = "android.prekeyForceRefreshInterval"; private static final String CDSI_LIBSIGNAL_NET = "android.cds.libsignal.3"; - private static final String RX_MESSAGE_SEND = "android.rxMessageSend"; + private static final String RX_MESSAGE_SEND = "android.rxMessageSend.2"; private static final String LINKED_DEVICE_LIFESPAN_SECONDS = "android.linkedDeviceLifespanSeconds"; private static final String MESSAGE_BACKUPS = "android.messageBackups"; private static final String CAMERAX_CUSTOM_CONTROLLER = "android.cameraXCustomController"; From 62af9dad500390f1685d9398149527e4016e1cb7 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 18 Apr 2024 16:43:51 -0400 Subject: [PATCH 038/113] Update translations and other static files. --- app/build.gradle.kts | 3 - .../push/SignalServiceNetworkAccess.kt | 3 +- app/src/main/res/values-af/strings.xml | 110 +++++++--- app/src/main/res/values-ar/strings.xml | 126 +++++++---- app/src/main/res/values-az/strings.xml | 110 +++++++--- app/src/main/res/values-bg/strings.xml | 110 +++++++--- app/src/main/res/values-bn/strings.xml | 110 +++++++--- app/src/main/res/values-bs/strings.xml | 124 +++++++---- app/src/main/res/values-ca/strings.xml | 116 +++++++---- app/src/main/res/values-cs/strings.xml | 118 +++++++---- app/src/main/res/values-da/strings.xml | 110 +++++++--- app/src/main/res/values-de/strings.xml | 142 ++++++++----- app/src/main/res/values-el/strings.xml | 110 +++++++--- app/src/main/res/values-es/strings.xml | 110 +++++++--- app/src/main/res/values-et/strings.xml | 110 +++++++--- app/src/main/res/values-eu/strings.xml | 110 +++++++--- app/src/main/res/values-fa/strings.xml | 116 +++++++---- app/src/main/res/values-fi/strings.xml | 110 +++++++--- app/src/main/res/values-fr/strings.xml | 196 +++++++++++------- app/src/main/res/values-ga/strings.xml | 128 ++++++++---- app/src/main/res/values-gl/strings.xml | 110 +++++++--- app/src/main/res/values-gu/strings.xml | 110 +++++++--- app/src/main/res/values-hi/strings.xml | 110 +++++++--- app/src/main/res/values-hr/strings.xml | 118 +++++++---- app/src/main/res/values-hu/strings.xml | 110 +++++++--- app/src/main/res/values-in/strings.xml | 106 +++++++--- app/src/main/res/values-it/strings.xml | 110 +++++++--- app/src/main/res/values-iw/strings.xml | 118 +++++++---- app/src/main/res/values-ja/strings.xml | 108 +++++++--- app/src/main/res/values-ka/strings.xml | 110 +++++++--- app/src/main/res/values-kk/strings.xml | 116 +++++++---- app/src/main/res/values-km/strings.xml | 112 +++++++--- app/src/main/res/values-kn/strings.xml | 110 +++++++--- app/src/main/res/values-ko/strings.xml | 112 +++++++--- app/src/main/res/values-ky/strings.xml | 106 +++++++--- app/src/main/res/values-lt/strings.xml | 118 +++++++---- app/src/main/res/values-lv/strings.xml | 114 ++++++---- app/src/main/res/values-mk/strings.xml | 110 +++++++--- app/src/main/res/values-ml/strings.xml | 110 +++++++--- app/src/main/res/values-mr/strings.xml | 110 +++++++--- app/src/main/res/values-ms/strings.xml | 106 +++++++--- app/src/main/res/values-my/strings.xml | 106 +++++++--- app/src/main/res/values-nb/strings.xml | 110 +++++++--- app/src/main/res/values-nl/strings.xml | 110 +++++++--- app/src/main/res/values-pa/strings.xml | 110 +++++++--- app/src/main/res/values-pl/strings.xml | 124 +++++++---- app/src/main/res/values-pt-rBR/strings.xml | 110 +++++++--- app/src/main/res/values-pt/strings.xml | 116 +++++++---- app/src/main/res/values-ro/strings.xml | 114 ++++++---- app/src/main/res/values-ru/strings.xml | 118 +++++++---- app/src/main/res/values-sk/strings.xml | 118 +++++++---- app/src/main/res/values-sl/strings.xml | 118 +++++++---- app/src/main/res/values-sq/strings.xml | 110 +++++++--- app/src/main/res/values-sr/strings.xml | 110 +++++++--- app/src/main/res/values-sv/strings.xml | 110 +++++++--- app/src/main/res/values-sw/strings.xml | 110 +++++++--- app/src/main/res/values-ta/strings.xml | 110 +++++++--- app/src/main/res/values-te/strings.xml | 110 +++++++--- app/src/main/res/values-th/strings.xml | 106 +++++++--- app/src/main/res/values-tl/strings.xml | 116 +++++++---- app/src/main/res/values-tr/strings.xml | 110 +++++++--- app/src/main/res/values-ug/strings.xml | 112 +++++++--- app/src/main/res/values-uk/strings.xml | 132 ++++++++---- app/src/main/res/values-ur/strings.xml | 110 +++++++--- app/src/main/res/values-vi/strings.xml | 106 +++++++--- app/src/main/res/values-yue/strings.xml | 106 +++++++--- app/src/main/res/values-zh-rCN/strings.xml | 106 +++++++--- app/src/main/res/values-zh-rHK/strings.xml | 106 +++++++--- app/src/main/res/values-zh-rTW/strings.xml | 106 +++++++--- app/src/main/res/values/strings.xml | 6 +- app/static-ips.gradle.kts | 1 - 71 files changed, 5293 insertions(+), 2350 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9400d1b705..7dfac31b6e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -178,7 +178,6 @@ android { buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3.signal.org\"") buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.signal.org\"") buildConfigField("String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\"") - buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\"") buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.signal.org\"") buildConfigField("String", "SIGNAL_SFU_URL", "\"https://sfu.voip.signal.org\"") buildConfigField("String", "SIGNAL_STAGING_SFU_URL", "\"https://sfu.staging.voip.signal.org\"") @@ -195,7 +194,6 @@ android { buildConfigField("String[]", "SIGNAL_CONTENT_PROXY_IPS", rootProject.extra["content_proxy_ips"] as String) buildConfigField("String[]", "SIGNAL_CDSI_IPS", rootProject.extra["cdsi_ips"] as String) buildConfigField("String[]", "SIGNAL_SVR2_IPS", rootProject.extra["svr2_ips"] as String) - buildConfigField("String[]", "SIGNAL_KEY_BACKUP_IPS", rootProject.extra["key_backup_ips"] as String) buildConfigField("String", "SIGNAL_AGENT", "\"OWA\"") buildConfigField("String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\"") buildConfigField("String", "SVR2_MRENCLAVE", "\"a6622ad4656e1abcd0bc0ff17c229477747d2ded0495c4ebee7ed35c1789fa97\"") @@ -378,7 +376,6 @@ android { buildConfigField("String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\"") buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3-staging.signal.org\"") buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.staging.signal.org\"") - buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"") buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.staging.signal.org\"") buildConfigField("String", "SVR2_MRENCLAVE", "\"acb1973aa0bbbd14b3b4e06f145497d948fd4a98efc500fcce363b3b743ec482\"") buildConfigField("String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"") 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 151626d190..b8efa93325 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt @@ -52,8 +52,7 @@ open class SignalServiceNetworkAccess(context: Context) { BuildConfig.SIGNAL_SFU_URL.stripProtocol() to BuildConfig.SIGNAL_SFU_IPS.toSet(), BuildConfig.CONTENT_PROXY_HOST.stripProtocol() to BuildConfig.SIGNAL_CONTENT_PROXY_IPS.toSet(), BuildConfig.SIGNAL_CDSI_URL.stripProtocol() to BuildConfig.SIGNAL_CDSI_IPS.toSet(), - BuildConfig.SIGNAL_SVR2_URL.stripProtocol() to BuildConfig.SIGNAL_SVR2_IPS.toSet(), - BuildConfig.SIGNAL_KEY_BACKUP_URL.stripProtocol() to BuildConfig.SIGNAL_KEY_BACKUP_IPS.toSet() + BuildConfig.SIGNAL_SVR2_URL.stripProtocol() to BuildConfig.SIGNAL_SVR2_IPS.toSet() ) ) ) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 4b011409b4..2dd57d264b 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -415,6 +415,7 @@ Sluit aan + Vol Kon nie media stuur nie @@ -613,6 +614,7 @@ Ontargiveer Skrap Kies alles + %1$d geselekteer %1$d geselekteer @@ -864,6 +866,7 @@ Uitnodiging gestuur %1$d uitnodigings gestuur + \"%1$s\" kan nie outomaties deur jou by hierdie groep gevoeg word nie.\n\nHulle is genooi om aan te sluit, maar sal geen groepboodskappe sien voordat hulle aanvaar nie. Hierdie gebruikers kan nie outomaties deur jou by hierdie groep gevoeg word nie.\n\nHulle is genooi om by die groep aan te sluit, maar sal geen groepboodskappe sien voordat hulle aanvaar nie. @@ -1044,7 +1047,9 @@ Bewerk naam en foto Legaatgroep + Hierdie groep is ’n Legaatgroep. Funksies soos groepadmins is slegs vir Nuwe Groepe beskikbaar. + Hierdie groep is ’n Legaatgroep. Vir toegang tot nuwe funksies soos @vermeldings en admins, Hierdie Legaatgroep kan nie na ’n Nuwe Groep opgradeer word nie omdat dit te groot is. Die maksimum groepgrootte is %1$d gradeer hierdie groep op. @@ -1271,6 +1276,7 @@ Skrap + %1$d het (%2$s) geselekteer %1$d het (%2$s) geselekteer @@ -1335,17 +1341,13 @@ Jy het die groep verlaat. Jy het die groep bygewerk. Die groep is bygewerk. - + Uitgaande stemoproep - + Uitgaande video-oproep - - Onbeantwoorde stemoproep - - Onbeantwoorde video-oproep - + Inkomende stemoproep - + Inkomende video-oproep Gemiste stemoproep @@ -1355,10 +1357,6 @@ Gemiste stemoproep terwyl kennisgewingprofiel aan was Gemiste video-oproep terwyl kennisgewingprofiel aan is - - Jy het ’n stemoproep geweier - - Jy het ’n video-oproep geweier %1$s · %2$s %1$s het die groep bygewerk. @@ -1567,30 +1565,55 @@ %1$s kan nou Betalings aanvaar - %1$s het ’n groepoproep begin · %2$s - Jy het om %1$s ’n groepoproep begin - %1$s is in die groepoproep · %2$s - Jy is in die groepoproep · %1$s - %1$s en %2$s is in die groepoproep · %3$s - Groepoproep · %1$s - - %1$s het ’n groepoproep begin - Jy het ’n groepoproep begin - %1$s is in die groepoproep - Jy is in die groepoproep - %1$s en %2$s is in die groepoproep - Groepoproep + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Jy - - %1$s, %2$s en %3$d ander is in die groepoproep · %4$s - %1$s, %2$s en %3$d ander is in die groepoproep · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s en %3$d ander is in die groepoproep - %1$s, %2$s en %3$d ander is in die groepoproep + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Kan nie \'n verifiëringskode aanvra nie. Gaan asseblief netwerkverbinding na en probeer weer. Niestandaard-nommerformaat + Die nommer wat jy ingesleutel het (%1$s), lyk of dit in \'n niestandaard-formaat is. \n\nHet jy %2$s bedoel? Signal Android - Telefoonnommerformaat @@ -2709,6 +2733,8 @@ Laai tans Vind meer uit Sluit aan by oproep + + Call back Keer terug na oproep Oproep is vol Nooi vriende uit @@ -2773,6 +2799,7 @@ Verlaat oproep Die volgende mense het dalk van toestelle verander of ’n herinstallasie gedoen. Verifieer jou veiligheidsnomemr met hulle om privaatheid te verseker. Bekyk + Vorige geverifieer @@ -3086,6 +3113,7 @@ Verstek Hoog + Maksimum @@ -3446,6 +3474,7 @@ Gestuur aan %1$s Jou op %1$s om %2$s %1$s op %2$s om %3$s + Aan Van Transaksiebesonderhede, wat die betalingsbedrag en transaksietyd insluit, is deel van die MobileCoin-grootboek. @@ -3508,6 +3537,7 @@ Bevestig betaling Netwerkgelde Geraamde %1$s + Aan Totale bedrag Saldo: %1$s @@ -4112,6 +4142,7 @@ Na knipbord gekopieer Admin + Keur goed Weier @@ -4139,6 +4170,7 @@ Stemboodskap · %1$s + %1$s na %2$s @@ -4185,6 +4217,7 @@ %1$s en %2$s het aangesluit %1$s, %2$s en %3$s het aangesluit %1$s, %2$s en nog %3$d het aangesluit + %1$s het verlaat %1$s en %2$s het verlaat %1$s, %2$s en %3$s het verlaat @@ -4562,6 +4595,7 @@ Boodskappe Verdwynboodskappe Toepassingveiligheid + Versper skermgrepe in die onlangs-lys en binne-in die toepassing Signal-boodskappe en oproepe, herlei oproepe altyd, en verseëlde afsender Verstek-tydhouer vir nuwe kletse @@ -4618,11 +4652,14 @@ Mediakwaliteit Gestuurde mediakwaliteit Stuur van hoëkwaliteitmedia sal meer data gebruik. + Hoog + Standaard Oproepe + Outo Gebruik pasgemaakte kleure Kletskleur @@ -4834,6 +4871,7 @@ Neem ’n foto Kies ’n foto Foto + Teks Stoor Vee avatar uit @@ -4894,7 +4932,7 @@ Voeg ’n boodskap toe Voeg ’n antwoord toe Stuur na - Eenkeerkyk-boodskap + View once media Een of meer items was te groot Een of meer items was ongeldig Te veel items gekies @@ -5487,7 +5525,7 @@ %1$s %2$s Jy - + %1$s tot %2$s Antwoord @@ -6704,5 +6742,11 @@ Wysig nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index fbbbd47752..b65cd08e3d 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -427,6 +427,7 @@ انضم + ممتليء خطأُ في إرسال الوسائط @@ -677,6 +678,7 @@ إلغاء الأرشفة حذف تحديد الكل + تم تحديد %1$d تم تحديد %1$d @@ -940,6 +942,7 @@ تم إرسال %1$d دعوة تم إرسال %1$d دعوة + لا يمكنك إضافة \"%1$s\" تلقائياً إلى هذه المجموعة. \n\n لقد تمت دعوة ذلك الشخص للانضمام، ولن يرى أيّة رسالة من رسائل المجموعة حتى يقبلها. لا يمكنك إضافة هؤلاء المستخدمين تلقائياً إلى هذه المجموعة . \n\n لقد تمت دعوتهم للانضمام، ولن يروا أيّة رسالة من رسائل المجموعة حتى يقبلون. @@ -1184,7 +1187,9 @@ عدّل الاسم والصورة مجموعة من الطراز القديم + هذه مجموعة من الطراز القديم. إن الميزات مثل خاصية المشرفين على المجموعة، متاحة فقط في المجموعات الجديدة. + هذه مجموعة من الطراز القديم. للوصول إلى ميزات مثل ‎@mentions‏ والمشرفين، لا يمكن ترقية هذه المجموعة القديمة الطراز إلى مجموعة جديدة لأنها كبيرة جدا. الحد الأقصى للمجموعة هو %1$d. يُرجى ترقية هذه المجموعة. @@ -1447,6 +1452,7 @@ حذف + تم تحديد %1$d (%2$s) تم تحديد %1$d (%2$s) @@ -1515,17 +1521,13 @@ لقد تركت المجموعة. قمت بتحديث المجموعة. تم تحديث المجموعة بنجاح. - + مكالمة صوتية صادرة - + مكالمة فيديو صادرة - - مكالمة صوتية لم يتم الرد عليها - - مكالمة فيديو لم يتم الرد عليها - + مكالمة صوتية واردة - + مكالمة فيديو واردة مكالمة صوتية فائتة @@ -1535,10 +1537,6 @@ مكالمة صوتية فائتة أثناء تشغيل الحساب الشخصي للإشعارات اتصال فيديو فائت أثناء تشغيل الحساب الشخصي للإشعار - - لقد رفضت مكالمة صوتية - - لقد رفضت مكالمة فيديو %1$s . %2$s %1$s قام بتحديث المجموعة. @@ -1779,38 +1777,63 @@ يُمكن لـ %1$s قبول عمليات الدفع الآن - قام العضو %1$s ببدء مكالمة جماعية · %2$s - لقد بدأتَ مكالمة جماعية · %1$s - العضو %1$s موجود في المكالمة الجماعية · %2$s - إنك في مكالمة جماعية · %1$s - العضوان %1$s و %2$s موجودان في مكالمة جماعية · %3$s - مكالمة جماعية · %1$s - - قام العضو %1$s ببدء مكالمة جماعية - لقد قمت ببدء مكالمة جماعية - العضو %1$s موجود في مكالمة جماعية - إنك في مكالمة جماعية - العضوان %1$s و %2$s موجودان في مكالمة جماعية - المكالمة الجماعية + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$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 + + + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$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, and %3$d others are in the group call + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2379,6 +2402,7 @@ تعذّر طلب رمز تحقق. يُرجى التحقّق من اتصالك بالانترنت ثم حاول مرة أُخرى. نسق العدد غير المعياري + ‫يظهر أن العدد الذي قمت بإدخاله (%1$s) ليس بالنسق المعياري.\n\nأ قصدك هو %2$s ؟ سيجنال Android - نسق رقم الهاتف @@ -3057,6 +3081,8 @@ قيد التحميل لمعرفة المزيد الانضمام للمكالمة + + Call back العودة للمكالمة المكالمة ممتلئة دعوة الأصدقاء @@ -3121,6 +3147,7 @@ مغادرة المكالمة ربما قام هؤلاء الأشخاص بإعادة تثبيت التطبيق أو قاموا بتغيير أجهزتهم. يُرجى تأكيد رقم الأمان معهم لضمان الخصوصية. إظهار + سبق التحقق منه @@ -3462,6 +3489,7 @@ افتراضي بالغ الأهميّة + قصوى @@ -3834,6 +3862,7 @@ مُرسَلة إلى %1$s أنت، بتاريخ %1$s على الساعة %2$s %1$s، بتاريخ %2$s على الساعة %3$s + إلى مِن ستجد تفاصيل المعاملات بما في ذلك مبالغ الدفوعات وتاريخ المعاملة متضمنة في دفتر حسابات MobileCoin. @@ -3896,6 +3925,7 @@ تأكيد الدفع رسوم الشبكة مُقدَّرة بـ %1$s + إلى المبلغ الإجمالي الرصيد : %1$s @@ -4536,6 +4566,7 @@ تمّ النسخ إلى الحافظة مشرف + الموافقة رفض @@ -4563,6 +4594,7 @@ رسالة صوتية · %1$s + %1$s إلى %2$s @@ -4617,6 +4649,7 @@ انضم %1$s و %2$s انضم %1$s، %2$s و%3$s انضم %1$s، %2$s و%3$d آخرون + غادر العضو %1$s غادر %1$s و %2$s غادر %1$s، %2$s و%3$s @@ -5002,6 +5035,7 @@ التراسُل الرسائل المختفية أمان التطبيق + منع لقطات الشاشة داخل التطبيق وفي قائمة الأحدَث محادثات ومكالمات سيجنال، مناوبة الاتصالات دوما، والمُرسِل المختوم المُهلة الافتراضية للدردشات الجديدة @@ -5058,11 +5092,14 @@ جودة الوسائط جودة الوسائط المرسلة إن إرسال وسائط بجودة عالية سوف يستخدم كما أكبر من البيانات. + بالغ الأهميّة + قياسي المكالمات + تلقائي استخدام ألوان مخصصة لون الدردشة @@ -5286,6 +5323,7 @@ التقط صورة اختيار صورة صورة + نص حفظ محو صورة الحساب @@ -5362,7 +5400,7 @@ إضافة نص إضافة رد إرسال إلى - وسيط للعرض مرة واحدة + View once media عنصر واحد أو أكثر كان كبيرًا جدًا عنصر واحد أو أكثر غير صالح تم تحديد عدد كبير جدًا من العناصر @@ -5975,7 +6013,7 @@ ‏%1$s‏%2$s‏ أنت - + %1$s إلى %2$s الرَّدّ @@ -7288,5 +7326,11 @@ تعديل الملاحظة + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index ddc9a1306a..ca9166c594 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -415,6 +415,7 @@ Qoşul + Dolub Mesaj göndərmə xətası @@ -613,6 +614,7 @@ Arxivdən çıxar Sil Hamısını seç + %1$d seçildi %1$d seçildi @@ -864,6 +866,7 @@ Dəvət göndərildi %1$d dəvət göndərildi + \"%1$s\", sizin tərəfinizdən bu qrupa avtomatik olaraq əlavə edilə bilməz. Qoşulması üçün dəvət göndərildi və qəbul edənə qədər qrup mesajını görməyəcəklər. Bu istifadəçiləri bu qrupa avtomatik olaraq əlavə edə bilməzsiniz.\n\nQoşulmaq üçün dəvət göndərildi və qəbul edənə qədər qrup mesajlarını görməyəcəklər. @@ -1044,7 +1047,9 @@ Ada və şəklə düzəliş et Köhnə Qrup + Bu bir Köhnə Qrupdur. Qrup adminləri kimi özəlliklər, yalnız Yeni Qruplar üçün əlçatandır. + Bu bir Köhnə Qrupdur. @adçəkmələr və adminlər kimi yeni özəllikərə müraciət etmək üçün, Bu Köhnə Qrup çox böyük olduğu üçün yüksəldilə bilmir. Maksimum qrup ölçüsü %1$d. bu qrupu yüksəldin. @@ -1271,6 +1276,7 @@ Sil + %1$d seçildi (%2$s) %1$d seçildi (%2$s) @@ -1335,17 +1341,13 @@ Qrupu tərk etdiniz. Qrupu yenilədiniz. Qrup yeniləndi. - + Gedən audio zəng - + Gedən video zəng - - Cavablanmamış audio zəng - - Cavablanmamış video zəng - + Gələn audio zəng - + Gələn video zəng Buraxılmış audio zəng @@ -1355,10 +1357,6 @@ Profil statusu aktiv ikən buraxılmış səsli zəng Profil statusu aktiv ikən buraxılmış video zəng - - Bir audio zəngi rədd etdiniz - - Bir video zəngi rədd etdiniz %1$s · %2$s %1$s qrupu yenilədi. @@ -1567,30 +1565,55 @@ %1$s artıq Ödənişləri qəbul edə bilər - %1$s bir qrup zəngi başlatdı · %2$s - Qrup zənginə başlama vaxtınız · %1$s - %1$s qrup zəngindədir · %2$s - Qrup zəngindəsiniz · %1$s - %1$s və %2$s qrup zəngindədir · %3$s - Qrup zəngi · %1$s - - %1$s bir qrup zəngi başlatdı - Bir qrup zəngi başlatdınız - %1$s qrup zəngindədir - Qrup zəngindəsiniz - %1$s və %2$s qrup zəngindədir - Qrup zəngi + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Siz - - %1$s, %2$s və digər %3$d nəfər qrup zəngindədir · %4$s - %1$s, %2$s və digər %3$d nəfər qrup zəngindədir · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s və digər %3$d nəfər qrup zəngindədir - %1$s, %2$s və digər %3$d nəfər qrup zəngindədir + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Təsdiq kodunu tələb etmək mümkün deyil. Şəbəkə bağlantınızı yoxlayıb yenidən cəhd edin. Standart olmayan nömrə formatı + Daxil etdiyiniz nömrə (%1$s) standartlara cavab vermir.\n\nBunu nəzərdə tuturdunuz: %2$s? Signal Android - Telefon nömrə formatı @@ -2709,6 +2733,8 @@ Yüklənir Daha ətraflı Zəngə qoşul + + Call back Zəngə qayıt Zəng dolub Dostlarını dəvət et @@ -2773,6 +2799,7 @@ Zəngi tərk et Aşağıdakı şəxslər yenidən quraşdırmış və ya cihazlarını dəyişdirmiş ola bilər. Gizliliyin təmin olunduğuna əmin olmaq üçün güvənlik nömrənizi təsdiqləyin. Bax + Daha əvvəl təsdiqlənib @@ -3086,6 +3113,7 @@ İlkin Yüksək + Maksimum @@ -3446,6 +3474,7 @@ Göndərildi: %1$s %1$s, %2$s %2$s , %3$s %1$s + Alıcı Göndərən Ödəniş miqdarı və əməliyyat vaxtını əhatə edən əməliyyat təfərrüatları MobileCoin Ledger-in bir hissəsidir. @@ -3508,6 +3537,7 @@ Ödənişi təsdiqləyin Şəbəkə haqqı Təxminən %1$s + Bura Cəmi məbləğ Balans: %1$s @@ -4112,6 +4142,7 @@ Lövhəyə kopyalandı Admin + Təsdiqlə Rədd et @@ -4139,6 +4170,7 @@ Səsli mesaj · %1$s + %1$s - %2$s @@ -4185,6 +4217,7 @@ %1$s və %2$s qoşuldu %1$s, %2$s və %3$s qoşuldu %1$s, %2$s və digər %3$d nəfər qoşuldu + %1$s nəfər qaldı %1$s və %2$s nəfər qaldı %1$s, %2$s və %3$s nəfər qaldı @@ -4562,6 +4595,7 @@ Mesajlaşma Yox olan mesajlar Tətbiq təhlükəsizliyi + Sonuncular siyahısında və tətbiq daxilində ekran şəkillərini blokla Signal mesajları və zəngləri, zəng köçürmə və gizli göndərən Yeni çatlar üçün standart taymer @@ -4618,11 +4652,14 @@ Media keyfiyyəti Göndərilən media keyfiyyəti Yüksək keyfiyyətli media göndərərkən, daha çox verilənlər istifadə ediləcək. + Yüksək + Standard Zənglər + Avto Özəl rənglər istifadə et Çat ekranının rəngi @@ -4834,6 +4871,7 @@ Bir şəkil çəkin Bir foto seçin Foto + Mətn Saxla Avatarı təmizlə @@ -4894,7 +4932,7 @@ Bir mesaj əlavə et Bir cavab əlavə et Göndər - Bir dəfə baxıla bilən mesaj + View once media Bir və ya daha çox element çox böyük idi Bir və ya daha çox element etibarsız idi Həddindən çox element seçildi @@ -5487,7 +5525,7 @@ %1$s %2$s Siz - + %1$s - %2$s Cavabla @@ -6704,5 +6742,11 @@ Qeydi redaktə et + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index e313010cfb..65f40103a9 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -415,6 +415,7 @@ Присъедини се + Пълно Грешка при изпращане на медиен файл @@ -613,6 +614,7 @@ Разархивиране Изтриване Избери всичко + %1$d избрано %1$d избрани @@ -864,6 +866,7 @@ Поканата е изпратена Изпратени са %1$d покани + Не можете да добавите \"%1$s\" автоматично към тази група. Той/тя е получил/а покана за присъединяване и няма да вижда съобщенията в групата, преди да приеме. Не можете да добавите тези потребители автоматично към групата. Те са получили покани да се присъединят и няма да виждат съобщенията в групата, преди да приемат. @@ -1044,7 +1047,9 @@ Редактирайте име и снимка Стар вид група + Това е група от стар вид. Функции като администратори на групи са достъпни само за нов вид групи. + Това е група от стар вид. За достъп до нови функции като @споменавяния и администратори, Тази група не може да бъде обновена до Нова, защото е прекалено голяма. Максиналният размер на група е %1$d. Обновете тази група @@ -1271,6 +1276,7 @@ Изтриване + %1$d избрано (%2$s) %1$d избрани (%2$s) @@ -1335,17 +1341,13 @@ Напуснахте групата. Обновихте групата. Групата беше актуализирана. - + Изходящо гласово повикване - + Изходящо видео повикване - - Гласово повикване без отговор - - Видео повикване без отговор - + Входящо гласово повикване - + Входящо видео повикване Пропуснато гласово повикване @@ -1355,10 +1357,6 @@ Пропуснато гласово обаждане при включен профил за известия Пропуснато видео обаждане при включен профил за известия - - Отказахте гласово повикване - - Отказахте видео повикване %1$s · %2$s %1$s обнови групата. @@ -1567,30 +1565,55 @@ %1$s вече може да приема Плащания - %1$s започна групов разговор · %2$s - Започнахте групово повикване · %1$s - %1$s е в групов разговор · %2$s - Вие сте в групов разговор · %1$s - %1$s и %2$s са в групов разговор · %3$s - Групов разговор · %1$s - - %1$s започна групов разговор - Започнахте групово повикване - %1$s е в груповия разговор - Вие сте в груповия разговор - %1$s и %2$s са в груповия разговор - Групов разговор + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Вие - - %1$s, %2$s и %3$d друг са в груповия разговор · %4$s - %1$s, %2$s и %3$d други са в груповия разговор · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s и %3$d друг са в груповия разговор - %1$s, %2$s и %3$d други са в груповия разговор + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Неуспешно искане на код за потвърждение. Моля, проверете мрежовата си връзка и опитайте отново. Нестандартен формат на номера + Въведеният от вас номер (%1$s) изглежда е в нестандартен формат.\n\nДа нямате предвид %2$s? Signal Android – Формат на телефонен номер @@ -2709,6 +2733,8 @@ Зареждане Научете повече Присъединете се към разговор + + Call back Върнете се към разговора Обаждането е пълно Покани приятели @@ -2773,6 +2799,7 @@ Напусни обаждането Следните контакти може да са преинсталирали или сменили устройствата си. Потвърдете номерата за сигурност. Преглед + Предишно потвърдено @@ -3086,6 +3113,7 @@ По подразбиране Високо + Максимално @@ -3446,6 +3474,7 @@ Изпратено на %1$s От вас на %1$s в %2$s От %1$s на %2$s в %3$s + До От Данните за трансакцията, включително сумата на плащането и часът на трансакцията, са част от Счетоводната книга на MobileCoin. @@ -3508,6 +3537,7 @@ Потвърди плащане Мрежова такса Очаквано %1$s + До Обща сума Баланс: %1$s @@ -4112,6 +4142,7 @@ Копирано Администратор + Одобряване Охтвърляне @@ -4139,6 +4170,7 @@ Гласово съобщение · %1$s + %1$s до %2$s @@ -4185,6 +4217,7 @@ %1$s и %2$s се присъединиха %1$s, %2$s и %3$s се присъединиха %1$s, %2$s и %3$d други се присъединиха + %1$s напусна %1$s и %2$s напуснаха %1$s, %2$s и %3$s напуснаха @@ -4562,6 +4595,7 @@ Съобщения Изчезващи съобщения Сигурност + Блокиране на екранни снимки в списъка със скорошни разговори и в приложението Съобщения и повиквания в Signal, повикванията винаги да се предават и запечатан подател Таймер по подразбиране за нови чатове @@ -4618,11 +4652,14 @@ Качество на мултимедията Качество на изпратената мултимедия Изпращането на висококачествени мултимедийни файлове ще използва повече данни. + Високо + Стандартно Обаждания + Автоматично Използвай ръчно зададени цветове Цвят на чата @@ -4834,6 +4871,7 @@ Направи снимка Избери снимка Снимка + Текст Запази Премахни аватар @@ -4894,7 +4932,7 @@ Добави съобщение Добавяне на отговор Изпращане до - Съобщение за еднократно гледане + View once media Един или повече елементи бяха твърде големи Един или повече елементи бяха невалидни Избрани са твърде много елементи @@ -5487,7 +5525,7 @@ %1$s %2$s Вие - + %1$s до %2$s Отговори @@ -6704,5 +6742,11 @@ Редактиране на бележка + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 47b209c95e..d393ea0d68 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -415,6 +415,7 @@ যোগদান করুন + ভর্তি ছবি/ভিডিও পাঠাতে ত্রুটি @@ -613,6 +614,7 @@ আর্কাইভ মুক্ত মুছে ফেলুন সব নির্বাচন করুন + %1$d নির্বাচিত %1$d নির্বাচিত @@ -864,6 +866,7 @@ আমন্ত্রণ পাঠানো হয়েছে %1$dটি আমন্ত্রণ পাঠানো হয়েছে + আপনার দ্বারা এই গ্রুপে স্বয়ংক্রিয়ভাবে \"%1$s\" কে যুক্ত করা যাবে না।\n\n তাদের যোগদানের জন্য আমন্ত্রণ জানানো হয়েছে এবং তারা সেটি গ্রহণ না করা পর্যন্ত গ্রুপের কোনও বার্তা দেখতে পাবে না। আপনার দ্বারা এই গ্রুপে স্বয়ংক্রিয়ভাবে এই ব্যবহারকারীদের যুক্ত করা যাবে না।\n\n তাদের যোগদানের জন্য আমন্ত্রণ জানানো হয়েছে এবং তারা সেটি গ্রহণ না করা পর্যন্ত গ্রুপের কোনও বার্তা দেখতে পাবে না। @@ -1044,7 +1047,9 @@ নাম এবং ছবি সম্পাদনা করেন লিগ্যাসি গ্রুপ + এটি একটি লিগ্যাসি গ্রুপ। গ্রুপ এডমিন এর মতো বৈশিষ্ট্যগুলি কেবল নতুন গ্রুপগুলির জন্য উপলভ্য। + এটি একটি লিগ্যাসি গ্রুপ। @মেনশন এবং এডমিনের মতো নতুন বৈশিষ্ট্যগুলি অ্যাক্সেস করতে, এই লিগ্যাসি গ্রুপটি একটি নতুন গ্রুপে আপগ্রেড করা যাবে না কারণ এটি অনেক বড়। সর্বাধিক গ্রুপের আকার %1$d। এই গ্রুপটি আপগ্রেড করুন। @@ -1271,6 +1276,7 @@ মুছে ফেলুন + %1$d নির্বাচিত (%2$s) %1$d নির্বাচিত (%2$s) @@ -1335,17 +1341,13 @@ আপনি গ্রুপ ত্যাগ করেছেন। আপনি গ্রুপ আপডেট করেছেন। গ্রুপটি আপডেট করা হয়েছিল। - + ভয়েস কল যাচ্ছে - + ভিডিও কল যাচ্ছে - - উত্তর না দেওয়া ভয়েস কল - - উত্তর না দেওয়া ভিডিও কল - + ভয়েস কল আসছে - + ভিডিও কল আসছে মিসড ভয়েস কল @@ -1355,10 +1357,6 @@ নোটিফিকেশন প্রোফাইল চালু থাকা অবস্থায় মিসড ভয়েস কল নোটিফিকেশন প্রোফাইল চালু থাকা অবস্থায় মিসড ভিডিও কল - - আপনি একটি ভয়েস কল প্রত্যাখ্যান করেছেন - - আপনি একটি ভিডিও কল প্রত্যাখ্যান করেছেন %1$s · %2$s %1$s গ্রুপ আপডেট করেছে। @@ -1567,30 +1565,55 @@ %1$s এখন পেমেন্ট গ্রহণ করতে পারবেন - %1$s একটি গ্রুপ কল শুরু করেছে । %2$s - আপনি একটি গ্ৰুপ কল শুরু করেছেন · %1$s - %1$s গ্রুপ কল এ আছেন। %2$s - আপনি গ্রুপ কল এ আছেন। %1$s - %1$s এবং %2$s গ্রুপ কল এ আছেন। %3$s - গ্রুপ কল। %1$s - - %1$s একটি গ্রুপ কল শুরু করেছেন - আপনি একটি গ্রুপ কল শুরু করেছেন - %1$s একটি গ্রুপ কল এ আছেন - আপনি গ্রুপ কল এ আছেন - %1$s এবং %2$s গ্রুপ কল এ আছেন - গ্রুপ কল + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s আপনি - - %1$s, %2$s, এবং %3$d অন্যান্যরা গ্রুপ কল এ আছেন। %4$s - %1$s, %2$s, এবং%3$d অন্যান্যরা গ্রুপ কল এ আছেন। %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, ও %3$d জন গ্রুপ কলটিতে আছে - %1$s, %2$s ও আরও %3$d জন গ্রুপ কলটিতে আছেন + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ যাচাইকরণ কোডের জন্য অনুরোধ করতে সক্ষম নয়। অনুগ্রহ করে আপনার নেটওয়ার্ক সংযোগ ঠিক আছে কি না দেখুন এবং আবার চেষ্টা করুন। নন-স্ট্যান্ডার্ড নম্বর বিন্যাস + আপনি যে নম্বরটি প্রবেশ করেছেন (%1$s) সেটি একটি অ-মানক বিন্যাস বলে মনে হচ্ছে।\n\nআপনি কি %2$s বলতে চান? Signal অ্যান্ড্রয়েড - ফোন নম্বর ফর্ম্যাট @@ -2709,6 +2733,8 @@ লোড হচ্ছে আরও জানুন কলে যোগ দিন + + Call back কলে ফেরত যান কলটিতে জায়গা নেই বন্ধুদের আমন্ত্রণ @@ -2773,6 +2799,7 @@ কল ছেড়ে যান নিম্নোক্ত লোকেরা পুনরায় ইনস্টল করে থাকতে পারেন অথবা ডিভাইস বদলে ফেলতে পারে। গোপনীয়তা নিশ্চিত করতে আপনার নিরাপত্তা নাম্বার তাদের সাথে ভেরিফাই করুন। দেখান + আগে ভেরিফাই করা হয়েছে @@ -3086,6 +3113,7 @@ সচারচর উচ্চ + সর্বাধিক @@ -3446,6 +3474,7 @@ প্রাপক %1$s আপনি %1$s সময় %2$s %1$s তারিখ %2$s সময় %3$s + প্রতি থেকে লেনদেনের বিস্তারিত বিবরণের মধ্যে রয়েছে পেমেন্টের পরিমাণ এবং লেনদেনের সময় যা MobileCoin Ledger-এর একটি অংশ। @@ -3508,6 +3537,7 @@ পেমেন্ট নিশ্চিত করুন নেটওয়ার্ক ফিস আনুমানিক %1$s + প্রতি সর্বমোট পরিমাণ ব্যালেন্স: %1$s @@ -4112,6 +4142,7 @@ ক্লিপবোর্ডে অনুলিপি করা হয়েছে প্রশাসক + অনুমোদন প্রত্যাখ্যান @@ -4139,6 +4170,7 @@ ভয়েস বার্তা। %1$s + %1$s থেকে %2$s @@ -4185,6 +4217,7 @@ %1$s এবং %2$s যোগ দিয়েছে %1$s, %2$s এবং %3$s যোগ দিয়েছে %1$s, %2$s, এবং অন্যান্য %3$dজন যোগ দিয়েছে + %1$s চলে গেছে %1$s এবং %2$s চলে গেছে %1$s, %2$s এবং%3$s চলে গেছে @@ -4562,6 +4595,7 @@ বাদানুবাদ অদৃশ্য বার্তা অ্যাপ এর নিরাপত্তা + রিসেন্ট লিস্ট এবং অ্যাপের মধ্যে স্ক্রীনশট ব্লক করুন Signal মেসেজ ও কল করে, সবসময় কল রিলে করে এবং প্রেরককে সিল করে রাখে নতুন চ্যাটের জন্য ডিফল্ট টাইমার @@ -4618,11 +4652,14 @@ মিডিয়ার মান পাঠানো মিডিয়ার মান উচ্চ মানের মিডিয়া প্রেরণে আরও ডেটা ব্যবহার হবে। + উচ্চ + স্ট্যান্ডার্ড কল সমূহ + স্বয়ংক্রিয় কাস্টম রঙ ব্যবহার করুন চ্যাটের রং @@ -4834,6 +4871,7 @@ একটি ছবি তুলুন একটি ছবি চয়ন করুন ছবি + টেক্সট সংরক্ষন অবতার সাফ করুন @@ -4894,7 +4932,7 @@ একটি বার্তা যোগ করুন একটি জবাব যোগ করুন পাঠান - একবার বার্তা দেখুন + View once media এক বা একাধিক আইটেম খুব বড় ছিল৷ এক বা একাধিক আইটেম বাতিল ছিল অনেকগুলি আইটেম নির্বাচন করা হয়েছে @@ -5487,7 +5525,7 @@ %1$s %2$s আপনি - + %1$s থেকে %2$s প্রত্যুত্তর @@ -6704,5 +6742,11 @@ নোট এডিট করুন + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index b7bf374051..0c0b216a0b 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -421,6 +421,7 @@ Pristupi + Popunjeno Greška prilikom slanja priloga @@ -645,6 +646,7 @@ Dearhiviraj Izbriši Odaberi sve + %1$d stavka je odabrana %1$d stavke su odabrane @@ -902,6 +904,7 @@ %1$d pozivnica je poslano %1$d pozivnice su poslane + “%1$s” ne može preko Vas biti automatski uvršten/a u grupu.\n\nPozvan/a je da se pridruži i neće vidjeti grupne poruke sve dok ne prihvati pozivnicu. Ove osobe ne mogu se preko Vas automatski uvrstiti u grupu.\n\nPozvane su da se pridruže grupi i neće vidjeti grupne poruke sve dok ne prihvate pozivnicu. @@ -1114,7 +1117,9 @@ Unesite naziv i dodajte sliku Stara grupa + Ovo je stara grupa. Opcije poput grupnog administratora dostupne su samo za nove grupe. + Ovo je stara grupa. Da biste mogli koristiti opcije poput @spomena i administracije grupe, Ova grupa ne može se preinačiti u novu grupu jer je prevelika. Maksimum za grupu iznosi %1$d. nadogradi ovu grupu. @@ -1359,6 +1364,7 @@ Izbriši + %1$d odabrana stavka (%2$s) %1$d odabrane stavke (%2$s) @@ -1425,17 +1431,13 @@ Napustili ste grupu. Ažurirali ste grupu. Grupa je ažurirana. - + Odlazni glasovni poziv - + Odlazni video poziv - - Propušteni glasovni poziv - - Propušteni video poziv - + Dolazni glasovni poziv - + Dolazni video poziv Propušteni glasovni poziv @@ -1445,10 +1447,6 @@ Propušteni glasovni poziv dok je profil obavijesti uključen Propušteni glasovni poziv dok je profil obavijesti uključen - - Odbili ste glasovni poziv - - Odbili ste video poziv %1$s · %2$s %1$s je ažurirao/la grupu. @@ -1673,34 +1671,59 @@ %1$s sada može prihvatiti Plaćanja - %1$s je započeo/la grupni poziv · %2$s - Porenuli ste grupni poziv · %1$s - %1$s učestvuje u grupnom pozivu · %2$s - Učestvujete u grupnom pozivu · %1$s - %1$s i %2$s učestvuju u grupnom pozivu · %3$s - Grupni poziv · %1$s - - %1$s je započeo/la grupni poziv - Pokrenuli ste grupni poziv - %1$s učestvuje u grupnom pozivu - Učestvujete u grupnom pozivu - %1$s i %2$s učestvuju u grupnom pozivu - Grupni poziv + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Vi - - %1$s, %2$s i %3$d druga osoba učestvuje u grupnom pozivu · %4$s - %1$s, %2$s i %3$d druge osobe učestvuju u grupnom pozivu · %4$s - %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu · %4$s - %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s i %3$d druga osoba učestvuje u grupnom pozivu - %1$s, %2$s i %3$d druge osobe učestvuju u grupnom pozivu - %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu - %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Nije moguće zatražiti verifikacioni kȏd. Provjerite internet konekciju i pokušajte ponovo. Nestandardni oblik broja + Broj koji ste unijeli (%1$s) izgleda nije standardnog oblika.\n\nJeste li mislili na %2$s? Signal Android – oblik broja telefona @@ -2883,6 +2907,8 @@ Učitavam Saznajte više Pridruži se pozivu + + Call back Vrati se pozivu Poziv je popunjen Pozovi prijatelje @@ -2947,6 +2973,7 @@ Završi poziv Ove su osobe možda iznova instalirale Signal ili promijenile uređaj. Potvrdite svoj sigurnosni broj s njima da osigurate privatnost. Pregled + Ranije potvrđeno @@ -3274,6 +3301,7 @@ Standardno Visoki + Maksimalni @@ -3640,6 +3668,7 @@ Isplaćeno %1$s Vi, %1$s u %2$s %1$s, %2$s u %3$s + Za Od Detalji o transakciji, uključujući iznos i vrijeme transakcije, dio su MobileCoin registra. @@ -3702,6 +3731,7 @@ Potvrdi plaćanje Mrežna naknada Procijenjeno %1$s + Za Ukupni iznos Stanje: %1$s @@ -4324,6 +4354,7 @@ Kopirano u međuspremnik Administrator + Odobri Odbij @@ -4351,6 +4382,7 @@ Glasovna poruka · %1$s + %1$s do %2$s @@ -4401,6 +4433,7 @@ %1$s i %2$s su se pridružili %1$s, %2$s i %3$s su se pridružili %1$s, %2$s i %3$d drugih su se pridružili + %1$s je otišao/la %1$s i %2$s su otišli %1$s, %2$s i %3$s su otišli @@ -4782,6 +4815,7 @@ Poruke Nestajuće poruke Sigurnost aplikacije + Onemogući snimku zaslona u popisu nedavnih razgovora i unutar aplikacije Signal poruke i pozivi, preusmjeravanje poziva i zapečaćeni pošiljalac Predefinisani rok za nove chatove @@ -4838,11 +4872,14 @@ Kvalitet zapisa Kvalitet poslatih zapisa Slanje visokokvalitetnih zapisa kreirat će veći protok. + Visoki + Standardni Pozivi + Automatski Koristi druge boje Boja chata @@ -5060,6 +5097,7 @@ Uslikaj Izaberi fotografiju Fotografija + Tekst Pohrani Ukloni sličicu profila @@ -5128,7 +5166,7 @@ Napiši poruku Odgovor Pošalji za - Jednokratna poruka + View once media Jedna ili više datoteka su prevelike Jedna ili više datoteka nisu validne Označeno je previše datoteka @@ -5731,7 +5769,7 @@ %1$s %2$s Vi - + %1$s do %2$s Odgovori @@ -6972,7 +7010,7 @@ Nadimak - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Nadimci i bilješke se pohranjuju u Signalu i sveobuhvatno su šifrirani. Samo ih vi možete vidjeti. Ime @@ -6986,9 +7024,9 @@ Pohrani - Delete? + Izbrisati? - This will permanently delete any nickname and note you’ve set. + Ovim će se trajno izbrisati svi nadimci i bilješke koje ste postavili. @@ -6996,5 +7034,11 @@ Uredi bilješku + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 692f5971a0..2827bfab87 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -415,6 +415,7 @@ Afegeix-m\'hi + Ple Error en enviar contingut @@ -613,6 +614,7 @@ Restaura Eliminar Selecciona tot + %1$d de seleccionat %1$d de seleccionats @@ -864,6 +866,7 @@ Invitació enviada %1$d invitacions enviades + No podeu afegir automàticament %1$s a aquest grup. \n\nHan estat convidats a afegir-s\'hi i no veuran cap missatge del grup fins que no ho acceptin. No podeu afegir aquests usuaris automàticament a aquest grup.\n\nHan estat convidats a afegir-s\'hi i no veuran cap missatge del grup fins que ho acceptin. @@ -1044,7 +1047,9 @@ Editeu el nom i la imatge Grup de llegat + Aquest és un grup de llegat. Característiques com ara els administradors de grup només estan disponibles per als grups nous. + Aquest és un grup de llegat. Per accedir a funcions noves com ara les @mencions i als administradors, Aquest grup de llegat no es pot actualitzar a un grup nou perquè és massa gros. La mida màxima d\'un grup és %1$d. actualitzeu aquest grup. @@ -1271,6 +1276,7 @@ Suprimits + %1$d de seleccionat (%2$s) %1$d de seleccionats (%2$s) @@ -1335,17 +1341,13 @@ Heu abandonat el grup. Heu actualitzat el grup. S\'ha actualitzat el grup. - + Trucada sortint - + Videotrucada sortint - - Trucada sense contestar - - Videotrucada sense contestar - + Trucada entrant - + Videotrucada entrant Trucada perduda @@ -1355,10 +1357,6 @@ Trucada de veu perduda mentre els ajustos de notificació estaven activats Videotrucada perduda mentre els ajustos de notificació estaven activats - - Has rebutjat una trucada - - Has rebutjat una videotrucada %1$s · %2$s %1$s ha actualitzat el grup. @@ -1567,30 +1565,55 @@ %1$s ja pot acceptar Pagaments - %1$s ha iniciat una trucada de grup · %2$s - Has iniciat una trucada de grup · %1$s - %1$s és a la trucada de grup · %2$s - Sou a la trucada de grup · %1$s - %1$s i %2$s són a la trucada de grup · %3$s - Trucada de grup · %1$s - - %1$s ha iniciat una trucada de grup. - Heu iniciat una trucada de grup. - %1$s és a la trucada de grup. - Sou a la trucada de grup. - %1$s i %2$s són a la trucada de grup. - Trucada de grup + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Vós - - %1$s, %2$s i %3$d més són a la trucada de grup · %4$s - %1$s, %2$s i %3$d més són a la trucada de grup · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s i %3$d més són a la trucada de grup. - %1$s, %2$s i %3$d més són a la trucada de grup. + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ No s\'ha pogut sol·licitar un codi de verificació. Comprova la connexió de xarxa i torna-ho a provar. Format de número no estàndard + El número que heu introduït (%1$s) sembla d\'un format no estàndard.\n\nVoleu dir %2$s? Signal d\'Android. Format del número de telèfon @@ -2709,6 +2733,8 @@ Es carrega Més informació Afegeix-me a la trucada + + Call back Torna a la trucada La trucada és plena Convideu-hi amistats @@ -2773,6 +2799,7 @@ Surt de la trucada Aquesta gent podria haver reinstallar o dispositius canviats. Comproveu vostre número de seguretat amb ells, per assegurar la privadesa. Mostra + Verificat prèviament @@ -3086,6 +3113,7 @@ Per defecte Alta + Màxima @@ -3446,6 +3474,7 @@ Enviat a %1$s Vós a %1$s a %2$s %1$s a %2$s a %3$s + A De Les dades de la transacció, inclosos l\'import del pagament i el moment de la transacció, formen part de MobileCoin Ledger. @@ -3508,6 +3537,7 @@ Confirmeu el pagament Comissió de xarxa Estimació: %1$s + A Quantitat total Saldo: %1$s @@ -4112,6 +4142,7 @@ Copiat al porta-retalls Administrador + Aprova-la Rebutja-la @@ -4139,6 +4170,7 @@ Missatge de veu · %1$s + %1$s a %2$s @@ -4185,6 +4217,7 @@ %1$s i %2$s s\'hi han afegit. %1$s, %2$s i %3$s s\'hi han afegit. %1$s, %2$s i %3$d més s\'hi han afegit. + %1$s n\'ha sortit. %1$s i %2$s n\'han sortit. %1$s, %2$s i %3$s n\'han sortit. @@ -4562,6 +4595,7 @@ Missatges Missatges efímers Seguretat de l\'aplicació + Bloqueja les captures de pantalla a les llistes de recents i dins de l\'aplicació Missatges i trucades del Signal: sempre retransmissió de trucades i remitent segellat Temporitzador per defecte per als xats nous @@ -4618,11 +4652,14 @@ Qualitat dels mitjans Qualitat dels mitjans enviats L’enviament de mitjans d’alta qualitat usarà més dades. + Alta + Estàndard Trucades + Automàtic Usa els colors personalitzats Color del xat @@ -4834,6 +4871,7 @@ Fes una fotografia Trieu una fotografia Foto + Text Desa Neteja l\'avatar @@ -4894,7 +4932,7 @@ Afegiu-hi un missatge Afegiu-hi una resposta Enviar a - Mostra el missatge un cop + View once media Un o més elements eren massa grossos. Un o més elements no eren vàlids. Massa elements seleccionats @@ -5487,7 +5525,7 @@ %1$s %2$s Vós - + %1$s a %2$s Respon @@ -6680,7 +6718,7 @@ Sobrenom - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Els sobrenoms i les notes es guarden a Signal i estan xifrats d\'extrem a extrem. Només els podràs veure tu. Nom @@ -6694,9 +6732,9 @@ Desar - Delete? + Eliminar? - This will permanently delete any nickname and note you’ve set. + Això eliminarà permanentment qualsevol sobrenom i nota que hagis establert. @@ -6704,5 +6742,11 @@ Editar nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index e8e6391984..3d04178ef8 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -421,6 +421,7 @@ Připojit + Plno Chyba při odesílání médií @@ -645,6 +646,7 @@ Nearchivovat Smazat Vybrat vše + %1$d vybrán %1$d vybráno @@ -902,6 +904,7 @@ %1$d pozvánek odesláno %1$d pozvánek odesláno + “%1$s” nemůže být přidán do skupiny automaticky.\n\nByl pozván, ale neuvidí žádné zprávy, dokud pozvání nepřijme. Tyto uživatele nemůžete přidat do skupiny automaticky.\n\nByli pozváni, ale neuvidí žádné zprávy, dokud pozvání nepřijmou. @@ -1114,7 +1117,9 @@ Upravit název a obrázek Starší skupina + Toto je starší verze skupiny. Funkce jako správci skupin jsou dostupné pouze pro nové skupiny. + Toto je starší verze skupiny. Pro přístup k novým funkcím jako @zmínky a správci, Tuto starší verzi skupiny nelze aktualizovat na novou skupinu, protože je příliš velká. Maximální velikost skupiny je %1$d. aktualizovat tuto skupinu. @@ -1359,6 +1364,7 @@ Odstranit + %1$d vybraný (%2$s) %1$d vybrané (%2$s) @@ -1425,17 +1431,13 @@ Opustili jste skupinu Upravili jste skupinu. Skupina byla aktualizována. - + Odchozí hlasový hovor - + Odchozí videohovor - - Nepřijatý hlasový hovor - - Nepřijatý videohovor - + Příchozí hlasový hovor - + Příchozí videohovor Zmeškaný hlasový hovor @@ -1445,10 +1447,6 @@ Zmeškaný hlasový hovor při zapnutém profilu oznámení Zmeškaný videohovor při zapnutém profilu oznámení - - Odmítli jste hlasový hovor - - Odmítli jste videohovor %1$s . %2$s %1$s upravil skupinu. @@ -1673,34 +1671,59 @@ %1$s teď může přijímat Platby - %1$s zahájil skupinový hovor · %2$s - Zahájili jste skupinový hovor · %1$s - %1$s se účastní skupinového hovoru · %2$s - Účastníte se skupinového hovoru · %1$s - %1$s a %2$s se účastní skupinového hovoru · %3$s - Skupinový hovor · %1$s - - %1$s zahájil skupinový hovor - Zahájili jste skupinový hovor - %1$s se účastní skupinového hovoru - Účastníte se skupinového hovoru - %1$s a %2$s se účastní skupinového hovoru - Skupinový hovor + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Vy - - %1$s, %2$s a %3$d další se účastní skupinového hovoru · %4$s - %1$s, %2$s a %3$d další se účastní skupinového hovoru · %4$s - %1$s, %2$s a %3$d dalších se účastní skupinového hovoru · %4$s - %1$s, %2$s a %3$d dalších se účastní skupinového hovoru · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s a %3$d další se účastní skupinového hovoru - %1$s, %2$s a %3$d dalších se účastní skupinového hovoru - %1$s, %2$s a %3$d dalších se účastní skupinového hovoru - %1$s, %2$s a %3$d dalších se účastní skupinového hovoru + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Ověřovací kód nelze vyžádat. Zkontrolujte připojení k internetu a zkuste to znovu. Nestandardní formát čísla + Zadané číslo (%1$s) má zřejmě nestandardní formát.\n\nMysleli jste %2$s? Signal pro Android - Formát telefonního čísla @@ -2883,6 +2907,8 @@ Načítám Zjistit více Připojit se k hovoru + + Call back Vrátit se k hovoru Hovor je plný Pozvat přátele @@ -2947,6 +2973,7 @@ Opustit hovor Následující lidé možná přeinstalovali nebo změnili zařízení. Ověřte s nimi svoje bezpečnostní číslo pro zajištění soukromí. Zobrazit + Předchozí ověřeno @@ -3274,6 +3301,7 @@ Výchozí Vysoká + Max @@ -3640,6 +3668,7 @@ Odesláno pro %1$s Vy na %1$s v %2$s %1$s na %2$s v %3$s + Komu Od Podrobnosti transakce, včetně částky platby a času transakce, jsou součástí knihy MobileCoin. @@ -3702,6 +3731,7 @@ Potvrdit platbu Poplatek sítě Odhadováno %1$s + Komu Celková částka Zůstatek: %1$s @@ -4324,6 +4354,7 @@ Zkopírováno do schránky Správce + Schválit Odmítnout @@ -4351,6 +4382,7 @@ Hlasová zpráva · %1$s + %1$s pro %2$s @@ -4401,6 +4433,7 @@ %1$s a %2$s se připojili %1$s, %2$s a %3$s se připojili. %1$s, %2$s, %3$d a další se připojili + %1$s odešel %1$s a %2$s odešli %1$s, %2$s a %3$s odešli @@ -4782,6 +4815,7 @@ Odesílání zpráv Mizející zprávy Zabezpečení aplikace + Blokovat snímky obrazovky v seznamu posledních položek a uvnitř aplikace Signal zprávy a hovory, vždy předávat hovory, a utajený odesílatel. Výchozí časovač pro nové chaty @@ -4838,11 +4872,14 @@ Kvalita médií Kvalita odeslaných médií Odesílání médií ve vysoké kvalitě spotřebovává více dat. + Vysoká + Standard Volání + Auto Použít vlastní barvy Barva chatu @@ -5060,6 +5097,7 @@ Vyfotit Vybrat fotku Fotografie + Text Uložit Smazat avatar @@ -5128,7 +5166,7 @@ Přidat zprávu Přidat odpověď Odeslat uživateli - Zpráva pro jednorázové zobrazení + View once media Jeden nebo více souborů je moc velkých Jeden nebo více souborů je neplatných Příliš mnoho vybraných položek @@ -5731,7 +5769,7 @@ %1$s %2$s Vy - + %1$s pro %2$s Odpověď @@ -6996,5 +7034,11 @@ Upravit poznámku + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index cece668daf..8306b72af6 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -415,6 +415,7 @@ Deltag + Fuld Fejl ved afsendelse af mediefil @@ -613,6 +614,7 @@ Flyt fra arkiv Slet Vælg alle + %1$d valgt %1$d valgte @@ -864,6 +866,7 @@ Invitation sendt %1$d invitationer sendt + %1$s kan ikke automatisk tilføjes til gruppen af dig. \n\nDe er blevet inviteret til at deltage, og kan ikke se nogen gruppebeskeder, før de accepterer. Disse brugere kan ikke automatisk tilføjes til gruppen af dig. De er blevet inviteret til at deltage, og kan ikke se gruppebeskeder før de accepterer. @@ -1044,7 +1047,9 @@ Redigér navn og billede Forældet gruppe + Dette er en forældret gruppe. Funktioner som gruppeadministratorer, er kun tilgængelige for nye grupper + Dette er en forældet gruppe. For adgang til nye funktioner som @omtaler og administrator, Denne forældede gruppe er for stor til at kunne opgraderes til en Ny gruppe. Den maksimale størrelse på grupper er %1$d opgrader denne gruppe. @@ -1271,6 +1276,7 @@ Slet + %1$d valgt (%2$s) %1$d valgte (%2$s) @@ -1335,17 +1341,13 @@ Du har forladt gruppen. Du opdaterede gruppen. Gruppen blev opdateret. - + Udgående stemmeopkald - + Udgående videoopkald - - Ubesvaret stemmeopkald - - Ubesvaret videoopkald - + Indgående stemmeopkald - + Indgående videoopkald Ubesvaret stemmeopkald @@ -1355,10 +1357,6 @@ Ubesvaret taleopkald, mens notifikationsprofilen var aktiveret Ubesvaret videoopkald, mens notifikationsprofilen var aktiveret - - Du afviste et stemmeopkald - - Du afviste et videoopkald %1$s · %2$s %1$s opdaterede gruppen. @@ -1567,30 +1565,55 @@ %1$s kan nu acceptere betalinger - %1$s startede et gruppeopkald · %2$s - Du startede et gruppeopkald · %1$s - %1$s er med i gruppeopkaldet · %2$s - Du er med i gruppeopkaldet · %1$s - %1$s og %2$s er med i gruppeopkaldet · %3$s - Gruppeopkald · %1$s - - %1$s startede et gruppeopkald - Du startede et gruppeopkald - %1$s er med i gruppeopkaldet - Du er med i gruppeopkaldet - %1$s og %2$s er med i gruppeopkaldet - Gruppeopkald + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Dig - - %1$s, %2$s, og %3$d andre deltager i gruppesamtalen · %4$s - %1$s, %2$s, og %3$d andre er med i gruppeopkaldet · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, og %3$d andre deltager i gruppesamtalen - %1$s, %2$s, og %3$d andre er med i gruppeopkaldet + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Kunne ikke anmode om verificeringskoden. Tjek din netværksforbindelse, og prøv igen. Ikke-standardiseret nummerformat + Det nummer du har indtastet (%1$s) ser ud til at være et ikke-standardiseret format.\n\nMente du %2$s? Signal Android - Telefonnummerformat @@ -2709,6 +2733,8 @@ Henter Læs mere Deltag i opkald + + Call back Tilbage til opkald Opkald er fuld Inviter venner @@ -2773,6 +2799,7 @@ Forlad opkaldet Følgende personer kan have geninstalleret eller skiftet enheder. Bekræft dit sikkerhedsnummer med dem for at sikre privatlivets fred. Vis + Tidligere bekræftet @@ -3086,6 +3113,7 @@ Standard Høj + Max. @@ -3446,6 +3474,7 @@ Sendt til %1$s Dig på %1$s ved %2$s %1$s på %2$s kl. %3$s + Til Fra Transaktionsoplysninger, herunder betalingsbeløb og tidspunkt for transaktionen, er en del af MobileCoin-registret. @@ -3508,6 +3537,7 @@ Bekræft betaling Netværksgebyr Anslået %1$s + Til Samlet beløb Saldo: %1$s @@ -4112,6 +4142,7 @@ Kopieret til udklipsholder Administrator + Godkend Afvis @@ -4139,6 +4170,7 @@ Talebesked · %1$s + %1$s til %2$s @@ -4185,6 +4217,7 @@ %1$s og %2$s sluttede sig til opkaldet %1$s, %2$s og %3$s sluttede sig til opkaldet %1$s, %2$s og %3$d andre sluttede sig til opkaldet + %1$s forlod opkaldet %1$s og %2$s forlod opkaldet %1$s, %2$s og %3$s forlod opkaldet @@ -4562,6 +4595,7 @@ Beskeder Forsvindende beskeder App-sikkerhed + Bloker muligheden for skærmbilleder i oversigten over senest anvendte apps og i Signal-appen Signal-beskeder og opkald, videresend altid opkald og forseglet afsender. Standardudløbstid for nye chats @@ -4618,11 +4652,14 @@ Mediekvalitet Kvalitet af sendte mediefiler Afsendelse af mediefiler i høj kvalitet bruger mere data. + Høj + Standard Opkald + Auto Brug tilpassede farver Chatfarve @@ -4834,6 +4871,7 @@ Tag et billede Vælg et billede Billede + Tekst Gem Fjern avatar @@ -4894,7 +4932,7 @@ Tilføj en besked Tilføj et svar Send til - Vis besked én gang + View once media Et eller flere emner var for store Et eller flere emner var ugyldige Der er valgt for mange emner @@ -5487,7 +5525,7 @@ %1$s %2$s Dig - + %1$s til %2$s Besvar @@ -6704,5 +6742,11 @@ Rediger note + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f09c49dcef..c682bb5197 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -415,6 +415,7 @@ Beitreten + Voll Fehler beim Senden der Medieninhalte @@ -613,6 +614,7 @@ Dearchivieren Löschen Alle auswählen + %1$d ausgewählt %1$d ausgewählt @@ -722,7 +724,7 @@ Personalisiert: %1$s Standard: %1$s - Kein Eintrag + Keine Für das Aufnehmen eines Fotos ist die Kamera-Berechtigung erforderlich. @@ -864,8 +866,9 @@ Einladung versendet %1$d Einladungen versendet + »%1$s« kann von dir nicht automatisch zu dieser Gruppe hinzugefügt werden.\n\nDer Nutzer wurde eingeladen beizutreten, wird aber keine Gruppennachrichten erhalten, bis er die Einladung angenommen hat. - Diese Nutzer können von dir nicht automatisch zu dieser Gruppe hinzugefügt werden.\n\nSie wurden eingeladen, der Gruppe beizutreten, werden aber keine Gruppennachrichten erhalten, bis sie die Einladung angenommen haben. + Diese Personen können von dir nicht automatisch zu dieser Gruppe hinzugefügt werden.\n\nSie wurden eingeladen, der Gruppe beizutreten, werden aber keine Gruppennachrichten erhalten, bis sie die Einladung angenommen haben. Was sind Gruppen neuen Typs? @@ -974,9 +977,9 @@ Einladungen Von dir eingeladene Personen Du hast keine ausstehenden Einladungen. - Von Gruppenmitgliedern Eingeladene + Von Gruppenmitgliedern eingeladen Keine ausstehenden Einladungen anderer Gruppenmitglieder. - Du kannst keine Details zu von anderen Gruppenmitgliedern eingeladenen Personen ansehen. Erst wenn die Eingeladenen der Gruppe beitreten, sind ihre Details für dich und die anderen Gruppenmitglieder einsehbar. Bis zum Beitritt erhalten die Eingeladenen auch keine Gruppennachrichten. + Details der von anderen Gruppenmitgliedern eingeladenen Personen werden nicht angezeigt. Erst wenn die Eingeladenen der Gruppe beitreten, sind ihre Details für dich und die anderen Gruppenmitglieder einsehbar. Bis zum Beitritt erhalten die Eingeladenen auch keine Gruppennachrichten. Einladung widerrufen Einladungen widerrufen @@ -1007,7 +1010,7 @@ Mitglieder hinzufügen - Gib dieser Gruppe einen Namen + Gruppennamen wählen Gruppe erstellen Erstellen Mitglieder @@ -1044,7 +1047,9 @@ Name und Bild bearbeiten Gruppe alten Typs + Dies ist eine Gruppe alten Typs. Funktionen wie Gruppen-Admins sind nur in Gruppen neuen Typs verfügbar. + Dies ist eine Gruppe alten Typs. Für neue Funktionen wie @Erwähnungen und Admins, Diese Gruppe alten Typs kann nicht zu einer Gruppe neuen Typs aktualisiert werden. Die maximale Gruppengröße von %1$d Mitgliedern ist überschritten. aktualisiere diese Gruppe. @@ -1054,7 +1059,7 @@ Gruppenbeschreibung hinzufügen … - Mich über Erwähnungen benachrichtigen + Mich bei Erwähnungen benachrichtigen Benachrichtigungen erhalten, wenn du in stummgeschalteten Chats erwähnt wirst? Immer benachrichtigen Nicht benachrichtigen @@ -1171,13 +1176,13 @@ Aktualisiere Signal zum Verwenden von Gruppen-Links - Die von dir verwendete Signal-Version unterstützt nicht diesen Gruppen-Link. Aktualisiere auf die neueste Version, um dieser Gruppe über einen Link beizutreten. + Die von dir verwendete Signal-Version unterstützt diesen Gruppen-Link nicht. Aktualisiere auf die neueste Version, um dieser Gruppe über einen Link beizutreten. Signal aktualisieren Gruppen-Link ist ungültig Freunde einladen - Teile einen Link mit Freunden, damit sie dieser Gruppe einfach beitreten können. + Teile einen Link mit Personen, damit sie dieser Gruppe einfach beitreten können. Link aktivieren und teilen Link teilen @@ -1208,7 +1213,7 @@ Teilen - Mit Kontakten teilen (SMS) + Mit Kontakten teilen Teilen über … Abbrechen @@ -1271,6 +1276,7 @@ Löschen + %1$d ausgewählt (%2$s) %1$d ausgewählt (%2$s) @@ -1289,7 +1295,7 @@ Später erinnern - Überprüfung deiner Signal-PIN + Signal-PIN überprüfen Wir fragen dich gelegentlich nach deiner PIN, damit du sie nicht vergisst. PIN überprüfen Leg los @@ -1302,7 +1308,7 @@ Antworten - Signal-Anruf wird hergestellt + Signal-Anruf wird gestartet Den Signal Anrufdienst starten Signals Anrufdienst wird gestoppt @@ -1310,7 +1316,7 @@ Benachrichtigungen einschalten? - Versäume nie eine Nachricht deiner Kontakte und Gruppen. + Versäume keine Nachricht deiner Kontakte und Gruppen. Einschalten Jetzt nicht @@ -1335,17 +1341,13 @@ Du hast die Gruppe verlassen. Du hast die Gruppe aktualisiert. Die Gruppe wurde aktualisiert. - + Ausgehender Sprachanruf - + Ausgehender Videoanruf - - Unbeantworteter Sprachanruf - - Unbeantworteter Videoanruf - + Eingehender Sprachanruf - + Eingehender Videoanruf Verpasster Sprachanruf @@ -1355,10 +1357,6 @@ Verpasster Sprachanruf bei aktiviertem Benachrichtigungsprofil Verpasster Videoanruf bei aktiviertem Benachrichtigungsprofil - - Du hast einen Sprachanruf abgelehnt - - Du hast einen Videoanruf abgelehnt %1$s · %2$s %1$s hat die Gruppe aktualisiert. @@ -1391,7 +1389,7 @@ Du hast die Gruppe erstellt. Gruppe aktualisiert. - Lade mit einem Gruppen-Link Freunde zu dieser Gruppe ein. + Lade mit einem Gruppen-Link Personen zu dieser Gruppe ein. Du hast %1$s hinzugefügt. @@ -1567,30 +1565,55 @@ %1$s kann Zahlungen jetzt akzeptieren. - %1$s hat einen Gruppenanruf begonnen · %2$s - Du hast einen Gruppenanruf gestartet · %1$s - %1$s nimmt am Gruppenanruf teil · %2$s - Du nimmst am Gruppenanruf teil · %1$s - %1$s und %2$s nehmen am Gruppenanruf teil · %3$s - Gruppenanruf · %1$s - - %1$s hat einen Gruppenanruf gestartet - Du hast einen Gruppenanruf gestartet - %1$s nimmt am Gruppenanruf teil - Du nimmst am Gruppenanruf teil - %1$s und %2$s nehmen am Gruppenanruf teil - Gruppenanruf + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Du - - %1$s, %2$s und %3$d weitere Person nehmen am Gruppenanruf teil · %4$s - %1$s, %2$s und %3$d weitere Personen nehmen am Gruppenanruf teil · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s und %3$d weitere Person nehmen am Gruppenanruf teil - %1$s, %2$s und %3$d weitere Personen nehmen am Gruppenanruf teil + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Ein Verifizierungscode kann nicht angefordert werden. Bitte überprüfe deine Netzverbindung und versuche es erneut. Nicht standardisiertes Zahlenformat + Die eingegebene Telefonnummer (%1$s) scheint nicht im Standardformat zu sein.\n\nHast du %2$s gemeint? Signal Android - Telefonnummernformat @@ -2709,6 +2733,8 @@ Wird geladen … Mehr erfahren Anruf beitreten + + Call back Zurück zum Anruf Anruf ist voll Freunde einladen @@ -2773,6 +2799,7 @@ Anruf verlassen Die folgenden Personen haben Signal vielleicht erneut installiert oder das Gerät gewechselt. Verifiziert eure gemeinsame Sicherheitsnummer zur Sicherstellung der Privatsphäre. Anzeigen + Zuvor verifiziert @@ -3086,6 +3113,7 @@ Standard Hoch + Maximal @@ -3446,6 +3474,7 @@ Gesendet an %1$s Du am %1$s um %2$s %1$s am %2$s um %3$s + An Von Transaktionsdetails wie Zahlbetrag und Transaktionszeit sind Teil des MobileCoin-Kontenblatts. @@ -3508,6 +3537,7 @@ Zahlung bestätigen Netzwerkgebühr Geschätzt: %1$s + An Gesamtbetrag Kontostand: %1$s @@ -4112,6 +4142,7 @@ In Zwischenablage kopiert Admin + Bestätigen Ablehnen @@ -4139,6 +4170,7 @@ Sprachnachricht · %1$s + %1$s an %2$s @@ -4185,6 +4217,7 @@ %1$s und %2$s sind beigetreten %1$s, %2$s und %3$s sind beigetreten %1$s, %2$s und %3$d weitere Personen sind beigetreten + %1$s ist ausgetreten %1$s und %2$s sind ausgetreten %1$s, %2$s und %3$s sind ausgetreten @@ -4562,6 +4595,7 @@ Nachrichtenübermittlung Verschwindende Nachrichten App-Sicherheit + Bildschirmfotos in der Anwendung und der Liste der letzten Anwendungen blockieren Signal-Nachrichten und -Anrufe, Anrufe immer indirekt und Vertraulicher Absender Standardablaufzeit für neue Chats @@ -4618,11 +4652,14 @@ Medienqualität Medienqualität für Versand Das Senden von Medieninhalten hoher Qualität verbraucht mehr Datenvolumen. + Hoch + Standard Anrufe + Auto Personalisierte Farben Chat-Farbe @@ -4834,6 +4871,7 @@ Nimm ein Foto auf Wähle ein Foto Foto + Text Speichern Avatar löschen @@ -4894,7 +4932,7 @@ Füg eine Nachricht hinzu Antwort hinzufügen Senden an - Einmalig anzeigbare Nachricht + View once media Mindestens ein Element war zu groß Mindestens ein Element war ungültig Zu viele Elemente ausgewählt @@ -5487,7 +5525,7 @@ %1$s %2$s Du - + %1$s an %2$s Antworten @@ -6680,7 +6718,7 @@ Spitzname - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Spitznamen und Notizen werden mit Signal gespeichert und sind Ende-zu-Ende-verschlüsselt. Sie sind nur für dich sichtbar. Vorname @@ -6694,9 +6732,9 @@ Speichern - Delete? + Löschen? - This will permanently delete any nickname and note you’ve set. + Dadurch werden diese Spitznamen und Notizen unwiderruflich gelöscht. @@ -6704,5 +6742,11 @@ Notiz bearbeiten + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 1bbac05455..17e186f78a 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -415,6 +415,7 @@ Μπες στην κλήση + Γεμάτο Σφάλμα στην αποστολή πολυμέσων @@ -613,6 +614,7 @@ Επαναφορά Διαγραφή Επιλογή όλων + %1$d επιλεγμένο %1$d επιλεγμένα @@ -864,6 +866,7 @@ Στάλθηκε πρόσκληση Στάλθηκαν %1$d προσκλήσεις + Δεν μπορείς να προσθέσεις τον/την \"%1$s\" αυτόματα στην ομάδα.\n\nΈχει λάβει πρόσκληση, και δεν θα βλέπει τα μηνύματα της ομάδας μέχρι να την αποδεχτεί. Δεν μπορείς να προσθέσεις αυτόματα αυτούς τους χρήστες στην ομάδα.\n\nΈχουν λάβει πρ΄σκληση, και δεν θα βλέπουν τα μηνύματα της ομάδας μέχρι να την αποδεχτούν. @@ -1044,7 +1047,9 @@ Επεξεργασία ονόματος και φωτογραφίας Ομάδα παλαιού τύπου + Αυτή είναι ομάδα παλαιού τύπου. Δυνατότητες όπως οι διαχειριστές ομάδας είναι διαθέσιμα μόνο στις ομάδες νέου τύπου. + Αυτή είναι μια ομάδα παλαιού τύπου. Για πρόσβαση σε δυνατότητες όπως οι @αναφορές και οι διαχειριστές ομάδας, Αυτή η ομάδα παλαιού τύπου δεν μπορεί να αναβαθμιστεί σε Ομάδα νέου τύπου επειδή είναι πολύ μεγάλη. Το μέγιστο μέγεθος της ομάδας είναι %1$d. αναβάθμισε την ομάδα. @@ -1271,6 +1276,7 @@ Διαγραφή + %1$d επιλεγμένο (%2$s) %1$d επιλεγμένα (%2$s) @@ -1335,17 +1341,13 @@ Αποχώρησες απ\' την ομάδα. Ενημέρωσες την ομάδα Η ομάδα ανανεώθηκε. - + Εξερχόμενη κλήση - + Εξερχόμενη βιντεοκλήση - - Αναπάντητη κλήση - - Αναπάντητη βιντεοκλήση - + Εισερχόμενη κλήση - + Εισερχόμενη βιντεοκλήση Αναπάντητη κλήση @@ -1355,10 +1357,6 @@ Αναπάντητη φωνητική κλήση ενώ είναι ενεργό το προφίλ ειδοποιήσεων Αναπάντητη βιντεοκλήση ενώ είναι ενεργό το προφίλ ειδοποιήσεων - - Απέρριψες μια κλήση - - Απέρριψες μια βιντεοκλήση %1$s · %2$s Ο/Η %1$s ενημέρωσε την ομάδα. @@ -1567,30 +1565,55 @@ Ο χρήστης %1$s μπορεί να αποδεχτεί πληρωμές - Ο/Η %1$s ξεκίνησε μια ομαδική κλήση · %2$s - Ξεκίνησες μια ομαδική κλήση · %1$s - Ο/Η %1$s είναι στην ομαδική κλήση · %2$s - Είσαι στην ομαδική κλήση · %1$s - Οι %1$s και %2$s είναι στην ομαδική κλήση · %3$s - Ομαδική κλήση · %1$s - - Ο/Η %1$s ξεκίνησε μια ομαδική κλήση - Ξεκίνησες μια ομαδική κλήση - Ο/Η %1$s είναι στην ομαδική κλήση - Είσαι στην ομαδική κλήση - Οι %1$s και %2$s είναι στην ομαδική κλήση - Ομαδική κλήση + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Εσύ - - Οι %1$s, %2$s και %3$d ακόμα είναι στην κλήση · %4$s - Οι %1$s, %2$s και %3$d ακόμα είναι στην ομαδική κλήση · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - Οι %1$s, %2$s και %3$d ακόμα είναι στην ομαδική κλήση - Οι %1$s, %2$s και %3$d ακόμα είναι στην ομαδική κλήση + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Αδυναμία αίτησης κωδικού επαλήθευσης. Έλεγξε τη σύνδεση στο δίκτυο και προσπάθησε ξανά. Μη-τυπική μορφή αριθμού + Ο αριθμός που έγραψες (%1$s) φαίνεται να μην έχει μια τυπική μορφή.n\nΜήπως εννοούσες %2$s; Signal Android - Μορφή αριθμού τηλεφώνου @@ -2709,6 +2733,8 @@ Φορτώνει Μάθε περισσότερα Είσοδος στην κλήση + + Call back Επιστροφή στην κλήση Η κλήση είναι γεμάτη Πρόσκληση φίλων @@ -2773,6 +2799,7 @@ Αποχώρηση από την κλήση Τα παρακάτω άτομα ίσως επανεγκατεστησαν το Signal ή άλλαξαν συσκευή. Επιβεβαίωσε τους αριθμούς ασφαλείας με αυτά, για να διασφαλίσεις την ιδιωτικότητα. Εμφάνιση + Προηγουμένως επιβεβαιωμένοι @@ -3086,6 +3113,7 @@ Προκαθορισμένη Υψηλή + Μέγιστη @@ -3446,6 +3474,7 @@ Στάλθηκε σε %1$s Εσείς στις %1$s στις %2$s %1$s στις %2$s στις %3$s + Προς Από Τα στοιχεία της συναλλαγής, συμπεριλαμβανομένου του ποσού πληρωμής και του χρόνου συναλλαγής, αποτελούν μέρος του MobileCoin Ledger. @@ -3508,6 +3537,7 @@ Επιβεβαίωση πληρωμής Χρέωση δικτύου Εκτιμώμενο %1$s + Προς Συνολικό ποσό Υπόλοιπο: %1$s @@ -4112,6 +4142,7 @@ Αντιγράφηκε στο πρόχειρο Διαχειριστής/τρια + Έγκριση Απόρριψη @@ -4139,6 +4170,7 @@ Μήνυμα φωνής · %1$s + %1$s προς %2$s @@ -4185,6 +4217,7 @@ Οι %1$s και %2$s μπήκαν Οι %1$s, %2$s και %3$s μπήκαν Οι %1$s, %2$s και %3$d ακόμα μπήκαν + Ο/Η %1$s έφυγε Οι %1$s και %2$s έφυγαν Οι %1$s, %2$s και %3$s έφυγαν @@ -4562,6 +4595,7 @@ Συνομιλίες Μηνύματα που εξαφανίζονται Ασφάλεια εφαρμογής + Να μην επιτρέπονται τα στιγμιότυπα οθόνης (screenshots) στη λίστα με τα πρόσφατα και μέσα στην εφαρμογή Κλήσεις και μηνύματα Signal, κλήσεις που πάντα αναμεταδίδονται, και προστατευμένος αποστολέας Προκαθορισμένο χρονόμετρο για νέες συνομιλίες @@ -4618,11 +4652,14 @@ Ποιότητα πολυμέσων Ποιότητα απεσταλμένων πολυμέσων Η αποστολή πολυμέσων υψηλής ποιότητας θα καταναλώνει περισσότερα δεδομένα. + Υψηλή + Κανονική Κλήσεις + Αυτόματο Χρήση προσαρμοσένων χρωμάτων Χρώμα συνομιλίας @@ -4834,6 +4871,7 @@ Λήψη φωτογραφίας Επιλογή φωτογραφίας Φωτογραφία + Κείμενο Αποθήκευση Εκκαθάριση εικόνας προφίλ @@ -4894,7 +4932,7 @@ Προσθήκη μηνύματος Προσθήκη απάντησης Αποστολή σε - Μήνυμα μιας μόνο προβολής + View once media Ένα ή περισσότερα αντικείμενα ήταν πολύ μεγάλα Ένα ή περισσότερα αντικείμενα δεν ήταν έγκυρα Επιλέχθηκαν πάρα πολλά αντικείμενα @@ -5487,7 +5525,7 @@ %1$s %2$s Εσύ - + %1$s έως %2$s Απάντηση @@ -6704,5 +6742,11 @@ Επεξεργασία σημείωσης + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index af131f2bf1..2a0faff43c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -415,6 +415,7 @@ Unirse + Completa Fallo al enviar adjunto @@ -613,6 +614,7 @@ Desarchivar Eliminar Seleccionarlos + %1$d seleccionado %1$d seleccionados @@ -864,6 +866,7 @@ Invitación enviada %1$d invitaciones enviadas + No se puede añadir automáticamente a %1$s al grupo. Ha recibido una invitación y no podrá ver ningún mensaje hasta que la acepte. No se puede añadir automáticamente a estas personas al grupo. Han recibido una invitación y no podrán ver ningún mensaje hasta que la acepten. @@ -1044,7 +1047,9 @@ Editar nombre e imagen Grupo antiguo + Este grupo usa el sistema antiguo de Signal. Funciones como admins de grupo o menciones solo están disponibles en el nuevo sistema. + Este es un grupo del sistema antiguo. Para acceder a funciones como @menciones y admins, Este grupo no se puede actualizar al nuevo sistema porque es demasiado grande. El tamaño máximo de grupo es de%1$d participantes. actualiza este grupo. @@ -1271,6 +1276,7 @@ Eliminar + %1$d seleccionado (%2$s) %1$d seleccionados (%2$s) @@ -1335,17 +1341,13 @@ Has abandonado el grupo. Has actualizado el grupo. Se actualizó el grupo. - + Llamada realizada - + Videollamada realizada - - Llamada no atendida - - Videollamada no atendida - + Llamada entrante - + Videollamada entrante Llamada perdida @@ -1355,10 +1357,6 @@ Llamada de voz perdida mientras el perfil de notificación estaba activado Videollamada perdida mientras el perfil de notificación estaba activado - - Has rechazado una llamada - - Has rechazado una videollamada %1$s · %2$s %1$s ha actualizado el grupo. @@ -1567,30 +1565,55 @@ %1$s ya puede aceptar Pagos - %1$s ha iniciado una llamada en grupo · %2$s - Has iniciado una llamada en grupo · %1$s - %1$s participa en la llamada · %2$s - Participas en la llamada · %1$s - %1$s y %2$s participan en la llamada · %3$s - Llamada en grupo · %1$s - - %1$s ha(s) iniciado una llamada en grupo - Has iniciado una llamada en grupo - %1$s participa en la llamada en grupo - Participas en la llamada - %1$s y %2$s participan en la llamada - Llamada en grupo + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s - - %1$s, %2$s y %3$d persona más participan en la llamada · %4$s - %1$s, %2$s y %3$d personas más participan en la llamada · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s y %3$d persona más participan en la llamada - %1$s, %2$s y %3$d personas más participan en la llamada + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ No se puede solicitar el código de verificación. Por favor, comprueba tu conexión y vuelve a intentarlo. Número en formato no estándar + El número que has introducido (%1$s) parece usar un formato no estándar.\n\n ¿Es %2$s el correcto? Signal Android - Formato de número @@ -2709,6 +2733,8 @@ Cargando Saber más Unirse a la llamada + + Call back Volver a la llamada La llamada está llena Invitar personas @@ -2773,6 +2799,7 @@ Abandonar llamada Las siguentes personas pueden haber reinstalado Signal o cambiado su dispositivo. Verifica tus cifras de seguridas con ellas para asegurar vuestra privacidad. Ver + Identidad previamente verificada @@ -3086,6 +3113,7 @@ Por defecto Alta + Máxima @@ -3446,6 +3474,7 @@ Enviado a %1$s Tú el %1$s a las %2$s %1$s el %2$s a las %3$s + Para De Los detalles de la transacción incluyen la cantidad y fecha del pago, pero no su origen o destino y figuran en el libro mayor de MobileCoin. @@ -3508,6 +3537,7 @@ Confirmar pago Tasa de la red Estimación en %1$s + Para Suma total Balance: %1$s @@ -4112,6 +4142,7 @@ Copiado al portapapeles Admin + Aprobar Denegar @@ -4139,6 +4170,7 @@ Nota de voz · %1$s + %1$s de %2$s @@ -4185,6 +4217,7 @@ %1$s y %2$s se han unido %1$s, %2$s y %3$s se han unido %1$s, %2$s y %3$d más se han unido + %1$s se ha ido %1$s y %2$s se han ido %1$s, %2$s y %3$s se han ido @@ -4562,6 +4595,7 @@ Mensajería Desaparición de mensajes Seguridad de la aplicación + Bloquea capturas de pantalla en la lista de aplicaciones recientes y dentro de la aplicación Los mensajes y llamadas de Signal siempre usan remitente confidencial y las llamadas se desvían Tiempo de desaparición de mensajes por defecto en nuevos chats @@ -4618,11 +4652,14 @@ Calidad de fotos y vídeos Calidad de envío de fotos y vídeos Se usarán más datos al seleccionar la opción de alta calidad. + Alta + Normal Llamadas + Auto Usar colores personalizados Color del chat @@ -4834,6 +4871,7 @@ Toma una foto Selecciona una foto Foto + Texto Guardar Eliminar avatar @@ -4894,7 +4932,7 @@ Añadir mensaje Añadir respuesta Enviar a - Mensaje para ver una vez + View once media Uno o más elementos son demasiado grandes Uno o más elementos no son válidos Demasiados elementos seleccionados @@ -5487,7 +5525,7 @@ %1$s %2$s - + %1$s a «%2$s» Responder @@ -6704,5 +6742,11 @@ Editar nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 1daf79e4cf..a6443e1d33 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -415,6 +415,7 @@ Liitu + Täis Meedia saatmisel tekkis tõrge @@ -613,6 +614,7 @@ Ennista Kustuta Vali kõik + %1$d valitud %1$d valitud @@ -864,6 +866,7 @@ Kutse saadetud %1$d kutset saadetud + “%1$s” ei saa sinu poolt automaatselt gruppi lisada.\n\nNad on kutsutud grupiga liituma ja ei näe grupi sõnumeid enne kui on liitumisega nõustunud. Neid kasutajaid ei saa sinu poolt automaatselt gruppi lisada.\n\nNad on kutsutud grupiga liituma ja ei näe grupi sõnumeid enne kui on liitumisega nõustunud. @@ -1044,7 +1047,9 @@ Muuda nime ja pilti Vana grupp + See on vana grupp. Uued funktsioonid, nt grupi administraatorid on saadaval ainult uutes gruppides. + See on vana grupp. Uute funktsioonide, näiteks @mainimised ja administraatorid, kasutamiseks, Seda vana gruppi ei saa uueks grupiks uuendada, sest see on liiga suur. Maksimaalne grupi suurus on %1$d. uuenda see grupp @@ -1271,6 +1276,7 @@ Kustuta + %1$d valis (%2$s) %1$d valis (%2$s) @@ -1335,17 +1341,13 @@ Sa lahkusid grupist. Sa uuendasid gruppi. Grupp on uuendatud. - + Väljuv häälkõne - + Väljuv videokõne - - Vastamata häälkõne - - Vastamata videokõne - + Sissetulev häälkõne - + Sissetulev videokõne Vastamata häälkõne @@ -1355,10 +1357,6 @@ Vastamata häälkõne teavituste profiili sisselülitatuse ajal Vastamata videokõne teavituste profiili sisselülitatuse ajal - - Sa keeldusid häälkõnest - - Sa keeldusid videokõnest %1$s · %2$s %1$s uuendas gruppi. @@ -1567,30 +1565,55 @@ %1$s saab nüüd makseid vastu võtta - %1$s alustas grupikõne · %2$s - Alustasid grupikõne · %1$s - %1$s on selles grupikõnes · %2$s - Sa oled grupikõnes · %1$s - %1$s ja %2$s on grupikõnes · %3$s - Grupikõne · %1$s - - %1$s alustas grupikõne - Sa alustasid grupikõne - %1$s on grupikõnes - Sa oled grupikõnes - %1$s ja %2$s on grupikõnes - Grupikõne + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Sina - - %1$s, %2$s ja %3$d teine on grupikõnes · %4$s - %1$s, %2$s ja %3$d teist on grupikõnes · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s ja %3$d teine on grupikõnes - %1$s, %2$s ja %3$d teist on grupikõnes + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Kinnituskoodi küsimine nurjus. Palun kontrolli oma võrguühendust ja proovi uuesti. Ebastandardne numbriformaat + Sisestatud number (%1$s) tundub olevat mittestandardses formaadis.\n\nKas mõtlesid hoopis %2$s? Signal Android - telefoninumbri formaat @@ -2709,6 +2733,8 @@ Laadimine Rohkem infot Liitu kõnega + + Call back Pöördu kõnesse tagasi Kõne on täis Kutsu sõpru @@ -2773,6 +2799,7 @@ Lahku kõnest Need inimised on võibolla rakenduse reinstallinud või seadet vahetanud. Privaatsuse kindlustamiseks kontrolli nendega turvanumbrit. Näita + Eelnevalt kinnitatud @@ -3086,6 +3113,7 @@ Vaikimisi Kõrge + Maksimaalne @@ -3446,6 +3474,7 @@ Saadetud kasutajale %1$s Sina vestluses %1$s ajal %2$s %1$s vestluses %2$s ajal %3$s + Saaja Saatja Tehingu andmed, sh maksesumma ja tehingu aeg on osa MobileCoini arvestusraamatust. @@ -3508,6 +3537,7 @@ Kinnita makse Võrgutasu Hinnanguline %1$s + Saaja Kogusumma Saldo: %1$s @@ -4112,6 +4142,7 @@ Lõikelauale kopeeritud Administraator + Nõustu Keeldu @@ -4139,6 +4170,7 @@ Häälsõnum · %1$s + %1$s saajale %2$s @@ -4185,6 +4217,7 @@ %1$s ja %2$s liitusid %1$s, %2$s ja %3$s liitusid %1$s, %2$s ja %3$d muud liitusid + %1$s lahkus %1$s ja %2$s lahkusid %1$s, %2$s ja %3$s lahkusid @@ -4562,6 +4595,7 @@ Sõnumside Kaduvad sõnumid Rakenduse turvalisus + Blokeeri kuvatõmmised hiljutiste fotode nimekirjas ja rakenduse sees Signali sõnumid ja kõned, alati kõnede suunamine ja turvatud saatja Uute gruppide sõnumite vaikimisi kestus @@ -4618,11 +4652,14 @@ Meedia kvaliteet Saadetud meedia kvaliteet Kõrge kvaliteediga meedia saatmine kasutab rohkem andmesidet. + Kõrge + Standardne Kõned + Automaatne Kasuta kohandatud värve Vestluse värv @@ -4834,6 +4871,7 @@ Tee foto Vali foto Foto + Tekst Salvesta Eemalda profiilipilt @@ -4894,7 +4932,7 @@ Lisa sõnum Lisa vastus Vali saaja - Ühekordne sõnum + View once media Üks või mitu üksust olid liiga suured Üks või mitu üksust olid kehtetud Liiga palju elemente valitud @@ -5487,7 +5525,7 @@ %1$s %2$s Sina - + %1$s saajale %2$s Vasta @@ -6704,5 +6742,11 @@ Muuda märkust + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index f98216cb28..7e5645604f 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -415,6 +415,7 @@ Batu + Beteta Errorea multimedia bidaltzean @@ -613,6 +614,7 @@ Desartxibatu Ezabatu Hautatu dena + %1$d hautatua %1$d hautatua @@ -864,6 +866,7 @@ Bidali da gonbidapena %1$d gonbidapen bidali dira + \"%1$s\" erabiltzailea ezin duzu talde honetara modu automatikoan gehitu.\n\nGehitzeko gonbidapena bidali zaie eta ezingo dute talde mezurik ikusi gonbidapena onartu arte. Ezin duzu modu automatikoan erabiltzaile hauek taldera gehitu. Gehitzeko gonbidapena bidali zaie eta ezingo dute talde mezurik ikusi onartu arte. @@ -1044,7 +1047,9 @@ Editatu izena eta irudia Jarauntsitako Taldea + Hau Jarauntsitako Talde bat da. Talde administratzaile moduko ezaugarriak Talde Berrietan erabil daitezke bakarrik. + Hau Jarauntsitako Talde bat da. Talde administratzaile eta @aipamenak moduko ezaugarriak erabili ahal izateko, Jarauntsitako Talde Hau ezin da Talde Berrira eguneratu oso handia delako. Gehienezko talde tamaina %1$d da. eguneratu talde hau. @@ -1271,6 +1276,7 @@ Ezabatu + %1$dek hautatua (%2$s) %1$d(e)k hautatua (%2$s) @@ -1335,17 +1341,13 @@ Taldea utzi duzu. Taldea eguneratu duzu Taldea eguneratu egin da. - + Irteerako ahots-deia - + Irteerako bideodeia - - Erantzun gabeko ahots-deia - - Erantzun gabeko bideodeia - + Sarrerako ahots-deia - + Sarrerako bideodeia Ahots-dei galdua @@ -1355,10 +1357,6 @@ Ahots-dei bat baztertu da, jakinarazpen-profila aktibatuta dagoelako Bideodei bat baztertu da, jakinarazpen-profila aktibatuta dagoelako - - Ahots-dei bat baztertu duzu - - Bideodei bati uko egin diozu %1$s · %2$s %1$s erabiltzaileak taldea eguneratu du. @@ -1567,30 +1565,55 @@ %1$s(e)k orain ordainketak onar ditzake - %1$s erabiltzaileak talde-deia hasi du %2$s - Taldeko dei bat hasi duzu · %1$s - %1$s erabiltzailea talde-deian dago %2$s - Talde-deian zaude %1$s - %1$s eta %2$s talde-deian daude %3$s - Talde-deia %1$s - - %1$s erabiltzaileak talde-deia hasi du - Talde-deia hasi duzu - %1$s talde-deian dago - Talde-deian zaude - %1$s eta %2$s talde-deian daude - Talde-deia + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Zu - - %1$s, %2$s eta beste %3$d talde deian daude %4$s - %1$s, %2$s eta beste %3$d talde-deian daude %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s eta beste %3$d talde-deian daude - %1$s, %2$s eta beste %3$d talde-deian daude + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Ezin da egiaztapen-koderik eskatu. Egiaztatu sarera konektatuta zaudela eta saiatu berriro. Zenbaki-formatu ez-estandarra + Idatzi duzun zenbakiak (%1$s) formatu ez-estandar bat du.\n\n%2$s esan nahi al zenuen? Signal Android - Telefono-zenbakien formatua @@ -2709,6 +2733,8 @@ Kargatzen Gehiago jakin Deian sartu + + Call back Deira itzuli Deia beteta dago Gonbidatu lagunak @@ -2773,6 +2799,7 @@ Deia utzi Ondoko pertsonek agian gailuak aldatu edo Signal berrinstalatu egin dute. Egiaztatu beraiekin duzun segurtasun zenbakia pribatutasuna ziurtatzeko. Ikusi + Aurretik egiaztatua @@ -3086,6 +3113,7 @@ Lehenetsia Altua + Maximoa @@ -3446,6 +3474,7 @@ %1$s(r)i bidalia Zuk (%1$s, %2$s) %1$s(e)k (%2$s, %3$s) + Hartzailea: Igorlea: Transakzioaren xehetasunak (ordainketaren zenbatekoa, eta transakzioaren data eta ordua barne) MobileCoin-en liburu nagusian ageri dira. @@ -3508,6 +3537,7 @@ Baieztatu ordainketa Sare-komisioa %1$sestimatua + Hartzailea: Zenbatekoa, guztira Balantzea: %1$s @@ -4112,6 +4142,7 @@ Arbelera kopiatuta Administratzailea + Onartu Ukatu @@ -4139,6 +4170,7 @@ Ahots-mezua · %1$s + %1$s tik %2$s ra @@ -4185,6 +4217,7 @@ %1$s eta %2$s batu dira %1$s, %2$s eta %3$s batu dira %1$s, %2$s eta beste %3$d batu dira + %1$s joan da %1$s eta %2$s joan dira %1$s, %2$s eta %3$s joan dira @@ -4562,6 +4595,7 @@ Mezularitza Mezuen desagerpena App segurtasuna + Blokeatu pantaila-argazkiak azkenaldikoen zerrendan eta aplikazioaren barruan Signal mezuak eta deiak, beti deiak transmititu, eta igorle zigilatua Txat berrietarako tenporizadore lehenetsia @@ -4618,11 +4652,14 @@ Media kalitatea Bidali media kalitatea Kalitate handiko multimedia bidaltzeak datu gehiago erabiliko ditu. + Altua + Estandarra Deiak + Auto Erabili kolore pertsonalizatuak Txat-kolorea @@ -4834,6 +4871,7 @@ Argazki bat atera Aukeratu argazkia Argazkia + Testua Gorde Garbitu abatarra @@ -4894,7 +4932,7 @@ Gehitu mezu bat Gehitu erantzun bat Bidali honi: - Mezua behin ikusi + View once media Elementu bat edo gehiago handiegiak dira Elementu bat edo gehiago handiegiak dira Elementu gehiegi hautatu dira @@ -5487,7 +5525,7 @@ %1$s%2$s Zu - + %1$s tik %2$s ra Erantzun @@ -6704,5 +6742,11 @@ Editatu oharra + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 9ab2a94eba..73323554ac 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -415,6 +415,7 @@ پیوستن + پر شده خطا در ارسال رسانه @@ -613,6 +614,7 @@ لغو بایگانی پاک کردن انتخاب همه + %1$d انتخاب شد %1$d انتخاب شد @@ -864,6 +866,7 @@ دعوت‌نامه ارسال شد %1$d دعوت‌نامه ارسال شد + «%1$s» نمی‌تواند به صورت خودکار توسط شما به این گروه اضافه شود.\n\nآن‌ها برای پیوستن دعوت شده‌اند و پیام‌های گروهی را تا زمانی که دعوت را نپذیرند، نخواهند دید. این کاربران نمی‌توانند به صورت خودکار توسط شما به این گروه اضافه شوند.\n\nآن‌ها برای پیوستن به گروه دعوت شده‌اند و پیام‌های گروهی را تا زمانی که دعوت را بپذیرند، نخواهند دید. @@ -1044,7 +1047,9 @@ ویرایش نام و عکس گروه موروثی + این یک گروه قدیمی است. قابلیت‌هایی نظیر مدیران گروه فقط در گروه‌های جدید موجود هستند. + این گروه یک گروه قدیمی است. برای دسترسی به قابلیت‌های جدید همانند اشاره‌ها@ و مدیران، این گروه قدیمی نمی‌تواند به یک گروه جدید ارتقا پیدا کند چون بسیار بزرگ است. حداکثر اندازهٔ گروه %1$d است. این گروه را ارتقا دهید. @@ -1271,6 +1276,7 @@ پاک کردن + %1$d انتخاب شد (%2$s) %1$d انتخاب شد (%2$s) @@ -1335,17 +1341,13 @@ شما گروه را ترک کردید. شما گروه را به‌روزرسانی کردید. گروه به‌روزرسانی شد. - + تماس صوتی خروجی - + تماس تصویری خروجی - - تماس صوتی بی‌پاسخ - - تماس تصویری بی‌پاسخ - + تماس صوتی دریافتی - + تماس تصویری دریافتی تماس صوتی ازدست‌رفته @@ -1355,10 +1357,6 @@ تماس صوتی ازدست‌رفته در حالی که نمایۀ اعلان فعال است تماس تصویری ازدست‌رفته در حالی که نمایۀ اعلان فعال است - - یک تماس صوتی را رد کردید - - یک تماس تصویری را رد کردید %1$s · %2$s %1$s گروه را به‌روزرسانی کرد. @@ -1567,30 +1565,55 @@ %1$s اکنون می‌تواند پرداخت‌ها را بپذیرد - %1$s یک تماس گروهی آغاز کرد · %2$s - شما یک تماس گروهی شروع کردید · %1$s - %1$s در تماس گروهی است · %2$s - شما در تماس گروهی هستید · %1$s - %1$s و %2$s در تماس گروهی هستند · %3$s - تماس گروهی · %1$s - - %1$s یک تماس گروهی آغاز کرد - شما یک تماس گروهی آغاز کردید - %1$s در تماس گروهی است - شما در تماس گروهی هستید - %1$s و %2$s در تماس گروهی هستند - تماس گروهی + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s شما - - %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند · %4$s - %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند - %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ امکان درخواست کد تأیید وجود ندارد. لطفاً اتصال شبکه خود را بررسی و دوباره امتحان کنید. قالب شمارهٔ غیر-استاندارد + به نظر می‌رسد شماره‌ای که وارد کردید (%1$s) در قالبی غیر-استاندارد باشد.\n\nآیا منظورتان %2$s بود؟ سیگنال اندروید - قالب شماره تلفن @@ -2709,6 +2733,8 @@ در حال بارگیری بیشتر یاد بگیرید پیوستن به تماس + + Call back بازگشت به تماس ظرفیت تماس تکمیل است دعوت دوستان @@ -2773,6 +2799,7 @@ ترک کردن تماس افراد زیر ممکن است دستگاه خود را تغییر داده یا دوباره نصب کرده باشند. برای اطمینان از حفظ حریم خصوصی شمارهٔ ایمنی خود را با آن‌ها وارسی کنید. مشاهده + قبلاً وارسی شده است @@ -3086,6 +3113,7 @@ پیش‌فرض بالا + بالاترین @@ -3446,6 +3474,7 @@ به %1$s ارسال شد شما روز %1$s در %2$s %1$s روز %2$s در %3$s + به از جزئیات تراکنش شامل مبلغ پرداخت و زمان تراکنش بخشی از MobileCoin Ledger هستند. @@ -3508,6 +3537,7 @@ پرداخت را تأیید کنید هزینهٔ شبکه زمان تخمین زده %1$s + به کل مبلغ موجودی: %1$s @@ -4112,6 +4142,7 @@ روی کلیپ‌بورد کپی شد مدیر + موافقت رد کردن @@ -4139,6 +4170,7 @@ پیام صوتی · %1$s + %1$s به %2$s @@ -4185,6 +4217,7 @@ %1$s و %2$s پیوستند %1$s، %2$s و %3$s پیوستند %1$s، %2$s و %3$d نفر دیگر پیوستند + %1$s ترک کرد %1$s و %2$s ترک کردند %1$s، %2$s و %3$s ترک کردند @@ -4562,6 +4595,7 @@ ‌پیام‌رسانی پیام‌های ناپدید شونده امنیت برنامه + مسدود کردن عکس از صفحه در فهرست موارد اخیر و در برنامه پیام‌ها و تماس‌های سیگنال، عبور همیشگی تماس‌ها از سرور‌های سیگنال، و فرستندهٔ ناشناس زمان‌سنج پیش‌فرض برای گفتگوهای جدید @@ -4618,11 +4652,14 @@ کیفیت رسانه کیفیت ارسال رسانه ارسال رسانه با کیفیت بالا از داده‌های بیشتری استفاده خواهد کرد. + عالی + استاندارد تماس‌ها + خودکار استفاده از رنگ‌های سفارشی رنگ گفتگو @@ -4834,6 +4871,7 @@ یک عکس بگیرید یک عکس انتخاب کنید عکس + متن ذخیره پاک کردن آواتار @@ -4894,7 +4932,7 @@ افزودن یک پیام یک پاسخ اضافه کنید ارسال به - پیام یکبار مصرف + View once media یک یا چند مورد بسیار بزرگ بودند یک یا چند مورد نامعتبر بودند موارد بسیار زیادی انتخاب شده است @@ -5487,7 +5525,7 @@ %1$s %2$s شما - + %1$s به %2$s پاسخ @@ -6680,7 +6718,7 @@ نام مستعار - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + نام‌های مستعار و یادداشت‌ها در سیگنال ذخیره می‌شوند و سرتاسر رمزگذاری شده‌اند. این‌ها فقط برای شما قابل‌مشاهده هستند. نام کوچک @@ -6694,9 +6732,9 @@ ذخیره - Delete? + پاک شود؟ - This will permanently delete any nickname and note you’ve set. + با این کار هر نام مستعار و یادداشتی که تنظیم کرده‌اید برای همیشه پاک خواهد شد. @@ -6704,5 +6742,11 @@ ویرایش یادداشت + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index db55f741a4..9f28e1b4d1 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -415,6 +415,7 @@ Liity + Täynnä Virhe median lähettämisessä @@ -613,6 +614,7 @@ Kumoa arkist. Poista Valitse kaikki + %1$d valittu %1$d valittu @@ -864,6 +866,7 @@ Kutsu lähetetty %1$d kutsua lähetetty + Et voi lisätä käyttäjää %1$s automaattisesti tähän ryhmään.\n\nHänet on kutsuttu liittymään ryhmään, ja hän näkee ryhmäviestit vasta hyväksyttyään kutsun. Et voi lisätä näitä käyttäjiä automaattisesti tähän ryhmään.\n\nHeidät on kutsuttu liittymään ryhmään, ja he näkee ryhmäviestit vasta hyväksyttyään kutsun. @@ -1044,7 +1047,9 @@ Muokkaa nimeä ja kuvaa Vanhan tyyppinen ryhmä + Tämä on vanhan tyyppinen ryhmä. Ylläpitäjätoiminnot ja uudet ominaisuudet ovat käytettävissä vain uusissa ryhmissä. + Tämä on vanhan tyyppinen ryhmä. Käyttääksesi uusia ominaisuuksia, kuten @mainintoja ja ylläpitäjyyttä, Tätä vanhan tyyppistä ryhmää ei voida päivittää uuden tyyppiseksi, koska se on liian suuri. Ryhmässä voi olla enintään %1$d jäsentä. päivitä tämä ryhmä. @@ -1271,6 +1276,7 @@ Poista + %1$d valittu (%2$s) %1$d valittu (%2$s) @@ -1335,17 +1341,13 @@ Olet poistunut ryhmästä. Päivitit ryhmää. Ryhmä päivitettiin. - + Lähtevä äänipuhelu - + Lähtevä videopuhelu - - Vastaamaton äänipuhelu - - Vastaamaton videopuhelu - + Saapuva äänipuhelu - + Saapuva videopuhelu Vastaamaton äänipuhelu @@ -1355,10 +1357,6 @@ Vastaamaton äänipuhelu ilmoitusprofiilin ollessa käytössä Vastaamaton videopuhelu ilmoitusprofiilin ollessa käytössä - - Hylkäsit äänipuhelun - - Hylkäsit videopuhelun %1$s · %2$s %1$s päivitti ryhmää. @@ -1567,30 +1565,55 @@ %1$s voi nyt hyväksyä maksuja - %1$s aloitti ryhmäpuhelun · %2$s - Aloitit ryhmäpuhelun · %1$s - %1$s on ryhmäpuhelussa · %2$s - Olet ryhmäpuhelussa · %1$s - %1$s ja %2$s ovat ryhmäpuhelussa · %3$s - Ryhmäpuhelu · %1$s - - %1$s aloitti ryhmäpuhelun - Aloitit ryhmäpuhelun - %1$s on ryhmäpuhelussa - Olet ryhmäpuhelussa - %1$s ja %2$s ovat ryhmäpuhelussa - Ryhmäpuhelu + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Sinä - - %1$s, %2$s ja %3$d muu ovat ryhmäpuhelussa · %4$s - %1$s, %2$s ja %3$d muuta ovat ryhmäpuhelussa · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s ja %3$d muu ovat ryhmäpuhelussa - %1$s, %2$s ja %3$d muuta ovat ryhmäpuhelussa + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Vahvistuskoodia ei voida pyytää. Tarkista verkkoyhteys ja yritä uudelleen. Epästandardi numeron muoto + Syöttämäsi numero (%1$s) ei näytä olevan vakiomuotoinen.\n\nTarkoititko %2$s? Signal Androidille - Puhelinnumeron muoto @@ -2709,6 +2733,8 @@ Ladataan Lue lisää Liity puheluun + + Call back Palaa puheluun Puhelu on täynnä Kutsu ystäviä @@ -2773,6 +2799,7 @@ Poistu puhelusta Seuraavat henkilöt ovat saattaneet asentaa sovelluksen uudelleen tai vaihtaa laitetta. Varmenna turvanumerosi heidän kanssaan yksityisyyden takaamiseksi. Näytä + Aikaisemmin varmennettu @@ -3086,6 +3113,7 @@ Oletus Korkea + Maksimi @@ -3446,6 +3474,7 @@ Lähetetty henkilölle %1$s Sinä %1$s klo %2$s %1$s %2$s klo %3$s + Vastaanottajat Lähettäjä Tapahtuman tiedot, mukaan lukien maksun summa ja aika, tallennetaan MobileCoiniin. @@ -3508,6 +3537,7 @@ Vahvista maksu Verkkomaksu Arvio %1$s + Vastaanottaja: Kokonaissumma Saldo: %1$s @@ -4112,6 +4142,7 @@ Kopioitu leikepöydälle Ylläpitäjä + Hyväksy Hylkää @@ -4139,6 +4170,7 @@ Ääniviesti · %1$s + %1$s käyttäjälle %2$s @@ -4185,6 +4217,7 @@ %1$s ja %2$s liittyivät %1$s, %2$s ja %3$s liittyivät %1$s, %2$s ja %3$d muuta liittyivät + %1$s poistui %1$s ja %2$s poistuivat %1$s, %2$s ja %3$s poistuivat @@ -4562,6 +4595,7 @@ Viestintä Katoavat viestit Turvallisuus + Estä näyttökuvat Viimeisimmät-luettelosta ja sovelluksen sisältä Signal-viestit ja -puhelut, välitä puhelut aina ja lähettäjäsinetti Uusien keskustelujen oletusajastin @@ -4618,11 +4652,14 @@ Median laatu Lähetettävän median laatu Korkealaatuisen median lähetys kuluttaa enemmän dataa. + Korkea + Vakio Puhelut + Automaattinen Käytä omia värejä Keskustelun väri @@ -4834,6 +4871,7 @@ Ota kuva Valitse kuva Kuva + Teksti Tallenna Poista avatar @@ -4894,7 +4932,7 @@ Lisää viesti Lisää vastaus Lähetä henkilölle - Kerran katsottava viesti + View once media Yksi tai useampi valinnoista olivat liian suuria Yksi tai useampi valinnoista olivat virheellisiä Liikaa valintoja @@ -5487,7 +5525,7 @@ %1$s %2$s Sinä - + %1$s käyttäjälle %2$s Vastaa @@ -6704,5 +6742,11 @@ Muokkaa huomautusta + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index b88496ff31..ac7563e0d0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -244,7 +244,7 @@ Votre version de Signal est expirée. Vous pouvez afficher l’historique de vos messages, mais vous ne pourrez pas échanger de messages tant que vous n’aurez pas mis Signal à jour. - Aucun navigateur Web n’a été trouvé. + Navigateur web introuvable. Envoyer un courriel Un appel cellulaire est déjà en cours. Passer un appel vocal ? @@ -415,6 +415,7 @@ Me joindre + Complet Erreur d’envoi du média @@ -613,6 +614,7 @@ Désarchiver Supprimer Tout sélect. + %1$d sélectionné %1$d sélectionnés @@ -864,6 +866,7 @@ Invitation envoyée %1$d invitations envoyées + « %1$s » ne peut pas être ajouté automatiquement par vous à ce groupe.\n\nUne invitation a été envoyée et l’utilisateur ne verra aucun message du groupe tant que l’invitation n’aura pas été acceptée. Ces utilisateurs ne peuvent pas être ajoutés automatiquement par vous à ce groupe.\n\nDes invitations ont été envoyées et ces utilisateurs ne verront aucun message du groupe tant que les invitations n’auront pas été acceptées. @@ -1044,7 +1047,9 @@ Modifier le nom et l’image Groupe hérité + Ce groupe est un groupe hérité. Les fonctions telles que l’administration des groupes ne sont proposées que pour les Nouveaux groupes. + Ce groupe est un groupe hérité. Pour accéder aux nouvelles fonctions telles que les @mentions et l’administration, Ce groupe hérité ne peut pas être converti en Nouveau groupe, car il comporte trop de membres. Le nombre maximal de membres d’un groupe est %1$d. convertissez ce groupe @@ -1271,6 +1276,7 @@ Supprimer + %1$d sélectionnée (%2$s) %1$d sélectionnées (%2$s) @@ -1291,7 +1297,7 @@ Me le rappeler plus tard Confirmer votre NIP Signal Nous vous demanderons de temps en temps de confirmer votre NIP afin que vous vous en souveniez. - Confirmer le NIP + Confirmer le PIN Commençons Nouveau groupe Inviter des amis @@ -1335,17 +1341,13 @@ Vous avez quitté le groupe Vous avez mis le groupe à jour. Le groupe a été mis à jour. - + Appel vocal sortant - + Appel vidéo sortant - - Appel vocal sans réponse - - Appel vidéo sans réponse - + Appel vocal entrant - + Appel vidéo entrant Appel vocal manqué @@ -1355,10 +1357,6 @@ Appel vocal manqué lorsque le profil de notification était activé Appel vidéo manqué lorsque le profil de notification était activé - - Vous avez refusé un appel vocal - - Vous avez refusé un appel vidéo %1$s · %2$s %1$s a mis le groupe à jour. @@ -1567,30 +1565,55 @@ %1$s accepte désormais les paiements - %1$s a lancé un appel de groupe · %2$s - Vous avez lancé un appel de groupe · %1$s - %1$s fait partie de l’appel de groupe · %2$s - Vous faites partie de l’appel de groupe · %1$s - %1$s et %2$s font partie de l’appel de groupe · %3$s - Appel de groupe · %1$s - - %1$s a lancé un appel de groupe - Vous avez lancé un appel de groupe - %1$s fait partie de l’appel de groupe - Vous faites partie de l’appel de groupe - %1$s et %2$s font partie de l’appel de groupe - Appel de groupe + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Vous - - %1$s, %2$s, et %3$d autre personne font partie de l’appel de groupe · %4$s - %1$s, %2$s, et %3$d autres personnes font partie de l’appel de groupe · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, et %3$d autre personne font partie de l’appel de groupe - %1$s, %2$s, et %3$d autres personnes font partie de l’appel de groupe + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -1710,7 +1733,7 @@ Besoin d’aide ? Votre NIP est un code à %1$d chiffres ou plus que vous avez créé et qui peut être numérique ou alphanumérique. Si vous avez oublié votre NIP, vous pouvez en créer un nouveau. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. Si vous ne vous souvenez pas de votre NIP, vous pouvez en créer un nouveau. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. - Créer un nouveau NIP + Créer un nouveau PIN Contacter l’assistance Annuler Ignorer @@ -1723,7 +1746,7 @@ Créer votre NIP Vous avez épuisé tous vos essais de NIP, mais vous pouvez encore accéder à votre compte Signal en créant un nouveau NIP. Afin de protéger vos données personnelles et votre sécurité, votre compte sera restauré sans informations de profil enregistrées ni préférences. - Créer un nouveau NIP + Créer un nouveau PIN @@ -1739,8 +1762,8 @@ Avertissement - Si vous désactivez le PIN, vous perdrez toutes vos données lors de votre réinscription sur Signal, à moins que vous ne les sauvegardiez et les restauriez manuellement. Vous ne pouvez pas activer le blocage de l’inscription si le PIN est désactivé. - Désactiver le NIP + La désactivation du code PIN entraînera la perte de toutes vos données lors de votre réinscription sur Signal, à moins de procéder à une sauvegarde et une restauration manuelles. Vous ne pouvez pas activer le blocage d’inscription si le code PIN est désactivé. + Désactiver le code PIN Notez cette appli @@ -2063,6 +2086,7 @@ Demande de code de vérification impossible. Vérifiez votre connexion réseau puis réessayez. Format de numéro non standard + Le numéro que vous avez saisi (%1$s) semble avoir un format atypique.\n\nSerait-ce plutôt %2$s ? Signal pour Android – Format de numéro de téléphone @@ -2106,8 +2130,8 @@ Contacter l’assistance - Activer le blocage de l’inscription ? - Désactiver le blocage de l’inscription ? + Activer le blocage d’inscription ? + Désactiver le blocage d’inscription ? Si vous oubliez votre NIP Signal lors d’une nouvelle inscription à Signal, vous ne pourrez pas accéder à votre compte pendant sept jours. Activer Désactiver @@ -2213,7 +2237,7 @@ Version d’Android : Version de Signal : Package Signal : - Blocage de l’inscription : + Blocage d’inscription : Paramètres régionaux : @@ -2245,7 +2269,7 @@ Vous avez marqué comme non confirmé Le message n’a pas pu être traité Problème de remise - Demande de message + Invitation par message Vous avez masqué ce contact. Écrivez-lui de nouveau pour l’ajouter à votre liste. Photo @@ -2513,7 +2537,7 @@ Signal Nouveau message - Demande d’échange de messages + Invitation par message Vous %1$s • Story @@ -2522,7 +2546,7 @@ De nouvelles façons de rester en contact - Numéro de téléphone protégé + Confidentialité du numéro de téléphone Votre numéro de téléphone ne s’affiche plus dans les conversations. Il reste cependant visible aux utilisateurs qui l’ont déjà enregistré dans leurs contacts. @@ -2709,6 +2733,8 @@ Chargement En savoir plus Me joindre à l’appel + + Call back Revenir à l’appel L’appel est complet Inviter des amis @@ -2773,6 +2799,7 @@ Quitter l’appel Les personnes suivantes ont peut-être réinstallé Signal ou changé d’appareil. Pour assurer la confidentialité, confirmez votre numéro de sécurité avec elles. Afficher + Confirmé précédemment @@ -3086,6 +3113,7 @@ Valeur par défaut Élevée + Maximale @@ -3191,14 +3219,14 @@ En savoir plus Graphique illustrant l’endroit où l’icône de l’application de remplacement sera visible. - Désactiver le NIP + Désactiver le code PIN Activer le NIP - Si vous désactivez le NIP, vous perdrez toutes vos données lors de votre réinscription sur Signal, à moins que vous ne les sauvegardiez et les restauriez manuellement. Vous ne pouvez pas activer le blocage de l’inscription si le NIP est désactivé. + La désactivation du code PIN entraînera la perte de toutes vos données lors de votre réinscription sur Signal, à moins de procéder à une sauvegarde et une restauration manuelles. Vous ne pouvez pas activer le blocage d’inscription si le code PIN est désactivé. Les NIP font en sorte que les informations sont enregistrées chiffrées dans Signal afin que vous seul puissiez y accéder. Votre profil, vos paramètres et vos contacts seront restaurés quand vous réinstallerez Signal. Vous n’aurez pas besoin de votre NIP pour ouvrir l’appli. Valeur par défaut du système Langue Appels et messages Signal - Paramètres de NIP avancés + Paramètres de PIN avancés Messages et appels gratuits et confidentiels vers les utilisateurs de Signal Envoyer le journal de débogage Supprimer le compte @@ -3446,6 +3474,7 @@ Envoyé à %1$s Vous, le %1$s à %2$s %1$s, le %2$s à %3$s + À De Le détail des transactions, dont le montant des paiements et l’heure des transactions font partie du Grand-livre MobileCoin. @@ -3508,6 +3537,7 @@ Confirmer le paiement Frais de réseau %1$s estimés + À Montant total Solde : %1$s @@ -3717,7 +3747,7 @@ Votre PIN doit comporter au moins %1$d chiffres Créer un nouveau NIP - Vous pouvez changer votre NIP tant que cet appareil est inscrit. + Vous pouvez changer de code PIN tant que cet appareil est enregistré. Créer votre NIP Les PIN peuvent vous aider à restaurer votre compte et garder vos données chiffrées avec Signal. Choisissez une NIP plus robuste @@ -3730,7 +3760,7 @@ Échec de création du NIP Votre NIP n’a pas été enregistré. Nous vous inviterons à en créer un plus tard. Le NIP a été créé. - Saisissez votre NIP de nouveau. + Saisissez de nouveau votre PIN. Création du NIP… @@ -3738,12 +3768,12 @@ Les NIP font en sorte que les informations sont enregistrées chiffrées dans Signal afin que vous seul puissiez y accéder. Votre profil, vos paramètres et vos contacts seront restaurés quand vous réinstallerez Signal. Vous n’aurez pas besoin de votre NIP pour ouvrir l’appli. En savoir plus - Blocage de l’inscription = NIP - Votre blocage de l’inscription s’appelle désormais un NIP et il accomplit encore plus. Mettez-le à jour maintenant. + Blocage d’inscription = PIN + Le blocage d’inscription fait peau neuve : il dépend désormais du code PIN et gagne en efficacité. Mettez-le à jour. Mettre le NIP à jour Créer votre NIP En savoir plus sur les codes PIN - Désactiver le NIP + Désactiver le code PIN Saisissez votre NIP Signal @@ -3761,7 +3791,7 @@ - Saisissez votre NIP + Saisissez votre PIN Saisissez le NIP que vous avez créé pour votre compte. Il est différent de votre code de confirmation reçu par texto. Saisissez le PIN choisi pour votre compte. @@ -3800,7 +3830,7 @@ - %1$s recevra une demande de message de votre part. Vous pourrez appeler une fois la demande de message acceptée. + %1$s recevra une invitation par message. Vous pourrez l’appeler une fois votre invitation acceptée. Créer un NIP @@ -3921,21 +3951,21 @@ Délai d’inactivité avant verrouillage de l’écran NIP Signal Créer un NIP - Changer votre NIP - Rappels du NIP + Changer de code PIN + Rappels de code PIN Désactiver - Confirmer le NIP + Confirmer le PIN Confirmer votre NIP Signal Assurez-vous de mémoriser votre NIP ou de le conserver à l’abri, car il ne peut pas être récupéré. Si vous oubliez votre NIP, vous risquez de perdre des données lors de la réinscription de votre compte Signal. Le NIP est erroné. Veuillez réessayer. - Échec d’activation du blocage de l’inscription. - Échec de désactivation du blocage de l’inscription. + Impossible d’activer le blocage d’inscription. + Impossible de désactiver le blocage d’inscription. Aucun - Blocage de l’inscription - Vous devez saisir votre NIP de blocage de l’inscription + Blocage d’inscription + Saisissez le PIN du blocage d’inscription Votre NIP comporte au moins %1$d chiffres ou caractères Trop d’essais - Vous avez fait trop d’essais infructueux de saisie du NIP de blocage de l’inscription. Veuillez réessayer dans un jour. + Trop grand nombre de tentatives de saisie du code PIN. Veuillez réessayer dans un jour. Vous avez fait trop d’essais. Veuillez réessayer plus tard. Erreur de connexion au service Sauvegardes @@ -4112,6 +4142,7 @@ A été copié dans le presse-papiers Administrateur + Approuver Refuser @@ -4139,6 +4170,7 @@ Message vocal · %1$s + %1$s à %2$s @@ -4185,6 +4217,7 @@ %1$s et %2$s se sont joints %1$s, %2$s et %3$s se sont joints %1$s, %2$s et %3$d se sont joints + %1$s a quitté l’appel %1$s et %2$s ont quitté l’appel %1$s, %2$s et %3$s ont quitté l’appel @@ -4397,13 +4430,13 @@ Impossible d’envoyer le paiement - Pour envoyer un paiement à cet utilisateur, la personne doit accepter votre demande d’échange de messages. Envoyez-lui un message pour créer une demande d’échange de messages. + Pour envoyer un paiement à cet utilisateur, celui-ci doit d’abord accepter une invitation par message de votre part. Envoyez-lui un message pour l’inviter à discuter avec vous sur Signal. Envoyez un message Vous n’avez aucun groupe en commun avec cette personne. Examinez attentivement les demandes avant d’accepter pour éviter les messages indésirés. Aucun de vos contacts ou des personnes avec lesquelles vous discutez n’est dans ce groupe. Examinez attentivement les demandes avant d’accepter pour éviter les messages indésirés. - À propos des demandes d’échange de messages + À propos des invitations par message Valider Voici un aperçu de la couleur de la conversation. @@ -4430,15 +4463,15 @@ Compte - On vous le demandera moins souvent au fil du temps - Exige votre NIP Signal pour réinscrire votre numéro de téléphone avec Signal + Les rappels deviennent moins fréquents au fil du temps + Exige votre code PIN Signal pour réenregistrer votre numéro de téléphone sur Signal Changer de numéro de téléphone - Données de votre compte + Données du compte - Données de votre compte + Données du compte Exporter un rapport contenant les données de votre compte Signal. Ce rapport ne contient aucun message ou contenu multimédia. %1$s @@ -4562,6 +4595,7 @@ Messagerie Messages éphémères 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é Minuterie par défaut pour les nouvelles conversations @@ -4618,11 +4652,14 @@ Qualité des médias Qualité des médias envoyés Envoyer des médias de haute qualité demande plus de données. + Élevée + Standard Appels + Auto Utiliser des couleurs personnalisées Couleur de la conversation @@ -4795,7 +4832,7 @@ Supprimer %1$s ? - Vous ne verrez pas cette personne lors d\'une recherche. Vous recevrez une demande si cette personne essaye de vous envoyer un message à l\'avenir. + Cette personne n’apparaîtra plus dans vos résultats de recherche, mais pourra toujours vous envoyer une invitation par message si elle décide de vous écrire. %1$s a été supprimé @@ -4834,6 +4871,7 @@ Prendre une photo Choisir une photo Photo + Texte Enregistrer Supprimer l’avatar @@ -4894,7 +4932,7 @@ Ajouter un message Ajouter une réponse Envoyer à - Message éphémère + View once media Un élément ou plus étaient trop grands Un élément ou plus étaient invalides Trop d’éléments sélectionnés @@ -5487,7 +5525,7 @@ %1$s %2$s Vous - + %1$s à %2$s Répondre @@ -5576,7 +5614,7 @@ Les contacts Signal sont des personnes en qui vous avez confiance pour au moins une des raisons suivantes : - Commencer une conversation + Vous avez démarré une conversation avec eux Vous avez accepté leur invitation par message @@ -6244,7 +6282,7 @@ Configurer votre nom d\'utilisateur Signal - Protégez votre numéro de téléphone, choisissez un nom d’utilisateur facultatif et découvrez les liens de conversation. + Protégez la confidentialité de votre numéro de téléphone, choisissez un nom d’utilisateur facultatif et découvrez les liens de conversation. Fermer @@ -6253,7 +6291,7 @@ De nouvelles façons de rester en contact - Protégez votre numéro de téléphone, choisissez un nom d’utilisateur facultatif et découvrez les liens de conversation. + Protégez la confidentialité de votre numéro de téléphone, choisissez un nom d’utilisateur facultatif et découvrez les liens de conversation. Fermer @@ -6680,7 +6718,7 @@ Pseudo - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Les pseudos et les notes sont stockés dans Signal et chiffrés de bout en bout. Vous seul y avez accès. Prénom @@ -6694,9 +6732,9 @@ Enregistrer - Delete? + Supprimer ? - This will permanently delete any nickname and note you’ve set. + Cette action supprimera définitivement les pseudos et les notes que vous avez ajoutés. @@ -6704,5 +6742,11 @@ Modifier les notes + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 2dacd97971..4c3c86191e 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -424,6 +424,7 @@ Téigh le + Lán Tharla botún fad agus a bhí an meán á sheoladh @@ -661,6 +662,7 @@ Díchartlannú Scrios Roghnaigh uile + %1$d roghnaithe %1$d roghnaithe @@ -921,6 +923,7 @@ %1$d gcuireadh seolta %1$d cuireadh seolta + Ní féidir leat “%1$s” a chur leis an ngrúpa seo go huathoibríoch.\n\nFuair an duine sin cuireadh le dul isteach ann, agus ní fheicfidh sé/sí aon teachtaireachtaí grúpa go dtí go nglacfar é. Ní féidir leat na húsáideoirí seo a chur leis an ngrúpa seo go huathoibríoch.\n\nFuair siad cuireadh le dul isteach sa ghrúpa, agus ní fheicfidh siad aon teachtaireachtaí grúpa go dtí go nglacfar é. @@ -1149,7 +1152,9 @@ Cuir an t-ainm agus pictiúr in eagar Grúpa Oidhreachta + Seanleagan de Ghrúpa é seo. Níl gnéithe amhail riarthóirí grúpa ar fáil ach i nGrúpaí Nua. + Seanleagan de Ghrúpa é seo. Chun gnéithe nua amhail @tráchtanna agus riarthóirí a rochtain, Ní féidir an seanleagan seo den Ghrúpa a uasghrádú chuig Grúpa Nua toisc go bhfuil sé rómhór. Is í %1$d uasmhéid an ghrúpa. uasghrádaigh an grúpa seo. @@ -1403,6 +1408,7 @@ Scrios + %1$d roghnaithe (%2$s) %1$d roghnaithe (%2$s) @@ -1470,17 +1476,13 @@ D\'fhág tú an bhaicle. Nuashonraigh tú an bhaicle Nuashonraíodh an grúpa. - + Guthghlao amach - + Físghlao amach - - Guthghlao nár freagraíodh - - Físghlao nár freagraíodh - + Guthghlao isteach - + Físghlao isteach Guthghlao caillte @@ -1490,10 +1492,6 @@ Guthghlao caillte agus an phróifíl fógraí casta uirthi Físghlao caillte agus an phróifíl fógraí casta uirthi - - Dhiúltaigh tú do ghuthghlao - - Dhiúltaigh tú d\'fhísghlao %1$s · %2$s Nuashonraigh %1$s an bhaicle @@ -1726,36 +1724,61 @@ Is féidir le %1$s glacadh le hÍocaíochtaí anois - Thosaigh %1$s ar ghlao grúpa · %2$s - Thosaigh tú ar ghrúpghlao · %1$s - Tá %1$s sa ghlao grúpa · %2$s - Tá tusa sa ghlao grúpa · %1$s - Tá %1$s agus %2$s sa ghlao grúpa · %3$s - Glao grúpa · %1$s - - %1$s started a group call - You started a group call - Tá %1$s sa ghlao grúpa - You are in the group call - Tá %1$s agus %2$s sa ghlao grúpa - Glao grúpa + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Tusa - - Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa · %4$s - Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa · %4$s - Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa · %4$s - Tá %1$s, %2$s agus %3$d nduine eile sa ghlao grúpa · %4$s - Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa - Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa - Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa - Tá %1$s, %2$s agus %3$d nduine eile sa ghlao grúpa - Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2300,6 +2323,7 @@ Ní féidir cód fíoraithe a iarraidh. Seiceáil an nasc líonra agus triail arís. Formáid uimhreacha neamhchaighdeánach + Is cosúil go bhfuil formáid neamhchaighdeánach ar an uimhir a chuir tú isteach (%1$s).\n\nAn raibh %2$s i gceist agat? Signal Android — Formáid don Uimhir Ghutháin @@ -2970,6 +2994,8 @@ Ag Lódáil Foghlaim tuilleadh Téigh isteach sa ghlao + + Call back Return To Call Call is Full Tabhair cuirí do chairde @@ -3034,6 +3060,7 @@ Leave Call Seans go bhfuair na daoine seo a leanas gléasanna nua nó go ndearna siad athshuiteáil ar an aip. Deimhnigh d\'uimhir shábháilteachta leo ar eagla na heagla. Amharc + Fíoraithe roimhe @@ -3368,6 +3395,7 @@ An réamhshocrú Ard + Uasmhéid @@ -3737,6 +3765,7 @@ Sent to %1$s Tusa ar %1$s ar %2$s %1$s ar %2$s ar %3$s + Chuig Ó Tá sonraí idirbhirt, méid na híocaíochta agus am an idirbhirt san áireamh, mar chuid de MobileCoin Ledger. @@ -3799,6 +3828,7 @@ Confirm Payment Network Fee Estimated %1$s + Chuig Méid iomlán Iarmhéid: %1$s @@ -4430,6 +4460,7 @@ Macasamhlaithe chuig an ngearrthaisce Riarthóir + Ceadaigh í Diúltaigh dó @@ -4457,6 +4488,7 @@ Glórphost · %1$s + %1$s chuig %2$s @@ -4509,6 +4541,7 @@ Chuaigh %1$s agus %2$s isteach ann Chuaigh %1$s, %2$s agus %3$s isteach ann Chuaigh %1$s, %2$s agus %3$d eile isteach ann + D\'fhág %1$s D\'fhág %1$s agus %2$s D\'imigh %1$s, %2$s agus %3$s as @@ -4892,6 +4925,7 @@ Ag seoladh teachtaireachtaí Teachtaireachtaí a imíonn as amharc App Security + Cuir bac ar ghabhálacha scáileáin sa liosta le déanaí agus laistigh den aip Teachtaireachtaí agus glaonna Signal, atreoraigh glaonna i gcónaí, agus seoltóir séalaithe Amadóir réamhshocraithe ar chomhráite nua @@ -4948,11 +4982,14 @@ Media Quality Sent Media Quality Sending high quality media will use more data. + Ard + Caighdeánach Glaonna + auto Úsáid dathanna saincheaptha Dath an chomhrá @@ -5173,6 +5210,7 @@ Glac grianghraf Roghnaigh grianghraf Grianghraf + Téacs Sábháil Glan an tAbhatár @@ -5245,7 +5283,7 @@ Cuir teachtaireacht leis Cuir freagra leis Seol chuig - Teachtaireacht amhairc aonuaire + View once media Bhí mír amháin nó níos mó rómhór Bhí mír amháin nó níos mó neamhbhailí Roghnaíodh an iomarca nithe @@ -5853,7 +5891,7 @@ %1$s %2$s Tusa - + %1$s chuig %2$s Freagair @@ -7118,7 +7156,7 @@ Leasainm - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Stóráiltear leasainmneacha agus nótaí le Signal agus iad criptithe ó cheann ceann. Níl siad infheicthe ach agatsa amháin. Ainm @@ -7132,9 +7170,9 @@ Sábháil - Delete? + Scrios? - This will permanently delete any nickname and note you’ve set. + Scriosfar aon leasainm agus nóta a shocraigh tú. @@ -7142,5 +7180,11 @@ Cuir nóta in eagar + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index ba00b65aae..5ccd40b4a2 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -415,6 +415,7 @@ Unirse + Completo Erro ao enviar o multimedia @@ -613,6 +614,7 @@ Desarquivar Borrar Marcar todo + %1$d seleccionado %1$d seleccionados @@ -864,6 +866,7 @@ Invitación enviada %1$d invitacións enviadas + Non podes engadir a «%1$s» automaticamente a este grupo.\n\nRecibiron invitacións para participar e non verán ningunha mensaxe do grupo ata que acepten. Estes usuarios non se poden engadir automaticamente a este grupo.\n\nRecibiron unha invitación para participar no grupo e non verán ningunha mensaxe do grupo ata que acepten. @@ -1044,7 +1047,9 @@ Editar nome e imaxe Grupo Clásico + Este é un Grupo Clásico. Características como a administración de grupos só están dispoñibles nos Novos Grupos. + Este é un Grupo Clásico. Para ter novas características como @mencións e adminitradores, Este Grupo Clásico non pode actualizarse a Novos Grupos porque é demasiado grande. O tamaño máximo do grupo é %1$d. actualizar este grupo. @@ -1271,6 +1276,7 @@ Borrar + %1$d seleccionados (%2$s) %1$d seleccionado (%2$s) @@ -1335,17 +1341,13 @@ Abandonaches o grupo. Actualizaches o grupo. Actualizouse o grupo. - + Chamada de voz saínte - + Chamada de vídeo saínte - - Chamada de voz non respondida - - Chamada de vídeo non respondida - + Chamada de voz entrante - + Chamada de vídeo entrante Chamada de voz perdida @@ -1355,10 +1357,6 @@ Chamada de voz perdida mentres o perfil de notificación estaba activado Videochamada perdida mentres o perfil de notificación estaba activado - - Rexeitaches unha chamada de voz - - Rexeitaches unha chamada de vídeo %1$s · %2$s %1$s actualizou o grupo. @@ -1567,30 +1565,55 @@ %1$s agora pode aceptar Pagamentos - %1$s iniciou unha chamada en grupo · %2$s - Iniciaches unha chamada de grupo · %1$s - %1$s está na chamada en grupo · %2$s - Estás na chamada en grupo - %1$s - %1$s e %2$s están na chamada en grupo · %3$s - Chamada en grupo · %1$s - - %1$s iniciou unha chamada en grupo - Iniciaches unha chamada de grupo - %1$s está na chamada en grupo - Estás na chamada en grupo - %1$s e %2$s están na chamada en grupo - Chamada en grupo + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Ti - - %1$s, %2$s e %3$d máis están na chamada en grupo · %4$s - %1$s, %2$se %3$d máis están na chamada en grupo · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s e %3$d máis están na chamada en grupo - %1$s, %2$s e %3$d máis están na chamada en grupo + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Non foi posible solicitar un código de verificación. Comproba a conexión a Internet e inténtao de novo. Número con formato non-estándar + O número escrito (%1$s) non semella estar nun formato estándar.\n\nSerá máis ben %2$s? Signal para Android - Formato de número de teléfono @@ -2709,6 +2733,8 @@ Cargando Saber máis Unirse á chamada + + Call back Volver chamar A chamada está completa Convidar amizades @@ -2773,6 +2799,7 @@ Deixar a chamada As seguintes persoas pode que reinstalasen ou cambiasen de dispositivo. Comproba o número de seguranza con elas para garantir a privacidade. Ver + Verificado previamente @@ -3086,6 +3113,7 @@ Por defecto Alta + Máxima @@ -3446,6 +3474,7 @@ Enviado a %1$s Ti o %1$s ás %2$s %1$s o %2$s ás %3$s + Para De Os detalles da transacción, nos que se inclúen a cantidade que se paga e a hora, forman parte do Libro de contas de MobileCoin. @@ -3508,6 +3537,7 @@ Confirmar pagamento Taxa de rede Estimación: %1$s + Para Contía total Saldo: %1$s @@ -4112,6 +4142,7 @@ Copiado ao portapapeis Administrador + Aprobar Rexeitar @@ -4139,6 +4170,7 @@ Mensaxe de voz · %1$s + %1$s a %2$s @@ -4185,6 +4217,7 @@ %1$s e %2$s uníronse %1$s, %2$s e %3$s uníronse %1$s, %2$s e outros %3$d uníronse + %1$s saíu %1$s e %2$s saíron %1$s, %2$s e %3$s saíron @@ -4562,6 +4595,7 @@ Mensaxería Desaparición das mensaxes Seguranza da app + Bloquea as capturas na listaxe de recentes e na aplicación Mensaxes e chamadas Signal, redirección de chamadas e remitente selado Establecer duración das novas conversas @@ -4618,11 +4652,14 @@ Calidade multimedia Calidade dos envíos Enviando multimedia con alta calidade usarás máis datos + Alta + Estándar Chamadas + Auto Empregar cores personalizadas Cor da conversa @@ -4834,6 +4871,7 @@ Sacar foto Escoller unha foto Fotografía + Texto Gardar Desbotar avatar @@ -4894,7 +4932,7 @@ Engadir mensaxe Engadir unha resposta Enviar a - Mensaxe para ver unha vez + View once media Un ou máis elementos son demasiado grandes Un ou máis elementos son incorrectos Demasiados elementos seleccionados @@ -5487,7 +5525,7 @@ %1$s %2$s Ti - + %1$s a %2$s Responder @@ -6704,5 +6742,11 @@ Editar nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index bc7a69555c..103ed249a1 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -415,6 +415,7 @@ જોડાઓ + ભરેલ મીડિયા મોકલવામાં ભૂલ @@ -613,6 +614,7 @@ અનઆર્કાઇવ કરો ડિલીટ કરો બધા પસંદ કરો + %1$d પસંદ કર્યો %1$d પસંદ કર્યા @@ -864,6 +866,7 @@ આમંત્રણ મોકલ્યું %1$d આમંત્રણો મોકલ્યા + \"%1$s\" ને તમારા દ્વારા આ ગ્રુપમાં આપમેળે ઉમેરી શકાશે નહીં.\n\nતેમને જોડાવા માટે આમંત્રિત કરવામાં આવ્યા છે, અને જ્યાં સુધી તેઓ સ્વીકારશે નહીં ત્યાં સુધી કોઈ ગ્રુપ મેસેજ જોઇ શકાશે નહીં. આ વપરાશકર્તાઓ તમારા દ્વારા આ ગ્રુપમાં આપમેળે ઉમેરી શકાતા નથી. \n\n તેમને ગ્રુપમાં જોડાવા માટે આમંત્રિત કરવામાં આવ્યા છે, અને જ્યાં સુધી તેઓ સ્વીકારે નહીં ત્યાં સુધી તેઓ કોઈ ગ્રુપ મેસેજ જોશે નહીં. @@ -1044,7 +1047,9 @@ નામ અને ચિત્ર સંપાદિત કરવું લેગેસી ગ્રુપ + આ એક લિગેસી ગ્રુપ છે. ગ્રુપ એડમીન જેવી સુવિધાઓ ફક્ત નવા ગ્રુપ માટે ઉપલબ્ધ છે. + આ એક જૂનું ગ્રુપ છે. @મેન્શન અને એડમિન જેવી નવી સુવિધાઓ ઍક્સેસ કરવા માટે, આ લેગસી ગ્રુપને નવા ગ્રુપમાં અપગ્રેડ કરી શકાતું નથી કારણ કે તે ખૂબ મોટું છે. ગ્રુપનું મહત્તમ કદ %1$d છે. આ ગ્રુપને અપગ્રેડ કરો. @@ -1271,6 +1276,7 @@ ડિલીટ કરો + %1$d એ (%2$s) પસંદ કર્યા %1$d એ (%2$s) પસંદ કર્યું @@ -1335,17 +1341,13 @@ તમે ગ્રુપ છોડી દીધું છે. તમે ગ્રુપ ને અપડેટ કર્યું. ગ્રુપ અપડેટ કરવામાં આવ્યું હતું. - + આઉટગોઇંગ વૉઇસ કૉલ - + આઉટગોઈંગ વિડિયો કૉલ - - અનુત્તરિત વૉઇસ કૉલ - - અનુત્તરિત વિડિયો કૉલ - + ઇનકમિંગ વૉઇસ કૉલ - + ઈનકમિંગ વિડિયો કૉલ મિસ્ડ વૉઇસ કૉલ @@ -1355,10 +1357,6 @@ નોટિફિકેશન પ્રોફાઇલ ચાલુ હતી ત્યારે વૉઇસ કૉલ મિસ થયો નોટિફિકેશન પ્રોફાઇલ ચાલુ હતી ત્યારે વીડિયો કૉલ મિસ થયો - - તમે વૉઇસ કૉલ નકાર્યો - - તમે વિડિયો કૉલ નકાર્યો %1$s . %2$s %1$s ગ્રુપ અપડેટ કર્યુ. @@ -1567,30 +1565,55 @@ %1$s હવે પેમેન્ટ સ્વીકારી શકે છે - %1$s એ ગ્રુપ કૉલ શરૂ કર્યો · %2$s - તમે ગ્રૂપ કૉલ શરૂ કર્યો · %1$s - %1$s એ ગ્રુપ કૉલમાં છે· %2$s - તમે ગ્રુપ કૉલમાં છો· %1$s - %1$sઅને %2$sએ ગ્રુપ કૉલમાં છે · %3$s - ગ્રુપ કૉલ · %1$s - - %1$s એ ગ્રુપ કૉલ શરૂ કર્યો - તમે ગ્રુપ કૉલ શરૂ કર્યો - %1$s એ ગ્રુપ કૉલમાં છે· - તમે ગ્રુપ કૉલમાં છો - %1$sઅને %2$sએ ગ્રુપ કૉલમાં છે · - ગ્રુપ કૉલ + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s તમે - - %1$s, %2$s, અને %3$d અન્યો આ કૉલમાં છે%4$s - %1$s, %2$s, અને %3$d અન્યો આ ગ્રુપ કૉલમાં છે%4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, અને %3$d અન્યો આ કૉલમાં છે - %1$s, %2$s, અને %3$d અન્યો આ ગ્રુપ કૉલમાં છે + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ ચકાસણી કોડની વિનંતી કરવામાં અસમર્થ. કૃપા કરીને તમારું નેટવર્ક કનેક્શન તપાસો અને ફરી પ્રયાસ કરો. બિન-પ્રમાણભૂત નંબર ફોર્મેટ + તમે દાખલ કરેલ નંબર (%1$s) બિન-પ્રમાણભૂત ફોર્મેટ હોય તેમ લાગે છે.\n\n શું તમારો અર્થ %2$s હતો? Signal Android - ફોન નંબર ફોર્મેટ @@ -2709,6 +2733,8 @@ લોડ કરી રહ્યું છે વધુ શીખો કૉલમાં જોડાઓ + + Call back કૉલ પર પાછા જાઓ કૉલ પૂર્ણ છે મિત્રોને આમંત્રિત કરો @@ -2773,6 +2799,7 @@ કોલ છોડો નીચેના લોકોએ ઉપકરણોને ફરીથી ઇન્સ્ટોલ અથવા બદલ્યા હશે. ગોપનીયતાની ખાતરી કરવા માટે તેમની સાથે તમારો સલામતી નંબર ચકાસો. વ્યૂ + અગાઉ ચકાસાયેલ @@ -3086,6 +3113,7 @@ ડિફોલ્ટ ઉચ્ચ + મહત્તમ @@ -3446,6 +3474,7 @@ %1$s ને મોકલાયેલ તમે %1$s પર %2$s  %1$s પર %2$s એ %3$s + તરફ: માંથી: પેમેન્ટની રકમ અને ટ્રાન્ઝેક્શનના સમય સહિતની ટ્રાન્ઝેક્શન સંબંધી વિગતો MobileCoin લેજરનો ભાગ છે. @@ -3508,6 +3537,7 @@ પેમેન્ટની પુષ્ટિ કરો નેટવર્ક ફી અંદાજિત %1$s + તરફ: કુલ રકમ બેલેન્સ: %1$s @@ -4112,6 +4142,7 @@ ક્લિપબોર્ડ પર કૉપી કરેલું એડમિન + મંજૂર નામંજૂર કરો @@ -4139,6 +4170,7 @@ વૉઈસ મેસેજ . %1$s + %1$sમાંથી %2$s @@ -4185,6 +4217,7 @@ %1$s અને %2$s જોડાયા %1$s, %2$s અને %3$s જોડાયા %1$s, %2$s અને %3$d અન્ય જોડાયા + %1$s છોડ્યું %1$s અને %2$s છોડ્યું %1$s, %2$s અને %3$s છોડ્યું @@ -4562,6 +4595,7 @@ મેસેજ કરી રહ્યા છીએ અદૃશ્ય થઈ રહેલા મેસેજ એપ્લિકેશનની સુરક્ષા + તાજેતરની સૂચિમાં અને ઍપમાંના સ્ક્રીનશોટને બ્લૉક કરો Signal મેસેજ અને કૉલ્સ, હંમેશા રિલે કૉલ્સ, અને સીલ કરેલ મોકલનાર નવી ચેટ માટે ડિફોલ્ટ ટાઈમર @@ -4618,11 +4652,14 @@ મીડિયા ગુણવત્તા મોકલેલ મીડિયાની ગુણવત્તા ઉચ્ચ ગુણવત્તાના મીડિયા મોકલવાથી વધુ ડેટા વાપરશે. + ઉચ્ચ + સ્ટાન્ડર્ડ કૉલ્સ + આપમેળે કસ્ટમ કલર્સનો ઉપયોગ કરો ચેટ કલર @@ -4834,6 +4871,7 @@ ફોટો પાડો ફોટો પસંદ કરો ફોટો + ટેક્સ્ટ સેવ કરો અવતાર દૂર કરો @@ -4894,7 +4932,7 @@ મેસેજ ઉમેરો જવાબ ઉમેરો આમને મોકલો - એક વખત જોવાનો મેસેજ + View once media એક અથવા વધુ વસ્તુઓ બહુ મોટી હતી એક અથવા વધુ વસ્તુઓ અમાન્ય હતી બહુ બધી વસ્તુઓ પસંદ કરી @@ -5487,7 +5525,7 @@ %1$s %2$s તમે - + %1$sતરફ%2$s જવાબ @@ -6704,5 +6742,11 @@ નોંધમાં ફેરફાર કરો + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index fea79ea198..a8eed9722d 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -415,6 +415,7 @@ जुड़ें + पूर्ण मीडिया भेजने में त्रुटि @@ -613,6 +614,7 @@ अनारक्षित करें डिलीट करें सभी को चुन लो + %1$d चुना हुआ %1$d चुना हुआ @@ -864,6 +866,7 @@ आमंत्रण भेजा गया %1$d आमंत्रण भेजे गए + “%1$s” को आपके द्वारा स्वचालित रूप से इस ग्रुप में शामिल नहीं किया जा सकता।\n\nउन्हें जुड़ने के लिए आमंत्रित किया गया है, और उन्हें ग्रुप के कोई भी मेसेज दिखाई नहीं देंगे जब तक वे स्वीकार नहीं कर लेते। इन उपयोगकर्ताओं को आपके द्वारा स्वचालित रूप से इस ग्रुप में शामिल नहीं किया जा सकता।\n\nउनको ग्रुप में जुड़ने के लिए आमंत्रित किया गया है, जब तक वे आमंत्रण स्वीकार नहीं कर लेते, ग्रुप के संदेश नहीं देख सकते। @@ -1044,7 +1047,9 @@ नाम और चित्र संपादित करें लेगेसी ग्रुप + यह एक लेगेसी ग्रुप है। फ़ीचर्स जैसे कि ग्रुप एडमिन केवल नए ग्रुप्स के लिए उपलब्ध हैं। + यह एक लेगेसी ग्रुप है। नए फ़ीचर्स जैसे कि @mentions और एडमिन तक पहुँच प्राप्त करने के लिए, इस लेगेसी ग्रुप को एक नए ग्रुप में अपग्रेड नहीं किया जा सकता क्योंकि यह बहुत बड़ा है। अधिकतम ग्रुप साइज़ है %1$d। इस समूह को अपग्रेड करें। @@ -1271,6 +1276,7 @@ डिलीट करें + %1$d चुना हुआ (%2$s) %1$d चुना हुआ (%2$s) @@ -1335,17 +1341,13 @@ आपने समूह छोड़ दिया है आपने समूह को अपडेट किया है। ग्रुप को अपडेट किया गया था। - + आउटगोइंग वॉयस कॉल - + आउटगोइंग वीडियो कॉल - - अनुत्तरित वॉयस कॉल - - अनुत्तरित वीडियो कॉल - + इनकमिंग वॉयस कॉल - + इनकमिंग वीडियो कॉल छूटी हुई वॉयस कॉल @@ -1355,10 +1357,6 @@ नोटिफ़िकेशन प्रोफ़ाइल चालू होने पर वॉयस कॉल छूट गई नोटिफ़िकेशन प्रोफ़ाइल चालू होने पर वीडियो कॉल छूट गई - - आपने एक वॉयस कॉल काट दी - - आपने एक वीडियो कॉल काट दी %1$s · %2$s %1$s ने समूह को अद्यतन किया @@ -1567,30 +1565,55 @@ %1$s अब भुगतान स्वीकार कर सकते हैं - %1$s ने एक ग्रुप कॉल शुरू की · %2$s - आपने एक ग्रुप कॉल शुरू की · %1$s - %1$s ग्रुप कॉल में है · %2$s - आप ग्रुप कॉल में हैं · %1$s - %1$s और %2$s ग्रुप कॉल में हैं · %3$s - ग्रुप कॉल · %1$s - - %1$s ने एक ग्रुप कॉल शुरू की - आपने एक ग्रुप कॉल शुरू की - %1$s ग्रुप कॉल में है - आप ग्रुप कॉल मे हैं - %1$s और %2$s ग्रुप कॉल में हैं - ग्रुप कॉल + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s आप - - %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं · %4$s - %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं - %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ सत्यापन कोड का अनुरोध करने में असमर्थ। कृपया नेटवर्क जाँचें और फिर से प्रयास करें। नॉन-स्टैंडर्ड नंबर फ़ॉर्मैट + आपके ज़रिए दर्ज किया गया नंबर (%1$s) एक नॉन-स्टैंडर्ड फ़ॉर्मैट लगता है.\n\nक्या आपका मतलब %2$s था? Signal एंड्रॉइड - फोन नंबर का फ़ॉर्मेट @@ -2709,6 +2733,8 @@ लोड हो रहा है अधिक जानें कॉल से जुड़ें + + Call back कॉल पर वापस जाएँ कॉल पूर्ण है मित्रों को आमंत्रित करें @@ -2773,6 +2799,7 @@ कॉल छोड़ें हो सकता है कि निम्नलिखित लोगों ने रीइंस्टॉल किया हो या डिवाइसेस को बदल लिया हो। गोपनीयता को सुनिश्चित करने के लिए उनके साथ अपने सुरक्षा नंबर को वेरिफाई करें। देखना + पहले से वेरिफाई किया हुआ @@ -3086,6 +3113,7 @@ डिफ़ॉल्ट उच्च + अधिकतम @@ -3446,6 +3474,7 @@ %1$s को भेजा गया आपने %1$s को %2$s पर %1$s %2$s को %3$s पर + किस को द्वारा भुगतान राशि और लेन-देन के समय सहित लेन-देन विवरण, MobileCoin लेजर का हिस्सा हैं। @@ -3508,6 +3537,7 @@ भुगतान की पुष्टि करें नेटवर्क शुल्क अनुमानित %1$s + किस को कुल राशि बैलेंस: %1$s @@ -4112,6 +4142,7 @@ क्लिपबोर्ड पर कॉपी किया गया है एडमिन + स्वीकार करें अस्वीकार करें @@ -4139,6 +4170,7 @@ ऑडियो संदेश · %1$s + %1$sसे%2$s @@ -4185,6 +4217,7 @@ %1$s और %2$s समूह में शामिल हुए %1$s, %2$s %3$s और समूह में शामिल हुए %1$s, %2$s और %3$d अन्य उपयोगकर्ता शामिल हुए + %1$s समूह छोड़ गए %1$sऔर %2$s समूह छोड़ गए %1$s, %2$s और %3$s समूह छोड़ गए @@ -4562,6 +4595,7 @@ संदेश संवाद गायब होने वाले मेसेज ऐप सुरक्षा + रीसेंट सूची में और ऐप के अंदर स्क्रीनशॉट ब्लॉक करें Signal संदेश और कॉल, हमेशा रिले होने वाले कॉल और सील्ड सेंडर नई चैट के लिए डिफ़ॉल्ट टाइमर @@ -4618,11 +4652,14 @@ मीडिया क्वालिटी मीडिया क्वालिटी भेजी गई हाई क्वालिटी मीडिया भेजने के लिए अधिक डेटा खर्च होगा। + उच्च + मानक कॉल + स्वतः कस्टम रंगों का उपयोग करें चैट का रंग @@ -4834,6 +4871,7 @@ फ़ोटो खींचें कोई फ़ोटो चुनें तस्वीर + टेक्स्ट सेव अवतार हटाएँ @@ -4894,7 +4932,7 @@ एक मेसेज शामिल करें एक जवाब शामिल करें को भेजें - एक बार मेसेज देखें + View once media एक या उससे ज़्यादा आइटम बहुत बड़े थे एक या अधिक आइटम अमान्य थे बहुत ज़्यादा आइटम चुने गए @@ -5487,7 +5525,7 @@ %1$s%2$s आप - + %1$sसे%2$s उत्तर @@ -6704,5 +6742,11 @@ नोट संपादित करें + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 112bde85ff..bdd0b8c611 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -421,6 +421,7 @@ Pridruži se + Puno Pogreška prilikom slanja medijskog sadržaja @@ -645,6 +646,7 @@ Dearhiviraj Izbriši Odaberi sve + %1$d odabran %1$d odabrana @@ -902,6 +904,7 @@ %1$d poslanih pozivnica %1$d poslanih pozivnica + Ne možete automatski dodati \"%1$s\" u grupu.\n\nKorisnik je pozvan i neće vidjeti poruke grupe dok ne prihvati pozivnicu. Ne možete automatski dodati ove korisnike u grupu.\n\nKorisnik je pozvan u grupu i neće vidjeti poruke grupe dok ne prihvati pozivnicu. @@ -1114,7 +1117,9 @@ Uredi naziv i sliku Naslijeđena grupa + Ovo je Naslijeđena grupa. Značajke poput grupnih administratora su dostupne samo za Nove grupe. + Ovo je Naslijeđena grupa. Da biste pristupili novim značajkama poput @spominjanja i administratora, Ova Naslijeđena grupa ne može se nadograditi u Novu grupu jer je prevelika. Maksimalna veličina grupe je %1$d. nadogradi ovu grupu. @@ -1359,6 +1364,7 @@ Izbriši + Odabrano %1$d (%2$s) Odabrano %1$d (%2$s) @@ -1425,17 +1431,13 @@ Napustili ste grupu. Ažurirali ste grupu. Grupa je ažurirana. - + Odlazni glasovni poziv - + Odlazni videopoziv - - Propušteni glasovni poziv - - Propušteni videopoziv - + Dolazni glasovni poziv - + Dolazni videopoziv Propušteni glasovni poziv @@ -1445,10 +1447,6 @@ Propustili ste glasovni poziv dok je profil obavijesti bio uključen Propustili ste videopoziv dok je profil obavijesti bio uključen - - Odbili ste glasovni poziv - - Odbili ste videopoziv %1$s · %2$s %1$s je ažurirao grupu. @@ -1673,34 +1671,59 @@ %1$s sada može prihvaćati uplate - %1$s je započeo/la grupni poziv · %2$s - Započeli ste grupni poziv · %1$s - %1$s je na ovom grupnom pozivu · %2$s - Vi ste na ovom grupnom pozivu · %1$s - %1$s i %2$s su na ovom grupnom pozivu · %3$s - Grupni poziv · %1$s - - %1$s je započeo/la grupni poziv - Započeli ste grupni poziv - %1$s je na ovom grupnom pozivu - Vi ste na ovom grupnom pozivu - %1$s i %2$s su na ovom grupnom pozivu - Grupni poziv + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Vi - - %1$s, %2$s i još %3$d osoba su na ovom grupnom pozivu · %4$s - %1$s, %2$s i još %3$d osobe su na ovom grupnom pozivu · %4$s - %1$s, %2$s i još %3$d osoba je na ovom grupnom pozivu · %4$s - %1$s, %2$s i još %3$d osoba su na ovom grupnom pozivu · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s i još %3$d osoba su na ovom grupnom pozivu - %1$s, %2$s i još %3$d osobe su na ovom grupnom pozivu - %1$s, %2$s i još %3$d osoba je na ovom grupnom pozivu - %1$s, %2$s i još %3$d osoba je na ovom grupnom pozivu + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Nije moguće zatražiti potvrdni kôd. Provjerite mrežnu vezu i pokušajte ponovno. Nestandardni format broja + Čini se da je broj koji ste unijeli (%1$s) nestandardnog formata.\n\nJeste li mislili %2$s? Signal Android – format broja telefona @@ -2883,6 +2907,8 @@ Učitavanje Saznajte više Pridruži se pozivu + + Call back Povratak na poziv Poziv je pun Pozovi prijatelje @@ -2947,6 +2973,7 @@ Napusti poziv Sljedeće su osobe možda ponovo instalirale ili promijenile uređaje. Provjerite svoj sigurnosni broj s njima kako biste osigurali privatnost. Pregledaj + Prethodno provjereno @@ -3274,6 +3301,7 @@ Zadano Visoko + Najviše @@ -3640,6 +3668,7 @@ Poslano za %1$s Vi dana %1$s u %2$s %1$s dana %2$s u %3$s + Za Od Pojedinosti o prijenosu kao i iznos uplate i vrijeme prijenosa dio su MobileCoin Ledgera. @@ -3702,6 +3731,7 @@ Potvrdi plaćanje Mrežna naknada Procijenjeno %1$s + Za Ukupan iznos Stanje: %1$s @@ -4324,6 +4354,7 @@ Kopirano u međuspremnik Administrator + Odobri Odbij @@ -4351,6 +4382,7 @@ Glasovna poruka: %1$s + %1$s za %2$s @@ -4401,6 +4433,7 @@ %1$s i %2$s se pridružio/la %1$s, %2$s i %3$s se pridružio/la %1$s, %2$s i %3$d osoba su se pridružili + %1$s je napustio/la %1$s i %2$s je napustio/la %1$s, %2$s i %3$s je napustio/la @@ -4782,6 +4815,7 @@ Razmjena poruka Poruke koje nestaju Sigurnost aplikacije + Blokira mogućnost snimke zaslona u nedavnim razgovorima i unutar aplikacije Signal poruke i pozivi, uvijek preusmjeravanje poziva i zapečaćeni pošiljatelj Zadani vremenski period za nove poruke @@ -4838,11 +4872,14 @@ Kvaliteta medija Kvaliteta poslanih medija Slanje visokokvalitetnih medija zahtjeva veću potrošnju mobilnih podataka. + Visoko + Standardno Pozivi + Auto Koristi prilagođene boje Boja razgovora @@ -5060,6 +5097,7 @@ Uslikaj Izaberi fotografiju Fotografija + Tekst Spremi Izbriši Avatara @@ -5128,7 +5166,7 @@ Dodaj poruku Dodajte odgovor Pošalji - Jednom vidljiva poruka + View once media Jedna ili više stavki bile su prevelike Jedna ili više stavki bile su nevažeće Odabrano je previše stavki @@ -5731,7 +5769,7 @@ %1$s%2$s Vi - + %1$s za %2$s Odgovori @@ -6996,5 +7034,11 @@ Uredi bilješku + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 0fbeba3f65..4589ce4577 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -415,6 +415,7 @@ Csatlakozás + Tele Hiba történt a médiafájl küldése során @@ -613,6 +614,7 @@ Nem archív Törlés Összes kivál. + %1$d kiválasztva %1$d kiválasztva @@ -864,6 +866,7 @@ Meghívó elküldve %1$d meghívó elküldve + Nem lehet \"%1$s\" nevű kontaktodat automatikusan a csoporthoz adni.\n\nBár már meg lett hívva, addig mégsem láthatja az csoportüzeneteket, míg el nem fogadja a meghívást. Ezeket a felhasználókat nem lehet automatikusan a csoporthoz adni.\n\nBár már meg lettek hívva, addig mégsem láthatják a csoportüzeneteket, míg el nem fogadják a meghívást. @@ -1044,7 +1047,9 @@ Név és kép szerkesztése Régi típusú csoport + Ez egy régi típusú csoport. Az újabb funkciók (pl. csoport-adminok) csak az újabb típusú csoportokban érhetőek el. + Ez egy régi típusú csoport. Az újabb funkciók eléréséhez, mint pl. az @említések és adminok, Ezt a régi típusú csoportot nem lehet új típusúra frissíteni, mert túl nagy. A maximális csoportméret %1$d tag. frissítsd a csoportot! @@ -1271,6 +1276,7 @@ Törlés + %1$d kiválasztva (%2$s) %1$d kiválasztva (%2$s) @@ -1335,17 +1341,13 @@ Kiléptél a csoportból. Frissítetted a csoportot. A csoport frissítve lett. - + Kimenő hanghívás - + Kimenő videóhívás - - Nem fogadott hanghívás - - Nem fogadott videóhívás - + Bejövő hanghívás - + Bejövő videóhívás Nem fogadott hanghívás @@ -1355,10 +1357,6 @@ Nem fogadott hanghívás, miközben az értesítési profil be van kapcsolva Nem fogadott videohívás, miközben az értesítési profil be van kapcsolva - - Elutasítottál egy hanghívást - - Elutasítottál egy videóhívást %1$s · %2$s %1$s frissítette a csoportot. @@ -1567,30 +1565,55 @@ %1$s mostantól fogadhat Kifizetéseket - %1$s csoport hívást indított · %2$s - Elindítottál egy csoporthívást · %1$s - %1$s csoporthívásban van · %2$s - Csoport hívásban vagy · %1$s - %1$s és %2$s csoporthívásban van · %3$s - Csoporthívás · %1$s - - %1$s csoporthívást indított - Elindítottad a csoporthívást - %1$s csoporthívásban van - Csoporthívásban vagy - %1$s és %2$s csoporthívásban van - Csoporthívás + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Te - - %1$s, %2$s és %3$d másik személy csoporthívásban van · %4$s - %1$s, %2$s és %3$d másik személy csoporthívásban van · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s és %3$d másik személy csoporthívásban van - %1$s, %2$s és %3$d másik személy csoporthívásban van + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Nem lehet ellenőrző kódot kérni. Kérjük, ellenőrizd a hálózati kapcsolatot, és próbáld újra! Nem szabványos számformátum + A begépelt szám (%1$s) nem tűnik szabványos formátumúnak.\n\nNem erre gondoltál: %2$s ? Signal Android - Telefonszám formátum @@ -2709,6 +2733,8 @@ Betöltés Tudj meg többet! Belépés a hívásba + + Call back Vissza a híváshoz A hívás betelt Barátok meghívása @@ -2773,6 +2799,7 @@ Kilépés a hívásból A következő személyek lehet, hogy újratelepítették a Signalt vagy készüléket cseréltek. Ellenőrizd biztonsági számotokat a privacy megőrzése érdekében! Megtekintés + Korábban megerősítve @@ -3086,6 +3113,7 @@ Alapértelmezett Magas + Legmagasabb @@ -3446,6 +3474,7 @@ Címzett: %1$s Te %1$s napján %2$s időpontban %1$s %2$s napján %3$s időpontban + Címzett Feladó A tranzakció részletei, beleértve a fizetési összeget és a tranzakció idejét, a MobileCoin Ledger részét képezik. @@ -3508,6 +3537,7 @@ Fizetés megerősítése Hálózati díj Becsült %1$s + Címzett Teljes összeg Egyenleg: %1$s @@ -4112,6 +4142,7 @@ Vágólapra másolva Admin + Jóváhagyás Elutasítás @@ -4139,6 +4170,7 @@ Hangüzenet · %1$s + %1$s (címzett: %2$s) @@ -4185,6 +4217,7 @@ %1$s és %2$s csatlakozott %1$s, %2$s és %3$s csatlakozott %1$s, %2$s és %3$d másik személy csatlakozott + %1$s kilépett %1$s és %2$s kilépett %1$s, %2$s és %3$s kilépett @@ -4562,6 +4595,7 @@ Üzenetküldés Eltűnő üzenetek App biztonság + Képernyőképek készítésének letiltása a futó alkalmazások listájában és az alkalmazáson belül Signal üzenetek és hívások, átjátszási beállítások, rejtett feladó Alapértelmezett időzítő új csevegésekhez @@ -4618,11 +4652,14 @@ Médiafájl minősége Elküldött médiafájl minősége A nagy felbontású médiafájl elküldése több adatforgalommal jár. + Magas + Normál Hívások + Auto Egyedi színek használata Csevegés színe @@ -4834,6 +4871,7 @@ Kép készítése Fotó választás Fotó + Szöveg Mentés Profilkép törlése @@ -4894,7 +4932,7 @@ Szöveg hozzáadása Válasz hozzáadása Címzett - Egyszer megjelenő üzenet + View once media Egy vagy több elem túl nagy méretű Egy vagy több elem érvénytelen formátumú Túl sok kijelölt elem @@ -5487,7 +5525,7 @@ %1$s %2$s Te - + %1$s (címzett: %2$s) Válasz @@ -6704,5 +6742,11 @@ Megjegyzés szerkesztése + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 59ae4d0253..cc7ae1f80a 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -412,6 +412,7 @@ Bergabung + Penuh Gagal mengirim media @@ -597,6 +598,7 @@ Buka Arsip Hapus Pilih semua + %1$d dipilih @@ -845,6 +847,7 @@ %1$d undangan dikirimkan + \"%1$s\" tidak dapat secara otomatis ditambahkan ke grup oleh Anda.\n\nMereka telah diundang untuk bergabung, dan tidak akan melihat pesan grup sampai mereka menerimanya. Pengguna ini tidak dapat secara otomatis Anda tambahkan ke dalam grup.\n\nMereka telah mendapatkan undangan, dan tidak dapat melihat pesan di dalam grup sampai mereka menerimanya. @@ -1009,7 +1012,9 @@ Sunting nama dan gambar Grup Lama + Ini adalah Grup Lama. Fitur-fitur seperti admin grup hanya tersedia untuk Grup Baru. + Ini adalah Grup Lama. Untuk mengakses fitur-fitur baru seperti @mention dan admin, Grup lama ini tidak bisa ditingkatkan menjadi grup yang baru karena terlalu besar. Ukuran maksimum grup adalah %1$d. perbarui grup ini. @@ -1227,6 +1232,7 @@ Hapus + %1$d dipilih (%2$s) @@ -1290,17 +1296,13 @@ Anda telah keluar dari grup. Anda memperbarui grup. Grup telah diiperbarui. - + Panggilan suara keluar - + Panggilan video keluar - - Panggilan suara tidak terjawab - - Panggilan video tidak terjawab - + Panggilan suara masuk - + Panggilan video masuk Panggilan suara tidak terjawab @@ -1310,10 +1312,6 @@ Panggilan suara tidak terjawab saat profil notifikasi aktif Panggilan video tidak terjawab saat profil notifikasi aktif - - Anda menolak panggilan suara - - Anda menolak panggilan video %1$s . %2$s %1$s memperbarui grup. @@ -1514,28 +1512,53 @@ %1$s sekarang dapat menerima Pembayaran - %1$s memulai panggilan grup. %2$s - Anda memulai panggilan grup · %1$s - %1$s berada di panggilan grup. %2$s - Anda berada di panggilan grup. %1$s - %1$s dan %2$s berada di panggilan grup. %3$s - Panggilan grup. %1$s - - %1$s memulai panggilan grup - Anda memulai panggilan grup - %1$s berada di panggilan grup - Anda berada dalam panggilan grup - %1$s dan %2$s berada di panggilan grup - Panggilan grup + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Anda - - %1$s, %2$s, dan %3$d lainnya berada di panggilan grup. %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, dan %3$d lainnya berada di panggilan grup + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ Tidak dapat meminta kode verifikasi. Harap periksa koneksi internet dan coba lagi. Format nomor tidak umum + Nomor yang Anda masukkan (%1$s) terlihat bukan format nomor yang umum.\n\nApakah maksud Anda %2$s? Signal Android - Format Nomor Telepon @@ -2622,6 +2646,8 @@ Memuat Pelajari lebih lanjut Bergabung panggilan + + Call back Kembali ke panggilan Panggilan penuh Undang teman @@ -2686,6 +2712,7 @@ Tinggalkan panggilan Orang-orang berikut mungkin telah memasang ulang atau mengganti perangkat. Verifikasi nomor keamanan Anda dengan mereka untuk memastikan privasi. Lihat + Diverifikasi sebelumnya @@ -2992,6 +3019,7 @@ Standar Tinggi + Maks @@ -3349,6 +3377,7 @@ Dikrim ke %1$s Anda pada %1$s pada %2$s %1$s pada %2$s pada %3$s + Kepada Dari Detail transaksi termasuk jumlah pembayaran dan waktu dari transaksi adalah bagian dari Ledger MobileCoin. @@ -3411,6 +3440,7 @@ Konfirmasi pembayaran Biaya jaringan Perkiraan %1$s + Kepada Jumlah total Saldo: %1$s @@ -4006,6 +4036,7 @@ Disalin ke papan klip Admin + Terima Tolak @@ -4033,6 +4064,7 @@ Pesan suara. %1$s + %1$s hingga %2$s @@ -4077,6 +4109,7 @@ %1$s dan %2$s telah bergabung %1$s,%2$s, dan %3$s telah bergabung %1$s, %2$s, %3$d dan lainnya telah bergabung + %1$s telah keluar %1$s dan %2$s telah keluar %1$s, %2$s dan %3$s keluar @@ -4452,6 +4485,7 @@ Olah pesan Penghilangan pesan Keamanan aplikasi + Blokir tangkapan layar di daftar terbaru dan di dalam aplikasi Pesan dan panggilan Signal, selalu relay panggilan, dan menggunakan pengirim tertutup Timer default untuk obrolan baru @@ -4508,11 +4542,14 @@ Kualitas media Kualitas media terkirim Mengirimkan media dengan kualitas tinggi akan menggunakan lebih banyak data. + Tinggi + Standar Panggilan + Auto Gunakan warna kustom Warna obrolan @@ -4721,6 +4758,7 @@ Ambil gambar Pilih sebuah foto Foto + Teks Simpan Hapus avatar @@ -4777,7 +4815,7 @@ Tambah pesan Tambah balasan Kirim ke - Pesan satu tayangan + View once media Ukuran satu atau beberapa media terlalu besar Satu atau beberapa media salah Terlalu banyak media dipilih @@ -5365,7 +5403,7 @@ %1$s %2$s Anda - + %1$s hingga %2$s Balas @@ -6558,5 +6596,11 @@ Edit catatan + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index dc536f4f00..25b2b896ba 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -415,6 +415,7 @@ Unisciti + Piena Errore durante l\'invio del media @@ -613,6 +614,7 @@ Non archiviare Elimina Scegli tutto + %1$d selezionata %1$d selezionate @@ -864,6 +866,7 @@ Invito mandato %1$d inviti mandati + “%1$s” non può essere aggiunto automaticamente a questo gruppo da te.\n\nÈ stato invitato a unirsi al gruppo e non vedrà alcun messaggio del gruppo fino a quando non accetterà. Questi utenti non possono essere aggiunti automaticamente a questo gruppo da te.\n\nSono stati invitati a unirsi al gruppo e non vedranno alcun messaggio del gruppo fino a quando non accetteranno. @@ -1044,7 +1047,9 @@ Modifica nome e immagine Gruppi Legacy + Questo è un Gruppo Legacy. Funzionalità come gli amministratori di gruppo sono disponibili solo per i Nuovi Gruppi. + Questo è un Gruppo Legacy. Per accedere a nuove funzionalità come @menzioni e amministratori, Questo Gruppo Legacy non può essere aggiornato a un Nuovo Gruppo perché è troppo grande. La dimensione massima del gruppo è %1$d. aggiorna questo gruppo. @@ -1271,6 +1276,7 @@ Elimina + %1$d selezionato (%2$s) %1$d selezionati (%2$s) @@ -1335,17 +1341,13 @@ Hai lasciato il gruppo. Hai aggiornato il gruppo. Il gruppo è stato aggiornato. - + Chiamata in uscita - + Videochiamata in uscita - - Chiamata senza risposta - - Videochiamata senza risposta - + Chiamata in arrivo - + Videochiamata in arrivo Chiamata persa @@ -1355,10 +1357,6 @@ Chiamata vocale persa per lo stato del profilo Videochiamata persa per lo stato del profilo - - Hai rifiutato una chiamata - - Hai rifiutato una videochiamata %1$s · %2$s %1$s ha aggiornato il gruppo. @@ -1567,30 +1565,55 @@ Ora %1$s può accettare e ricevere i pagamenti - %1$s ha iniziato una chiamata di gruppo · %2$s - Hai iniziato una chiamata di gruppo · %1$s - %1$s è nella chiamata di gruppo · %2$s - Sei nella chiamata di gruppo · %1$s - %1$s e %2$s sono nella chiamata di gruppo · %3$s - Chiamata di gruppo · %1$s - - %1$s ha iniziato una chiamata di gruppo - Hai iniziato una chiamata di gruppo - %1$s è nella chiamata di gruppo - Sei nella chiamata di gruppo - %1$s e %2$s sono nella chiamata di gruppo - Chiamata di gruppo + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Tu - - %1$s, %2$s e %3$d altra persona sono nella chiamata di gruppo · %4$s - %1$s, %2$s e %3$d altre persone sono nella chiamata di gruppo · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s e %3$d altra persona sono nella chiamata di gruppo - %1$s, %2$s e %3$d altre persone sono nella chiamata di gruppo + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Impossibile richiedere un codice di verifica. Controlla la tua connessione di rete e riprova. Formato del numero non standard + Il numero che hai inserito (%1$s) sembra essere in un formato non standard.\n\nIntendevi %2$s? Signal Android - Formato Numero di Telefono @@ -2709,6 +2733,8 @@ Caricamento Scopri di più Unisciti alla chiamata + + Call back Torna alla chiamata La chiamata è piena Invita amici @@ -2773,6 +2799,7 @@ Abbandona chiamata Le seguenti persone potrebbero aver reinstallato o cambiato i dispositivi. Verifica il tuo codice di sicurezza con loro per garantire la privacy. Mostra + Verificato in precedenza @@ -3086,6 +3113,7 @@ Predefinito Alta + Massima @@ -3446,6 +3474,7 @@ Inviato a %1$s You il %1$s alle %2$s %1$s il %2$s alle %3$s + A Da I dettagli della transazione, compreso l\'importo del pagamento e l\'ora della transazione, fanno parte del MobileCoin Ledger. @@ -3508,6 +3537,7 @@ Conferma pagamento Commissione rete Stimati %1$s + A Importo totale Saldo: %1$s @@ -4112,6 +4142,7 @@ Copiato negli appunti Amministratore + Approva Rifiuta @@ -4139,6 +4170,7 @@ Messaggio vocale · %1$s + %1$s a %2$s @@ -4185,6 +4217,7 @@ %1$s e %2$s si sono uniti %1$s, %2$s e %3$s si sono uniti %1$s, %2$s e altri %3$d si sono uniti + %1$s ha abbandonato %1$s e %2$s hanno abbandonato %1$s, %2$s e %3$s hanno abbandonato @@ -4562,6 +4595,7 @@ Messaggistica Messaggi a scomparsa Sicurezza app + Blocca gli screenshot nell\'elenco delle chat recenti e all\'interno dell\'app Messaggi e chiamate Signal, ritrasmetti sempre le chiamate e mittente sigillato Timer predefinito per nuove chat @@ -4618,11 +4652,14 @@ Qualità media Qualità media inviati L\'invio di media in alta qualità utilizzerà più dati. + Alta + Normale Chiamate + Auto Usa colori personalizzati Colore chat @@ -4834,6 +4871,7 @@ Scatta una foto Scegli una foto Foto + Testo Salva Rimuovi avatar @@ -4894,7 +4932,7 @@ Aggiungi un messaggio Aggiungi una risposta Invia a - Messaggio visualizzabile una volta + View once media Uno o più elementi erano troppo grandi Uno o più elementi non erano validi Troppi elementi selezionati @@ -5487,7 +5525,7 @@ %1$s %2$s Tu - + %1$s a %2$s Rispondi @@ -6704,5 +6742,11 @@ Modifica appunti + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 68b4d43db3..378816fe65 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -421,6 +421,7 @@ הצטרף + מלאה שגיאה בשליחת מדיה @@ -645,6 +646,7 @@ הוצאה מארכיון מחיקה בחירת הכל + %1$d נבחר %1$d נבחרו @@ -902,6 +904,7 @@ %1$d הזמנות נשלחו %1$d הזמנות נשלחו + “%1$s” לא יכול/ה להתווסף באופן אוטומטי אל קבוצה זו על ידך.\n\nהוא/יא הוזמן/ה להצטרף, ולא יראה/תראה הודעות קבוצה כלשהן עד שהוא/היא יאשר/תאשר. משתמשים אלו אינם יכולים להתווסף באופן אוטומטי אל קבוצה זו על ידך.\n\nהם הוזמנו להצטרף אל הקבוצה, ולא יראו הודעות קבוצה כלשהן עד שהם יאשרו. @@ -1114,7 +1117,9 @@ ערוך שם ותמונה קבוצה מיושנת + זוהי קבוצה מיושנת. מאפיינים כמו מנהלני קבוצה זמינים רק בקבוצות חדשות. + זוהי קבוצה מיושנת. כדי להשיג גישה אל מאפיינים חדשים כמו @אזכורים ומנהלנים, קבוצה מיושנת זו אינה יכולה להשתדרג אל קבוצה חדשה מאחר שהיא גדולה מדי. גודל הקבוצה המרבי הוא %1$d. שדרג קבוצה זו. @@ -1359,6 +1364,7 @@ מחיקה + %1$d נבחר (%2$s) %1$d נבחרו (%2$s) @@ -1425,17 +1431,13 @@ עזבת את הקבוצה. עדכנת את הקבוצה. הקבוצה עודכנה. - + שיחה קולית יוצאת - + שיחת וידאו יוצאת - - שיחה קולית שלא נענתה - - שיחת וידאו שלא נענתה - + שיחה קולית נכנסת - + שיחת וידאו נכנסת שיחה קולית שלא נענתה @@ -1445,10 +1447,6 @@ שיחה קולית שלא נענתה בזמן שפרופיל ההתראות פועל שיחת וידאו שלא נענתה בזמן שפרופיל ההתראות פועל - - דחית שיחה קולית - - דחית שיחת וידאו %1$s · %2$s %1$s עדכן את הקבוצה. @@ -1673,34 +1671,59 @@ ל%1$s יש אפשרות לקבל תשלומים עכשיו - %1$s התחיל/ה שיחה קבוצתית · %2$s - התחלת שיחה קבוצתית · %1$s - %1$s בשיחה הקבוצתית · %2$s - את/ה בשיחה הקבוצתית · %1$s - %1$s וגם %2$s בשיחה הקבוצתית · %3$s - שיחה קבוצתית · %1$s - - %1$s התחיל/ה שיחה קבוצתית - התחלת שיחה קבוצתית - %1$s בשיחה הקבוצתית - את/ה בשיחה הקבוצתית - %1$s וגם %2$s בשיחה הקבוצתית - שיחה קבוצתית + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$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, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$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, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ לא ניתן לבקש קוד אימות. כדאי לבדוק את חיבור הרשת שלך ולנסות שוב. תסדיר אי־תקני של מספר + המספר שהכנסת (%1$s) כנראה בתסדיר בלתי תקני.\n\nהאם התכוונת אל %2$s? Signal Android - תסדיר מספר טלפון @@ -2883,6 +2907,8 @@ טוען למד עוד הצטרף לשיחה + + Call back חזור לשיחה השיחה מלאה הזמן חברים @@ -2947,6 +2973,7 @@ עזוב שיחה האנשים הבאים ייתכן התקינו מחדש את היישום או שינו מכשירים. וודא את מספר הביטחון שלך איתם כדי להבטיח פרטיות. הצג + וֻדָּא בעבר @@ -3274,6 +3301,7 @@ ברירת מחדל גבוה + מרבי @@ -3640,6 +3668,7 @@ נשלח אל %1$s את/ה בתאריך %1$s בשעה %2$s %1$s בתאריך %2$s בשעה %3$s + אל מאת פרטי עסקה כולל סכום התשלום וזמן העסקה הם חלק מ–MobileCoin Ledger. @@ -3702,6 +3731,7 @@ אשר תשלום עמלת רשת משוער %1$s + אל סכום כולל מאזן: %1$s @@ -4324,6 +4354,7 @@ הועתק ללוח מנהלן + אשר דחה @@ -4351,6 +4382,7 @@ הודעה קולית · %1$s + %1$s אל %2$s @@ -4401,6 +4433,7 @@ %1$s וגם %2$s הצטרפו %1$s, %2$s וגם %3$s הצטרפו %1$s, %2$s ועוד %3$d אחרים הצטרפו + %1$s עזב/ה %1$s וגם %2$s עזבו %1$s, %2$s וגם %3$s עזבו @@ -4782,6 +4815,7 @@ תכתובת הודעות נעלמות אבטחת יישום + חסימת צילומי מסך ברשימת השיחות האחרונות ובתוך האפליקציה הודעות ושיחות של Signal, ממסר שיחות תמיד, ושולח אטום קוצב זמן ברירת מחדל עבור צ׳אטים חדשים @@ -4838,11 +4872,14 @@ איכות מדיה הגדר איכות מדיה שליחת מדיה באיכות גבוהה תשתמש ביותר נתונים. + גבוהה + תקנית שיחות + אוטומטי השתמש בצבעים מותאמים אישית צבע צ\'אט @@ -5060,6 +5097,7 @@ צלם בחר תמונה תמונה + מלל שמור נקה יצגן @@ -5128,7 +5166,7 @@ הוסף הודעה הוסף תשובה שליחה אל - הודעה לצפייה חד־פעמית + View once media פריט אחד או יותר היו יותר מדי גדולים פריט אחד או יותר היו בלתי תקפים יותר מדי פריטים נבחרו @@ -5731,7 +5769,7 @@ %1$s%2$s את/ה - + %1$s אל %2$s השב @@ -6996,5 +7034,11 @@ עריכת הערה + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index fed7e4035a..ec4ec0c06f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -302,7 +302,7 @@ 送信されませんでした。タップして詳細を表示 - 一部だけ送信しました。タップして詳細を表示 + 一部のみ送信。タップして詳細を表示 送信に失敗しました。 %1$s がグループを抜けました。 送信保留中 @@ -412,6 +412,7 @@ 参加する + 満席 メディア送信エラー @@ -597,6 +598,7 @@ アーカイブを解除する 消去する すべて選択する + %1$d件選択済み @@ -845,6 +847,7 @@ %1$d件の招待を送信済み + 「%1$s」を自動的にこのグループに追加することはできません。\n\nユーザーは招待を受け入れるまで、グループメッセージを閲覧できません。 これらのユーザーをを自動的にこのグループに追加することはできません。\n\nユーザーは招待を受け入れるまで、グループメッセージを閲覧できません。 @@ -1009,7 +1012,9 @@ 名前と写真の編集 レガシーグループ + これはレガシーグループです。グループ管理者などの機能は新しいグループでのみ利用可能です。 + これはレガシーグループです。@メンションや管理者などの新機能を使用するには、 このレガシーグループは大きすぎるため、新しいグループにアップグレードできません。グループの最大人数は%1$d人です。 このグループをアップグレードしてください。 @@ -1227,6 +1232,7 @@ 消去 + %1$d件選択済み (%2$s) @@ -1290,17 +1296,13 @@ グループを抜けました。 グループを更新しました。 グループが更新されました。 - + 音声通話発信 - + ビデオ通話発信 - - 音声通話応答なし - - ビデオ通話応答なし - + 音声通話着信 - + ビデオ通話着信 音声通話着信あり @@ -1310,10 +1312,6 @@ 通知プロファイルがオンのときに不在着信になった音声通話がありました 通知プロファイルがオンのときに不在着信になったビデオ通話がありました - - 音声通話を拒否しました - - ビデオ通話を拒否しました %1$s · %2$s %1$s がグループを更新しました。 @@ -1514,28 +1512,53 @@ %1$s さんが決済機能を有効にしました - %1$s がグループ通話を開始しました · %2$s - グループ通話を開始しました · %1$s - %1$s はグループ通話中です · %2$s - グループ通話中です · %1$s - %1$s と %2$s はグループ通話中です · %3$s - グループ通話 · %1$s - - %1$s がグループ通話を開始しました - グループ通話を開始しました - %1$s はグループ通話中です - グループ通話中です - %1$s と %2$s はグループ通話中です - グループ通話 + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s あなた - - %1$s, %2$s ほか%3$d名がグループ通話中です · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s ほか%3$d名がグループ通話中です + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ 認証コードをリクエストできません。ネットワーク接続を確認して再度試してください。 非標準の数字フォーマット + あなたが入力した数字 (%1$s) は、非標準のフォーマットのようです。\n\n%2$s を意図していすか? Signal Android - 電話番号フォーマット @@ -2622,6 +2646,8 @@ 読み込んでいます 詳しく見る 通話に参加する + + Call back 通話に戻る 満席です 友達を招待する @@ -2686,6 +2712,7 @@ 通話から抜ける 以下の方はアプリを再インストールしたか端末を変更した可能性があります。プライバシーを保証するために安全番号を検証してください。 表示する + 以前に検証済み @@ -2992,6 +3019,7 @@ 既定 + 最大 @@ -3349,6 +3377,7 @@ %1$s に送金済み あなた (%1$s %2$s) %1$s (%2$s %3$s) + 送金先 送金元 決済金額や決済時刻などの取引の詳細は、MobileCoin台帳に記載されます。 @@ -3411,6 +3440,7 @@ 決済を確認 ネットワーク手数料 概算 %1$s + 宛先 合計金額 残高: %1$s @@ -4006,6 +4036,7 @@ クリップボードにコピーしました 管理者 + 承認する 拒否する @@ -4033,6 +4064,7 @@ 音声メッセージ · %1$s + %1$s から %2$s @@ -4077,6 +4109,7 @@ %1$s と %2$s が参加しました %1$s, %2$s および %3$s が参加しました %1$s, %2$s ほか%3$d名が参加しました + %1$s が退出しました %1$s と %2$s が退出しました %1$s, %2$s および %3$s が退出しました @@ -4452,6 +4485,7 @@ メッセージ送受信 消えるメッセージ アプリのセキュリティ + 「最近使ったアプリ」やアプリ内でのスクリーンショットをブロックします。 Signalメッセージと通話、通話を常に中継、送信者の秘匿化 新規チャットの既定タイマー @@ -4508,11 +4542,14 @@ メディアの画質 送信メディアの画質 高画質なメディアの送信には、より多くのデータを使用します。 + 高画質 + 標準 通話 + 自動 カスタム色を使用 チャットの色 @@ -4721,6 +4758,7 @@ 写真を撮影 画像を選択 写真 + テキスト 保存 アバターを削除 @@ -4777,7 +4815,7 @@ メッセージを追加してください 返信を追加 宛先 - 使い捨てメッセージ + View once media 1つ以上のアイテムのサイズが大きすぎます 1つ以上のアイテムが不正です 選択されたアイテムが多すぎます @@ -5365,7 +5403,7 @@ %1$s %2$s あなた - + %1$s から %2$s 返信 @@ -6558,5 +6596,11 @@ メモを編集する + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 4a8047ac9f..8443443513 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -415,6 +415,7 @@ შემოუერთდი + სრული მედია ფაილის გაგზავნისას შეცდომა მოხდა @@ -613,6 +614,7 @@ დეარქივაცია წაშლა მონიშნე ყველა + %1$d არჩეულია %1$d არჩეულია @@ -864,6 +866,7 @@ მოწვევა გაგზავნილია გაგზავნილია %1$d მოწვევა + “%1$s”-ის შენს მიერ ამ ჯგუფში ავტომატურად დამატება შეუძლებელია.\n\nისინი მოწვეულნი არიან გასაწევრიანებლად და ვერ ნახავენ ჯგუფურ შეტყობინებებს, სანამ მოწვევას არ დაეთანხმებიან. შენ მიერ ამ მომხმარებლების ამ ჯგუფში ავტომატურად დამატება შეუძლებელია.\n\nისინი მოწვეული არიან ჯგუფში გასაწევრიანებლად და ვერ ნახავენ ჯგუფურ შეტყობინებებს, სანამ მოწვევას არ დაეთანხმებიან. @@ -1044,7 +1047,9 @@ სახელისა და სურათის რედაქტირება მოძველებული ჯგუფი + ეს მოძველებული ჯგუფია. ჯგუფის ადმინების მსგავსი ფუნქციები მხოლოდ ახალი ჯგუფებისთვისაა ხელმისაწვდომი. + ეს მოძველებული ჯგუფია. იმისათვის, რომ მიიღო წვდომა ახალ ფუნქციებზე, მაგალითად @მონიშვნებსა და ადმინებზე, ეს მოძველებული ჯგუფი ვერ განახლდება ახალ ჯგუფად, რადგან ზედმეტად დიდია. ჯგუფის მაქსიმალური ზომა არის %1$d. ამ ჯგუფის განახლება. @@ -1271,6 +1276,7 @@ წაშლა + %1$d არჩეულია (%2$s) %1$d არჩეულია (%2$s) @@ -1335,17 +1341,13 @@ შენ დატოვე ჯგუფი. შენ განაახლე ჯგუფი. ჯგუფი განახლებულია. - + გამავალი ხმოვანი ზარი - + გამავალი ვიდეო ზარი - - უპასუხო ხმოვანი ზარი - - უპასუხო ვიდეო ზარი - + შემომავალი ხმოვანი ზარი - + შემომავალი ვიდეო ზარი გამოტოვებული ხმოვანი ზარი @@ -1355,10 +1357,6 @@ გამოტოვებული ხმოვანი ზარი ჩართული შეტყობინების პროფილის დროს გამოტოვებული ვიდეო ზარი ჩართული შეტყობინების პროფილის დროს - - შენ უარყავი ხმოვანი ზარი - - შენ უარყავი ვიდეო ზარი %1$s · %2$s %1$s-მა განაახლა ჯგუფი. @@ -1567,30 +1565,55 @@ %1$s-ს უკვე შეუძლია ტრანზაქციები მიიღოს - %1$s-მა ჯგუფური ზარი წამოიწყო · %2$s - შენ დიწყე ჯგუფური ზარი · %1$s - %1$s ჯგუფურ ზარზეა · %2$s - ჯგუფურ ზარზე ხარ · %1$s - %1$s და %2$s ჯგუფურ ზარზე არიან · %3$s - ჯგუფური ზარი · %1$s - - %1$s-მა ჯგუფური ზარი წამოიწყო - შენ დაიწყე ჯგუფური ზარი - %1$s ჯგუფურ ზარზეა - ჯგუფურ ზარზე ხარ - %1$s და %2$s ჯგუფურ ზარზე არიან - ჯგუფური ზარი + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s შენ - - %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარში არიან · %4$s - %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარზე არიან · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარში არიან - %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარზე არიან + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ ვერიფიკაციის კოდის მოთხოვნა შეუძლებელია. გთხოვთ, შეამოწმო შენი ქსელის კავშირი და თავიდან სცადო. ნომრის არასტანდარტული ფორმატი + შენ მიერ შეყვანილი ნომერი (%1$s), როგორც ჩანს, არასტანდარტული ფორმატისაა. %2$s-ს გულისხმობდი? Signal Android-ი - ტელეფონის ნომრის ფორმატი @@ -2709,6 +2733,8 @@ იტვირთება გაიგე მეტი ზარზე შესვლა + + Call back ზარზე დაბრუნება ზარი გადავსებულია მოიწვიე მეგობრები @@ -2773,6 +2799,7 @@ ზარის დატოვება ამ ადამიანებმა შესაძლოა გადააყენეს ან შეცვალეს მოწყობილობები. კონფიდენციალურობის უზრუნველსაყოფად, დაადასტურე შენი უსაფრთხოების ნომერი მათთან. View + წინა ვერიფიცირებული @@ -3086,6 +3113,7 @@ ძირითადი მაღალი + მაქსიმალური @@ -3446,6 +3474,7 @@ გაგზავნილია %1$s-სთან შენ %1$s-ს %2$s-ზე %1$s %2$s-ს %3$s-ზე + To From ტრანზაქციის დეტალები, მათ შორის გადახდის თანხა და ტრანზაქციის დრო, არის MobileCoin Ledger-ის ნაწილი. @@ -3508,6 +3537,7 @@ ტრანზაქციის დადასტურება ქსელის საფასური ნავარაუდევი %1$s + To ჯამი ბალანსი: %1$s @@ -4112,6 +4142,7 @@ დაკოპირებულია ბუფერში ადმინი + დადასტურება უარყოფა @@ -4139,6 +4170,7 @@ ხმოვანი შეტყობინება · %1$s + %1$s %2$s-ს @@ -4185,6 +4217,7 @@ %1$s და %2$s შემოგიერთდნენ %1$s, %2$s და %3$s შემოგიერთდნენ %1$s, %2$s და %3$d სხვები შემოგიერთდნენ + %1$s გავიდა %1$s და %2$s გავიდნენ %1$s, %2$s და %3$s გავიდნენ @@ -4562,6 +4595,7 @@ მიმოწერა გაქრობადი შეტყობინებები აპის უსაფრთხოება + მიმდინარე სიასა და აპის შიგნით სქრინშოტების დაბლოკვა Signal-ის შეტყობინებები და ზარები, ყოველთვის გადამისამართებული ზარებია და დაფარული გამომგზავნი ნაგულისხმევი ტაიმერი ახალი ჩატებისთვის @@ -4618,11 +4652,14 @@ მედია-ფაილების ხარისხი გაგზავნილი მედია-ფაილების ხარისხი მაღალი ხარისხის მედია-ფაილის გაგზავნა უფრო მეტ მობილურ ინტერნეტს გამოიყენებს. + მაღალი + სტანდარტული ზარები + ავტომატური ფერების მორგება ჩატის ფერი @@ -4834,6 +4871,7 @@ სურათის გადაღება ფოტოს არჩევა ფოტო + ტექსტი შენახვა ავატარის წაშლა @@ -4894,7 +4932,7 @@ შეტყობინების დამატება პასუხის დამატება გაეგზავნოთ ჩამოთვლილებს - ერთხელ სანახავი შეტყობინება + View once media ერთი ან მეტი ელემენტი ზედმეტად დიდი იყო ერთი ან მეტი ელემენტი არავალიდური იყო მონიშნულია ზედმეტი ელემენტი @@ -5487,7 +5525,7 @@ %1$s %2$s შენ - + %1$s %2$s-ს პასუხი @@ -6704,5 +6742,11 @@ ჩანიშვნის რედაქტირება + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 41c8cdb7db..3e5c58810e 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -415,6 +415,7 @@ Қосылу + Толы Мультимедианы жіберу кезінде қате шықты @@ -613,6 +614,7 @@ Мұрағатты ашу Жою Барлығ. таңдау + %1$d таңдалды %1$d таңдалды @@ -864,6 +866,7 @@ Шақыру жіберілді %1$d шақыру жіберілді + Сіз “%1$s” контактісін бұл топқа автоматты түрде қоса алмайсыз.\n\nОған шақыру жіберілді, ол шақыруды қабылдамайынша, топтағы хаттарды көре алмайды. Сіз бұл пайдаланушыларды бұл топқа автоматты түрде қоса алмайсыз.\n\nОларға шақыру жіберілді, олар шақыруды қабылдамайынша, топтағы хаттарды көре алмайды. @@ -1044,7 +1047,9 @@ Атауы мен суретін өзгерту Ескі топ + Бұл – ескі топ. Топ әкімшілері сияқты функциялар тек Жаңа топтарда қолжетімді. + Бұл – ескі топ. @атап өту және әкімшілер сияқты жаңа функцияларды пайдалану үшін Бұл Ескі топты Жаңа топқа жаңарту мүмкін емес, себебі ол өте үлкен. Максималды топ өлшемі – %1$d. осы топты жаңартыңыз. @@ -1271,6 +1276,7 @@ Жою + %1$d таңдалды (%2$s) %1$d таңдалды (%2$s) @@ -1335,17 +1341,13 @@ Сіз топтан шығып кетіңіз. Сіз аватарды жаңарттыңыз. Бұл топ жаңартылды. - + Шығыс дауыстық қоңырау - + Шығыс видеоқоңырау - - Жауап берілмеген дауыстық қоңырау - - Жауап берілмеген видеоқоңырау - + Кіріс дауыстық қоңырау - + Кіріс видеоқоңырау Қабылданбаған дауыстық қоңырау @@ -1355,10 +1357,6 @@ Хабарландыру профилі ашылып тұрғанда, дауыстық қоңырау қабылданбады Хабарландыру профилі ашылып тұрғанда, видеоқоңырау қабылданбады - - Дауыстық қоңырауды көтермедіңіз - - Видеоқоңырауды көтермедіңіз %1$s · %2$s %1$s топты жаңартты. @@ -1567,30 +1565,55 @@ %1$s енді Төлемдер қабылдай алады - %1$s топтық қоңырауды бастады · %2$s - Топтық қоңырау басталған уақыт · %1$s - %1$s топтық қоңырауға қатысып отыр · %2$s - Топтық қоңырауға қатысып отырсыз · %1$s - %1$s және %2$s топтық қоңырауға қатысып отыр · %3$s - Топтық қоңырау · %1$s - - %1$s топтық қоңырауды бастады - Топтық қоңырау бастадыңыз - %1$s топтық қоңырауға қатысып отыр - Топтық қоңырауға қатысып отырсыз - %1$s және %2$s топтық қоңырауға қатысып отыр - Топтық қоңырау + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Сіз - - %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр · %4$s - %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр - %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Верификация кодын сұрау мүмкін емес. Желі байланысын тексеріп, қайталап көріңіз. Стандартты емес нөмір форматы + Сіз енгізген нөмірдің (%1$s) форматы стандартты емес екен.\n\n%2$s деп жазғыңыз келіп пе еді? Signal Android - Телефон нөмірінің форматы @@ -2709,6 +2733,8 @@ Жүктеу Толық ақпарат Қоңырауға қосылу + + Call back Қоңырауға оралу Қоңырауға бұдан артық адам қосыла алмайды Достарды шақыру @@ -2773,6 +2799,7 @@ Қоңыраудан шығу Мына адамдар құрылғыларын қайта орнатқан немесе өзгерткен болуы мүмкін. Құпиялылықты сақтау үшін олармен қауіпсіздік нөмірін тексеріңіз. View + Бұған дейін верификацияланды @@ -3086,6 +3113,7 @@ Әдепкі параметр Жоғары + Макс. @@ -3446,6 +3474,7 @@ %1$s деген кісіге жіберілді %1$s күні сағат %2$s %1$s %2$s күні сағат %3$s + To From Транзакция мәліметтері, оның ішінде төлем сомасы және транзакция уақыты – MobileCoin тіркеу кітабының бір бөлігі. @@ -3508,6 +3537,7 @@ Төлемді растау Желі ақысы Болжалды %1$s + To Жалпы сома Баланс: %1$s @@ -4112,6 +4142,7 @@ Буферге көшірілді Әкімші + Мақұлдау Қабылдамау @@ -4139,6 +4170,7 @@ Дауыстық хат · %1$s + %1$s – %2$s @@ -4185,6 +4217,7 @@ %1$s және %2$s қосылды %1$s, %2$s және %3$s қосылды %1$s, %2$s және тағы %3$d адам қосылды + %1$s шығып кетті %1$s және %2$s шығып кетті %1$s, %2$s және %3$s шығып кетті @@ -4562,6 +4595,7 @@ Хат алмасу Disappearing messages Қолданба қауіпсіздігі + Соңғылар тізімінде және қолданбаның ішінде скиншоттарды блоктау Signal хаттары мен қоңыраулары, үнемі ретрансляциялық қоңыраулар және қорғалған жіберуші Жаңа чаттарға арналған әдепкі таймер @@ -4618,11 +4652,14 @@ Мультимедиа сапасы Жіберілген мультимедиа сапасы Жоғары сапалы мультимедианы жібергенде, трафик көбірек қолданылады. + Жоғары + Стандарт Қоңыраулар + Авто Реттелмелі түстерді қолдану Чат түсі @@ -4834,6 +4871,7 @@ Суретке түсіру Фотосуретті таңдау Фотосурет + Мәтін Сақтау Аватарды тазарту @@ -4894,7 +4932,7 @@ Хат қосу Жауап қосу Алушы - Бір рет көрінетін хат + View once media Бір немесе бірнеше элемент өте үлкен болды Бір немесе бірнеше элемент дұрыс болмады Тым көп элемент таңдалды @@ -5487,7 +5525,7 @@ %1$s %2$s Сіз - + %1$s – %2$s Жауап қату @@ -6680,7 +6718,7 @@ Лақап ат - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Лақап аттар мен ескертпелер Signal-да сақталады және толықтай шифрланады. Ол тек сізге көрінеді. Аты @@ -6694,9 +6732,9 @@ Сақтау - Delete? + Жою керек пе? - This will permanently delete any nickname and note you’ve set. + Бұл функция сіз орнатқан кез келген лақап атты және ескертпені біржола жояды. @@ -6704,5 +6742,11 @@ Ескертпені өзгерту + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index 2fae57363a..3e80fdaca5 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -412,6 +412,7 @@ ចូលរួម + ពេញ បញ្ហាបញ្ជូនមេឌា @@ -597,6 +598,7 @@ លុបពីបណ្ណសារ លុប រើសយកទាំងអស់ + %1$d បានជ្រើសរើស @@ -845,6 +847,7 @@ ការអញ្ជើញ%1$d បានផ្ញើ + “%1$s” មិនអាចបន្ថែមដោយស្វ័យប្រវត្តិចូលក្រុមនេះដោយអ្នកបានទេ។.\n\nពួកគេត្រូវបានអញ្ជើញចូលរួម ហើយនឹងមិនឃើញសារក្នុងក្រុម នោះទេ រហូតដល់ពួកគេយល់ព្រមការចូលរួម។ អ្នកប្រើប្រាស់ទាំងនេះ មិនអាចបន្ថែមដោយស្វ័យប្រវត្តិចូលក្រុមនេះដោយអ្នកបានទេ។.\n\nពួកគេត្រូវបានអញ្ជើញចូលរួម ហើយពួកគេនឹងមិនឃើញសារក្នុងក្រុមនោះទេ រហូតដល់ពួកគេយល់ព្រមការចូលរួម។ @@ -1009,7 +1012,9 @@ កែប្រែឈ្មោះ និងរូបភាព ក្រុមមុន + នេះជាក្រុមជំនាន់មុនមួយ។ មុខងារដូចជា អ្នកគ្រប់គ្រងក្រុម មានតែសម្រាប់ក្រុមថ្មីប៉ុណ្ណោះ។ + នេះជាក្រុមជំនាន់មុន។ ដើម្បីចូលប្រើប្រាស់មុខងារថ្មី ដូចជា @mentions និងអ្នកគ្រប់គ្រង នេះជាក្រុមជំនាន់មុន មិនអាចដំឡើងទៅក្រុមថ្មីបាន ដោយសារវាមានទំហំធំពេក។ ទំហំអតិបរមារបស់ក្រុមគឺ %1$d។ ដំឡើងក្រុមនេះ។ @@ -1227,6 +1232,7 @@ លុប + %1$d បានជ្រើសរើស (%2$s) @@ -1290,17 +1296,13 @@ អ្នកបានចាកចេញពីក្រុម។ អ្នកបានធ្វើបច្ចុប្បន្នភាពក្រុម។ ក្រុមនេះ ត្រូវបានធ្វើបច្ចុប្បន្នភាព។ - + ការហៅចេញជាសំឡេង - + ការហៅចេញជាវីដេអូ - - ការហៅជាសំឡេងដែលមិនបានឆ្លើយតប - - ការហៅជាវីដេអូដែលមិនបានឆ្លើយតប - + ការហៅចូលជាសំឡេង - + ការហៅចូលជាវីដេអូ ការហៅជាសំឡេងខកមិនបានទទួល @@ -1310,10 +1312,6 @@ ខកមិនបានទទួលការហៅជាសំឡេង ពេលបើកប្រូហ្វាល់ជូនដំណឹង ខកមិនបានទទួលការហៅជាវីដេអូ ពេលបើកប្រូហ្វាល់ជូនដំណឹង - - អ្នកមិនព្រមទទួលការហៅជាសំឡេង - - អ្នកមិនព្រមទទួលការហៅជាវីដេអូ %1$s · %2$s %1$s បានធ្វើបច្ចុប្បន្នភាពក្រុម។ @@ -1514,28 +1512,53 @@ %1$s ឥឡូវអាចទទួលយកការបង់ប្រាក់បាន - %1$s បានចាប់ផ្តើមការហៅជាក្រុម · %2$s - អ្នកបានចាប់ផ្តើមការហៅជាក្រុម · %1$s - %1$s នៅក្នុងការហៅជាក្រុម · %2$s - អ្នកនៅក្នុងការហៅជាក្រុម · %1$s - %1$s និង %2$s នៅក្នុងការហៅជាក្រុម · %3$s - ការហៅជាក្រុម · %1$s - - %1$s បានចាប់ផ្តើមការហៅជាក្រុម - អ្នកបានចាប់ផ្តើមការហៅជាក្រុម - %1$s នៅក្នុងការហៅជាក្រុម - អ្នកនៅក្នុងការហៅជាក្រុម - %1$s និង %2$s នៅក្នុងការហៅជាក្រុម - ការហៅជាក្រុម + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s អ្នក - - %1$s, %2$s, និង%3$d ផ្សេងទៀតនៅក្នុងការហៅជាក្រុម · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, និង%3$d ផ្សេងទៀតនៅក្នុងការហៅជាក្រុម + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ មិនអាចស្នើសុំលេខកូដផ្ទៀងផ្ទាត់បានទេ។ សូមពិនិត្យមើលការតភ្ជាប់បណ្តាញរបស់អ្នក ហើយព្យាយាមម្តងទៀត។ ទម្រង់លេខមិនស្តង់ដារ + លេខដែលអ្នកបានបញ្ចូល (%1$s) មានទម្រង់លេខមិនស្តង់ដារ.\n\nតើអ្នកមានន័យថា %2$s? Signal Android - ទម្រង់លេខទូរស័ព្ទ @@ -2622,6 +2646,8 @@ កំពុងផ្ទុក សិក្សាបន្ថែម ចូលរួមការហៅ + + Call back ត្រឡប់ទៅការហៅ ការហៅពេញ អញ្ជើញមិត្តភក្តិ @@ -2686,6 +2712,7 @@ បិទការហៅ អ្នកទាំងនេះអាចដំឡើង ឬផ្លាស់ប្តូរឧបករណ៍។​ ផ្ទៀងផ្ទាត់លេខសុវត្ថិភាពរបស់អ្នកជាមួយពួកគេ ដើម្បីធានាឯកជនភាព។ បង្ហាញ + បានផ្ទៀងផ្ទាត់ពីមុន @@ -2992,6 +3019,7 @@ លំនាំដើម ខ្ពស់ + អតិបរិមា @@ -3349,6 +3377,7 @@ បានផ្ញើទៅ %1$s អ្នកលើ %1$s នៅ %2$s %1$s លើ %2$s នៅ %3$s + ទៅ ពី ព័ត៌មានលម្អិតអំពីប្រតិបត្តិការ រួមទាំងចំនួនទឹកប្រាក់បង់ និងពេលវេលានៃប្រតិបត្តិការ គឺជាផ្នែកមួយនៃ MobileCoin Ledger។ @@ -3411,6 +3440,7 @@ បញ្ជាក់ការទូទាត់ ថ្លៃសេវាបណ្តាញ ប៉ាន់ស្មាន %1$s + ទៅ ចំនួនសរុប សមតុល្យ៖ %1$s @@ -4006,6 +4036,7 @@ បានចម្លងទៅ clipboard អ្នកគ្រប់គ្រង + អនុញាតិ បដិសេធ @@ -4033,6 +4064,7 @@ សារជាសម្លេង%1$s + %1$s ទៅ%2$s @@ -4077,6 +4109,7 @@ %1$s និង %2$s បានចូលរួម %1$s %2$s និង %3$s បានចូលរួម %1$s %2$s និង %3$d បានចូលរួម + %1$s នៅសល់ %1$s និង %2$s នៅសល់ %1$s %2$s និង %3$s នៅសល់ @@ -4452,6 +4485,7 @@ ការផ្ញើសារ សារដែលបាត់ទៅវិញ សន្តិសុខកម្មវិធី + ទប់ស្កាត់រូបថតអេក្រង់នៅក្នុងបញ្ជីថ្មីៗនេះ និងក្នុងកម្មវិធី សារ និងការហៅ Signal តែងតែបញ្ជូនការហៅទូរសព្ទ និងអ្នកផ្ញើដែលបិទជិត ពេលវេលាតាមលំនាំដើមសម្រាប់ការសន្ទនាថ្មីៗ @@ -4508,11 +4542,14 @@ គុណភាពមេឌៀ មេឌៀគុណភាពល្អត្រូវបានផ្ញើចេញ ការផ្ញើឯកសារមេឌៀដែលមានគុណភាពខ្ពស់នឹងអស់ទិន្នន័យអុីនធឺណិតច្រើន + ខ្ពស់ + ស្ដង់ដារ ការហៅ + ស្វ័យប្រវត្តិ ប្រើពណ៌បង្កើតខ្លួនឯង ពណ៌ការជជែក @@ -4721,6 +4758,7 @@ ថតរូប ជ្រើសរើសរូបថត រូបភាព + អក្សរ រក្សាទុក លុបរូបតំណាង @@ -4777,7 +4815,7 @@ បន្ថែមសារមួយ បន្ថែមការឆ្លើយតប ផ្ញើទៅ - មើលសារម្ដង + View once media ធាតុមួយ ឬច្រើនមានទំហំធំពេក មានធាតុមួយ ឬច្រើនមិនត្រឹមត្រូវ ធាតុដែលបានជ្រើសរើសច្រើនពេក @@ -5365,7 +5403,7 @@ %1$s %2$s អ្នក - + %1$s ទៅ%2$s ឆ្លើយតប @@ -6534,7 +6572,7 @@ ឈ្មោះហៅក្រៅ - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + ឈ្មោះហៅក្រៅ និងកំណត់ចំណាំត្រូវបានរក្សាទុកជាមួយ Signal និងត្រូវបានអ៊ីនគ្រីបទាំងសងខាង។ មានតែអ្នកទេដែលអាចមើលឃើញពួកវា។ ឈ្មោះ @@ -6548,9 +6586,9 @@ រក្សាទុក - Delete? + លុបឬ? - This will permanently delete any nickname and note you’ve set. + ការធ្វើបែបនេះនឹងលុបជាអចិន្ត្រៃយ៍នូវឈ្មោះហៅក្រៅ និងកំណត់ចំណាំដែលអ្នកបានកំណត់។ @@ -6558,5 +6596,11 @@ កែកំណត់ចំណាំ + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index fa1a222497..b8dc3f67bf 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -415,6 +415,7 @@ ಸೇರು + ಸಂಪೂರ್ಣ ಮೀಡಿಯಾ ಕಳುಹಿಸುವಲ್ಲಿ ದೋಷ @@ -613,6 +614,7 @@ ಅನ್ಆ‌ರ್ಕೈವ್ ಅಳಿಸಿ ಎಲ್ಲ ಆಯ್ಕೆಮಾಡಿ + %1$d ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ %1$d ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ @@ -864,6 +866,7 @@ ಆಹ್ವಾನವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ %1$d ಆಹ್ವಾನಗಳನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ + \"%1$s\" ನೀವು ಈ ಗುಂಪಿಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸೇರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅವರನ್ನು ಸೇರಲು ಆಹ್ವಾನಿಸಲಾಗಿದೆ, ಮತ್ತು ಅವರು ಸ್ವೀಕರಿಸುವವರೆಗೂ ಯಾವುದೇ ಗುಂಪು ಸಂದೇಶಗಳನ್ನು ನೋಡುವುದಿಲ್ಲ. ಈ ಬಳಕೆದಾರರನ್ನು ನೀವು ಈ ಗುಂಪಿಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸೇರಿಸಲಾಗುವುದಿಲ್ಲ. ಅವರನ್ನು ಗುಂಪಿಗೆ ಸೇರಲು ಆಹ್ವಾನಿಸಲಾಗಿದೆ, ಮತ್ತು ಅವರು ಸ್ವೀಕರಿಸುವವರೆಗೆ ಯಾವುದೇ ಗುಂಪು ಸಂದೇಶಗಳನ್ನು ನೋಡುವುದಿಲ್ಲ. @@ -1044,7 +1047,9 @@ ಹೆಸರು ಮತ್ತು ಚಿತ್ರವನ್ನು ಎಡಿಟ್ ಮಾಡಿ ಹಳೆಯ ಗುಂಪು + ಇದು ಲೆಗಸಿ ಗ್ರೂಪ್. ಗ್ರೂಪ್‌ ಅಡ್ಮಿನ್‌ಗಳಂತಹ ವೈಶಿಷ್ಟ್ಯಗಳು ಹೊಸ ಗ್ರೂಪ್‌ಗಳಲ್ಲಿ ಮಾತ್ರ ಲಭ್ಯವಿರುತ್ತವೆ. + ಇದು ಲೆಗಸಿ ಗ್ರೂಪ್. @mentions ಮತ್ತು ಅಡ್ಮಿನ್‌ಗಳಂತಹ ಹೊಸ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು, ಈ ಹಳೆಯ ಗ್ರೂಪ್‌ ಅನ್ನು ಹೊಸ ಗ್ರೂಪ್‌ಗೆ ಅಪ್‌ಗ್ರೇಡ್ ಮಾಡಲಾಗದು. ಏಕೆಂದರೆ ಇದು ತುಂಬಾ ದೊಡ್ಡದಾಗಿದೆ. ಗರಿಷ್ಠ ಗ್ರೂಪ್ ಗಾತ್ರವು %1$d. ಈ ಗುಂಪನ್ನು ಅಪ್‌ಗ್ರೇಡ್ ಮಾಡಿ @@ -1271,6 +1276,7 @@ ಅಳಿಸಿ + %1$d ಅವರು (%2$s) ಆಯ್ಕೆ ಮಾಡಿದ್ದಾರೆ %1$d ಅವರು (%2$s) ಆಯ್ಕೆ ಮಾಡಿದ್ದಾರೆ @@ -1335,17 +1341,13 @@ ನೀವು ಗುಂಪನ್ನು ಬಿಟ್ಟಿದ್ದೀರಿ. ನೀವು ಗುಂಪನ್ನು ನವೀಕರಿಸಿದ್ದೀರಿ. ಗ್ರೂಪ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗಿದೆ. - + ಹೊರಹೋಗುವ ವಾಯ್ಸ್ ಕಾಲ್ - + ಹೊರಹೋಗುವ ವೀಡಿಯೊ ಕಾಲ್ - - ಉತ್ತರಿಸದ ವಾಯ್ಸ್ ಕಾಲ್ - - ಉತ್ತರಿಸದ ವೀಡಿಯೊ ಕಾಲ್ - + ಒಳಬರುವ ವಾಯ್ಸ್ ಕಾಲ್ - + ಒಳಬರುವ ವೀಡಿಯೊ ಕಾಲ್ ಮಿಸ್ಡ್ ವಾಯ್ಸ್ ಕಾಲ್ @@ -1355,10 +1357,6 @@ ಅಧಿಸೂಚನೆ ಪ್ರೊಫೈಲ್ ಆನ್ ಆಗಿರುವಾಗ ತಪ್ಪಿಹೋದ ಧ್ವನಿ ಕರೆ ಅಧಿಸೂಚನೆ ಪ್ರೊಫೈಲ್ ಆನ್ ಆಗಿರುವಾಗ ತಪ್ಪಿಹೋದ ವೀಡಿಯೊ ಕರೆ - - ನೀವು ವಾಯ್ಸ್ ಕಾಲ್ ಅನ್ನು ನಿರಾಕರಿಸಿದ್ದೀರಿ - - ನೀವು ಒಂದು ವೀಡಿಯೊ ಕಾಲ್ ಅನ್ನು ನಿರಾಕರಿಸಿದ್ದೀರಿ %1$s · %2$s %1$s ಗುಂಪನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ. @@ -1567,30 +1565,55 @@ %1$s ಅವರು ಈಗ ಪಾವತಿಗಳನ್ನು ಸ್ವೀಕರಿಸಬಹುದು - %1$s ಅವರು ಗ್ರೂಪ್ ಕಾಲ್ ಆರಂಭಿಸಿದ್ದಾರೆ · %2$s - ನೀವು ಗ್ರೂಪ್ ಕರೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ್ದೀರಿ · %1$s - %1$s ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ · %2$s - ನೀವು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದೀರಿ · %1$s - %1$s ಮತ್ತು %2$s ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ · %3$s - ಗ್ರೂಪ್ ಕಾಲ್ · %1$s - - %1$s ಅವರು ಗ್ರೂಪ್ ಕಾಲ್ ಆರಂಭಿಸಿದ್ದಾರೆ - ನೀವು ಗುಂಪು ಕರೆಯೊಂದನ್ನು ಪ್ರಾರಂಭಿಸಿದ್ದೀರಿ - %1$s ಅವರು ಗ್ರೂಪ್ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ - ನೀವು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದೀರಿ - %1$s ಮತ್ತು %2$s ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ - ಗ್ರೂಪ್ ಕಾಲ್ + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s ನೀವು - - %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ · %4$s - %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ - %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ ದೃಢೀಕರಣ ಕೋಡ್ ಅನ್ನು ವಿನಂತಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ. ಪ್ರಮಾಣೀಕೃತವಲ್ಲದ ಸಂಖ್ಯಾ ಸ್ವರೂಪ + ನೀವು ನಮೂದಿಸಿದ ಸಂಖ್ಯೆ (%1$s) ಪ್ರಮಾಣೀಕೃತವಲ್ಲದ ಸ್ವರೂಪದಲ್ಲಿ ಕಾಣಿಸುತ್ತಿದೆ.\n\nನೀವು ಅಂದುಕೊಂಡಿದ್ದು ಇದೇನಾ %2$s? Signal ಆಂಡ್ರಾಯ್ಡ್ - ಫೋನ್ ಸಂಖ್ಯೆ ಸ್ವರೂಪ @@ -2709,6 +2733,8 @@ ಲೋಡ್ ಆಗುತ್ತಿದೆ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ ಕರೆಗೆ ಸೇರಿ + + Call back ಕರೆಗೆ ಹಿಂತಿರುಗಿ ಕರೆ ಭರ್ತಿಯಾಗಿದೆ ಸ್ನೇಹಿತರನ್ನು ಆಹ್ವಾನಿಸಿ @@ -2773,6 +2799,7 @@ ಕರೆ ಬಿಡಿ ಈ ಕೆಳಗಿನ ಜನರು ಸಾಧನಗಳನ್ನು ಪುನಃ ಇನ್‌‌ಸ್ಟಾಲ್ ಮಾಡಿರಬಹುದು ಅಥವಾ ಬದಲಾಯಿಸಿರಬಹುದು. ಗೌಪ್ಯತೆಯನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು ಅವರೊಂದಿಗೆ ನಿಮ್ಮ ಸುರಕ್ಷತಾ ಸಂಖ್ಯೆಯನ್ನು ದೃಢೀಕರಿಸಿ. ತೋರಿಸು + ಮೊದಲೇ ಪರಿಶೀಲಿಸಲಾಗಿದೆ @@ -3086,6 +3113,7 @@ ಡೀಫಾಲ್ಟ್ ಹೆಚ್ಚು + ಗರಿಷ್ಠ @@ -3446,6 +3474,7 @@ %1$s ಗೆ ಕಳುಹಿಸಲಾಗಿದೆ ನೀವು %2$s ರಲ್ಲಿ %1$s ಆಗಿದ್ದೀರಿ %1$s ರಂದು %3$s ರಲ್ಲಿ %2$s + ಗೆ ಇಂದ ಪಾವತಿ ಮೊತ್ತ ಮತ್ತು ವಹಿವಾಟಿನ ಸಮಯ ಸೇರಿದಂತೆ ವಹಿವಾಟು ವಿವರಗಳು ಮೊಬೈಲ್‌ಕಾಯಿನ್‌ ಲೆಡ್ಜರ್‌ನ ಭಾಗವಾಗಿದೆ. @@ -3508,6 +3537,7 @@ ಪೇಮೆಂಟ್ ದೃಢೀಕರಿಸಿ ನೆಟ್‌ವರ್ಕ್‌ ಶುಲ್ಕ ಅಂದಾಜು ಮಾಡಿದ %1$s + ಗೆ ಒಟ್ಟು ಮೊತ್ತ ಬ್ಯಾಲೆನ್ಸ್: %1$s @@ -4112,6 +4142,7 @@ ಕ್ಲಿಪ್‌‌ಬೋರ್ಡಿಗೆ ನಕಲು ಮಾಡಲಾಗಿದೆ ಅಡ್ಮಿನ್‌‌ + ಅನುಮತಿಸಿ ನಿರಾಕರಿಸಿ @@ -4139,6 +4170,7 @@ ಧ್ವನಿ ಸಂದೇಶ . %1$s + %1$s ಗೆ %2$s @@ -4185,6 +4217,7 @@ %1$s ಹಾಗು %2$s ಸೇರಿಕೊಂಡಿದ್ದಾರೆ %1$s, %2$s ಹಾಗು %3$s ಸೇರಿಕೊಂಡಿದ್ದಾರೆ %1$s, %2$s ಹಾಗು %3$d ಇನ್ನಿತರು ಸೇರಿಕೊಂಡಿದ್ದಾರೆ + %1$s ಉಳಿದಿದ್ದಾರೆ %1$s ಹಾಗು %2$s ಉಳಿದಿದ್ದಾರೆ %1$s, %2$s ಹಾಗು %3$s ಉಳಿದಿದ್ದಾರೆ @@ -4562,6 +4595,7 @@ ಸಂದೇಶ ಕಳುಹಿಸುವಿಕೆ ಕಣ್ಮರೆಯಾಗುವ ಸಂದೇಶಗಳು ಆಪ್ ಸುರಕ್ಷತೆ + ಇತ್ತೀಚಿನ ಪಟ್ಟಿಯಲ್ಲಿ ಮತ್ತು ಆ್ಯಪ್‌ನ ಒಳಗೆ ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ Signal ಮೆಸೇಜ್‌ಗಳು ಮತ್ತು ಕಾಲ್‌ಗಳು, ಎಂದಿಗೂ ಕಾಲ್‌ಗಳನ್ನು ರಿಲೇ ಮಾಡಿ ಮತ್ತು ಸೀಲ್ ಮಾಡಿದ ಕಳುಹಿಸುವವರು ಹೊಸ ಚಾಟ್‌ಗಳಿಗೆ ಡೀಫಾಲ್ಟ್ ಟೈಮರ್ @@ -4618,11 +4652,14 @@ ಮಾಧ್ಯಮ ಗುಣಮಟ್ಟ ಮಾಧ್ಯಮ ಗುಣಮಟ್ಟವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ ಉನ್ನತ ಗುಣಮಟ್ಟದ ಮಾಧ್ಯಮವನ್ನು ಕಳುಹಿಸುವಿಕೆಯು ಹೆಚ್ಚು ಡೇಟಾವನ್ನು ಬಳಸಲಿದೆ. + ಹೆಚ್ಚು + ಪ್ರಮಾಣಿತ ಕರೆಗಳು + ಸ್ವಯಂಚಾಲಿತ ಕಸ್ಟಮ್ ಬಣ್ಣಗಳನ್ನು ಬಳಸಿ ಚಾಟ್ ಬಣ್ಣ @@ -4834,6 +4871,7 @@ ಚಿತ್ರವನ್ನು ತೆಗೆಯಿರಿ ಚಿತ್ರವನ್ನು ಆಯ್ಕೆಮಾಡಿ ಫೋಟೋ + ಪಠ್ಯ ಉಳಿಸಿ ಅವತಾರವನ್ನು ತೆಗೆಯಿರಿ @@ -4894,7 +4932,7 @@ ಮೆಸೇಜ್ ಸೇರಿಸಿ ಉತ್ತರ ಸೇರಿಸಿ ಇವರಿಗೆ ಕಳುಹಿಸಿ - ಏಕ ವೀಕ್ಷಣೆ ಮೆಸೇಜ್ + View once media ಒಂದು ಅಥವಾ ಹೆಚ್ಚು ಐಟಂಗಳು ತೀರಾ ದೊಡ್ಡದಾಗಿವೆ ಒಂದು ಅಥವಾ ಹೆಚ್ಚು ಐಟಂಗಳು ಅಮಾನ್ಯವಾಗಿವೆ ತುಂಬಾ ಐಟಂಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ @@ -5487,7 +5525,7 @@ %1$s %2$s ನೀವು - + %1$s ಗೆ %2$s ಉತ್ತರಿಸಿ @@ -6704,5 +6742,11 @@ ಟಿಪ್ಪಣಿ ಎಡಿಟ್ ಮಾಡಿ + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index dd00ace892..854916a849 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -412,6 +412,7 @@ 참가 + 최대한도 도달 미디어 전송 오류 @@ -597,6 +598,7 @@ 보관 해제 삭제 모두 선택 + %1$d개 선택됨 @@ -845,6 +847,7 @@ %1$d에게 초대가 전송됨 + \'%1$s\' 님을 내가 그룹에 자동으로 추가할 수 없습니다.\n\n가입 초대를 받았으며 수락할 때까지 그룹 메시지가 표시되지 않습니다. 이 사용자들을 내가 그룹에 자동으로 추가할 수 없습니다.\n\n그룹에 가입하도록 초대되었으며 수락할 때까지 그룹 메시지가 표시되지 않습니다. @@ -1009,7 +1012,9 @@ 이름과 그림 수정하기 구형 그룹 + 구형 그룹입니다. 그룹 관리자와 같은 기능은 새 그룹에서만 사용할 수 있습니다. + 구형 그룹입니다. @멘션 및 관리자와 같은 새로운 기능에 액세스하려면 이 구형 그룹의 규모가 너무 커서 새 그룹으로 업그레이드할 수 없습니다. 최대 그룹 규모는 %1$d입니다. 그룹을 업그레이드하세요. @@ -1227,6 +1232,7 @@ 삭제 + %1$d개 선택됨 (%2$s) @@ -1290,17 +1296,13 @@ 그룹에서 탈퇴했습니다. 그룹을 업데이트했습니다. 그룹이 업데이트 되었습니다. - + 발신 음성 통화 - + 발신 영상 통화 - - 미응답 음성 통화 - - 미응답 영상 통화 - + 수신 음성 통화 - + 수신 영상 통화 부재중 음성 통화 @@ -1310,10 +1312,6 @@ 알림 프로필을 사용하는 동안 받지 못한 전화가 있습니다. 알림 프로필을 사용하는 동안 받지 못한 화상 통화가 있습니다. - - 음성 통화를 거부했습니다. - - 영상 통화를 거부했습니다. %1$s · %2$s %1$s 님이 그룹을 업데이트했습니다. @@ -1514,28 +1512,53 @@ %1$s 님이 이제 결제를 수락할 수 있습니다. - %1$s 님이 그룹 통화를 시작했습니다(%2$s). - 내가 그룹 통화를 시작함 · %1$s - %1$s 님이 그룹 통화 중입니다(%2$s). - 그룹 통화 중입니다(%1$s). - %1$s 및 %2$s 님이 그룹 통화 중입니다(%3$s). - 그룹 통화: %1$s - - %1$s 님이 그룹 통화를 시작했습니다. - 그룹 통화를 시작함 - %1$s 님이 그룹 통화 중입니다. - 그룹 통화 중입니다. - %1$s 및 %2$s 님이 그룹 통화 중입니다. - 그룹 통화 + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s - - %1$s, %2$s 및 %3$d명이 그룹 통화 중입니다(%4$s). + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s 및 %3$d명이 그룹 통화 중입니다. + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ 확인 코드를 요청하지 못했습니다. 네트워크 연결을 확인하고 다시 시도하세요. 비표준 전화번호 형식 + 입력한 번호(%1$s)가 표준 형식이 아닙니다.\n\n혹시 입력하려던 번호가 %2$s인가요? Signal Android - 전화번호 형식 @@ -2622,6 +2646,8 @@ 로드 중 더 알아보기 통화 참여 + + Call back 통화로 돌아가기 통화 참가자 수 최대 친구 초대 @@ -2686,6 +2712,7 @@ 전화 종료 재설치했거나 기기를 바꾼 사람의 목록입니다. 보안을 보장하려면 안전 번호를 검증하세요. 보기 + 이전에 확인됨 @@ -2992,6 +3019,7 @@ 기본 높음 + 최대 @@ -3349,6 +3377,7 @@ %1$s에게 전송됨 %1$s의 %2$s %1$s, %2$s, %3$s + 받는이: 보낸이: 지불 금액 및 거래 시간을 비롯한 거래 세부 정보는 MobileCoin Ledger의 일부입니다. @@ -3411,6 +3440,7 @@ 결제 확인 네트워크 요금 예상 %1$s + 받는이: 총액 잔액: %1$s @@ -4006,6 +4036,7 @@ 클립보드로 복사됨 관리자 + 승인 거부 @@ -4033,6 +4064,7 @@ 음성 메시지 %1$s + %1$s~%2$s @@ -4077,6 +4109,7 @@ %1$s 및 %2$s 가입함 %1$s, %2$s 및 %3$s 가입함 %1$s, %2$s 및 %3$d명이 가입함 + %1$s 남음 %1$s 및 %2$s 남음 %1$s, %2$s 및 %3$s 남음 @@ -4452,6 +4485,7 @@ 메시징 사라지는 메시지 애플리케이션 보안 + 최근 사용 목록과 앱 내 스크린샷 차단 신호 메시지 및 통화, 항상 중계 통화 및 봉인된 발신자 새 대화를 위한 기본 타이머 @@ -4508,11 +4542,14 @@ 미디어 품질 미디어 전송 품질 고품질 미디어는 더 많은 모바일 데이터를 사용할 수 있습니다. + 높음 + 기본 전화 + 자동 사용자 지정 색상 사용하기 대화창 색 @@ -4721,6 +4758,7 @@ 사진을 찍다 사진 선택 사진 + 텍스트 저장 아바타 지우기 @@ -4777,7 +4815,7 @@ 메시지 추가 답장 추가 받는 사람 - 한 번 보기 메시지 + View once media 하나 이상의 항목이 너무 큽니다. 하나 이상의 항목이 유효하지 않습니다. 항목을 너무 많이 선택했습니다. @@ -5365,7 +5403,7 @@ %1$s %2$s - + %1$s~%2$s 답장 @@ -6534,7 +6572,7 @@ 별칭 - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + 별칭과 메모는 Signal에 엔드투엔드 암호화되어 저장됩니다. 해당 항목은 사용자에게만 표시됩니다. 이름 @@ -6548,9 +6586,9 @@ 저장 - Delete? + 삭제하시겠어요? - This will permanently delete any nickname and note you’ve set. + 설정한 별칭과 메모를 영구 삭제합니다. @@ -6558,5 +6596,11 @@ 메모 수정 + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 9c632968b6..f43f06e44b 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -412,6 +412,7 @@ Кошулуу + Толуп калган Медиафайлды жөнөтүүдө ката кетти @@ -597,6 +598,7 @@ Архивден чыгар Өчүрүү Баарын тандоо + %1$d тандалды @@ -845,6 +847,7 @@ %1$d чакыруу жөнөтүлдү + “%1$s” автоматтык түрдө бул топко кошула албайт.\n\nАны топко кошулууга чакыргансыз жана чакырууну кабыл алмайынча топтогу билдирүүлөрдү көрө албайт. Бул колдонуучулар автоматтык түрдө бул топко кошула алышпайт.\n\nАларды топко кошулууга чакыргансыз жана чакырууну кабыл алмайынча топтогу билдирүүлөрдү көрө алышпайт. @@ -1009,7 +1012,9 @@ Ысым менен сүрөттү өзгөртүү Эски топ + Бул эски топ. Топтун администраторлору сыяктуу функциялар жаңы топтордо гана иштейт. + Бул эски топ. @айтыпөтүүлөр жана администраторлор сыяктуу жаңы функцияларды пайдалануу үчүн Бул эски топ өтө чоң болгондуктан, жаңы топко жаңырбайт. Топтун көлөмү %1$d ашпаш керек. бул топту жаңыртыңыз. @@ -1227,6 +1232,7 @@ Өчүрүү + %1$d тандалды (%2$s) @@ -1290,17 +1296,13 @@ Топтон чыктыңыз. Топту жаңырттыңыз. Топ жаңыртылды. - + Чыгуучу аудио чалуу - + Чыгуучу аудио чалуу - - Жооп берилбеген аудио чалуу - - Жооп берилбеген аудио чалуу - + Келүүчү аудио чалуу - + Келүүчү аудио чалуу Кабыл алынбаган аудио чалуу @@ -1310,10 +1312,6 @@ Билдирмелер профили иштеп турганда кабыл алынбай калган аудио чалуу Билдирмелер профили иштеп турганда кабыл алынбай калган видео чалуу - - Аудио чалуудан баш тарттыңыз - - Видео чалуудан баш тарттыңыз %1$s · %2$s %1$s топту жаңыртты. @@ -1514,28 +1512,53 @@ %1$s эми төлөмдөрдү ала алат - %1$s топтун ичинде чалып баштады · %2$s - Топтук чалууну баштадыңыз · %1$s - %1$s топтун ичиндеги чалууда · %2$s - Топтук чалуудасыз · %1$s - %1$s менен %2$s топтук чалууда · %3$s - Топтук чалуу · %1$s - - %1$s топтун ичинде чалып баштады - Топтук чалууну баштадыңыз - %1$s топтук чалууда - Топтук чалуудасыз - %1$s менен %2$s топтук чалууда - Топтук чалуу + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Сиз - - %1$s, %2$s жана дагы %3$d топтук чалууда · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s жана дагы %3$d топтук чалууда + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ Ырастоо коду суралган жок. Байланышыңызды текшерип, кайталап көрүңүз. Телефон номери туура эмес + Телефон номерин туура эмес киргиздиңиз окшойт (%1$s).\n\nМындай эмес беле %2$s? Signal Android - Телефон номеринин форматы @@ -2622,6 +2646,8 @@ Жүктөлүүдө Кененирээк маалымат Чалууга кошулуу + + Call back Чалууга кайтуу Орун жок Досторду чакыруу @@ -2686,6 +2712,7 @@ Чалуудан чыгуу Төмөнкү адамдар Signal\'ды кайрадан орнотуп же түзмөктөрүн алмаштырышкан. Купуялыкты камсыз кылуу үчүн алар менен болгон коопсуздук кодуңузду ырастаңыз. Карап көрүү + Буга чейин ырасталган @@ -2992,6 +3019,7 @@ Демейки Шашылыш + Өтө @@ -3349,6 +3377,7 @@ %1$s жөнөтүлдү Сиз %1$s - %2$s %2$s күнү, саат %3$s\'та %1$s + Алуучу Жөнөтүүчү Транзакциянын чоо-жайы, ошондой эле төлөмдүн суммасы жана транзакция убактысы MobileCoin Ledger\'дин бир бөлүгү болуп саналат. @@ -3411,6 +3440,7 @@ Төлөмдү ырастоо Тармак комиссиясы Болжолдуу %1$s + Алуучу Жалпы суммасы Баланс: %1$s @@ -4006,6 +4036,7 @@ Буферге көчүрүлдү Админ + Ырастоо Четке кагам @@ -4033,6 +4064,7 @@ Үн билдирүү · %1$s + Кимден: %1$s Кимге: %2$s @@ -4077,6 +4109,7 @@ %1$s жана %2$s кошулушту %1$s, %2$s жана %3$s кошулушту %1$s, %2$s жана %3$d башкалар кошулушту + %1$s чыкты %1$s жана %2$s чыгышты %1$s, %2$s жана %3$s чыгышты @@ -4452,6 +4485,7 @@ Билдирүү алмашуу Жоголуп кетүүчү билдирүүлөр Колдонмонун коопсуздугу + Соңку колдонмолор тизмесинде жана колдонмолордун ичинде скриншотторду кулпулайсыз Signal билдирүүлөрү жана чалуулар, чалууларды багыттоо жана жашыруун жөнөтүүчү Жаңы маектер үчүн демейки таймер @@ -4508,11 +4542,14 @@ Медиа сапаты Жөнөтүлгөн медиа сапаты Жогорку сапаттагы медианы жөнөтүү көбүрөөк берилмелерди колдонот. + Шашылыш + Стандарт Чалуулар + Авто Ыңгайлаштырылган түстөрдү колдонуу Маектин түсү @@ -4721,6 +4758,7 @@ Сүрөткө тартуу Сүрөт тандоо Сүрөт + Текст Сактоо Аватарды тазалоо @@ -4777,7 +4815,7 @@ Билдирүү кошуу Жооп кошуу Кимге - Бир жолу көрүлүүчү билдирүү + View once media Бир же бир нече нерсе өтө чоң болуп чыкты Бир же бир нече нерсе жараксыз Өтө көп нерсе тандалды @@ -5365,7 +5403,7 @@ %1$s %2$s Сиз - + Кимден: %1$s Кимге: %2$s Жооп берүү @@ -6558,5 +6596,11 @@ Кыска жазууну оңдоо + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index faa1d27cfb..f9ce637e60 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -421,6 +421,7 @@ Prisijungti + Pilnas Klaida siunčiant mediją @@ -645,6 +646,7 @@ Nebearchyvuoti Ištrinti Žymėti viską + Pažymėta: %1$d Pažymėta: %1$d @@ -902,6 +904,7 @@ Išsiųsta %1$d pakvietimo Išsiųsta %1$d pakvietimų + Jūs negalite automatiškai pridėti naudotojo „%1$s“ į šią grupę.\n\nJis yra pakviestas prisijungti prie grupės ir nematys jokių grupėje esančių žinučių tol, kol nepriims kvietimo. Jūs negalite automatiškai pridėti šių naudotojų į šią grupę.\n\nJie yra pakviesti prisijungti prie grupės ir nematys jokių grupėje esančių žinučių tol, kol nepriims kvietimo. @@ -1114,7 +1117,9 @@ Taisyti pavadinimą ir paveikslą Pasenusi grupė + Tai yra pasenusi grupė. Tokios ypatybės, kaip grupės administratoriai, yra prieinamos tik naujosiose grupėse. + Tai yra pasenusi grupė. Norėdami naudoti tokias ypatybes kaip @paminėjimai ir administratoriai, Ši pasenusi grupė negali būti naujinta į naująją grupę, nes yra per didelė. Didžiausias grupės dydis yra %1$d. naujinkite šią grupę. @@ -1359,6 +1364,7 @@ Ištrinti + Pažymėta %1$d (%2$s) Pažymėtos %1$d (%2$s) @@ -1425,17 +1431,13 @@ Jūs išėjote iš grupės. Jūs atnaujinote grupę. Grupė buvo atnaujinta. - + Išsiunčiamasis balso skambutis - + Išsiunčiamasis vaizdo skambutis - - Neatsakytas balso skambutis - - Neatsakytas vaizdo skambutis - + Gaunamasis balso skambutis - + Gaunamasis vaizdo skambutis Praleistas balso skambutis @@ -1445,10 +1447,6 @@ Praleistas balso skambutis įjungus pranešimų profilį Praleistas vaizdo skambutis įjungus pranešimų profilį - - Tu atmetei balso skambutį - - Tu atmetei vaizdo skambutį %1$s · %2$s %1$s atnaujino grupę. @@ -1673,34 +1671,59 @@ %1$s dabar gali priimti Mokėjimus - %1$s pradėjo grupės skambutį · %2$s - Tu pradėjai grupės skambutį · %1$s - %1$s yra grupės skambutyje · %2$s - Jūs esate grupės skambutyje · %1$s - %1$s ir %2$s yra grupės skambutyje · %3$s - Grupės skambutis · %1$s - - %1$s pradėjo grupės skambutį - Jūs pradėjote grupės skambutį - %1$s yra grupės skambutyje - Jūs esate grupės skambutyje - %1$s ir %2$s yra grupės skambutyje - Grupės skambutis + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Jūs - - %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje · %4$s - %1$s, %2$s ir dar %3$d žmonės yra grupės skambutyje · %4$s - %1$s, %2$s ir dar %3$d žmonių yra grupės skambutyje · %4$s - %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje - %1$s, %2$s ir dar %3$d žmonės yra grupės skambutyje - %1$s, %2$s ir dar %3$d žmonių yra grupės skambutyje - %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Nepavyko paprašyti patvirtinimo kodo. Patikrink tinklo ryšį ir bandyk dar kartą. Nestandartinis numerio formatas + Atrodo, kad jūsų įvestas numeris (%1$s) yra nestandartinio formato.\n\nAr turėjote omenyje %2$s? Signal „Android“ - Telefono numerio formatas @@ -2883,6 +2907,8 @@ Įkeliama Sužinoti daugiau Prisijungti prie skambučio + + Call back Grįžti į skambutį Skambutis yra pilnas Pakviesti draugus @@ -2947,6 +2973,7 @@ Išeiti iš skambučio Gali būti, kad šie žmonės iš naujo įdiegė programėlę ar pakeitė įrenginį. Patvirtinkite savo saugumo numerį su kiekvienu iš jų, kad užtikrintumėte privatumą. Rodyti + Anksčiau patvirtinta(s) @@ -3274,6 +3301,7 @@ Numatytoji Aukšta + Maksimali @@ -3640,6 +3668,7 @@ Kam išsiųsta: %1$s Jūs ties %1$s, %2$s %1$s ties %2$s, %3$s + Skirta Nuo Išsami operacijos informacija, įskaitant mokėjimo sumą ir operacijos laiką, yra „MobileCoin Ledger“ dalis. @@ -3702,6 +3731,7 @@ Patvirtinti mokėjimą Tinklo mokestis Apskaičiuota %1$s suma + Kam Iš viso Likutis: %1$s @@ -4324,6 +4354,7 @@ Nukopijuota į iškarpinę Administratorius + Patvirtinti Atmesti @@ -4351,6 +4382,7 @@ Balso žinutė · %1$s + %1$s naudotojui %2$s @@ -4401,6 +4433,7 @@ Prisijungė %1$s ir %2$s Prisijungė %1$s, %2$s ir %3$s Prisijungė %1$s, %2$s dar %3$d žmonės(-ių) + %1$s išėjo %1$s ir %2$s išėjo %1$s, %2$s ir %3$s išėjo @@ -4782,6 +4815,7 @@ Susirašinėjimas Išnykstančios žinutės Programėlės saugumas + Blokuoti ekrano kopijas paskiausiųjų sąraše ir programėlėje Signal žinutės ir skambučiai, visada retransliuoti skambučius ir užantspauduotas siuntėjas Numatytasis laikmatis naujuose pokalbiuose @@ -4838,11 +4872,14 @@ Medijos kokybė Siunčiamos medijos kokybė Aukštos kokybės medijos siuntimas naudos daugiau tinklo duomenų. + Aukšta + Standartinė Skambučiai + Auto Naudoti tinkintas spalvas Pokalbio spalva @@ -5060,6 +5097,7 @@ Padaryti nuotrauką Pasirinkti nuotrauką Nuotrauka + Tekstas Įrašyti Išvalyti avatarą @@ -5128,7 +5166,7 @@ Pridėti žinutę Pridėti atsakymą Kam siųsti - Vienkartinės peržiūros žinutė + View once media Vienas ar daugiau elementų buvo per dideli Vienas ar daugiau elementų buvo neteisingi Pažymėta per daug elementų @@ -5731,7 +5769,7 @@ %1$s %2$s Jūs - + Nuo %1$s iki %2$s Atsakyti @@ -6996,5 +7034,11 @@ Redaguoti pastabą + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 89039e7b71..d4d4d456df 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -418,6 +418,7 @@ Pievienoties + Pilns Neizdevās nosūtīt multivides failu @@ -629,6 +630,7 @@ Atarhivēt Dzēst Atzīmēt visas + %1$d atlasītas %1$d atlasīta @@ -883,6 +885,7 @@ Uzaicinājums nosūtīts Nosūtīti %1$d uzaicinājumi + Jūs nevarat automātiski pievienot %1$s šai grupai. \n\nLietotāji ir uzaicināti pievienoties, un viņi neredzēs nevienu grupas ziņu, kamēr neapstiprinās savu dalību. Jūs nevarat automātiski pievienot šos lietotājus šai grupai. \n\nLietotāji ir uzaicināti pievienoties, un viņi neredzēs nevienu grupas ziņu, kamēr neapstiprinās savu dalību. @@ -1079,7 +1082,9 @@ Labot vārdu un attēlu Vecā tipa grupa + Šī ir vecā tipa grupa. Tādas funkcijas kā grupu administratori ir pieejamas tikai jaunā tipa grupām. + Šī ir vecā tipa grupa. Lai izmantotu jaunās funkcijāas, piemēram, @pieminējumi un administratori, Šo vecā tipa grupu nevar aktualizēt (pārveidot) uz jaunā tipa grupu, jo tā ir pārāk liela. Maksimālais grupas lielums ir %1$d. aktualizēt šo grupu. @@ -1315,6 +1320,7 @@ Dzēst + %1$d atlasīti (%2$s) %1$d atlasīts (%2$s) @@ -1380,17 +1386,13 @@ Jūs esat pametis grupu. Jūs aktualizējāt grupu. Šī grupa tika aktualizēta. - + Izejošs balss zvans - + Izejošs videozvans - - Neatbildēts balss zvans - - Neatbildēts videozvans - + Ienākošs balss zvans - + Ienākošs videozvans Neatbildēts balss zvans @@ -1400,10 +1402,6 @@ Neatbildēts balss zvans, kamēr bija aktivizēts paziņojumu profils Neatbildēts video zvans, kamēr bija aktivizēts paziņojumu profils - - Jūs noraidījāt balss zvanu - - Jūs noraidījāt videozvanu %1$s · %2$s %1$s aktualizēja grupu. @@ -1620,32 +1618,57 @@ %1$s tagad var pieņemt maksājumus - %1$s sāka grupas zvanu · %2$s - Jūs sākāt grupas zvanu · %1$s - %1$s ir grupas zvanā · %2$s - Jūs esat grupas zvanā · %1$s - %1$s un %2$s ir grupas zvanā · %3$s - Grupas zvans · %1$s - - %1$s sāka grupas zvanu - Jūs sākāt grupas zvanu - %1$s ir grupas zvanā - Jūs esat grupas zvanā - %1$s un %2$s ir grupas zvanā - Grupas zvans + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Jūs - - %1$s, %2$s un %3$d cits ir grupas zvanā · %4$s - %1$s, %2$s un %3$d cits ir grupas zvanā · %4$s - %1$s, %2$s un %3$d citi ir grupas zvanā · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s un %3$d cits ir grupas zvanā - %1$s, %2$s un %3$d cits ir grupas zvanā - %1$s, %2$s un %3$d citi ir grupas zvanā + + + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2142,6 +2165,7 @@ Nevar pieprasīt verifikācijas kodu. Lūdzu, pārbaudiet tīkla savienojumu un mēģiniet vēlreiz. Nestandarta numura formāts + Izskatās, ka numurs, kuru ievadījāt (%1$s), ir nestandarta formātā.\n\nVai domājāt %2$s? Signal Android - tālruņa Numura Formāts @@ -2796,6 +2820,8 @@ Ielādē Lasīt vairāk Pievienoties zvanam + + Call back Atgriezties pie zvana Zvana dalībnieku limits izsmelts Uzaicināt draugus @@ -2860,6 +2886,7 @@ Pamest zvanu Iespējams, ka šie cilvēki ir pārinstalējuši vai mainījuši ierīces. Pārbaudiet savu drošības numuru kopā ar šiem cilvēkiem, lai nodrošinātu privātumu. Skatīt + Iepriekšējā vereficētā @@ -3180,6 +3207,7 @@ Noklusējuma Augsta + Maksimāla @@ -3543,6 +3571,7 @@ Nosūtīts %1$s Jūs %1$s plkst. %2$s %1$s %2$s plkst. %3$s + Kam No Transakcijas informācija, tostarp maksājuma summa un transakcijas laiks, ir norādīta MobileCoin virsgrāmatā. @@ -3605,6 +3634,7 @@ Apstiprināt maksājumu Tīkla maksājuma likme Aprēķināts %1$s + Kam Kopējā summa Atlikums: %1$s @@ -4218,6 +4248,7 @@ Iekopēts starpliktuvē Administrators + Apstiprināt Atteikt pievienot @@ -4245,6 +4276,7 @@ Balss ziņa · %1$s + %1$s %2$s @@ -4293,6 +4325,7 @@ %1$s un %2$s pievienojās %1$s, %2$s un %3$s pievienojās %1$s, %2$s un %3$d citi pievienojās + %1$s izstājās %1$s un %2$s izstājās %1$s, %2$s un %3$s izstājās @@ -4672,6 +4705,7 @@ Ziņapmaiņa Gaistošās ziņas Lietotnes aizsardzība + Bloķēt ekrānuzņēmumus pēdējo sarunu sarakstā un pašā lietotnē Signal ziņas un zvani, pastāvīga zvanu pārsūtīšana un šifrēts sūtītājs Noklusējuma taimeris jaunām sarunām @@ -4728,11 +4762,14 @@ Multivides kvalitāte Nosūtītās multivides kvalitāte Sūtot augstas kvalitātes multivides saturu, tiks izmantots vairāk datu. + Augsta + Standarta Zvani + Automātiski Lietot pielāgotas krāsas Sarunas krāsa @@ -4947,6 +4984,7 @@ Uzņemt attēlu Izvēlēties fotoattēlu Attēls + Teksts Saglabāt Dzēst avatāru @@ -5011,7 +5049,7 @@ Pievienojiet ziņu Pievienot atbildi Sūtīt - Vienreiz skatāma ziņa + View once media Viens vai vairāki vienumi bija pārāk lieli Viens vai vairāki vienumi bija nederīgi Pārāk daudz izvēlētu vienumu @@ -5609,7 +5647,7 @@ %1$s %2$s Jūs - + %1$s %2$s Atbildēt @@ -6850,5 +6888,11 @@ Rediģēt piezīmi + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index c7b58217ef..163b62e8ac 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -415,6 +415,7 @@ Приклучи сѐ + Полно е Грешка при испраќање на медиумска датотека @@ -613,6 +614,7 @@ Деархивирај Избриши Избери сѐ + %1$d избрана %1$d избрани @@ -864,6 +866,7 @@ Поканата е испратена Испратени се %1$d покани + „%1$s“ не може автоматски да се додаде во оваа група од Вас.\n\nПоканет/а е да се приклучи и нема да гледа групни пораки додека не прифати. Овие корисници не можат да бидат автоматски додадени во оваа група од Вас.\n\nТие се поканети да се приклучат и нема да гледаат групни пораки додека не прифатат. @@ -1044,7 +1047,9 @@ Уреди име и слика Застарена група + Ова е застарена група. Опции како администратори на групи се достапни само со нови групи. + Ова е застарена група. За пристап до нови опции како @спомнувања и администратори, Ова е застарена група и не може да биде надградена во нова група бидејќи е преголема. Максималната големина на група е %1$d. надградете ја оваа група. @@ -1271,6 +1276,7 @@ Избриши + %1$d избрано (%2$s) %1$d избрани (%2$s) @@ -1335,17 +1341,13 @@ Ја напуштивте групата. Ја ажуриравте групата. Групата беше ажурирана. - + Појдовен гласовен повик - + Појдовен видео повик - - Неодговорен гласовен повик - - Неодоговорен видео повик - + Дојдовен гласовен повик - + Дојдовен видео повик Пропуштен гласовен повик @@ -1355,10 +1357,6 @@ Пропуштен гласовен повик додека беше овозможен режимот за известувања Пропуштен видео повик додека беше овозможен режимот за известувања - - Одбивте гласовен повик - - Одбивте видео повик %1$s · %2$s %1$s ја ажурираше групата. @@ -1567,30 +1565,55 @@ %1$s сега може да прифаќа плаќања - %1$s започна групен повик · %2$s - Започнавте групен повик · %1$s - %1$s е во групниот повик · %2$s - Вие сте во групниот повик · %1$s - %1$s и %2$s се во групниот повик · %3$s - Групен повик · %1$s - - %1$s започна групен повик - Вие започнавте групен повик - %1$s е во групниот повик - Вие сте во групниот повик - %1$s и %2$s се во групниот повик - Групен повик + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Вие - - %1$s, %2$s, и %3$d друг е во групниот повик · %4$s - %1$s, %2$s, и %3$d други се во групниот повик · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, и %3$d друг е во групниот повик - %1$s, %2$s, и %3$d други се во групниот повик + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Не може да се побара код за верификација. Ве молиме проверете ја вашата мрежна поврзаност и обидете се повторно. Нестандарден формат на број + Бројот што го внесовте (%1$s) изгледа дека е нестандарден формат.\n\nДали мислевте на %2$s? Signal Android - Формат на телефонски број @@ -2709,6 +2733,8 @@ Се вчитува Дознајте повеќе Приклучи се на повикот + + Call back Врати се на повикот Повикот е полн Покани пријатели @@ -2773,6 +2799,7 @@ Напушти повик Следниве луѓе можеби го реинсталирале Signal или ги промениле уредите. Проверете го вашиот безбедносен број со нив за да ја осигурате приватноста. Преглед + Претходно проверено @@ -3086,6 +3113,7 @@ Стандардно Високо + Максимално @@ -3446,6 +3474,7 @@ Испратено од %1$s Вие на %1$s во %2$s %1$s на %2$s во %3$s + До Од Деталите за трансакцијата, вклучувајќи го износот на плаќање и времето на трансакцијата, се дел од MobileCoin регистарот. @@ -3508,6 +3537,7 @@ Потврдете го плаќањето Мрежна такса Проценето %1$s + До Вкупен износ Салдо: %1$s @@ -4112,6 +4142,7 @@ Копирано на таблата со исечоци Администратор + Дозволи Одбиј @@ -4139,6 +4170,7 @@ Гласовна порака · %1$s + %1$s до %2$s @@ -4185,6 +4217,7 @@ %1$s и %2$s се приклучија %1$s, %2$s и %3$s се приклучија %1$s, %2$s и %3$d други се приклучија + %1$s напушти %1$s и %2$s напуштија %1$s, %2$s и %3$s напуштија @@ -4562,6 +4595,7 @@ Пораки Исчезнувачки пораки Безбедност на апликацијата + Блокирај слики од екранот во листата на неодамнешни разговори и во самата апликација Signal пораки и повици. Секогаш префрлај повици и запечатен испраќач. Стандарден тајмер за нови разговори @@ -4618,11 +4652,14 @@ Квалитет на медиумски датотеки Квалитет на испратена медиумска датотека Испраќањето на висококвалитетна медиумска датотека ќе користи повеќе податоци. + Висок + Стандарден Повици + Автоматски Користи сопствени бои Боја на разговор @@ -4834,6 +4871,7 @@ Сликај Избери слика Слика + Текст Зачувај Отстрани аватар @@ -4894,7 +4932,7 @@ Додај порака Додај одговор Испрати на - Еднократно видлива порака + View once media Еден или повеќе предмети беа премногу големи Еден или повеќе предмети беа невалидни Премногу избрани предмети @@ -5487,7 +5525,7 @@ %1$s %2$s Вие - + %1$s до %2$s Одговори @@ -6704,5 +6742,11 @@ Изменете белешка + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 2bfac15e85..848c56f0ad 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -415,6 +415,7 @@ ചേരുക + പൂർണ്ണമായി മീഡിയ അയയ്‌ക്കുന്നതിൽ പിശക് @@ -613,6 +614,7 @@ അൺആർക്കൈവ് ഇല്ലാതാക്കൂ എല്ലാം തിരഞ്ഞെടുക്കുക + %1$d തിരഞ്ഞെടുത്തു %1$d തിരഞ്ഞെടുത്തു @@ -864,6 +866,7 @@ ക്ഷണം അയച്ചു %1$d ക്ഷണങ്ങൾ അയച്ചു + \"%1$s\"നെ സ്വമേധയാ ഈ ഗ്രൂപ്പിലേക്ക് ചേർക്കാൻ നിങ്ങൾക്ക് കഴിയില്ല.\n\nഅവരെ അംഗത്വമെടുക്കാൻ ക്ഷണിച്ചിട്ടുണ്ട്, അവർ അത് സ്വീകരിക്കുന്നത് വരെ ഗ്രൂപ്പ് സന്ദേശങ്ങളൊന്നും കാണാൻ സാധിക്കുകയില്ല. ഈ ഉപയോക്താക്കളെ നിങ്ങൾക്ക് ഈ ഗ്രൂപ്പിലേക്ക് സ്വയമേവ ചേർക്കാൻ കഴിയില്ല.\n\nഅവരെ അംഗത്വമെടുക്കാൻ ക്ഷണിച്ചിട്ടുണ്ട്, അവർ അത് സ്വീകരിക്കുന്നത് വരെ ഗ്രൂപ്പ് സന്ദേശങ്ങളൊന്നും കാണാൻ സാധിക്കുകയില്ല. @@ -1044,7 +1047,9 @@ പേരും ചിത്രവും എഡിറ്റുചെയ്യുക ലെഗസി ഗ്രൂപ്പ് + ഇതൊരു ലെഗസി ഗ്രൂപ്പാണ്. ഗ്രൂപ്പ് അഡ്മിൻ പോലുള്ള സവിശേഷതകൾ പുതിയ ഗ്രൂപ്പുകൾക്ക് മാത്രമേ ലഭ്യമാകൂ. + ഇതൊരു ലെഗസി ഗ്രൂപ്പാണ്. @സൂചനകള്‍ അഡ്‌മിനുകള്‍ പോലുള്ള പുതിയ സവിശേഷതകൾ ആക്‌സസ് ചെയ്യുന്നതിന്, ഈ ലെഗസി ഗ്രൂപ്പിനെ ഒരു പുതിയ ഗ്രൂപ്പിലേക്ക് അപ്‌ഗ്രേഡുചെയ്യാൻ കഴിയില്ല കാരണം ഇത് വളരെ വലുതാണ്. പരമാവധി ഗ്രൂപ്പ് വലുപ്പം %1$d ആണ്. ഈ ഗ്രൂപ്പ് അപ്‌ഗ്രേഡുചെയ്യുക @@ -1271,6 +1276,7 @@ ഇല്ലാതാക്കൂ + %1$d തിരഞ്ഞെടുത്തു (%2$s) %1$d തിരഞ്ഞെടുത്തു (%2$s) @@ -1335,17 +1341,13 @@ നിങ്ങൾ ഗ്രൂപ്പ് വിട്ടു. നിങ്ങൾ ഗ്രൂപ്പ് അപ്‌ഡേറ്റുചെയ്‌തു. ഗ്രൂപ്പ് അപ്‌ഡേറ്റുചെയ്‌തു. - + ഔട്ട്ഗോയിംഗ് വോയ്‌സ് കോൾ - + ഔട്ട്ഗോയിംഗ് വീഡിയോ കോൾ - - ഉത്തരം ലഭിക്കാത്ത വോയ്‌സ് കോൾ - - ഉത്തരം ലഭിക്കാത്ത വീഡിയോ കോൾ - + ഇൻകമിംഗ് വോയ്‌സ് കോൾ - + ഇൻകമിംഗ് വീഡിയോ കോൾ മിസ്‌ഡ് വോയ്‌സ് കോൾ @@ -1355,10 +1357,6 @@ അറിയിപ്പ് പ്രൊഫൈൽ ഓണായിരിക്കുമ്പോൾ വോയ്സ് കോൾ മിസ്സ് ആയി അറിയിപ്പ് പ്രൊഫൈൽ ഓണായിരിക്കുമ്പോൾ വീഡിയോ കോൾ മിസ്സ് ആയി - - നിങ്ങൾ ഒരു വോയ്‌സ് കോൾ നിരസിച്ചു - - നിങ്ങൾ ഒരു വീഡിയോ കോൾ നിരസിച്ചു %1$s · %2$s %1$s ഗ്രൂപ്പ് അപ്‌ഡേറ്റുചെയ്‌തു. @@ -1567,30 +1565,55 @@ %1$s എന്നയാൾക്ക് ഇപ്പോൾ പേയ്മെന്റുകൾ സ്വീകരിക്കാൻ കഴിയും - %1$s ഒരു ഗ്രൂപ്പ് കോൾ ആരംഭിച്ചു . %2$s - നിങ്ങളൊരു ഗ്രൂപ്പ് കോൾ തുടങ്ങി · %1$s - %1$s ഗ്രൂപ്പ് കോളിലാണ് · %2$s - നിങ്ങൾ ഗ്രൂപ്പ് കോളിലാണ് · %1$s - %1$s, %2$s എന്നിവർ %3$s എന്ന ഗ്രൂപ്പ് കോളിലാണ് - ഗ്രൂപ്പ് കോൾ · %1$s - - %1$s ഒരു ഗ്രൂപ്പ് കോൾ ആരംഭിച്ചു - നിങ്ങൾ ഒരു ഗ്രൂപ്പ് കോൾ ആരംഭിച്ചു - %1$s ഗ്രൂപ്പ് കോളിലാണ് - നിങ്ങൾ ഗ്രൂപ്പ് കോളിലാണ് - %1$s - ഉം %2$s- ഉം ഗ്രൂപ്പ് കോളിലാണ് - ഗ്രൂപ്പ് കോൾ + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s നിങ്ങൾ - - %1$s, %2$s, പിന്നെ %3$d ആളും ഗ്രൂപ്പ് കോളിലാണ് · %4$s - %1$s, %2$s, പിന്നെ %3$d ആളുകളും ഈ കോളിലുണ്ട് · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s - ഉം, %2$s - ഉം പിന്നെ %3$d ആളും ഈ കോളിലുണ്ട് - %1$s, %2$s, പിന്നെ %3$d ആളുകളും ഈ കോളിലുണ്ട് + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ പരിശോധിച്ചുറപ്പിക്കൽ കോഡ് അഭ്യർത്ഥിക്കാനായില്ല. നിങ്ങളുടെ നെറ്റ്‌വർക്ക് കണക്ഷൻ പരിശോധിച്ചശേഷം വീണ്ടും ശ്രമിക്കുക. അംഗീകൃതമല്ലാത്ത നമ്പര്‍ രൂപം + നിങ്ങൾ നൽകിയ നമ്പർ (%1$s) അംഗീകൃതമല്ലാത്ത നമ്പര്‍ രൂപത്തില്‍ ആണെന്ന് തോന്നുന്നു.\n\nനിങ്ങൾ ഉദ്ദേശിച്ചത് %2$s എന്നാണോ? Signal ആൻഡ്രോയിഡ് - ഫോണ്‍ നമ്പര്‍ രുപം @@ -2709,6 +2733,8 @@ ലഭ്യമാക്കുന്നു കൂടുതൽ അറിയുക കോളിൽ ചേരുക + + Call back കോളിലേക്ക് മടങ്ങുക കോൾ നിറഞ്ഞു സുഹൃത്തുക്കളെ ക്ഷണിക്കുക @@ -2773,6 +2799,7 @@ കോൾ ഉപേക്ഷിക്കുക ഇനിപ്പറയുന്ന ആളുകൾ അവരുടെ ഉപകരണങ്ങൾ വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുകയോ മാറ്റുകയോ ചെയ്‌തിരിക്കാം. സ്വകാര്യത ഉറപ്പാക്കാൻ അവരുമായി നിങ്ങളുടെ സുരക്ഷാ നമ്പർ പരിശോധിക്കുക. കാണുക + മുമ്പ് പരിശോധിച്ചത് @@ -3086,6 +3113,7 @@ സ്ഥിരസ്ഥിതി ഉയർന്നത് + പരമാവധി @@ -3446,6 +3474,7 @@ %1$s-ലേക്ക് അയച്ചു നിങ്ങൾ %1$s-ന് %2$s-യിൽ %1$s%2$s-ന് %3$s-യിൽ + സ്വീകർത്താവ്: പ്രേഷിതാവ്: പേയ്മെന്റ് തുകയും ഇടപാടിന്റെ സമയവും ഉൾപ്പെടെയുള്ള ട്രാൻസാക്ഷൻ വിശദാംശങ്ങൾ MobileCoin ലെഡ്‌ജറിന്റെ ഭാഗമാണ്. @@ -3508,6 +3537,7 @@ പേയ്മെന്റ് സ്ഥിരീകരിക്കുക നെറ്റ്‌വർക്ക് ഫീസ് കണക്കാക്കിയ %1$s + സ്വീകർത്താവ്: ആകെ തുക ബാലൻസ്: %1$s @@ -4112,6 +4142,7 @@ ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തി അഡ്‌മിൻ‌ + അംഗീകരിക്കുക നിരസിക്കുക @@ -4139,6 +4170,7 @@ വോയിസ് മെസേജ് - %1$s + %1$s %2$s-നോട് @@ -4185,6 +4217,7 @@ %1$s-ഉം %2$s-ഉം ചേർന്നു %1$s-ഉം, %2$s-ഉം %3$s-ഉം ചേർന്നിട്ടുണ്ട് %1$s-ഉം, %2$s-ഉം %3$d-ഉം മറ്റുള്ളവരും ചേർന്നിട്ടുണ്ട് + %1$s ഇറങ്ങി പോയിട്ടുണ്ട് %1$s-ഉം %2$s-ഉം ഇറങ്ങി പോയിട്ടുണ്ട് %1$s-ഉം, %2$s-ഉം %3$s-ഉം ഇറങ്ങി പോയിട്ടുണ്ട് @@ -4562,6 +4595,7 @@ സന്ദേശ വിനിമയം അപ്രത്യക്ഷമാകുന്ന സന്ദേശങ്ങൾ ആപ്പ് സുരക്ഷ + റീസന്റ് ലിസ്റ്റിലും ആപ്പിനുള്ളിലും സ്‌ക്രീൻഷോട്ടുകൾ ബ്ലോക്ക് ചെയ്യുക Signal സന്ദേശങ്ങള്‍ കോളുകളും, എല്ലായ്പ്പോഴും കോളുകള് റിലേ ചെയ്യുക ഒപ്പം സീൽ ചെയ്ത അയച്ചയാൾ പുതിയ ചാറ്റുകൾക്കായുള്ള ഡിഫോൾട്ട് ടൈമർ @@ -4618,11 +4652,14 @@ മീഡിയ നിലവാരം അയയ്ക്കുന്ന മീഡിയയുടെ നിലവാരം ഉയർന്ന നിലവാരമുള്ള മീഡിയ അയയ്ക്കുന്നത് കൂടുതൽ ഡാറ്റ ഉപയോഗിക്കും. + ഉയർന്നത് + സ്റ്റാൻഡേർഡ് കോളുകൾ + ഓട്ടോ ഇഷ്‌ടാനുസൃത നിറങ്ങൾ ഉപയോഗിക്കുക ചാറ്റ് നിറം @@ -4834,6 +4871,7 @@ ഒരു ഫോട്ടോ എടുക്ക് ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക ഫോട്ടോ + വാചകം സംരക്ഷിക്കൂ അവതാർ മായ്‌ക്കുക @@ -4894,7 +4932,7 @@ ഒരു സന്ദേശം ചേര്‍ക്കുക ഒരു മറുപടി ചേർക്കുക ഇനിപ്പറയുന്നയാൾക്ക് അയയ്ക്കുക - ഒരിയ്ക്കല്‍ മാത്രം കാണാവുന്ന സന്ദേശം + View once media ഒന്നോ അതിലധികമോ ഇനങ്ങൾ വളരെ വലുതായിരുന്നു ഒന്നോ അതിലധികമോ ഇനങ്ങൾ അസാധുവാണ് വളരെയധികം ഇനങ്ങൾ തിരഞ്ഞെടുത്തു @@ -5487,7 +5525,7 @@ %1$s %2$s നിങ്ങൾ - + %1$s %2$s-നോട് മറുപടി @@ -6704,5 +6742,11 @@ കുറിപ്പ് എഡിറ്റ് ചെയ്യുക + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index c5d069a1fc..caf830e8fd 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -415,6 +415,7 @@ सामील व्हा + पूर्ण मिडिया पाठवण्यात त्रुटी @@ -613,6 +614,7 @@ अनआर्काईव्ह हटवा सर्व निवडा + %1$d निवडला %1$d निवडले @@ -864,6 +866,7 @@ आमंत्रण पाठवले %1$d आमंत्रणे पाठवली + \"%1$s\" आपल्याद्वारे स्वयंचलितपणे गटा मध्ये जोडले जाऊ शकत नाहीत.\n\nत्यांना गटात सामील होण्यासाठी आमंत्रित केले गेले आहे आणि ते स्वीकारेपर्यंत त्यांना कोणतेही गट संदेश दिसणार नाहीत. हे वापरकर्ते आपल्याद्वारे स्वयंचलितपणे गटामध्ये जोडले जाऊ शकत नाहीत. \n\nत्यांना गटात सामील होण्यासाठी आमंत्रित केले गेले आहे आणि ते स्वीकारेपर्यंत त्यांना कोणतेही गट संदेश दिसणार नाहीत. @@ -1044,7 +1047,9 @@ नाव आणि चित्र संपादित करा लेगसी गट + हा एक लेगसी गट आहे. गट ऍडमिन सारखे वैशिष्ट्ये फक्त नवीन गटांसाठी उपलब्ध आहेत. + हा एक लेगसी गट आहे. @उल्लेख आणि ऍडमिन सारखे वैशिष्ट्ये प्रवेश करण्यासाठी,  या लेगसी गटाला नवीन गटामध्ये श्रेणीसुधारित केला जाऊ शकत नाही कारण तो खूप मोठा आहे. गटाचा कमाल आकार %1$d आहे. हा गट श्रेणीसुधारित करा. @@ -1271,6 +1276,7 @@ हटवा + %1$d निवडले (%2$s) %1$d निवडले (%2$s) @@ -1335,17 +1341,13 @@ आपण गट सोडला आहे. आपण गट अद्ययावत केले. गट अद्यतनित केला गेला. - + बाहेर जाणारा व्हाईस कॉल - + बाहेर जाणारा व्हिडिओ कॉल - - अनुत्तरित व्हॉईस कॉल - - अनुत्तरित व्हिडिओ कॉल - + येणारा व्हाईस कॉल - + येणारा व्हिडिओ कॉल चूकवलेला व्हाईस कॉल @@ -1355,10 +1357,6 @@ अधिसूचना प्रोफाइल सुरू असताना मिस्ड व्हॉईस कॉल अधिसूचना प्रोफाइल सुरू असताना व्हिडिओ कॉल मिस केला - - आपण व्हाईस कॉलला नकार दिला - - आपण व्हिडिओ कॉलला नकार दिला %1$s · %2$s %1$s ने गट अद्ययावत केले. @@ -1567,30 +1565,55 @@ %1$s आता पेमेंट्स स्विकारु शकतात - %1$s ने गट कॉल चालू केला · %2$s - आपण गट कॉल · %1$s सुरू केला - %1$s गट कॉलमध्ये आहे · %2$s - आपण गट कॉलमध्ये आहात · %1$s - %1$s आणि %2$s गट कॉलमध्ये आहेत · %3$s - गट कॉल · %1$s - - %1$s ने गट कॉल चालू केला - आपण गट कॉल चालू केला - %1$s गट कॉलमध्ये आहे - आपण गट कॉलमध्ये आहात - %1$s आणि %2$s गट कॉलमध्ये आहेत - गट कॉल + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s आपण - - %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहे · %4$s - %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहेत · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहेत - %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहेत + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ सत्यापन कोडची विनंती करण्यास अक्षम. कृपया आपले नेटवर्क कनेक्शन तपासा आणि पुन्हा प्रयत्न करा. अ-मानक क्रमांक स्वरूप + तुम्ही प्रविष्‍ट केलेला क्रमांक (%1$s) हा अ-मानक स्वरूपाचा असल्याचे दिसते. \n\nतुम्हाला %2$s म्हणायचे होते का? Signal Android - फोन नंबर स्वरूप @@ -2709,6 +2733,8 @@ लोड करत आहे अधिक जाणा कॉलमध्ये सामील व्हा + + Call back कॉलवर परत या कॉल पूर्ण आहे मित्रांना आमंत्रित करा @@ -2773,6 +2799,7 @@ कॉल सोडा पुढील व्यक्तींनी डिव्हाइस पुनर्स्थापित केले किंवा बदलले असतील. गोपनीयता सुनिश्चित करण्यासाठी आपला सुरक्षितता नंबर त्यांच्यासोबत सत्यापित करा. बघा + आधीचे सत्यापित @@ -3086,6 +3113,7 @@ पूर्वनिर्धारित उच्च + कमाल @@ -3446,6 +3474,7 @@ %1$s ला पाठवले आपण %2$s वर %1$s %2$s वर %1$s वर %3$s + यांना याकडून पेमेंट रक्कम आणि व्यवहाराची वेळ समाविष्ट असलेला व्यवहाराचा तपशील, हे MobileCoin लेजरचा एक भाग आहेत. @@ -3508,6 +3537,7 @@ पेमेंटची पुष्टी करा नेटवर्क फी अंदाजे %1$s + यांना एकूण रक्कम बॅलंस: %1$s @@ -4112,6 +4142,7 @@ क्लिपबोर्ड वर कॉपी केले प्रशासक + स्वीकार करा नकार द्या @@ -4139,6 +4170,7 @@ व्हॉईस संदेश · %1$s + %1$s ते %2$s @@ -4185,6 +4217,7 @@ %1$s आणि %2$s सामील झाले %1$s, %2$s आणि %3$s सामील झाले %1$s, %2$s आणि %3$d इतर व्यक्ती सामील झाले + %1$s सोडून गेले %1$s आणि %2$s सोडून गेले %1$s, %2$s आणि %3$s सोडून गेले @@ -4562,6 +4595,7 @@ संदेशन हरवणारे संदेश अॅप सुरक्षा + अलीकडील यादीमध्ये आणि अ‍ॅपच्या आत स्क्रीनशॉट अवरोधित करा Signal संदेश आणि कॉल, कॉल नेहमी रिले करा आणि सील केलेला प्रेषक नवीन चॅट्ससाठी पूर्वनिर्धारित टायमर @@ -4618,11 +4652,14 @@ मिडिया दर्जा मिडिया पाठवण्याचा दर्जा मिडिया पाठवताना उच्च दर्जा हा पर्याय निवडल्यास अधिक डेटा वापरला जाईल. + उच्च + मानक कॉल + ऑटो कस्टम रंग वापरा चॅट रंग @@ -4834,6 +4871,7 @@ फोटो घ्या फोटो निवडा फोटो + मजकूर जतन करा अवतार साफ करा @@ -4894,7 +4932,7 @@ संदेश जोडा प्रत्युत्तर जोडा यांना पाठवा - एकदा-बघा संदेश + View once media एक किंवा अधिक आयटम खूप मोठे होते एक किंवा अधिक आयटम अवैध होते जास्त आयटम निवडले @@ -5487,7 +5525,7 @@ %1$s %2$s आपण - + %1$s ते %2$s प्रत्युत्तर द्या @@ -6704,5 +6742,11 @@ टीप संपादित करा + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 5045b6468c..68fae3eb74 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -412,6 +412,7 @@ Sertai + Penuh Ralat menghantar media @@ -597,6 +598,7 @@ Nyaharkib Padam Pilih semua + %1$d dipilih @@ -845,6 +847,7 @@ %1$d jemputan dihantar + \"%1$s\" tidak dapat ditambah secara automatik ke kumpulan ini oleh anda.\n\nMereka telah dijemput untuk menyertai kumpulan dan tidak akan nampak sebarang mesej kumpulan sehingga mereka menerimanya. Pengguna ini tidak dapat ditambah secara automatik ke kumpulan ini oleh anda.\n\nMereka telah dijemput untuk menyertai kumpulan ini dan tidak akan nampak sebarang mesej kumpulan sehingga mereka menerimanya. @@ -1009,7 +1012,9 @@ Edit nama dan gambar Kumpulan Legasi + Ini Kumpulan Legasi. Ciri-ciri seperti pentadbir kumpulan hanya tersedia untuk Kumpulan Baru, + Ini Kumpulan Legasi. Untuk mengakses ciri-ciri baru seperti @sebutan dan pentadbir, Kumpulan Legasi tidak boleh dinaik taraf kepada Kumpulan Baru kerana ia terlalu besar. Saiz maksimum kumpulan ialah %1$d. naik taraf kumpulan ini. @@ -1227,6 +1232,7 @@ Padam + %1$d dipilih (%2$s) @@ -1290,17 +1296,13 @@ Anda telah meninggalkan kumpulan. Anda telah mengemas kini kumpulan. Kumpulan telah dikemas kini. - + Panggilan suara keluar - + Panggilan video keluar - - Panggilan suara tidak dijawab - - Panggilan video tidak dijawab - + Panggilan suara masuk - + Panggilan video masuk Panggilan suara terlepas @@ -1310,10 +1312,6 @@ Panggilan suara terlepas semasa profil pemberitahuan dihidupkan Panggilan video terlepas semasa profil pemberitahuan dihidupkan - - Anda menolak panggilan suara - - Anda menolak panggilan video %1$s · %2$s %1$s telah mengemas kini kumpulan. @@ -1514,28 +1512,53 @@ %1$s kini boleh menerima Pembayaran - %1$s telah memulakan panggilan kumpulan · %2$s - Anda telah memulakan panggilan kumpulan · %1$s - %1$s berada dalam panggilan kumpulan · %2$s - Anda berada dalam panggilan kumpulan · %1$s - %1$s dan %2$s berada dalam panggilan kumpulan · %3$s - Panggilan kumpulan · %1$s - - %1$s telah memulakan panggilan kumpulan - Anda telah memulakan panggilan kumpulan - %1$s berada dalam panggilan kumpulan - Anda berada dalam panggilan kumpulan - %1$s dan %2$s berada dalam panggilan kumpulan - Panggilan kumpulan + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Anda - - %1$s, %2$s, dan %3$d orang yang lain berada dalam panggilan kumpulan · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, dan %3$d orang yang lain berada dalam panggilan kumpulan + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ Tidak dapat meminta kod pengesahan. Sila periksa sambungan rangkaian anda dan cuba lagi. Format nombor bukan standard + Nombor yang anda telah masukkan (%1$s) adalah dalam format bukan standard.\n\nAdakah anda maksudkan %2$s? Signal Android - Format Nombor Telefon @@ -2622,6 +2646,8 @@ Memuatkan Ketahui lebih lanjut Sertai panggilan + + Call back Kembali ke panggilan Panggilan penuh Jemput rakan @@ -2686,6 +2712,7 @@ Tinggalkan panggilan Orang yang berikut mungkin telah memasang semula atau telah menukar peranti. Sahkan nombor keselamatan anda dengan mereka untuk memastikan privasi. Lihat + Disahkan sebelum ini @@ -2992,6 +3019,7 @@ Lalai Tinggi + Maks @@ -3349,6 +3377,7 @@ Dihantar kepada %1$s Anda pada %1$s pada %2$s %1$s pada %2$s pada %3$s + Kepada Daripada Butiran transaksi termasuk jumlah pembayaran dan masa transaksi merupakan sebahagian daripada MobileCoin Ledger. @@ -3411,6 +3440,7 @@ Sahkan pembayaran Bayaran rangkaian Anggaran %1$s + Kepada Jumlah keseluruhan Baki: %1$s @@ -4006,6 +4036,7 @@ Disalin ke papan klip Pentadbir + Luluskan Tolak @@ -4033,6 +4064,7 @@ Mesej suara · %1$s + %1$s kepada %2$s @@ -4077,6 +4109,7 @@ %1$s dan %2$s telah menyertai kumpulan %1$s, %2$s dan %3$s telah menyertai kumpulan %1$s, %2$s dan %3$d orang yang lain telah menyertai kumpulan + %1$s telah meninggalkan kumpulan %1$s dan %2$s telah meninggalkan kumpulan %1$s, %2$s dan %3$s telah meninggalkan kumpulan @@ -4452,6 +4485,7 @@ Pemesejan Mesej hilang Keselamatan aplikasi + Sekat tangkapan skrin pada senarai terbaru dan di dalam aplikasi Mesej dan panggilan Signal, selalu menghantar panggilan, dan pengirim terlindung Pemasa lalai untuk sembang baharu @@ -4508,11 +4542,14 @@ Kualiti media Kualiti media yang dihantar Menghantar media berkualiti tinggi akan menggunakan lebih banyak data. + Tinggi + Standard Panggilan + Automatik Gunakan warna tersuai Warna sembang @@ -4721,6 +4758,7 @@ Ambil gambar Pilih gambar Foto + Teks Simpan Kosongkan avatar @@ -4777,7 +4815,7 @@ Tambah mesej Tambah balasan Hantar kepada - Lihat mesej sekali buka + View once media Saiz satu atau lebih item terlalu besar Satu atau lebih item tidak sah Terlalu banyak item dipilih @@ -5365,7 +5403,7 @@ %1$s%2$s Anda - + %1$s kepada %2$s Balas @@ -6558,5 +6596,11 @@ Edit nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 4ab2aa468a..98ba3116ad 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -412,6 +412,7 @@ ပူးပေါင်း + ပြည့်နေသည် မီဒီယာပို့ခြင်းအမှား @@ -597,6 +598,7 @@ ပြန်ထုတ်ရန် ဖျက်ရန် အားလုံးရွေးရန် + %1$d ရွေးထားပြီး @@ -845,6 +847,7 @@ ဖိတ်ကြားချက်များ %1$d ခု ပို့ခဲ့သည်။ + \"%1$s\" ကို ဤအုပ်စုထဲသို့ သင်အလိုအလျောက် ထည့်သွင်း၍ မရပါ။ \n\n သူတို့ကို အုပ်စုအတွင်းသို့ဝင်ရန် ဖိတ်ကြားထားသည်။ ထိုသူတို့ အုပ်စုထဲ ဝင်ရန် လက်ခံမှ အဖွဲ့အတွင်းရှိ မက်ဆေ့ခ်ျများကို တွေ့နိုင်မည်။ ဤအသုံးပြုသူများကို ဤအုပ်စုထဲသို့ သင်အလိုအလျောက် ထည့်သွင်း၍ မရပါ။ \n\n သူတို့ကို အုပ်စုအတွင်းသို့ဝင်ရန် ဖိတ်ကြားထားသည်။ ထိုသူတို့ အုပ်စုထဲ ဝင်ရန် လက်ခံမှ အဖွဲ့အတွင်းရှိ မက်ဆေ့ခ်ျများကို တွေ့နိုင်မည်။ @@ -1009,7 +1012,9 @@ နာမည်နှင့်ရုပ်ပုံကိုတည်းဖြတ်ပါ အမွေအဖွဲ့ + ၎င်းသည် အမွေအဖွဲ့ဖြစ်ပါသည်။ အဖွဲ့ စီမံသူများ စသဖြင့် အင်္ဂါရပ်များသည် အဖွဲ့သစ်များ အတွက်သာ ရရှိနိုင်ပါသည်။ + ၎င်းသည် အမွေအဖွဲ့ တစ်ဖွဲ့ဖြစ်ပါတယ်။ @မန်းရှင်းများ နဲ့ စီမံသူများ စတဲ့ အင်္ဂါရပ်အသစ်များကို အသုံးပြုရန်အတွက်၊ ဤ အမွေအဖွဲ့သည် အလွန်ကြီးသောဖြင့် အဖွဲ့သစ်သို့အဆင့်မြှင့်၍မရပါ။ အကြီးဆုံး အဖွဲ့အရွယ်အစားသည် %1$d ဦးသာ ဖြစ်ပါသည်။ ၎င်းအဖွဲ့ကို အဆင့်မြှင့်ပါ။ @@ -1227,6 +1232,7 @@ ဖျက်ရန် + %1$d ရွေးချယ်ခဲ့ပါသည် (%2$s) @@ -1290,17 +1296,13 @@ အဖွဲ့မှ သင်ထွက်လိုက်သည်။ အဖွဲ့အား သင်ပြောင်းလဲမှုပြုလိုက်သည်။ အဖွဲ့အား အပ်ဒိတ်ပြီးပါပြီ - + အထွက် အော်ဒီယိုကောလ် - + အထွက် ဗီဒီယိုကောလ် - - မဖြေဆိုသော အော်ဒီယိုကောလ် - - မဖြေဆိုသော ဗီဒီယိုကောလ် - + အဝင် အော်ဒီယိုကောလ် - + အဝင် ဗီဒီယိုကောလ် လွတ်သွားသော အော်ဒီယိုကောလ် @@ -1310,10 +1312,6 @@ အသိပေးချက် ပရိုဖိုင်ကို ဖွင့်ထားစဉ် အော်ဒီယိုကောလ် လွတ်သွားသည် အသိပေးချက် ပရိုဖိုင်ကို ဖွင့်ထားစဉ် ဗီဒီယိုကောလ် လွတ်သွားသည် - - သင်သည် အော်ဒီယိုကောလ်ကို ငြင်းပယ်လိုက်သည် - - သင်သည် ဗီဒီယိုကောလ်ကို ငြင်းပယ်လိုက်သည် %1$s · %2$s %1$s မှအဖွဲ့အား ပြောင်းလဲမှု လုပ်လိုက်သည်။ @@ -1514,28 +1512,53 @@ ယခုအခါ %1$s သည် ငွေပေးချေမှုများကို လက်ခံနိုင်ပါသည် - %1$s သည် အဖွဲ့လိုက်ခေါ်ဆိုမှု စခဲ့ပါသည် · %2$s - အဖွဲ့ကောလ်ကို သင် စတင်ခဲ့ပါသည် · %1$s - %1$s သည် အဖွဲ့လိုက်ခေါ်ဆိုမှုထဲ၌ ရှိပါသည် · %2$s - သင်သည် အဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိပါသည် · %1$s - %1$s နှင့် %2$s သည် အဖွဲ့လိုက်ခေါ်ဆိုမှုထဲ၌ ရှိပါသည် · %3$s - အဖွဲ့လိုက်ခေါ်ဆိုမှု · %1$s - - %1$s သည် အဖွဲ့လိုက်ခေါ်ဆိုမှု စခဲ့ပါသည် - အဖွဲ့လိုက် ဖုန်းခေါ်ဆိုမှု သင်စခဲ့ပါသည် - %1$s သည် အဖွဲ့လိုက်ခေါ်ဆိုမှုထဲ၌ ရှိပါသည် - သင်သည် အဖွဲ့လိုက်ခေါ်ဆိုမှုထဲ၌ ရှိပါသည် - %1$s နှင့် %2$s သည် အဖွဲ့လိုက်ခေါ်ဆိုမှုထဲ၌ ရှိပါသည် - အဖွဲ့လိုက် ခေါ်ဆိုမှု + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s သင် - - %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်များသည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိပါသည် · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်များသည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိပါသည် + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ အတည်ပြုကုဒ်တစ်ခုကို တောင်းဆိုနိုင်ခြင်းမရှိပါ။ သင့်ကွန်ရက် ချိတ်ဆက်မှုကို စစ်ဆေးပြီး ထပ်ကြိုးစားပါ။ စံပုံစံနှင့် မကိုက်ညီသည့် နံပါတ် + သင်ထည့်သွင်းထားသည့် နံပါတ် (%1$s) သည် စံပုံစံနှင့် မကိုက်ညီပုံပေါ်ပါသည်။\n\nသင်ဆိုလိုသည်မှာ %2$s ဟုတ်ပါသလား။ Signal Android - ဖုန်းနံပါတ် ပုံစံ @@ -2622,6 +2646,8 @@ လုပ်ဆောင်နေဆဲ ထပ်မံလေ့လာရန် ခေါ်ဆိုမှုထဲဝင်မယ် + + Call back ခေါ်ဆိုမှုသို့ပြန်သွားသည် ခေါ်ဆိုမှုထဲ လူပြည့်နေပါပြီ သူငယ်ချင်းများကို ဖိတ်ခေါ်ပါ @@ -2686,6 +2712,7 @@ ခေါ်ဆိုမှုမှ ထွက်ခွာမယ် အောက်ပါပုဂ္ဂိုလ်များသည် ကိရိယာများကို ပြန်လည်တပ်ဆင်နိုင်သည် သို့မဟုတ် ပြောင်းလဲနိုင်သည်။ သင်၏ ပုဂ္ဂိုလ်လုံခြုံရေးကို ကာကွယ်ရန် သင်၏​လုံခြုံရေးနံပါတ်ကို ၎င်းပုဂ္ဂိုလ်များဖြင့် စစ်ဆေးအတည်ပြုပါ။ ကြည့်ရှုပါ + ယခင်အတည်ပြုပြီး @@ -2992,6 +3019,7 @@ မူလ မြင့်သော + အများဆုံး @@ -3349,6 +3377,7 @@ %1$s သို့ပေးပို့ခဲ့ပြီး သင့်ကို %1$s ၊ %2$s အချိန်တွင် %1$s ကို %2$s ၊ %3$s အချိန်တွင် + သို့ မှ ငွေပေးချေမှု ငွေပမာဏနှင့် လုပ်ဆောင်သည့်အချိန် အပါအဝင် ငွေပေးငွေယူသည့် အသေးစိတ်အချက်အလက်များသည် MobileCoin စာအုပ်စာရင်း၏ အစိတ်အပိုင်းတစ်ခု ဖြစ်ပါသည်။ @@ -3411,6 +3440,7 @@ ငွေပေးချေမှု အတည်ပြုမယ် ကွန်ယက်အခကြေးငွေ ခန့်မှန်းထားသည် %1$s + သို့ စုစုပေါင်း ပမာဏ လက်ကျန်ငွေ - %1$s @@ -4006,6 +4036,7 @@ ကလစ်ဘုတ်သို့ကူးယူပြီး စီမံသူ + ခွင့်ပြုမယ် ငြင်းပယ်မယ် @@ -4033,6 +4064,7 @@ အသံ မက်ဆေ့ချ် · %1$s + %1$s မှ %2$s သို့ @@ -4077,6 +4109,7 @@ %1$s နှင့် %2$s တို့သည် အဖွဲ့ထဲသို့ ဝင်လာပါသည် %1$s, %2$s နှင့် %3$s တို့သည် အဖွဲ့ထဲသို့ ဝင်လာပါသည် %1$s, %2$s နှင့် အခြား %3$d ဦးသည် အဖွဲ့ထဲသို့ ဝင်လာပါသည် + %1$s အဖွဲ့မှ ထွက်သွားသည် %1$s နှင့် %2$s တို့သည် အဖွဲ့မှ ထွက်သွားသည် %1$s, %2$s နှင့် %3$s တို့သည် အဖွဲ့မှ ထွက်သွားသည် @@ -4452,6 +4485,7 @@ မက်ဆေ့ချ်ပေးပို့ခြင်း ပျောက်ကွယ်မည့် မက်ဆေ့ချ်များ အက်ပ် လုံခြုံရေး + နောက်ဆုံး စာရင်းနှင့် အက်ပ်အတွင်း စခရင်ရှော့(တ်)များကို ပိတ်ရန် Signal မက်ဆေ့ချ်များနှင့် ကောလ်များ၊ အမြဲတမ်း ထပ်ဆင့်ပို့ ကောလ်များနှင့် ချိပ်ပိတ်ပေးပို့သူ ချက်(တ်)အသစ်များအတွက် ပုံသေတိုင်မာ @@ -4508,11 +4542,14 @@ ဓာတ်ပုံ အရည်အသွေး မီဒီယာ အရည်အသွေး ပို့ပြီး အရည်အသွေးမြင့်သည့် မီဒီယာဖိုင်ကို ပို့လျှင် ဒေတာ ပိုသုံးပါလိမ့်မည်။ + မြင့်သော + အသင့်အတင့် ဖုန်းခေါ်ဆိုမှုများ + အော်တို စိတ်ကြိုက် အရောင်များ သုံးရန် ချက်(တ်)အရောင် @@ -4721,6 +4758,7 @@ ပုံရိုက်ရန် ဓာတ်ပုံ ရွေးရန် ရုပ်ပုံ + စာသား သိမ်းမယ် Avatar ရှင်းရန် @@ -4777,7 +4815,7 @@ မက်ဆေ့ချ် ပေါင်းထည့်ရန် ပြန်စာ ပေါင်းထည့်ရန် ဖော်ပြပါသို့ ပို့ရန် - မက်ဆေ့ချ်ကို တစ်ကြိမ်ကြည့်ရန် + View once media တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော ဖိုင်များသည် ကြီးလွန်းနေပါသည် တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော ဖိုင်များသည် အကျုံးမဝင်ပါ ဖိုင်များစွာကို ရွေးထားပါသည် @@ -5365,7 +5403,7 @@ %1$s %2$s သင် - + %1$s မှ %2$s သို့ စာပြန်မည် @@ -6558,5 +6596,11 @@ မှတ်ချက်ပြင်ဆင်တည်းဖြတ်မှု + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 21cf0cd9fe..11f574993d 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -415,6 +415,7 @@ Bli med + Full Kunne ikke sende media @@ -613,6 +614,7 @@ Hent fra arkiv Slett Velg alle + %1$d valgt %1$d valgt @@ -864,6 +866,7 @@ Invitasjonen er sendt %1$d invitasjoner er sendt + “%1$s” kan ikke bli automatisk lagt til denne gruppen av deg.\n\nPersonen har blitt invitert, men vil ikke se noen meldinger til gruppen før invitasjonen godtas. Disse brukerne kan ikke automatisk legges til av deg.\n\nDe er blitt invitert, men vil ikke se noen meldinger til gruppen før invitasjonen godtas. @@ -1044,7 +1047,9 @@ Rediger navn og bilde \"Legacy\"-grupper + Dette er en \"legacy\"-gruppe. Funksjoner som gruppeadministratorer er kun tilgjengelig for \"nye\" grupper. + Dette er en \"legacy\"-gruppe. For å få tilgang til de nye funksjonene, som @-omtaler og gruppeadministratorer, Denne gruppen kan ikke oppgraderes til ny gruppe fordi den er for stor. Maksimal gruppestørrelse er %1$d. Oppgrader denne gruppen. @@ -1271,6 +1276,7 @@ Slett + %1$d valgt (%2$s) %1$d valgt (%2$s) @@ -1335,17 +1341,13 @@ Du har forlatt gruppa. Du har oppdatert gruppa. Gruppen ble oppdatert. - + Utgående taleanrop - + Utgående videoanrop - - Ubesvart taleanrop - - Ubesvart videoanrop - + Innkommende taleanrop - + Innkommende videoanrop Tapt taleanrop @@ -1355,10 +1357,6 @@ Tapt taleanrop mens varslingsprofil var slått på Tapt videoanrop mens varslingsprofil var slått på. - - Du avviste et taleanrop - - Du avviste et videoanrop %1$s · %2$s %1$s oppdaterte gruppa. @@ -1567,30 +1565,55 @@ %1$s kan nå motta betalinger - %1$s startet en gruppesamtale · %2$s - Du startet en gruppesamtale · %1$s - %1$s er i gruppesamtalen · %2$s - Du er i gruppesamtalen · %1$s - %1$s og %2$s er i gruppesamtalen · %3$s - Gruppesamtale · %1$s - - %1$s startet en gruppesamtale - Du gjorde et gruppeanrop - %1$s er i gruppesamtalen - Du er med i gruppesamtalen - %1$s og %2$s er i gruppesamtalen - Gruppesamtale + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Deg - - %1$s, %2$s, og %3$d annen er i gruppesamtalen · %4$s - %1$s, %2$s, og %3$d andre er i gruppesamtalen · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, og %3$d annen er i gruppesamtalen - %1$s, %2$s, og %3$d andre er i gruppesamtalen + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Kan ikke be om bekreftelseskode. Sjekk internettilkoblingen og prøv igjen. Ikke-standard tallformat + Tallet du skrev inn (%1$s), ser ut til å være skrevet i et ikke-standard tallformat.\n\nMente du %2$s? Signal Android – format for telefonnummer @@ -2709,6 +2733,8 @@ Laster inn Lær mer Bli med i samtale + + Call back Gå tilbake til samtale Samtalen er full Inviter venner @@ -2773,6 +2799,7 @@ Forlat samtale Følgende personer kan ha reinstallert eller byttet enhet. Bekreft sikkerhetsnummeret ditt med dem for å sikre personvernet. Vis + Tidligere bekreftet @@ -3086,6 +3113,7 @@ Forvalgt Høy + Maks @@ -3446,6 +3474,7 @@ Sendt til %1$s Du %1$s på %2$s %1$s %2$s på %3$s + Til Fra Transaksjonsinformasjonen, inkludert betalingsbeløp og transaksjonstidspunkt, finner du i MobileCoin-hovedboken. @@ -3508,6 +3537,7 @@ Bekreft betaling Nettverksgebyr Beregnet %1$s + Til Totalt beløp Saldo: %1$s @@ -4112,6 +4142,7 @@ Kopiert til utklippstavle Administrator + Godkjenn Avslå @@ -4139,6 +4170,7 @@ Lydmelding · %1$s + %1$s til %2$s @@ -4185,6 +4217,7 @@ %1$s og %2$s ble med %1$s, %2$s og %3$s ble med %1$s, %2$s og %3$d andre ble med + %1$s forlot %1$s og %2$s forlot %1$s, %2$s og %3$s forlot @@ -4562,6 +4595,7 @@ Meldinger Tidsavgrensede meldinger App-sikkerhet + Blokker skjermbilder i sist brukte-listen og inne i appen Signal-meldinger og samtaler, omdiriger alle samtaler, og forseglet avsender Standard nedtelling for nye samtaler @@ -4618,11 +4652,14 @@ Mediekvalitet Sendt mediekvalitet Å sende høykvalitetsmedier vil bruke mer data. + Høy + Standard Samtaler + Auto Bruk selvvalgt farge Samtalefarge @@ -4834,6 +4871,7 @@ Ta et bilde Velg et bilde Bilde + Tekst Lagre Fjern ikon @@ -4894,7 +4932,7 @@ Legg til en melding Legg til et svar Send til - Se én gang-melding + View once media Elementene er for store Elementene er ugyldige For mange valgte elementer @@ -5487,7 +5525,7 @@ %1$s %2$s Deg - + %1$s til %2$s Svar @@ -6704,5 +6742,11 @@ Rediger notat + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f8a053b21f..317c304e78 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -415,6 +415,7 @@ Deelnemen + Vol Fout bij het verzenden van media @@ -613,6 +614,7 @@ Dearchiveren Verwijderen Alles selecteren + %1$d geselecteerd %1$d geselecteerd @@ -864,6 +866,7 @@ Uitnodiging verstuurd %1$d uitnodigingen verstuurd + ‘%1$s’ kan niet direct door jou worden toegevoegd aan de groep.\n\nIn plaats daarvan heeft deze persoon een uitnodiging gekregen om lid te worden van de groep, en zal geen groepsberichten kunnen zien totdat die de uitnodiging heeft geaccepteerd. Deze personen kunnen niet direct door jou aan de groep worden toegevoegd.\n\n In plaats daarvan hebben ze een uitnodiging gekregen om lid te worden van de groep, en ze zullen geen groepsberichten kunnen zien totdat ze de uitnodiging hebben geaccepteerd. @@ -1044,7 +1047,9 @@ Naam en afbeelding bewerken Verouderde groep + Dit is een verouderde groep. Om nieuwe functionaliteiten zoals groepsbeheer te kunnen gebruiken moet je een nieuwe groep aanmaken. + Dit is een verouderde groep; om nieuwe functionaliteiten te kunnen gebruiken zoals @vermeldingen en beheerders, Deze verouderde groep kan niet worden omgezet naar een nieuwe-stijl groep omdat het te veel leden bevat. Het maximum ledenaantal voor een groep is %1$d. moet je deze groep omzetten. @@ -1271,6 +1276,7 @@ Verwijderen + %1$d geselecteerd (%2$s) %1$d geselecteerd (%2$s) @@ -1335,17 +1341,13 @@ Je hebt de groep verlaten. Je hebt de groep aangepast. De groep is aangepast. - + Uitgaande spraakoproep - + Uitgaande video-oproep - - Onbeantwoorde spraakoproep - - Onbeantwoorde video-oproep - + Inkomende spraakoproep - + Inkomende video-oproep Gemiste spraakoproep @@ -1355,10 +1357,6 @@ Gemiste spraakoproep terwijl het meldingsprofiel was ingeschakeld Gemiste video-oproep terwijl meldingsprofiel was ingeschakeld - - Je hebt een spraakoproep geweigerd - - Je hebt een video-oproep geweigerd %1$s · %2$s %1$s heeft de groep aangepast. @@ -1567,30 +1565,55 @@ %1$s kan nu Betalingen accepteren - %1$s heeft een groepsoproep gestart · %2$s - Je hebt een groepsoproep gestart · %1$s - %1$s is in deze groepsoproep aanwezig · %2$s - Je neemt deel aan een groepsoproep · %1$s - %1$s en %2$s zijn in deze groepsoproep aanwezig · %3$s - Groepsoproep · %1$s - - %1$s heeft een groepsoproep gestart - Je hebt een groepsoproep gestart - %1$s is in deze groepsoproep aanwezig - Je neemt deel aan de groepsoproep - %1$s en %2$s zijn in deze groepsoproep aanwezig - Groepsoproep + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Jij - - %1$s, %2$s en %3$d ander zijn in deze groepsoproep aanwezig · %4$s - %1$s, %2$s en %3$d anderen zijn in deze groepsoproep aanwezig · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s en %3$d ander zijn in deze groepsoproep aanwezig - %1$s, %2$s en %3$d anderen zijn in deze groepsoproep aanwezig + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Kan geen verificatiecode aanvragen. Ga na dat je apparaat met het internet is verbonden en probeer het opnieuw. Afwijkend telefoonnummerformaat + Het door jou ingevulde telefoonnummer (%1$s) heeft een afwijkend formaat.\n\nBedoelde je %2$s? Signal Android - Telefoonnummerformaat @@ -2709,6 +2733,8 @@ Aan het laden Meer lezen Aan oproep deelnemen + + Call back Naar de oproep terugkeren De oproep is vol Vrienden uitnodigen @@ -2773,6 +2799,7 @@ Oproep verlaten De volgende personen hebben mogelijk de app opnieuw geïnstalleerd of hebben een ander apparaat in gebruik genomen. Verifieer jullie veiligheidsnummers om zeker te zijn dat je met de juiste personen communiceert. Weergeven + Was voorheen geverifieerd @@ -3086,6 +3113,7 @@ Systeemstandaard Hoog + Maximaal @@ -3446,6 +3474,7 @@ Verzonden aan %1$s jou op %1$s om %2$s %1$s op %2$s om %3$s + Naar Van Transactiegegevens, waaronder het bedrag en het tijdstip van de transactie, zijn onderdeel van het kasboek van MobileCoin. @@ -3508,6 +3537,7 @@ Betaling bevestigen Netwerkkosten Naar schatting %1$s + Naar Totaalbedrag Saldo: %1$s @@ -4112,6 +4142,7 @@ Gekopieerd naar klembord Beheerder + Toestaan Afwijzen @@ -4139,6 +4170,7 @@ Audiobericht · %1$s + %1$s naar %2$s @@ -4185,6 +4217,7 @@ %1$s en %2$s nemen nu deel %1$s, %2$s en %3$s nemen nu deel %1$s, %2$s en %3$d andere nemen nu deel + %1$s heeft de oproep verlaten %1$s en %2$s hebben de oproep verlaten %1$s, %2$s en %3$s hebben de oproep verlaten @@ -4562,6 +4595,7 @@ Berichten Verdwijnende berichten App-beveiliging + Screenshots in de lijst van recent geopende apps en in de app blokkeren Signal-berichten en -oproepen, alle oproepen omleiden en verzegelde afzender. Standaardtimer voor nieuwe chats @@ -4618,11 +4652,14 @@ Mediakwaliteit Media-verzendkwaliteit Als je media in hoge kwaliteit verzendt, verbruik je meer data. + Hoog + Standaard Oproepen + Auto Zelfgekozen kleur gebruiken Chatkleur @@ -4834,6 +4871,7 @@ Neem een foto Kies een afbeelding Galerij + Tekst Opslaan Profielafbeelding verwijderen @@ -4894,7 +4932,7 @@ Berichttekst toevoegen Een reactie toevoegen Versturen naar - Eenmaligeweergave-bericht + View once media Een of meerdere items zijn te groot Een of meerdere items zijn ongeldig Te veel items geselecteerd @@ -5487,7 +5525,7 @@ %1$s %2$s Jij - + %1$s naar %2$s Reageren @@ -6704,5 +6742,11 @@ Notitie bewerken + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 418f13ce92..4a51f7cfa6 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -415,6 +415,7 @@ ਸ਼ਾਮਲ ਹੋਵੋ + ਪੂਰਾ ਮੀਡੀਆ ਭੇਜਣ ਦੌਰਾਨ ਤਰੁੱਟੀ @@ -613,6 +614,7 @@ ਅਨਆਰਕਾਈਵ ਕਰੋ ਮਿਟਾਓ ਸਭ ਚੁਣੋ + %1$dਚੁਣਿਆ %1$dਚੁਣੇ @@ -864,6 +866,7 @@ ਸੱਦਾ ਭੇਜਿਆ ਗਿਆ %1$d ਸੱਦੇ ਭੇਜੇ ਗਏ + “%1$s” ਨੂੰ ਤੁਹਾਡੇ ਵੱਲੋਂ ਇਸ ਗਰੁੱਪ ਵਿੱਚ ਆਪਣੇ-ਆਪ ਸ਼ਾਮਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।\n\nਉਹਨਾਂ ਨੂੰ ਸ਼ਾਮਲ ਹੋਣ ਲਈ ਸੱਦਾ ਦਿੱਤਾ ਗਿਆ ਹੈ, ਅਤੇ ਜਦੋਂ ਤੱਕ ਉਹ ਮਨਜ਼ੂਰ ਨਹੀਂ ਕਰ ਲੈਂਦੇ, ਉਦੋਂ ਤੱਕ ਗਰੁੱਪ ਦੇ ਕੋਈ ਸੁਨੇਹੇ ਨਹੀਂ ਦੇਖ ਸਕਣਗੇ। ਇਹਨਾਂ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਤੁਹਾਡੇ ਵੱਲੋਂ ਇਸ ਗਰੁੱਪ ਵਿੱਚ ਆਪਣੇ-ਆਪ ਸ਼ਾਮਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।\n\nਉਹਨਾਂ ਨੂੰ ਗਰੁੱਪ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਣ ਲਈ ਸੱਦਾ ਦਿੱਤਾ ਗਿਆ ਹੈ, ਅਤੇ ਜਦੋਂ ਤੱਕ ਉਹ ਮਨਜ਼ੂਰ ਨਹੀਂ ਕਰ ਲੈਂਦੇ, ਉਦੋਂ ਤੱਕ ਗਰੁੱਪ ਦੇ ਕੋਈ ਸੁਨੇਹੇ ਨਹੀਂ ਦੇਖ ਸਕਣਗੇ। @@ -1044,7 +1047,9 @@ ਨਾਮ ਅਤੇ ਤਸਵੀਰ ਨੂੰ ਸੋਧੋ ਲੈਗਸੀ ਗਰੁੱਪ + ਇਹ ਇੱਕ ਲੈਗਸੀ ਗਰੁੱਪ ਹੈ। ਗਰੁੱਪ ਪਰਸ਼ਾਸ਼ਕ ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਸਿਰਫ਼ ਨਵੇਂ ਗਰੁੱਪਾਂ ਲਈ ਉਪਲਬਧ ਹਨ। + ਇਹ ਇੱਕ ਲੈਗਸੀ ਗਰੁੱਪ ਹੈ। @mentions ਅਤੇ ਐਡਮਿਨਾਂ ਵਰਗੀਆਂ ਨਵੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਲਈ, ਇਸ ਲੈਗਸੀ ਗਰੁੱਪ ਨੂੰ ਕਿਸੇ ਨਵੇਂ ਗਰੁੱਪ ਵਿੱਚ ਅੱਪਗ੍ਰੇਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਕਿਉਂਕਿ ਇਹ ਬਹੁਤ ਵੱਡਾ ਹੈ। ਗਰੁੱਪ ਦਾ ਵੱਧ ਤੋਂ ਵੱਧ ਆਕਾਰ %1$d ਹੈ। ਇਸ ਗਰੁੱਪ ਨੂੰ ਅੱਪਗ੍ਰੇਡ ਕਰੋ। @@ -1271,6 +1276,7 @@ ਮਿਟਾਓ + %1$dਚੁਣਿਆ (%2$s) %1$d ਚੁਣੇ (%2$s) @@ -1335,17 +1341,13 @@ ਤੁਸੀਂ ਗਰੁੱਪ ਛੱਡ ਦਿੱਤਾ ਹੈ। ਤੁਸੀਂ ਗਰੁੱਪ ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ। ਗਰੁੱਪ ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ ਸੀ। - + ਆਊਟਗੋਇੰਗ ਵੌਇਸ ਕਾਲ - + ਆਊਟਗੋਇੰਗ ਵੀਡੀਓ ਕਾਲ - - ਵੌਇਸ ਕਾਲ ਨਹੀਂ ਚੁੱਕੀ ਗਈ - - ਵੀਡੀਓ ਕਾਲ ਨਹੀਂ ਚੁੱਕੀ ਗਈ - + ਇਨਕਮਿੰਗ ਵੌਇਸ ਕਾਲ - + ਇਨਕਮਿੰਗ ਵੀਡੀਓ ਕਾਲ ਵੌਇਸ ਕਾਲ ਮਿਸ ਹੋਈ @@ -1355,10 +1357,6 @@ ਸੂਚਨਾ ਪ੍ਰੋਫਾਈਲ ਦੇ ਚਾਲੂ ਹੋਣ ਦੌਰਾਨ ਵੌਇਸ ਕਾਲ ਮਿਸ ਹੋਈ ਸੂਚਨਾ ਪ੍ਰੋਫਾਈਲ ਦੇ ਚਾਲੂ ਹੋਣ ਦੌਰਾਨ ਵੀਡੀਓ ਕਾਲ ਮਿਸ ਹੋਈ - - ਤੁਸੀਂ ਵੌਇਸ ਕਾਲ ਨੂੰ ਅਸਵੀਕਾਰ ਕੀਤਾ - - ਤੁਸੀਂ ਵੀਡੀਓ ਕਾਲ ਨੂੰ ਅਸਵੀਕਾਰ ਕੀਤਾ %1$s . %2$s %1$s ਨੇ ਗਰੁੱਪ ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ। @@ -1567,30 +1565,55 @@ %1$s ਹੁਣ ਭੁਗਤਾਨ ਸਵੀਕਾਰ ਕਰ ਸਕਦੇ ਹਨ - %1$s ਨੇ ਗਰੁੱਪ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ · %2$s - ਤੁਸੀਂ ਗਰੁੱਪ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ · %1$s - %1$s ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹੈ · %2$s - ਤੁਸੀਂ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹੋ · %1$s - %1$s ਅਤੇ %2$s ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹਨ · %3$s - ਗਰੁੱਪ ਕਾਲ · %1$s - - %1$s ਨੇ ਇੱਕ ਗਰੁੱਪ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ - ਤੁਸੀਂ ਗਰੁੱਪ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ - %1$s ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹੈ - ਤੁਸੀਂ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹੋ - %1$s ਅਤੇ %2$s ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹਨ - ਗਰੁੱਪ ਕਾਲ + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s ਤੁਸੀਂ - - %1$s, %2$s, ਤੇ %3$d ਹੋਰ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹਨ · %4$s - %1$s, %2$s, ਤੇ %3$d ਹੋਰ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹਨ · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, ਤੇ %3$d ਹੋਰ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹਨ - %1$s, %2$s, ਤੇ %3$d ਹੋਰ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹਨ + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ ਤਸਦੀਕ ਕੋਡ ਲਈ ਬੇਨਤੀ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ ਰਹੇ। ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। ਗ਼ੈਰ-ਮਿਆਰੀ ਅੰਕ ਰੂਪ + ਤੁਹਾਡੇ ਵਲੋਂ ਦਿੱਤਾ ਨੰਬਰ (%1$s) ਗੈਰ-ਮਿਆਰੀ ਜਾਪਦਾ ਹੈ।\n\nਕੀ ਤੁਹਾਡਾ ਮਤਲਬ %2$s ਹੈ? Signal Android - ਫ਼ੋਨ ਨੰਬਰ ਫਾਰਮਿਟ @@ -2709,6 +2733,8 @@ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ … ਹੋਰ ਜਾਣੋ ਕਾਲ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ + + Call back ਕਾਲ ’ਤੇ ਵਾਪਸ ਜਾਓ ਕਾਲ ਭਰੀ ਹੋਈ ਹੈ ਦੋਸਤਾਂ ਨੂੰ ਸੱਦਾ ਦਿਓ @@ -2773,6 +2799,7 @@ ਕਾਲ ਨੂੰ ਛੱਡੋ ਹੇਠ ਦਿੱਤੇ ਲੋਕਾਂ ਨੇ ਮੁੜ-ਸਥਾਪਤ ਕੀਤਾ ਜਾਂ ਡਿਵਾਈਸ ਨੂੰ ਬਦਲਿਆ ਹੋ ਸਕਦਾ ਹੈ। ਪਰਦੇਦਾਰੀ ਨੂੰ ਯਕੀਨੀ ਬਣਾਉਣ ਲਈ ਉਹਨਾਂ ਨਾਲ ਆਪਣੇ ਸੁਰੱਖਿਆ ਨੰਬਰ ਦੀ ਤਸਦੀਕ ਕਰੋ। ਵੇਖੋ + ਪਹਿਲਾਂ ਤਸਦੀਕਸ਼ੁਦਾ @@ -3086,6 +3113,7 @@ ਡਿਫੌਲਟ ਉੱਚਾ + ਅਧਿਕਤਮ @@ -3446,6 +3474,7 @@ %1$s ਨੂੰ ਭੇਜਿਆ ਤੁਸੀਂ %1$s ਨੂੰ %2$s ’ਤੇ %1$s, %2$s ਨੂੰ %3$s ’ਤੇ + ਪ੍ਰਤੀ ਵਲੋਂ ਭੁਗਤਾਨ ਦੀ ਰਕਮ ਅਤੇ ਟ੍ਰਾਂਜੈਕਸ਼ਨ ਦੇ ਸਮੇਂ ਸਮੇਤ ਟ੍ਰਾਂਜੈਕਸ਼ਨ ਦੇ ਵੇਰਵੇ MobileCoin ਲੈਜ਼ਰ ਦਾ ਹਿੱਸਾ ਹਨ। @@ -3508,6 +3537,7 @@ ਭੁਗਤਾਨ ਨੂੰ ਤਸਦੀਕ ਕਰੋ ਨੈੱਟਵਰਕ ਫ਼ੀਸ ਅੰਦਾਜ਼ਨ %1$s + ਪ੍ਰਤੀ ਕੁੱਲ ਰਕਮ ਬਕਾਇਆ: %1$s @@ -4112,6 +4142,7 @@ ਕਲਿਪਬੋਰਡ ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ ਐਡਮਿਨ + ਮਨਜ਼ੂਰ ਕਰੋ ਇਨਕਾਰ ਕਰੋ @@ -4139,6 +4170,7 @@ ਆਵਾਜ਼ ਸੁਨੇਹਾ · %1$s + %1$s ਤੋਂ %2$s @@ -4185,6 +4217,7 @@ %1$s ਅਤੇ %2$s ਸ਼ਾਮਲ ਹੋਏ %1$s, %2$s ਅਤੇ %3$s ਸ਼ਾਮਲ ਹੋਏ %1$s, %2$s ਅਤੇ %3$d ਹੋਰ ਸ਼ਾਮਲ ਹੋਏ + %1$s ਨੇ ਛੱਡਿਆ %1$s ਅਤੇ %2$s ਨੇ ਛੱਡਿਆ %1$s, %2$s ਅਤੇ %3$s ਨੇ ਛੱਡਿਆ @@ -4562,6 +4595,7 @@ ਸੁਨੇਹੇ ਲੈਣ-ਦੇਣ ਅਲੋਪ ਹੋਣ ਵਾਲੇ ਸੁਨੇਹੇ ਐਪ ਦੀ ਸੁਰੱਖਿਆ + ਹਾਲੀਆ ਸੂਚੀ ਵਿੱਚ ਅਤੇ ਐਪ ਦੇ ਵਿੱਚ ਸਕ੍ਰੀਨਸ਼ਾਟਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਗਾਓਨੂੰ ਪਾਬੰਦੀ ਲਗਾਓ Signal ਸੁਨੇਹੇ ਅਤੇ ਕਾਲਾਂ, ਹਮੇਸ਼ਾਂ ਕਾਲਾਂ ਰਿਲੇਅ ਕਰੋ, ਅਤੇ ਸੀਲਬੰਦ ਭੇਜਣ ਵਾਲਾ ਨਵੀਆਂ ਚੈਟਾਂ ਲਈ ਡਿਫੌਲਟ ਟਾਈਮਰ @@ -4618,11 +4652,14 @@ ਮੀਡੀਆ ਕੁਆਲਟੀ ਭੇਜੀ ਮੀਡੀਆ ਕੁਆਲਟੀ ਵਧੀਆ ਕੁਆਲਟੀ ਮੀਡੀਆ ਭੇਜਣ ਲਈ ਵੱਧ ਡਾਟਾ ਵਰਤਿਆ ਜਾਵੇਗਾ। + ਉੱਚਾ + ਸਟੈਂਡਰਡ ਕਾਲਾਂ + ਆਟੋ ਪਸੰਦੀਦਾ ਰੰਗ ਵਰਤੋ ਗੱਲਬਾਤ ਦਾ ਰੰਗ @@ -4834,6 +4871,7 @@ ਤਸਵੀਰ ਲਓ ਕੋਈ ਫ਼ੋਟੋ ਚੁਣੋ ਫੋਟੋ + ਟੈਕਸਟ ਸੰਭਾਲੋ ਅਵਤਾਰ ਨੂੰ ਮਿਟਾਓ @@ -4894,7 +4932,7 @@ ਸੁਨੇਹਾ ਜੋੜੋ ਜਵਾਬ ਜੋੜੋ ਇਹਨਾਂ ਨੂੰ ਭੇਜੋ - ਸੁਨੇਹੇ ਨੂੰ ਇੱਕ ਵਾਰ ਵੇਖੋ + View once media ਇੱਕ ਜਾਂ ਵੱਧ ਚੀਜ਼ਾਂ ਬਹੁਤ ਵੱਡੀਆਂ ਸਨ ਇੱਕ ਜਾਂ ਵੱਧ ਆਈਟਮਾਂ ਵਾਜਬ ਨਹੀਂ ਹਨ ਬਹੁਤ ਵੱਧ ਚੀਜ਼ਾਂ ਚੁਣੀਆਂ ਗਈਆਂ @@ -5487,7 +5525,7 @@ %1$s %2$s ਤੁਸੀਂ - + %1$s ਤੋਂ %2$s ਜਵਾਬ ਦਿਓ @@ -6704,5 +6742,11 @@ ਨੋਟ ਸੋਧੋ + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index ff20177965..1fb555f90b 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -421,6 +421,7 @@ Dołącz + Pełne Błąd przy wysyłaniu multimediów @@ -645,6 +646,7 @@ Cofnij z arch. Usuń Zazn. wszystko + %1$d wybrany %1$d wybrane @@ -902,6 +904,7 @@ %1$d zaproszeń wysłanych %1$d zaproszeń wysłanych + Nie możesz automatycznie dodać \"%1$s\" do tej grupy.\n\nUżytkownik został zaproszony i nie zobaczy żadnych wiadomości grupowych, dopóki nie przyjmie zaproszenia. Nie możesz automatycznie dodać tych użytkowników do tej grupy.\n\nUżytkownicy zostali zaproszeni i nie zobaczą żadnych wiadomości grupowych, dopóki nie przyjmą zaproszenia. @@ -1114,7 +1117,9 @@ Edytuj nazwę i zdjęcie Stara grupa + To stara wersja grupy. Takie funkcje jak administratorzy grup są dostępne tylko w Nowej grupie. + To Stara grupa. Aby móc korzystać z takich funkcji jak @wzmianki i administratorzy grup, Ta Stara grupa nie może zostać zaktualizowana do Nowej grupy, ponieważ jest zbyt duża. Maksymalny rozmiar grupy to %1$d. zaktualizuj tę grupę. @@ -1359,6 +1364,7 @@ Usuń + %1$d wybrany (%2$s) %1$d wybrane (%2$s) @@ -1425,17 +1431,13 @@ Opuściłeś(aś) grupę. Zaktualizowałeś(aś) grupę. Grupa została zaktualizowana. - + Wychodzące połączenie głosowe - + Wychodzące połączenie wideo - - Nieodebrane połączenie głosowe - - Nieodebrane połączenie wideo - + Przychodzące połączenie głosowe - + Przychodzące połączenie wideo Nieodebrane połączenie głosowe @@ -1445,10 +1447,6 @@ Nieodebrane połączenie głosowe przy włączonym profilu powiadomień Nieodebrane połączenie wideo przy włączonym profilu powiadomień - - Odrzucono połączenie głosowe - - Odrzucono połączenie wideo %1$s · %2$s %1$s zaktualizował(a) grupę. @@ -1673,34 +1671,59 @@ Od teraz %1$s przyjmuje płatności - %1$s rozpoczął(ęła) połączenie grupowe · %2$s - Rozpoczęto połączenie grupowe · %1$s - %1$s uczestniczy w rozmowie · %2$s - Uczestniczysz w rozmowie · %1$s - %1$s i %2$s uczestniczą w rozmowie · %3$s - Połączenie grupowe · %1$s - - %1$s rozpoczął(ęła) połączenie grupowe - Rozpocząłeś(ęłaś) połączenie grupowe - %1$s uczestniczy w rozmowie - Uczestniczysz w rozmowie - %1$s i %2$s uczestniczą w rozmowie - Połączenie grupowe + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Ty - - %1$s, %2$s i %3$d inny uczestniczy w rozmowie · %4$s - %1$s, %2$s i %3$d innych uczestniczy w rozmowie · %4$s - %1$s, %2$s i %3$d innych uczestniczy w rozmowie · %4$s - %1$s, %2$s i %3$d innych uczestniczy w rozmowie · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s i %3$d inny uczestniczy w rozmowie - %1$s, %2$s i %3$d innych uczestniczy w rozmowie - %1$s, %2$s i %3$d innych uczestniczy w rozmowie - %1$s, %2$s i %3$d innych uczestniczy w rozmowie + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Nie udało się zażądać kodu weryfikacyjnego. Sprawdź swoje połączenie sieciowe i spróbuj ponownie. Niestandardowy format numeru + Podany przez Ciebie numer (%1$s) wydaje się mieć niestandardowy format.\n\nCzy chodziło Ci o %2$s? Signal Android - Format numeru telefonu @@ -2883,6 +2907,8 @@ Ładowanie Dowiedz się więcej Dołącz do rozmowy + + Call back Wróć do rozmowy Połączenie jest pełne Zaproś znajomych @@ -2947,6 +2973,7 @@ Opuść rozmowę Następujące osoby mogły ponownie zainstalować aplikację lub zmienić urządzenie. Zweryfikuj wasze numery bezpieczeństwa, aby zapewnić prywatność. Zobacz + Wcześniej zweryfikowane @@ -3274,6 +3301,7 @@ Domyślny Wysoki + Maksymalny @@ -3640,6 +3668,7 @@ Wysłano do %1$s Ty, dnia %1$s o %2$s %1$s dnia %2$s o %3$s + Do Od Szczegóły transakcji, uwzględniające kwotę i czas transakcji, są częścią rejestru waluty MobileCoin. @@ -3702,6 +3731,7 @@ Potwierdź płatność Opłata sieciowa Szacowane %1$s + Do Suma Środki: %1$s @@ -4324,6 +4354,7 @@ Skopiowano do schowka Administrator + Zaakceptuj Odrzuć @@ -4351,6 +4382,7 @@ Wiadomość głosowa · %1$s + %1$s do %2$s @@ -4401,6 +4433,7 @@ %1$s i %2$s dołączyli(ły) %1$s, %2$s i %3$s dołączyli(ły) %1$s, %2$s i %3$d innych dołączyło + %1$s opuścił(a) rozmowę %1$s i %2$s opuścii(ły) rozmowę %1$s, %2$s i %3$s opuścili(ły) rozmowę @@ -4782,6 +4815,7 @@ Wiadomości Znikające wiadomości Bezpieczeństwo aplikacji + Blokuj zrzuty ekranu na liście aktywnych oraz w aplikacji Wiadomości i połączenia Signal, zawsze przekazuj połączenia i ukryty nadawca Domyślny czas dla nowych czatów @@ -4838,11 +4872,14 @@ Jakość multimediów Jakość wysyłanych multimediów Wysyłanie multimediów wysokiej jakości zużywa więcej danych. + Wysoka + Standardowa Połączenia + Automatyczny Użyj własnych kolorów Kolor czatu @@ -5060,6 +5097,7 @@ Zrób zdjęcie Wybierz zdjęcie Zdjęcie + Tekst Zapisz Usuń awatar @@ -5128,7 +5166,7 @@ Dodaj wiadomość Dodaj odpowiedź Wyślij do - Wiadomość jednorazowa + View once media Co najmniej jeden element był zbyt duży Co najmniej jeden element był nieprawidłowy Zbyt wiele wybranych elementów @@ -5731,7 +5769,7 @@ %1$s %2$s Ty - + %1$s do %2$s Odpowiedz @@ -6972,7 +7010,7 @@ Pseudonim - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Pseudonimy i notatki są przechowywane przez Signal za pomocą usługi szyfrowania typu end-to-end. Są one widoczne tylko dla Ciebie. Imię @@ -6986,9 +7024,9 @@ Zapisz - Delete? + Usunąć? - This will permanently delete any nickname and note you’ve set. + Spowoduje to trwałe usunięcie pseudonimu i notatki. @@ -6996,5 +7034,11 @@ Edytuj notatkę + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 8cb223688c..8d5f3ec620 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -415,6 +415,7 @@ Entrar + Lotada Erro ao enviar a mídia @@ -613,6 +614,7 @@ Desarquivar Excluir Marcar todas + %1$d selecionada %1$d selecionadas @@ -864,6 +866,7 @@ Convite enviado %1$d convites enviados + “%1$s” não pode ser automaticamente adicionado a este grupo por você.\n\nEles foram convidados a participar, e não verão nenhuma mensagem do grupo até que aceitem. Estas pessoas não podem ser adicionadas automaticamente no grupo.\n\nElas foram convidadas a participar do grupo, e não verão nenhuma mensagem do grupo até que aceitem. @@ -1044,7 +1047,9 @@ Editar nome e foto Grupo Legado + Este é um Grupo Legado. Recursos como administradores de grupos só estão disponíveis em Novos Grupos. + Este é um Grupo Legado. Para acessar novos recursos como @menções e administradores, Este Grupo Legado não pode ser atualizado para um Grupo Novo porque é muito grande. O tamanho máximo de grupos é %1$d. atualizar este grupo. @@ -1271,6 +1276,7 @@ Apagadas + %1$d selecionada (%2$s) %1$d selecionadas (%2$s) @@ -1335,17 +1341,13 @@ Você saiu do grupo. Você atualizou o grupo. O grupo foi atualizado. - + Chamada de voz efetuada - + Chamada de vídeo efetuada - - Chamada de voz não atendida - - Chamada de vídeo não atendida - + Chamada de voz recebida - + Chamada de vídeo recebida Chamada de voz perdida @@ -1355,10 +1357,6 @@ Chamada de voz perdida com o perfil de notificação ativado Chamada de vídeo perdida com o perfil de notificação ativado - - Você recusou uma chamada de voz - - Você recusou uma chamada de vídeo %1$s · %2$s %1$s atualizou o grupo. @@ -1567,30 +1565,55 @@ %1$s já pode aceitar Pagamentos - %1$s começou uma chamada em grupo · %2$s - Você começou uma chamada em grupo · %1$s - %1$s está na chamada em grupo · %2$s - Você está na chamada em grupo · %1$s - %1$s e %2$s estão na chamada em grupo · %3$s - Chamada em grupo · %1$s - - %1$s começou uma chamada em grupo - Você começou uma chamada em grupo - %1$s está na chamada em grupo - Você está na chamada em grupo - %1$s e %2$s estão na chamada em grupo - Chamada em grupo + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Você - - %1$s, %2$s e %3$d outra pessoa estão na ligação em grupo · %4$s - %1$s, %2$s e %3$d outras pessoas estão na ligação em grupo · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s e %3$d outra pessoa estão na ligação em grupo - %1$s, %2$s e %3$d outras pessoas estão na ligação em grupo + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Não foi possível solicitar um código de verificação. Verifique a sua conexão de rede e tente novamente. Formato de número de telefone não reconhecido + O número que você inseriu (%1$s) não parece estar no formato padrão de números de telefone.\n\nSerá que você quis dizer %2$s? Signal Android - Formato de números de telefone @@ -2709,6 +2733,8 @@ Carregando Saiba mais Participar da chamada + + Call back Voltar para a chamada A chamada está lotada Convidar amigos(as) @@ -2773,6 +2799,7 @@ Sair da chamada As seguintes pessoas podem ter reinstalado o Signal ou mudado de aparelho. Verifique o seu número de segurança com elas para garantir a sua privacidade e a delas. Exibir + Já verificado @@ -3086,6 +3113,7 @@ Padrão Alta + Máxima @@ -3446,6 +3474,7 @@ Enviado para %1$s Você em %1$s às %2$s %1$s em %2$s às %3$s + Para De Os detalhes da transação, incluindo o valor do pagamento e a hora da transação, fazem parte do Livro-razão MobileCoin. @@ -3508,6 +3537,7 @@ Confirmar pagamento Taxa de transação Estimada em %1$s + Para Valor total Saldo: %1$s @@ -4112,6 +4142,7 @@ Copiado para a área de transferência Admin + Aprovar Recusar @@ -4139,6 +4170,7 @@ Mensagem de voz · %1$s + %1$s para %2$s @@ -4185,6 +4217,7 @@ %1$s e %2$s entraram %1$s, %2$s e %3$s entraram %1$s, %2$s e %3$d outras pessoas entraram + %1$s saiu %1$s e %2$s saíram %1$s, %2$s e %3$s saíram @@ -4562,6 +4595,7 @@ Mensagens Mensagens efêmeras Segurança do app + Bloquear capturas de tela na lista de recentes e dentro do aplicativo Mensagens e chamadas no Signal, redirecionar chamadas e remetente oculto Cronômetro padrão para novos chats @@ -4618,11 +4652,14 @@ Qualidade das fotos Qualidade da mídia enviada Enviar mídia com alta qualidade usará mais dados de internet. + Alta + Padrão Chamadas + Auto Usar cores personalizadas Cor da conversa @@ -4834,6 +4871,7 @@ Tirar uma foto Escolher uma foto Foto + Texto Salvar Remover foto @@ -4894,7 +4932,7 @@ Adicionar uma mensagem Responder Enviar para - Mensagem efêmera + View once media Um ou mais itens são muito grandes Um ou mais itens são inválidos Muitos itens foram selecionados @@ -5487,7 +5525,7 @@ %1$s %2$s Você - + %1$s em %2$s Responder @@ -6704,5 +6742,11 @@ Editar nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 8884f945d3..56048cebf0 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -415,6 +415,7 @@ Entrar + Cheio(a) Erro ao enviar multimédia @@ -613,6 +614,7 @@ Desarquivar Eliminar Selec. tudo + %1$d selecionada %1$d selecionadas @@ -864,6 +866,7 @@ Convite enviado %1$d convites enviados + “%1$s” não pode ser adicionado(a) automaticamente para este grupo por si.\n\nEle(a) foi convidado(a) a juntar-se, mas não irá ver nenhuma mensagem do grupo até que ele(a) aceite. Estes utilizadores não podem ser adicionados automaticamente a este grupo por si.\n\nEles foram convidados a juntarem-se, mas não irão ver nenhuma mensagem do grupo até que aceite @@ -1044,7 +1047,9 @@ Editar nome e imagem Grupo legado + Este é um \'Grupo legado\'. Os recursos como administradores de grupo encontram-se apenas disponíveis para \'Grupos novos\'. + Este é um \'Grupo legado\'. Para aceder a novos recursos como as menções e administradores, Não pode ser feito o upgrade deste \'Grupo legado\' porque é demasiadamente grande. O tamanho máximo do grupo é de %1$d. faça o upgrade deste grupo. @@ -1271,6 +1276,7 @@ Eliminar + %1$d selecionado (%2$s) %1$d selecionados (%2$s) @@ -1335,17 +1341,13 @@ Você abandonou o grupo. Você atualizou o grupo. O grupo foi atualizado. - + A efetuar chamada de voz - + A efetuar videochamada - - Chamada de voz não atendida - - Videochamada não atendida - + A receber chamada de voz - + A receber videochamada Chamada de voz perdida @@ -1355,10 +1357,6 @@ Chamada de voz perdida no modo perfil de notificação Videochamada perdida no modo perfil de notificação - - Rejeitou uma chamada de voz - - Rejeitou uma videochamada %1$s · %2$s %1$s atualizou o grupo. @@ -1567,30 +1565,55 @@ %1$s pode agora aceitar Pagamentos - %1$s iniciou uma chamada em grupo · %2$s - Iniciou uma chamada de grupo · %1$s - %1$s está na chamada de grupo · %2$s - Está na chamada de grupo · %1$s - %1$s e %2$s estão na chamada de grupo · %3$s - Chamada de grupo · %1$s - - %1$s iniciou uma chamada de grupo - Você iniciou uma chamada de grupo - %1$s está na chamada de grupo - Você está na chamada de grupo - %1$s e %2$s estão na chamada de grupo - Chamada de grupo + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Você - - %1$s, %2$s, e %3$d outro estão na chamada de grupo · %4$s - %1$s, %2$s, e outros %3$d estão na chamada de grupo · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, e %3$d outro estão na chamada de grupo - %1$s, %2$s, e outros %3$d estão na chamada de grupo + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Não foi possível pedir o código de verificação. Verifique a sua ligação à internet e tente novamente. Formato de número não padronizado + O número que introduziu (%1$s) não parece estar num formato padronizado.\n\nSerá que queria dizer %2$s? Signal Android - Formato de números de telefone @@ -2709,6 +2733,8 @@ A carregar Saber mais Entrar na chamada + + Call back Regressar à chamada A chamada encontra-se completa Convidar amigos @@ -2773,6 +2799,7 @@ Abandonar chamada As seguintes pessoas podem ter reinstalado ou mudado de dispositivo. Verifique o seu número de segurança com eles para assegurar a privacidade. Ver + Verificado anteriormente @@ -3086,6 +3113,7 @@ Predefinição Elevada + Máxima @@ -3446,6 +3474,7 @@ Enviado por %1$s Você em %1$s às %2$s %1$s em %2$s às %3$s + Para De Os detalhes de pagamento incluindo o montante de pagamento e o horário da transação são parte do Ledger da MobileCoin. @@ -3508,6 +3537,7 @@ Confirmar pagamento Taxa de rede Estimado %1$s + Para Montante total Saldo: %1$s @@ -4112,6 +4142,7 @@ Copiado para a área de transferência Administrador + Aprovar Negar @@ -4139,6 +4170,7 @@ Mensagem de voz · %1$s + %1$s para %2$s @@ -4185,6 +4217,7 @@ %1$s e %2$s juntaram-se %1$s, %2$s e %3$s juntaram-se %1$s, %2$s e outros %3$d juntaram-se + %1$s restante(s) %1$s e %2$s restante(s) %1$s, %2$s e %3$s restante(s) @@ -4562,6 +4595,7 @@ Mensagens Destruição de mensagens Segurança da aplicação + Bloquear a captura de ecrã na lista de aplicações recentes abertas e dentro da aplicação As mensagens e chamadas do Signal, retransmitem sempre as chamadas e emissor selado Tempo padrão para chats novos @@ -4618,11 +4652,14 @@ Qualidade média Qualidade da multimédia enviada Enviar multimédia com alta qualidade utilizará mais dados de internet. + Elevada + Normal Chamadas + Auto Utilizar cores personalizadas Cor do chat @@ -4834,6 +4871,7 @@ Tirar uma fotografia Escolha uma fotografia Fotografia + Texto Guardar Remover avatar @@ -4894,7 +4932,7 @@ Adicionar uma mensagem Adicionar uma resposta Enviar para - Mensagem de visualização única + View once media Um ou mais itens são demasiado grandes Um ou mais itens são inválidos Demasiados itens selecionados @@ -5487,7 +5525,7 @@ %1$s%2$s Você - + %1$s para %2$s Responder @@ -6680,7 +6718,7 @@ Alcunha - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + As alcunhas e notas são armazenadas no Signal e encriptadas ponta a ponta. Só são visíveis para si. Primeiro nome @@ -6694,9 +6732,9 @@ Guardar - Delete? + Eliminar? - This will permanently delete any nickname and note you’ve set. + Isto irá eliminar permanentemente qualquer alcunha ou nota que tenha definido. @@ -6704,5 +6742,11 @@ Editar nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index a71e512b17..2b68e8604b 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -418,6 +418,7 @@ Alătură-te + Plin Eroare la trimiterea conținutului media @@ -629,6 +630,7 @@ Dezarhivează Șterge Selectează tot + %1$d selectată %1$d selectate @@ -883,6 +885,7 @@ %1$d invitații trimise %1$d de invitații trimise + \"%1$s\" nu poate fi adăugat automat la acest grup de către tine.\n\nAu fost invitați să se alăture și nu vor vedea niciun mesaj de grup până când nu acceptă. Acești utilizatori nu pot fi adăugați automat la acest grup de către tine.\n\nAu fost invitați să se alăture și nu vor vedea niciun mesaj de grup până când nu acceptă. @@ -1079,7 +1082,9 @@ Editare nume și poză Grup Vechi + Acesta este un grup vechi. Funcții precum administratorii de grup sunt disponibile doar grupurilor noi. + Acesta este un grup vechi. Pentru a accesa funcții precum @mențiuni și administratorii de grup, Acest grup vechi nu poate fi actualizat la unul nou, pentru că este prea mare. Dimensiunea maximă a unui grup este de %1$d. actualizează acest grup. @@ -1315,6 +1320,7 @@ Șterge + %1$d selectat (%2$s) %1$d selectate (%2$s) @@ -1380,17 +1386,13 @@ Ai părăsit grupul. Ai actualizat grupul. Grupul a fost actualizat. - + Apel de ieșire vocal - + Apel de ieșire video - - Apel vocal nepreluat - - Apel video nepreluat - + Apel de intrare vocal - + Apel de intrare video Apel vocal nepreluat @@ -1400,10 +1402,6 @@ Apel vocal pierdut în timp ce profilul de notificare este activat Apel video pierdut în timp ce profilul de notificare este activat - - Ai respins un apel vocal - - Ai respins un apel video %1$s · %2$s %1$s a actualizat grupul. @@ -1620,32 +1618,57 @@ %1$s poate să accepte acum Plăți - %1$s a început un apel de grup · %2$s - Ai început un apel de grup · %1$s - %1$s este în apelul de grup · %2$s - Ești în apelul de grup · %1$s - %1$s și %2$s sunt în apelul de grup · %3$s - Apel de grup · %1$s - - %1$s a început un apel de grup - Ai început un apel de grup - %1$s este în apelul de grup - Ești în apelul de grup - %1$s și %2$s sunt în apelul de grup - Apel de grup + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Tu - - %1$s, %2$s și %3$d sunt în apelul de grup · %4$s - %1$s, %2$s și alte %3$d persoane sunt în apelul de grup · %4$s - %1$s, %2$s și alte %3$d de persoane sunt în apelul de grup · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s și %3$d sunt în apelul de grup - %1$s, %2$s și alte %3$d persoane sunt în apelul de grup - %1$s, %2$s și alte %3$d de persoane sunt în apelul de grup + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2142,6 +2165,7 @@ Nu s-a putut cere un cod de verificare. Te rugăm să verifici conexiunea și să încerci din nou. Număr cu format non-standard + Numărul pe care l-ai introdus (%1$s) pare a fi în format non-standard.\n\nAi vrut să scrii %2$s? Signal Android - Formatul Numărului de Telefon @@ -2796,6 +2820,8 @@ Se încarcă Află mai multe Alătură-te apelului + + Call back Întoarcere la apel Numărul maxim de participanți a fost atins Invită prieteni @@ -2860,6 +2886,7 @@ Părăsește apelul Este posibil ca următoarele persoane să fi reinstalat sau schimbat dispozitivele. Verifică numărul tău de siguranță cu acestea pentru a asigura confidențialitatea. Vizualizare + Verificat anterior @@ -3180,6 +3207,7 @@ Implicit Mare + Maximă @@ -3543,6 +3571,7 @@ Trimis către %1$s Tine %1$s la %2$s %1$s %2$s la %3$s + Către De la Detaliile tranzacției inclusiv valoarea și data acesteia, fac parte din registrul MobileCoin. @@ -3605,6 +3634,7 @@ Confirmă plata Comision de rețea Estimat %1$s + Către Suma totală Sold: %1$s @@ -4218,6 +4248,7 @@ S-a copiat în clipboard Administrator + Aprobă Refuză @@ -4245,6 +4276,7 @@ Mesaj vocal · %1$s + %1$s la %2$s @@ -4293,6 +4325,7 @@ %1$s și %2$s s-au alăturat %1$s, %2$s și %3$s s-au alăturat %1$s, %2$s și %3$d alții s-au alăturat + %1$s a ieșit %1$s și %2$s au ieșit %1$s, %2$s și %3$s au ieșit @@ -4672,6 +4705,7 @@ Mesagerie Dispariție mesaje Securitate aplicație + Blochează capturile de ecran în lista cu aplicații recente și în interiorul aplicației Mesaje și apeluri Signal, redirecționare apeluri și expeditor ascuns Temporizator implicit pentru conversații noi @@ -4728,11 +4762,14 @@ Calitate Media Calitatea fișierelor media trimise Trimiterea fișierelor media la calitate înaltă va duce la un consum mai mare de date. + Mare + Standard Apeluri + Auto Utilizează culori personalizate Culoare conversație @@ -4947,6 +4984,7 @@ Fă o poză Alege o poză Poză + Text Salvează Șterge avatarul @@ -5011,7 +5049,7 @@ Adaugă un mesaj Adaugă un răspuns Trimite către - Mesaj vizibil o singură dată + View once media Unul sau mai multe elemente au fost prea mari Unul sau mai multe elemente au fost invalide Prea multe elemente selectate @@ -5609,7 +5647,7 @@ %1$s %2$s Tu - + %1$s la %2$s Răspunde @@ -6850,5 +6888,11 @@ Editează nota + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 20892e5f90..83f2f912cb 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -421,6 +421,7 @@ Войти + Заполнен Ошибка при отправке медиафайла @@ -645,6 +646,7 @@ Разархивировать Удалить Выбрать все + %1$d выбран %1$d выбрано @@ -902,6 +904,7 @@ %1$d приглашений отправлено %1$d приглашений отправлено + Вы не можете автоматически добавить «%1$s» в эту группу.\n\nЕму (ей) было отправлено приглашение присоединиться, и он(-а) не увидит сообщения в группе, пока не примет приглашение. Вы не можете автоматически добавить этих пользователей в эту группу.\n\nИм было отправлено приглашение присоединиться, и они не увидят сообщения в группе, пока не примут приглашение. @@ -1114,7 +1117,9 @@ Редактировать имя и фото Старая группа + Это Старая группа. Такие функции, как администраторы групп, доступны только для Новых групп. + Это Старая группа. Чтобы получить доступ к таким функциям, как @упоминания и администраторы групп, Эта Старая группа не может быть обновлена до Новой группы, потому что она слишком большая. Максимальный размер группы — %1$d. обновите эту группу. @@ -1359,6 +1364,7 @@ Удалить + %1$d выбран (%2$s) %1$d выбрано (%2$s) @@ -1425,17 +1431,13 @@ Вы покинули группу. Вы обновили группу. Группа была обновлена. - + Исходящий голосовой звонок - + Исходящий видеозвонок - - Неотвеченный голосовой звонок - - Неотвеченный исходящий видеозвонок - + Входящий голосовой звонок - + Входящий видеозвонок Пропущенный голосовой звонок @@ -1445,10 +1447,6 @@ Пропущенный аудиозвонок при включённом профиле уведомлений Пропущенный видеозвонок при включённом профиле уведомлений - - Вы отклонили голосовой звонок - - Вы отклонили видеозвонок %1$s · %2$s %1$s обновил(-а) группу. @@ -1673,34 +1671,59 @@ %1$s теперь может принимать платежи - %1$s начал(-а) групповой звонок · %2$s - Вы начали групповой звонок · %1$s - %1$s в групповом звонке · %2$s - Вы в групповом звонке · %1$s - %1$s и %2$s в групповом звонке · %3$s - Групповой звонок · %1$s - - %1$s начал(-а) групповой звонок - Вы начали групповой звонок - %1$s в групповом звонке - Вы в групповом звонке - %1$s и %2$s в групповом звонке - Групповой звонок + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$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, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$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, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Не удаётся запросить код подтверждения. Пожалуйста, проверьте ваше подключение к интернету и попробуйте ещё раз. Нестандартный номер телефона + Номер, который вы ввели (%1$s), похоже, не в стандартном формате.\n\nВы имели в виду %2$s? Signal Android - Формат номера телефона @@ -2883,6 +2907,8 @@ Загрузка Узнать больше Присоединиться к звонку + + Call back Вернуться к звонку Звонок заполнен Пригласить друзей @@ -2947,6 +2973,7 @@ Покинуть звонок Следующие люди могли переустановить Signal или сменить устройства. Подтвердите ваш код безопасности с ними, чтобы обеспечить конфиденциальность. Просмотреть + Ранее проверенный(-ая) @@ -3274,6 +3301,7 @@ По умолчанию Высокий + Максимальный @@ -3640,6 +3668,7 @@ Вы отправили %1$s Вы %1$s в %2$s %1$s %2$s в %3$s + Кому От Подробности транзакции, включая сумму платежа и время транзакции, являются частью распределённого реестра MobileCoin. @@ -3702,6 +3731,7 @@ Подтвердить платёж Комиссия сети Ориентировочно в %1$s + Кому Итоговая сумма Баланс: %1$s @@ -4324,6 +4354,7 @@ Скопировано в буфер обмена Администратор + Принять Отклонить @@ -4351,6 +4382,7 @@ Голосовое сообщение · %1$s + %1$s в %2$s @@ -4401,6 +4433,7 @@ %1$s и %2$s присоединились %1$s, %2$s и %3$s присоединились %1$s, %2$s и ещё %3$d присоединились + %1$s покинул(-а) звонок %1$s и %2$s покинули звонок %1$s, %2$s и %3$s покинули звонок @@ -4782,6 +4815,7 @@ Общение Исчезающие сообщения Безопасность приложения + Блокировать снимки экрана в списке недавних приложений и внутри приложения Звонки и сообщения Signal, ретрансляция звонков и запечатанный отправитель Таймер по умолчанию для новых чатов @@ -4838,11 +4872,14 @@ Качество медиа Качество отправляемого медиа Отправление медиа в высоком качестве использует больше данных. + Высокое + Стандартное Звонки + Авто Использовать пользовательские цвета Цвет чата @@ -5060,6 +5097,7 @@ Сделать снимок Выбрать фото Фото + Текст Сохранить Удалить аватар @@ -5128,7 +5166,7 @@ Добавьте сообщение Добавьте ответ Отправить - Одноразовое сообщение + View once media Один или несколько элементов были слишком большими Один или несколько элементов были недействительными Выбрано слишком много элементов @@ -5731,7 +5769,7 @@ %1$s %2$s Вы - + %1$s в %2$s Ответить @@ -6996,5 +7034,11 @@ Изменить заметку + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index dac2e2d647..655913e374 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -421,6 +421,7 @@ Pridať sa + Plný Chyba pri odosielaní médií @@ -645,6 +646,7 @@ Odarchivovať Vymazať Označiť všetko + %1$d vybrané %1$d vybraných @@ -902,6 +904,7 @@ Bolo odoslanej %1$d pozvánky Bolo odoslaných %1$d pozvánok + “%1$s” nemôžete automaticky pridať do tejto skupiny.\n\nTomuto používateľovi bola odoslaná pozvánka, aby sa pridal, a kým pozvanie nepríjme, neuvidí žiadne správy zo skupiny. Týchto používateľov nemôžete automaticky pridať do tejto skupiny.\n\nBoli im odoslané pozvánky, aby sa pridali, a kým pozvanie nepríjmu, neuvidia žiadne správy zo skupiny. @@ -1114,7 +1117,9 @@ Zmeniť názov a obrázok Zastaraná skupina + Toto je Zastaraná skupina. Funkcie ako administrátori skupín sú dostupné len pre Nové skupiny. + Toto je Zastaraná skupina. Pre prístup k novým funkciám ako @spomenutia a administrátori Táto Zastaraná skupina nemôže byť modernizovaná na Novú skupinu, lebo je príliš veľká. Maximálna veľkosť skupiny je %1$d. modernizujte túto skupinu. @@ -1359,6 +1364,7 @@ Vymazať + %1$d vybrané (%2$s) %1$d vybraných (%2$s) @@ -1425,17 +1431,13 @@ Opustili ste skupinu. Aktualizovali ste skupinu. Skupina bola aktualizovaná - + Odchádzajúci hlasový hovor - + Odchádzajúci videohovor - - Neprijatý hlasový hovor - - Neprijatý videohovor - + Prichádzajúci hlasový hovor - + Prichádzajúci videohovor Zmeškaný hlasový hovor @@ -1445,10 +1447,6 @@ Zmeškaný hlasový hovor pri zapnutom profile upozornení Zmeškaný videohovor pri zapnutom profile upozornení - - Odmietli ste hlasový hovor - - Odmietli ste videohovor %1$s · %2$s %1$s aktualizoval/a skupinu. @@ -1673,34 +1671,59 @@ %1$s odteraz môže prijímať platby - %1$s zahájil(a) skupinový hovor · %2$s - Zahájili ste skupinový hovor · %1$s - %1$s je v skupinovom hovore · %2$s - Ste v skupinovom hovore · %1$s - %1$s a %2$s sú v skupinovom hovore · %3$s - Skupinový hovor · %1$s - - %1$s zahájil(a) skupinový hovor - Zahájili ste skupinový hovor - %1$s je v skupinovom hovore - Ste v skupinovom hovore - %1$s a %2$s sú v skupinovom hovore - Skupinový hovor + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Vy - - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore - %1$s, %2$s a %3$d ďalší sú v skupinovom hovore + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Nie je možné požiadať o overovací kód. Skontrolujte pripojenie k sieti a skúste to znova. Neštandardný formát čísla + Zdá sa, že zadané číslo (%1$s) má neštandardný formát.\n\n Mali ste na mysli %2$s? Signal Android - Formát telefónneho čísla @@ -2883,6 +2907,8 @@ Načítava sa Dozvedieť sa viac Pripojiť sa k hovoru + + Call back Vrátiť sa k hovoru Hovor je plne obsadený Pozvať priateľov @@ -2947,6 +2973,7 @@ Opustiť hovor Nasledujúce osoby si možno preinštalovali alebo zmenili zariadenia. Overte si s nimi vaše bezpečnostné číslo pre zaistenie súkromia. Zobraziť + V minulosti overený @@ -3274,6 +3301,7 @@ Predvolená Vysoká + Maximálna @@ -3640,6 +3668,7 @@ Odoslané používateľovi %1$s Vy na %1$s s %2$s %1$s na %2$s s %3$s + Komu Od Podrobnosti o transakcii vrátane sumy a času transakcie sú súčasťou MobileCoin Ledger. @@ -3702,6 +3731,7 @@ Potvrďte platbu Sieťový poplatok Odhadované %1$s + Komu Celková suma Zostatok: %1$s @@ -4324,6 +4354,7 @@ Skopírované do schránky Admin + Prijať Odmietnuť @@ -4351,6 +4382,7 @@ Hlasová správa · %1$s + od %1$s do %2$s @@ -4401,6 +4433,7 @@ %1$s a %2$s sa pripojili k hovoru %1$s, %2$s a %3$s sa pripojili k hovoru %1$s, %2$s a %3$d ďalší sa pripojili k hovoru + %1$s opustil(a) hovor %1$s a %2$s opustili hovor %1$s, %2$s a %3$s opustili hovor @@ -4782,6 +4815,7 @@ Odosielanie správ Miznúce správy Zabezpečenie aplikácie + Zakázať vytváranie snímkov obrazovky v aplikácii a zozname nedávnych konverzácií Správy a hovory Signal, vždy opakované hovory a zapečatený odosielateľ Predvolený časovač pre nové čety @@ -4838,11 +4872,14 @@ Kvalita médií Kvalita odoslaných médií Odosielanie médií vo vysokej kvalite využije viac dát. + Vysoká + Štandardná Volania + Automatická Použite vlastné farby Farba četu @@ -5060,6 +5097,7 @@ Odfoťte sa Vyberte fotku Fotka + Text Uložiť Vymazať avatara @@ -5128,7 +5166,7 @@ Pridajte správu Pridať odpoveď Odoslať - Zobraziť správu raz + View once media Jedna, alebo viac položiek bolo príliš veľkých Jedna, alebo viac položiek je neplatných Je vybratých príliš veľa položiek @@ -5731,7 +5769,7 @@ %1$s %2$s Vy - + od %1$s do %2$s Odpovedať @@ -6996,5 +7034,11 @@ Upraviť poznámku + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index c5e4472ef6..f2a53c94b8 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -421,6 +421,7 @@ Pridruži se + Polno Napaka pri pošiljanju medijske datoteke @@ -645,6 +646,7 @@ Od-arhiviraj Izbriši Označi vse + %1$d izbran %1$d izbrana @@ -902,6 +904,7 @@ %1$d vabila so poslana %1$d vabil je poslanih + Uporabnika_ce “%1$s” ne morete avtomatično dodati k skupini.\n\nPoslano mu_ji je bilo vabilo in dokler ga ne potrdi, ne bo videl_a sporočil skupine. Teh uporabnikov_ic ne morete avtomatično dodati v skupino.\n\nPoslano jim je bilo vabilo in dokler ga ne potrdijo, ne bodo videli_e sporočil skupine. @@ -1114,7 +1117,9 @@ Uredi ime in sliko Stara oblika skupine + To je Stara skupina. Funkcije, kot so skrbniki_ce skupin, so na voljo le pri Novih skupinah. + To je Stara skupina. Za dostop do naprednih funkcij, kot so @omembe in skrbniki_ce skupin, Ta Stara skupina je prevelika, da bi bila lahko nadgrajena v Novo skupino. Največja velikost skupine je %1$d. nadgradite to skupino. @@ -1359,6 +1364,7 @@ Izbriši + %1$d izbran (%2$s) %1$d izbrana (%2$s) @@ -1425,17 +1431,13 @@ Zapustili ste skupino. Posodobili ste skupino. Skupina je bila posodobljena - + Odhodni glasovni klic - + Odhodni video klic - - Neodgovorjen glasovni klic - - Neodgovorjen video klic - + Dohodni glasovni klic - + Dohodni video klic Zgrešen glasovni klic @@ -1445,10 +1447,6 @@ Neodgovorjen zvočni klic, medtem ko je profil za obvestila vklopljen Neodgovorjen video klic, medtem ko je profil za obvestila vklopljen - - Zavrnili ste glasovni klic - - Zavrnili ste video klic %1$s · %2$s %1$s je posodobil_a skupino. @@ -1673,34 +1671,59 @@ %1$s lahko zdaj sprejema plačila - %1$s je začel_a skupinski klic. · %2$s - Začeli ste skupinski klic · %1$s - %1$s je v skupinskem klicu. · %2$s - Ste v skupinskem klicu · %1$s - %1$s in %2$s sta v skupinskem klicu · %3$s - Skupinski klic · %1$s - - %1$s je začel_a skupinski klic - Začeki ste skupinski klic - %1$s je v skupinskem klicu. - Ste v skupinskem klicu - %1$s in %2$s sta v skupinskem klicu - Skupinski klic + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Vi - - %1$s, %2$s, in še %3$d drug_a so v skupinskem klicu · %4$s - %1$s, %2$s, in še %3$d druga_i so v skupinskem klicu · %4$s - %1$s, %2$s, in še %3$d drugi_e so v skupinskem klicu · %4$s - %1$s, %2$s in še %3$d drugih je v skupinskem klicu · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, in še %3$d drug_a so v skupinskem klicu - %1$s, %2$s, in še %3$d druga_i so v skupinskem klicu - %1$s, %2$s in še %3$d drugi_e so v skupinskem klicu - %1$s, %2$s in še %3$d drugih je v skupinskem klicu + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Ni mogoče zahtevati kode za preverjanje. Preverite svojo omrežno povezavo in poskusite znova. Nestandardni format številk + Številka, ki ste jo vpisali (%1$s) je v nestandardnem formatu.\n\nSte morda mislili %2$s? Signal Android - Format telefonskih številk @@ -2883,6 +2907,8 @@ Nalagam Izvedite več Pridruži se klicu + + Call back Zopet se pridruži klicu Klic je polno zaseden Vabilo prijateljem_icam @@ -2947,6 +2973,7 @@ Zapusti klic Našteti uporabniki_ce so najbrž ponovno namestili Signal ali zamenjali napravo. Za potrditev istovetnosti ponovno preverite varnostno število z njimi. Preglej + Prej potrjeno @@ -3274,6 +3301,7 @@ Privzeto Visoka + Najvišja @@ -3640,6 +3668,7 @@ Poslano %1$s Vi na %1$s za %2$s %1$s na %2$s za %3$s + Naslovnik Pošiljatelj Podatki o transakcijah, vključno z zneskom plačil in časom transakcij, so del MobileCoinove knjige transakcij MobileCoin Ledger. @@ -3702,6 +3731,7 @@ Potrditev plačila Omrežna pristojbina Predviden znesek %1$s + Naslovnik Skupni znesek Stanje: %1$s @@ -4324,6 +4354,7 @@ Kopirano v odložišče Skrbnik_ca + Potrdi Zavrni @@ -4351,6 +4382,7 @@ Glasovno sporočilo · %1$s + %1$s do %2$s @@ -4401,6 +4433,7 @@ Pridružila_li sta se %1$s in %2$s Pridružili_le so se %1$s, %2$s in %3$s Pridružila_li sta se %1$s, %2$s in še %3$d drugih + %1$s je odšel_la %1$s in %2$s sta odšla_li %1$s, %2$s in %3$s so odšli_le @@ -4782,6 +4815,7 @@ Sporočanje Izginjajoča sporočila Varnost aplikacije + Prepreči zajem slike zaslona na seznamu nedavnih sporočil in v aplikaciji Sporočila in klici Signal, posredovanje klicev in zakriti pošiljatelj Privzeti odštevalnik za nove klepete @@ -4838,11 +4872,14 @@ Kvaliteta medisjkih datotek Kvaliteta poslanih medijskih datotek Pošiljanje medijskih datotek boljše kvalitete porabi več podatkov. + Visoka + Standardna Klici + Avtomatično Uporabi barve po meri Barva klepeta @@ -5060,6 +5097,7 @@ Ustvari fotografijo Izberi fotografijo Slika + Besedilo Shrani Izbriši avatar @@ -5128,7 +5166,7 @@ Dodaj sporočilo Dodaj odgovor Poslano uporabniku_ci - Enkratni ogled sporočila + View once media En ali več predmetov je bilo prevelikih En ali več predmetov je bilo neveljavnih Preveč izbranih predmetov @@ -5731,7 +5769,7 @@ %1$s %2$s Vi - + %1$s do %2$s Odgovori @@ -6996,5 +7034,11 @@ Uredi zaznamek + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 67a24c0a37..4a1a5067a9 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -415,6 +415,7 @@ Bashkohu + Plot Ka ndodhur një gabim gjatë dërgimit të medias @@ -613,6 +614,7 @@ Çarkivoje Fshiji Përzgjidh të gjitha + %1$d i përzgjedhur %1$d të përzgjedhura @@ -864,6 +866,7 @@ Ftesa u dërgua %1$d ftesat u dërguan + “%1$s” s\\’mund të shtohet automatikisht te ky grup nga ju.\n\nËshtë ftuar të marrë pjesë, dhe s\\’do të shohë ndonjë mesazh grupi, para se të pranojnë ftesën. Këta përdorues s\\’mund të shtohen automatikisht te ky grup nga ju.\n\n Janë ftuar të marrin pjesë te grupi, dhe s\\’do të shohin ndonjë mesazh grupi, përpara se të pranojnë ftesat. @@ -1044,7 +1047,9 @@ Përpunoni emër dhe foto Grup i Dikurshëm + Ky është një Grup i Dikurshëm. Veçori të tilla, bie fjala, përgjegjës grupi mund të kihen vetëm për Grupe të Rinj. + Ky është një Grup i Dikurshëm. Që të përdorni veçori të reja, të tilla si @përmendje dhe përgjegjës, Ky Grup i Dikurshëm s\\’mund të përmirësohet si Grup i Ri, ngaqë është shumë i madh. Madhësia maksimum për grupet është %1$d. përmirësoni këtë grup. @@ -1271,6 +1276,7 @@ Fshiji + %1$d i përzgjedhur (%2$s) %1$d të përzgjedhur (%2$s) @@ -1335,17 +1341,13 @@ E keni lënë grupin. E përditësuat grupin. Grupi u përditësua. - + Thirrje zanore dalëse - + Thirrje video dalëse - - Thirrje zanore pa përgjigje - - Thirrje video pa përgjigje - + Thirrje zanore hyrëse - + Thirrje video hyrëse Thirrje zanore e humbur @@ -1355,10 +1357,6 @@ Thirrje zanore e humbur edhe pse njoftimet janë aktive Thirrje me video e humbur edhe pse njoftimi i profilit është aktiv - - Refuzove thirrjen zanore - - Refuzove thirrjen video %1$s · %2$s %1$s përditësoi grupin. @@ -1567,30 +1565,55 @@ %1$s tani mund të pranojë pagesat - %1$s nisi një thirrje grupi · %2$s - Ti fillove një thirrje në grup · %1$s - %1$s gjendet te thirrja e grupit · %2$s - Gjendeni te thirrja e grupit · %1$s - %1$s dhe %2$s gjenden te thirrja e grupit · %3$s - Thirrje grupi · %1$s - - %1$s nisi një thirrje grupi - Nisët një thirrje grupi - %1$s gjendet te thirrja e grupit - Gjendeni te thirrja e grupit - %1$s dhe %2$s gjenden te thirrja e grupit - Thirrje grupi + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Ju - - %1$s, %2$s, dhe %3$d tjetër gjenden te thirrja e grupit · %4$s - %1$s, %2$s, dhe %3$d të tjerë gjenden te thirrja e grupit · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, dhe %3$d tjetër gjenden te thirrja e grupit - %1$s, %2$s, dhe %3$d të tjerë gjenden te thirrja e grupit + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Nuk mund të kërkohet një kod verifikimi. Të lutem, kontrollo lidhjen me rrjetin dhe riprovo. Format jostandard numrash + Numri që dhatë (%1$s) duket të jetë në një format jo standard.\n\nKishit ndërmend %2$s? Signal Android - Formati i numrit të telefonit @@ -2709,6 +2733,8 @@ Po ngarkohet Mësoni më tepër Hyni në thirrje + + Call back Kthehuni te thirrja Thirrja është plot Ftoni shokë @@ -2773,6 +2799,7 @@ Dilni nga thirrja Personat vijues mund të kenë riinstaluar ose ndryshuar pajisjet. Për të siguruar privatësi, verifikoni me ta numrin tuaj të sigurisë. Shihni + Verifikuar më herët @@ -3086,6 +3113,7 @@ Parazgjedhje Lartësi + Maks. @@ -3446,6 +3474,7 @@ Dërguar te %1$s Ju, në %1$s më %2$s %1$s në %2$s më %3$s + Për Nga Detajet e transaksionit duke përfshirë shumën e pagesës dhe kohën e transaksionit janë pjesë e Regjistrit të MobileCoin. @@ -3508,6 +3537,7 @@ Ripohoni pagesën Tarifë rrjeti Afërsisht %1$s + Për Shuma totale Depozitë : %1$s @@ -4112,6 +4142,7 @@ U kopjua në të papastër Përgjegjës + Miratoje Hidhe poshtë @@ -4139,6 +4170,7 @@ Mesazh zanor · %1$s + %1$s për %2$s @@ -4185,6 +4217,7 @@ Erdhën %1$s dhe %2$s Erdhën %1$s, %2$s dhe %3$s Erdhën %1$s, %2$s dhe %3$d të tjerë + %1$s iku %1$s dhe %2$s ikën %1$s, %2$s dhe %3$s ikën @@ -4562,6 +4595,7 @@ Shkëmbim Mesazhesh Zhdukje mesazhesh Siguri aplikacioni + Blloko foto të ekranit te lista e të rejave dhe brenda aplikacionit Mesazhe dhe thirrje Signal, thirrjet kaloji përherë përmes relesh, dhe dërgues të vulosur Kohëmatës i parazgjedhur për bisedat e reja @@ -4618,11 +4652,14 @@ Cilësi media Cilësi medie të dërguar Dërgimi i medias në cilësi të lartë do të përdorë më tepër të dhëna. + Lartësi + Standard Thirrje + Auto Përdor ngjyra vetjake Ngjyra e sfondit të bisedës @@ -4834,6 +4871,7 @@ Bëni një foto Zgjidhni një foto Foto + Tekst Ruaje Spastroje avatarin @@ -4894,7 +4932,7 @@ Shtoni një mesazh Shtoni një përgjigje Dërguar - Mesazh për parje vetëm një herë + View once media Një ose më tepër objekte qenë shumë të mëdhenj Një ose më tepër objekte qenë të pavlefshëm Shumë objekte të përzgjedhur @@ -5487,7 +5525,7 @@ %1$s %2$s Ju - + %1$s për %2$s Përgjigju @@ -6704,5 +6742,11 @@ Përpuno shënimin + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index e938911616..a57bfb83d6 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -415,6 +415,7 @@ Придружите се + Пуно Грешка приликом слања медија @@ -613,6 +614,7 @@ Врати из архиве Избриши Изабери све + Изабрано: %1$d Изабрано: %1$d @@ -864,6 +866,7 @@ Позивница је послата Послатих позивница: %1$d + Не можете аутоматски додати корисника „%1$s“ у групу.\n\n Позивница је послата том кориснику и корисник неће видети групне поруке док је не прихвати. Ове кориснике не можете аутоматски додати у групу.\n\nПозвани су да се придруже групи и неће видети ниједну групну поруку док не прихвате позивницу. @@ -1044,7 +1047,9 @@ Измените име и слику Стара група + Ово је старе група. Функционалности попут администратора групе доступне су само у новим групама. + Ово је стара група. Да бисте приступили новим функционалностима попут @помињања и администратора, Ова стара група не може да се надогради на нову групу јер је превелика. Максимална величина групе је %1$d. надоградите ову групу. @@ -1271,6 +1276,7 @@ Избриши + Изабрано: %1$d (%2$s) Изабрано: %1$d (%2$s) @@ -1335,17 +1341,13 @@ Напустили сте групу. Ажурирали сте групу. Група је ажурирана. - + Одлазни гласовни позив - + Одлазни видео позив - - Неодговорен гласовни позив - - Неодговорен видео позив - + Долазни гласовни позив - + Долазни видео позив Пропуштен гласовни позив @@ -1355,10 +1357,6 @@ Пропуштен је гласовни позив док је профил за обавештења био укључен Пропуштен је гласовни позив док је профил за обавештења био укључен - - Одбили сте гласовни позив - - Одбили сте видео позив %1$s · %2$s %1$s је ажурирао/ла групу. @@ -1567,30 +1565,55 @@ %1$s сада може да прихвата плаћања - %1$s је започео/ла групни позив · %2$s - Започели сте групни позив · %1$s - %1$s је у групном позиву · %2$s - Ви сте у групном позиву · %1$s - %1$s и %2$s су у групном позиву · %3$s - Групни позив · %1$s - - %1$s је започео/ла групни позив - Покренули сте групни позив - %1$s је у групном позиву - Ви сте у групном позиву - %1$s и %2$s су у групном позиву - Групни позив + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Ви - - У групном позиву су %1$s, %2$s и још %3$d особа · %4$s - У групном позиву су %1$s, %2$s и још њих %3$d · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - У групном позиву су %1$s, %2$s и још %3$d особа - У групном позиву су %1$s, %2$s и још њих %3$d + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Није могуће затражити шифру за верификацију. Проверите да ли сте повезани на интернет и пробајте поново. Нестандардни формат броја + Број који сте унели (%1$s) није у стандардном формату.\n\nДа ли сте мислили да унесете: %2$s? Signal Android – формат броја телефона @@ -2709,6 +2733,8 @@ Учитава се Сазнајте више Укључите се у позив + + Call back Вратите се на позив Позив је пун Позовите пријатеље @@ -2773,6 +2799,7 @@ Напусти позив Људи наведени у наставку су вероватно поново инсталирали апликацију или променили уређаје. Верификујте сигурносни број за дописивање са њима како бисте обезбедили приватност. Прикажи + Претходно верификовано @@ -3086,6 +3113,7 @@ Подразумевано Висок квалитет + Максимално @@ -3446,6 +3474,7 @@ Послато кориснику %1$s Ви %1$s у %2$s %1$s %2$s у %3$s + Прималац Пошиљалац Детаљи трансакције, укључујући износ плаћања и време трансакције, део су књиге MobileCoin-а. @@ -3508,6 +3537,7 @@ Потврди плаћање Мрежна накнада Процењено %1$s + Прималац Укупан износ Стање: %1$s @@ -4112,6 +4142,7 @@ Копирано Администратор + Одобравам Одбијам @@ -4139,6 +4170,7 @@ Гласовна порука · %1$s + %1$s за: %2$s @@ -4185,6 +4217,7 @@ %1$s и %2$s су се придружили %1$s, %2$s и %3$s су се придружили %1$s, %2$s и још њих %3$d су се придружили + %1$s је напустио/ла групу %1$s и %2$s су напустили групу %1$s, %2$s и %3$s су напустили групу @@ -4562,6 +4595,7 @@ Поруке Нестајуће поруке Безбедност апликације + Блокирајте снимке екрана у листи недавних апликација и унутар апликације Поруке и позиви преко Signal-а, увек преузмеравај позиве, запечаћени пошиљалац Подразумевани тајмер за нова ћаскања @@ -4618,11 +4652,14 @@ Квалитет медија Квалитет послатог медија Слање висококвалитетних медија ће користити више података. + Висок квалитет + Стандардни квалитет Позиви + Аутоматски Користите прилагођене боје Боја ћаскања @@ -4834,6 +4871,7 @@ Снимите фотографију Одаберите слику Слика + Текст Сачувај Уклони аватар @@ -4894,7 +4932,7 @@ Напишите поруку Напишите одговор Пошаљи кориснику - Једнократна порука + View once media Једна или више ставки су превелике Једна или више ставки су неважеће Изабрано је превише ставки @@ -5487,7 +5525,7 @@ %1$s %2$s Ви - + %1$s за: %2$s Одговорите @@ -6704,5 +6742,11 @@ Измени белешку + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 46eed56847..ecb0a347b4 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -415,6 +415,7 @@ Gå med + Fullt Fel vid sändning av media @@ -613,6 +614,7 @@ Avarkivera Ta bort Markera alla + %1$d vald %1$d valda @@ -864,6 +866,7 @@ Inbjudan skickad %1$d inbjudningar skickade + \"%1$s\" kan inte läggas till automatiskt till denna grupp av dig.\n\nPersonen har blivit inbjuden att gå med och kommer inte att se några gruppmeddelanden förrän personen accepterar. Dessa användare kan inte automatiskt läggas till i denna grupp.\n\nDe har blivit inbjudna att gå med i gruppen och kommer inte att se några gruppmeddelanden förrän de accepterar. @@ -1044,7 +1047,9 @@ Ändra namn och bild Äldre grupp + Detta är en Äldre grupp. Funktioner som gruppadministratörer är endast tillgängliga för Nya grupper. + Detta är en Äldre grupp. För att komma åt nya funktioner som @omnämnanden och administratörer, Denna Äldre gruppen kan inte uppgraderas till en Ny grupp eftersom den är för stor. Den största gruppstorleken är %1$d. uppgradera denna grupp. @@ -1271,6 +1276,7 @@ Ta bort + %1$d vald (%2$s) %1$d valda (%2$s) @@ -1335,17 +1341,13 @@ Du har lämnat gruppen. Du uppdaterade gruppen. Gruppen uppdaterades. - + Utgående röstsamtal - + Utgående videosamtal - - Obesvarat röstsamtal - - Obesvarat videosamtal - + Inkommande röstsamtal - + Inkommande videosamtal Missat röstsamtal @@ -1355,10 +1357,6 @@ Missat röstsamtal medan aviseringsprofilen var aktiverad Missat videosamtal medan aviseringsprofilen var aktiverad - - Du avböjde ett röstsamtal - - Du avböjde ett videosamtal %1$s · %2$s %1$s uppdaterade gruppen. @@ -1567,30 +1565,55 @@ %1$s kan nu ta emot betalningar - %1$s startade ett gruppsamtal · %2$s - Du startade ett gruppsamtal · %1$s - %1$s är i gruppsamtalet · %2$s - Du är i gruppsamtalet · %1$s - %1$s och %2$s är i gruppsamtalet · %3$s - Gruppsamtal · %1$s - - %1$s startade ett gruppsamtal - Du startade ett gruppsamtal - %1$s är i gruppsamtalet - Du är i gruppsamtalet - %1$s och %2$s är i gruppsamtalet - Gruppsamtal + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Du - - %1$s, %2$s och %3$d andra är i gruppsamtalet · %4$s - %1$s, %2$s och %3$d andra är i gruppsamtalet · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s och %3$d andra är i gruppsamtalet - %1$s, %2$s och %3$d andra är i gruppsamtalet + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Det gick inte att begära en verifieringskod. Kontrollera din nätverksanslutning och försök igen. Icke-standardiserat sifferformat + Siffran du angav (%1$s) verkar vara ett icke-standardformat.\n\nMenade du %2$s? Signal Android - Telefonnummerformat @@ -2709,6 +2733,8 @@ Läser in Läs mer Gå med i samtalet + + Call back Återgå till samtal Samtalet är fullt Bjud in vänner @@ -2773,6 +2799,7 @@ Lämna samtalet Följande personer kan ha installerat om eller bytt enheter. Verifiera ditt säkerhetsnummer med dem för att säkerställa integriteten. Visa + Tidigare verifierade @@ -3086,6 +3113,7 @@ Standard Hög + Max @@ -3446,6 +3474,7 @@ Skickad till %1$s Du på %1$s i %2$s %1$s på %2$s i %3$s + Till Från Transaktionsdetaljer inklusive betalningsbelopp och transaktionstid är en del av MobileCoin Ledger. @@ -3508,6 +3537,7 @@ Bekräfta betalning Nätverksavgift Beräknad %1$s + Till Totalt belopp Saldo: %1$s @@ -4112,6 +4142,7 @@ Kopierade till urklipp Administratör + Godkänn Avfärda @@ -4139,6 +4170,7 @@ Röstmeddelande · %1$s + %1$s till %2$s @@ -4185,6 +4217,7 @@ %1$s och %2$s gick med %1$s, %2$s och %3$s gick med %1$s, %2$s och %3$d andra gick med + %1$s lämnade %1$s och %2$s lämnade %1$s, %2$s och %3$s lämnade @@ -4562,6 +4595,7 @@ Meddelanden Försvinnande meddelanden Appsäkerhet + Blockera skärmdumpar i senaste-listan och appen Signal-meddelanden och -samtal, vidarebefordra samtal och dold avsändare Standard tidsgräns för nya chattar @@ -4618,11 +4652,14 @@ Mediekvalitet Skickade mediekvalitet Om du skickar högkvalitativa medier kommer du att använda mera data. + Hög + Standard Samtal + Automatiskt Använd anpassade färger Chattfärg @@ -4834,6 +4871,7 @@ Ta en bild Välj ett foto Foto + Text Spara Ta bort avatar @@ -4894,7 +4932,7 @@ Lägg till ett meddelande Lägg till ett svar Skicka till - Visa engångsmeddelande + View once media Ett eller flera objekt var för stora Ett eller flera objekt var ogiltiga För många objekt valda @@ -5487,7 +5525,7 @@ %1$s %2$s Du - + %1$s till %2$s Svara @@ -6704,5 +6742,11 @@ Redigera anteckning + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index a30af491f8..1453d23cf3 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -415,6 +415,7 @@ Jiunge + Imejaa Hitilafu imetokea wakati wa kutuma media @@ -613,6 +614,7 @@ Toa hifadhini Futa Chagua zote + %1$d iliyochaguliwa %1$diliyochaguliwa @@ -864,6 +866,7 @@ Mialiko imetumwa Mialiko %1$d imetumwa + \"%1$s\" hawezi kuongezwa na wewe kiotomatiki kwenye kikundi hiki.\n\nAmealikwa kujiunga, na hataona ujumbe wowote wa kikundi mpaka atakapokubali. Watumiaji hawa hawawezi kuongezwa na wewe kiotomatiki kwenye kikundi hiki.\n\nWamealikwa kujiunga na kikundi, na hawataona ujumbe wowote wa kikundi mpaka watakapokubali. @@ -1044,7 +1047,9 @@ Badilisha jina na picha Kikundi Kizee + Hiki ni Kikundi Kizee. Vipengele kama vile wasimamizi wa vikundi vinapatikana tu kwa Vikundi Vipya. + Hiki ni Kikundi Kizee. Ili upate huduma mpya kama @kutajwa na kuteua wasimamizi, Kikundi hiki cha Urithi hakiwezi kupandishwa hadhi kuwa Kikundi kipya kwa sababu ni kubwa mno. Ukubwa wa juu wa kikundi ni %1$d. boresha kikundi hiki. @@ -1271,6 +1276,7 @@ Futa + %1$d amemchagua (%2$s) %1$dimechaguliwa (%2$s) @@ -1335,17 +1341,13 @@ Umeondoka kwenye kundi. Umesasisha kikundi Kikundi kimesasishwa. - + Simu ya kawaida uliyopiga - + Simu ya video uliyopiga - - Simu ya kawaida isiyopokelewa - - Simu ya video isiyopokelewa - + Simu ya kawaida inayopigwa - + Simu ya video inayopigwa Simu ya kawaida fifi @@ -1355,10 +1357,6 @@ Simu ya sauti ambayo haijapokelewa wakati wasifu wa arifa ulipowashwa Simu ya video ambayo haijapokelewa wakati wasifu wa arifa ulipokuwa umewashwa - - Umekataa simu ya kawaida - - Umekataa simu ya video %1$s · %2$s 1%1$s amesasisha kikundi @@ -1567,30 +1565,55 @@ %1$s sasa anaweza kupokea Malipo - %1$s ameanzisha mazungumzo ya simu ya kikundi · %2$s - Umeanzisha simu ya kikundi · %1$s - %1$s yuko kwenye mazungumzo ya simu ya kikundi · %2$s - Uko kwenye mazungumzo ya simu ya kikundi · %1$s - %1$s na %2$s wako kwenye mazungumzo ya simu ya kikundi · %3$s - Mazungumzo ya simu ya kikundi · %1$s - - %1$s ameanzisha mazungumzo ya simu ya kikundi - Umeanzisha mazungumzo ya simu ya kikundi - %1$s yuko kwenye mazungumzo ya simu ya kikundi - Uko kwenye mazungumzo ya simu ya kikundi - %1$s na %2$s wako kwenye mazungumzo ya simu ya kikundi - Mazungumzo ya simu ya kikundi + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Wewe - - %1$s, %2$s na%3$d mwingine yuko kwenye mazungumzo ya simu ya kikundi · %4$s - %1$s, %2$s na %3$dwako kwenye mazungumzo ya simu ya kikundi · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s,na %3$d mwingine yuko kwenye mazungumzo ya simu ya kikundi - %1$s, %2$s na %3$d wengine wako kwenye mazungumzo ya simu ya kikundi + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Inashindwa kuomba msimbo wa uthibitisho. Tafadhali angalia muunganisho wa mtandao wako kisha ujaribu tena. Mfumo wa nambari usio wa kiwango + Nambari uliyoingiza (%1$s) inaonekana kuwa isiyo ya mfumo wa nambari usio wa kiwango.\n\nUlimaanisha %2$s? Signal Android - Mfumo wa Nambari ya Simu @@ -2709,6 +2733,8 @@ Inapakia Jifunze zaidi Jiunge na mazungumzo ya simu + + Call back Rudi kwenye mazungumzo ya simu Mazungumzo ya simu yamejaa Alika marafiki @@ -2773,6 +2799,7 @@ Ondoka kwenye mazungumzo ya simu Huenda watu wafuatao wamesakinisha upya programu zao au kubadilisha vifaa. Thibitisha nambari yako ya usalama unayotumia kwao ili kuhakikisha unalinda faragha yako. Tazama + Iliyothibitishwa awali @@ -3086,6 +3113,7 @@ Chaguo msingi Juu + Upeo @@ -3446,6 +3474,7 @@ Yametumwa kwa %1$s Wewe tarehe %1$s saa %2$s %1$s tarehe %2$s saa %3$s + Kwa Kutoka kwa Maelezo ya muamala, ikiwa ni pamoja na kiwango cha malipo na wakati wa muamala ni sehemu ya Leja ya MobileCoin. @@ -3508,6 +3537,7 @@ Thibitisha malipo Ada ya mtandao Kadirio la %1$s + Kwa Kiasi jumla Salio: %1$s @@ -4112,6 +4142,7 @@ Imenakiliwa kwenye bodi ya kunakili Msimamizi + Idhinisha Kataa @@ -4139,6 +4170,7 @@ Ujumbe wa sauti · %1$s + %1$shadi %2$s @@ -4185,6 +4217,7 @@ %1$s na %2$s amejiunga %1$s, %2$s na %3$s wamejiunga %1$s, %2$s na wengine %3$d wamejiunga + %1$s ameondoka %1$s na %2$s wameondoka %1$s, %2$s na %3$s wameondoka @@ -4562,6 +4595,7 @@ Kutuma ujumbe Jumbe zinazotoweka Usalama wa programu + Zuia screenshots katika orodha ya rekodi na ndani ya programu Ujumbe na simu za Signal, elekeza simu kila wakati, na kutumia huduma ya kuficha ujumbe Kipima muda chaguo-msingi cha gumzo mpya @@ -4618,11 +4652,14 @@ Ubora wa Media Ubora wa media iliyotumwa Kutuma media za ubora wa juu kutatumia data zaidi. + Juu + Kiwango Simu + Kiotomatiki Tumia rangi maalum Rangi ya gumzo @@ -4834,6 +4871,7 @@ Piga picha Chagua picha Picha + Maandishi Hifadhi Futa umbo @@ -4894,7 +4932,7 @@ Ongeza ujumbe Ongeza jibu Tuma kwa - Ujumbe wa kuangaliwa mara moja + View once media Kitu kimoja au zaidi vilikuwa vikubwa sana Kitu kimoja au zaidi vilikuwa batili Vitu vingi zaidi vimechaguliwa @@ -5487,7 +5525,7 @@ %1$s %2$s Wewe - + %1$shadi %2$s Jibu @@ -6704,5 +6742,11 @@ Hariri kidokezo + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 72c29ce4d3..28122fb4c0 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -415,6 +415,7 @@ சேரவும் + நிரம்பியது மீடியா அனுப்புவதில் பிழை @@ -613,6 +614,7 @@ வெளிப்படுத்து நீக்கு எல்லாம் + %1$d தேர்ந்தெடுக்கப்பட்டது %1$d தேர்ந்தெடுக்கப்பட்டது @@ -864,6 +866,7 @@ அழைப்பிதழ் அனுப்பப்பட்டது %1$d அழைப்பிதழ்கள் அனுப்பப்பட்டன + “%1$s” ஐ தானாக இந்த குழுவில் சேர்க்க முடியாது. குழுவில் சேர அவர்கள் அழைக்கப்பட்டுள்ளனர். எந்தவொரு குழு செய்திகளையும் அவர்கள் ஏற்றுக்கொள்ளும் வரை பார்க்க மாட்டார்கள். இந்த பயனர்களை இந்த குழுவில் தானாக உங்களால் சேர்க்க முடியாது. குழுவில் சேர அவர்கள் அழைக்கப்பட்டுள்ளனர், அவர்கள் ஏற்றுக்கொள்ளும் வரை எந்த குழு செய்திகளையும் பார்க்க மாட்டார்கள். @@ -1044,7 +1047,9 @@ பெயர் மற்றும் படத்தைத் திருத்தவும் பழைய மரபு குழு + இது ஒரு மரபு குழு. குழு நிர்வாகிகள் போன்ற அம்சங்கள் புதிய குழுக்களுக்கு மட்டுமே கிடைக்கும். + இது ஒரு மரபு குழு. @குறிப்புகள் மற்றும் நிர்வாகிகள் போன்ற புதிய அம்சங்களை அணுக, இந்த மரபு குழுவை புதிய குழுவாக மேம்படுத்த முடியாது, ஏனெனில் அது அளவு மிகப் பெரியது. அதிகபட்ச குழு அளவு%1$d. இந்த குழுவை மேம்படுத்தவும். @@ -1271,6 +1276,7 @@ நீக்கு + %1$d தேர்ந்தெடுக்கப்பட்டது (%2$s) %1$d தேர்ந்தெடுக்கப்பட்டது (%2$s) @@ -1335,17 +1341,13 @@ நீங்கள் குழுவை விட்டு விலகினீர்கள். நீங்கள் குழுவைப் புதுப்பித்தீர்கள். குழு புதுப்பிக்கப்பட்டது. - + வெளிச்செல்லும் குரல் அழைப்பு - + வெளிச்செல்லும் வீடியோ அழைப்பு - - பதிலளிக்கப்படாத குரல் அழைப்பு - - பதிலளிக்கப்படாத வீடியோ அழைப்பு - + உள்வரும் குரல் அழைப்பு - + உள்வரும் வீடியோ அழைப்பு தவறவிட்ட குரல் அழைப்பு @@ -1355,10 +1357,6 @@ அறிவிப்பு புரொஃபைல் இயக்கப்பட்டிருக்கும்போது தவறவிட்ட குரல் அழைப்பு அறிவிப்பு புரொஃபைல் இயக்கப்பட்டிருக்கும் போது தவறவிட்ட வீடியோ அழைப்பு - - குரல் அழைப்பை மறுத்துவிட்டீர்கள் - - வீடியோ அழைப்பை மறுத்துவிட்டீர்கள் %1$s · %2$s %1$s குழு புதுப்பிக்கப்பட்டது . @@ -1567,30 +1565,55 @@ %1$s இப்போது கட்டணங்களை ஏற்க முடியும் - %1$sகுழு அழைப்பைத் தொடங்கினார்%2$s - குழு அழைப்பைத் தொடங்கியுள்ளீர்கள் · %1$s - %1$s குழு அழைப்பில் உள்ளார் %2$s - நீங்கள் குழு அழைப்பில் இருக்கிறீர்கள். %1$s - %1$s மற்றும் %2$s குழு அழைப்பில் உள்ளனர் %3$s - குழு அழைப்பு . %1$s - - %1$s குழு அழைப்பைத் தொடங்கினார் - நீங்கள் ஒரு தொடங்கினீர்கள் குழு அழைப்பு - %1$s குழு அழைப்பில் இருக்கிறார் - நீங்கள் குழு அழைப்பில் இருக்கிறீர்கள் - %1$s மற்றும் %2$s குழு அழைப்பில் உள்ளனர் - குழு அழைப்பு + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s நீங்கள் - - %1$s, %2$s,. மற்றும் கூடுதலாக%3$d குழு உறுப்பினர் அழைப்பில் உள்ளனர் %4$s - %1$s, %2$s மற்றும் கூடுதலாக%3$d குழு உறுப்பினர்கள் அழைப்பில் உள்ளனர் %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, மற்றும் %3$dகூடுதலாக குழு உறுப்பினர்கள் அழைப்பில் உள்ளனர் - %1$s, %2$s, மற்றும் கூடுதலாக %3$d குழு உறுப்பினர்கள் அழைப்பில் உள்ளனர் + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ சரிபார்ப்புக் குறியீட்டைக் கோர முடியவில்லை. உங்கள் நெட்வொர்க் இணைப்பைச் சரிபார்த்து, மீண்டும் முயலவும். நான்-ஸ்டாண்டர்ட் எண் வடிவம் + நீங்கள் உள்ளிட்ட எண் (%1$s) நான்-ஸ்டாண்டர்ட் எண் வடிவத்தைக் கொண்டுள்ளது.\n\nநீங்கள் %2$s-ஐ நினைத்தீர்களா? சிக்னல் ஆண்ட்ராய்டு - தொலைபேசி எண் வடிவம் @@ -2709,6 +2733,8 @@ ஏற்றுதல் மேலும் அறிக அழைப்பில் சேரவும் + + Call back அழைப்புக்குத் திரும்பு அழைப்பு நிரம்பியுள்ளது நண்பர்களை அழை @@ -2773,6 +2799,7 @@ அழைப்பை விடுங்கள் பின்வரும் நபர்கள் தொலைபேசிகளை மீண்டும் நிறுவியிருக்கலாம் அல்லது மாற்றியிருக்கலாம். தனியுரிமையை உறுதிப்படுத்த அவர்களுடன் உங்கள் பாதுகாப்பு எண்ணைச் சரிபார்க்கவும். காண்க + முன்பு சரிபார்க்கப்பட்டது @@ -3086,6 +3113,7 @@ இயல்புநிலை உயர் + மேக்ஸ் @@ -3446,6 +3474,7 @@ அனுப்பப்பட்டது %1$s நீங்கள் %1$s இல் %2$s %1$s ஆன் %2$s இல் %3$s + செய்ய இருந்து கட்டணத் தொகை மற்றும் பரிவர்த்தனை நேரம் உள்ளிட்ட பரிவர்த்தனை விவரங்கள் MobileCoin லெட்ஜரின் பகுதியாகும். @@ -3508,6 +3537,7 @@ கட்டணத்தை உறுதிப்படுத்தவும் பிணைய கட்டணம் மதிப்பிடப்பட்டுள்ளது %1$s + செய்ய மொத்தத் தொகை இருப்பு: %1$s @@ -4112,6 +4142,7 @@ கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது நிர்வாகி + ஒப்புதல் மறுக்க @@ -4139,6 +4170,7 @@ குரல் செய்தி:%1$s + %1$s to %2$s @@ -4185,6 +4217,7 @@ %1$s மற்றும் %2$sசேர்ந்தார் %1$s, %2$s மற்றும் %3$s சேர்ந்தார் %1$s, %2$s and %3$dஅதிக உறுப்பினர்கள் இணைந்தனர் + %1$s வெளியேறினர் %1$s மற்றும் %2$s வெளியேறினர் %1$s, %2$s மற்றும் %3$sவெளியேறினர் @@ -4562,6 +4595,7 @@ செய்தி அனுப்புதல் காணாமல் போகும் செய்திகள் பயன்பாட்டு பாதுகாப்பு + சமீபத்தியவை பட்டியல் மற்றும் செயலிக்கு உள்ளே ஸ்கிரீன்ஷாட் எடுப்பதைத் தடுக்கவும் Signal செய்திகள் மற்றும் அழைப்புகள், எப்போதும் ரிலே அழைப்புகள் மற்றும் சீல் அனுப்பியவர் புதிய சாட்களுக்கு இயல்புநிலை டைமர் @@ -4618,11 +4652,14 @@ மீடியா தரம் அனுப்பிய மீடியா தரம் உயர்தரத்தை அனுப்புகிறது ஊடகம் அதிக தரவைப் பயன்படுத்தும். + உயர் + தரநிலை அழைப்புகள் + ஆட்டோ தனிப்பயன் நிறங்களை பயன்படுத்தவும் சாட் கலர் @@ -4834,6 +4871,7 @@ படம் எடுக்கவும் ஒன்றை தேர்ந்தெடு புகைப்படம் புகைப்படம் + உரை சேமி அவதார் அழி @@ -4894,7 +4932,7 @@ கூட்டு ஒரு செய்தி ஒரு பதிலைச் சேர்க்கவும் இவருக்கு அனுப்பு - ஒருமுறை பார்க்கும் செய்தி + View once media ஒன்று அல்லது அதற்கு மேற்பட்ட பொருட்கள் மிகப் பெரியதாக இருந்தன ஒன்று அல்லது அதற்கு மேற்பட்ட பொருட்கள் செல்லாதவை பல பொருட்கள் தேர்ந்தெடுக்கப்பட்டுள்ளன @@ -5487,7 +5525,7 @@ %1$s %2$s நீங்கள் - + %1$s to %2$s பதில் @@ -6704,5 +6742,11 @@ குறிப்பைத் திருத்து + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index b373172138..36a934a7e3 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -415,6 +415,7 @@ చేరండి + సంపూర్ణమైన మీడియాను పంపడంలో లోపం చోటుచేసుకుంది @@ -613,6 +614,7 @@ అన్ఆర్కైవ్ తొలగించండి అన్నిటిని ఎంచుకోండి + %1$dఎంచుకోబడింది %1$d ఎంచుకోబడింది @@ -864,6 +866,7 @@ ఆహ్వానం పంపబడింది %1$d ఆహ్వానాలు పంపబడ్డాయి + “%1$s” ను మీరు స్వయంచాలకంగా ఈ గుంపుకు చేర్చలేరు. \n\n చేరడానికి వారిని ఆహ్వానించారు మరియు వారు అంగీకరించే వరకు ఏ సమూహ సందేశాలను చూడలేరు. ఈ వినియోగదారులను మీరు స్వయంచాలకంగా ఈ గుంపుకు చేర్చలేరు. \n\\ n గుంపులో చేరమని వారిని ఆహ్వానించారు మరియు వారు అంగీకరించే వరకు ఏ సమూహ సందేశాలను చూడలేరు. @@ -1044,7 +1047,9 @@ పేరు మరియు చిత్రం సవరించండి లెగసీ సమూహం + ఇది లెగసీ సమూహం . సమూహ నిర్వాహకులు వంటి లక్షణాలు క్రొత్త సమూహాలకు మాత్రమే అందుబాటులో ఉంటాయి. + ఇది లెగసీ గ్రూప్. @ ప్రస్తావనలు మరియు నిర్వాహకులు వంటి క్రొత్త లక్షణాలను ప్రాప్యత చేయడానికి, ఈ లెగసీ గ్రూప్ క్రొత్త సమూహానికి అప్‌గ్రేడ్ చేయబడదు ఎందుకంటే ఇది చాలా పెద్దది. గరిష్ట సమూహ పరిమాణం %1$d. ఈ సమూహాన్ని అప్‌గ్రేడ్ చేయండి @@ -1271,6 +1276,7 @@ తొలగించండి + %1$d ఎంచుకున్నారు (%2$s) %1$d ఎంచుకున్నారు (%2$s) @@ -1335,17 +1341,13 @@ మీరు సమూహం నుండి వైదొలిగారు మీరు ఈ సమూహాన్ని నవీకరించారు. సమూహం నవీకరించబడింది. - + అవుట్‌గోయింగ్ వాయిస్ కాల్ - + అవుట్‌గోయింగ్ వీడియో కాల్ - - సమాధానం ఇవ్వని వాయిస్ కాల్ - - సమాధానం ఇవ్వని వీడియో కాల్ - + ఇన్‌కమింగ్ వాయిస్ కాల్ - + ఇన్‌కమింగ్ వీడియో కాల్ మిస్డ్ వాయిస్ కాల్ @@ -1355,10 +1357,6 @@ నోటిఫికేషన్ ప్రొఫైల్ ఆన్‌లో ఉన్నప్పుడు మిస్ అయిన స్వర కాల్ నోటిఫికేషన్ ప్రొఫైల్ ఆన్‌లో ఉన్నప్పుడు మిస్ అయిన వీడియో కాల్ - - మీరు ఒక వాయిస్ కాల్‌ను నిరాకరించారు - - మీరు ఒక వీడియో కాల్‌ను నిరాకరించారు %1$s · %2$s %1$s సమూహాన్ని నవీకరించారు. @@ -1567,30 +1565,55 @@ %1$s ఇప్పుడు చెల్లింపులను అంగీకరించవచ్చు. - %1$s సమూహ కాల్ ప్రారంభించారు. %2$s - మీరు గ్రూప్ కాల్ ప్రారంభించారు · %1$s - %1$s సమూహ కాల్‌లో ఉన్నారు · %2$s - మీరు గ్రూప్ కాల్‌లో ఉన్నారు. %1$s - %1$s మరియు %2$s గ్రూప్ కాల్‌లో ఉన్నారు · %3$s - సమూహ కాల్ · %1$s - - %1$s సమూహ కాల్ ప్రారంభించారు - మీరు సమూహ కాల్ ప్రారంభించారు - %1$s సమూహ కాల్‌లో ఉన్నారు - మీరు గ్రూప్ కాల్‌లో ఉన్నారు - %1$s మరియు %2$s గ్రూప్ కాల్‌లో ఉన్నారు - సమూహ కాల్ + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s మీరు - - %1$s, %2$s మరియు %3$d ఇతర ఈ కాల్‌లో ఉన్నారు · %4$s - %1$s, %2$s, మరియు %3$d ఇతరులు ఈ సమూహ కాల్‌లో ఉన్నారు · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s మరియు %3$d ఇతర ఈ సమూహ కాల్‌లో ఉన్నారు - %1$s, %2$s, మరియు %3$d ఇతరులు ఈ సమూహ కాల్‌లో ఉన్నారు + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ ధృవీకరణ కోడ్‌ను అభ్యర్ధించలేకపోయింది. దయచేసి నెట్‌వర్క్ కనెక్షన్ తనిఖీ చేసి, మళ్ళీ ప్రయత్నించండి. నాన్-స్టాండర్డ్ నెంబర్ ఫార్మెట్ + మీరు నమోదు చేసిన నెంబరు (%1$s) నాన్-స్టాండర్డ్ ఫార్మెట్‌లో కనిపిస్తోంది.\n\n%2$s అని మీ అర్ధమా? Signal Android - ఫోన్ నెంబర్ ఫార్మెట్ @@ -2709,6 +2733,8 @@ లోడ్ ఇంకా నేర్చుకో కాల్‌లో చేరండి + + Call back కాల్‌కు తిరిగి వెళ్ళు కాల్ నిండింది స్నేహితులను ఆహ్వానించండి @@ -2773,6 +2799,7 @@ కాల్ ని వదిలి కింది వ్యక్తులు పరికరాలను తిరిగి ఇన్‌స్టాల్ చేసి ఉండవచ్చు లేదా మార్చవచ్చు. గోప్యతను నిర్ధారించడానికి వారితో మీ భద్రతా సంఖ్యను ధృవీకరించండి. వీక్షణ + మునుపటి ధృవీకరించబడింది @@ -3086,6 +3113,7 @@ అప్రమేయం అధికం + గరిష్టం @@ -3446,6 +3474,7 @@ %1$sకు పంపబడింది మీరు %2$s వద్ద %1$sపై ఉన్నారు %1$s %3$s వద్ద %2$sపై ఉన్నారు + ఎవరికి ఎవరి నుండి చెల్లింపు మొత్తం మరియు లావాదేవీ సమయంతో సహా లావాదేవీ వివరాలు MobileCoin లెడ్జర్‌లో భాగం. @@ -3508,6 +3537,7 @@ పేమెంట్‌ని ధృవీకరించండి నెట్‌వర్క్ ఫీజు అంచనా వేయబడింది %1$s + ఎవరికి మొత్తం అమౌంట్ బ్యాలెన్స్: %1$s @@ -4112,6 +4142,7 @@ క్లిప్బోర్డ్కు కాపీ చేయబడింది అడ్మిన్ + ఆమోదించడానికి తిరస్కరించండి @@ -4139,6 +4170,7 @@ వాయిస్ సందేశం · %1$s + %1$sకు%2$s @@ -4185,6 +4217,7 @@ %1$s మరియు %2$s చేరారు %1$s,%2$s మరియు %3$s చేరారు %1$s, %2$s, మరో %3$d మంది చేరారు + %1$s వెళ్ళిపోయారు %1$s మరియు %2$s వెళ్ళిపోయారు %1$s, %2$s మరియు %3$s వెళ్ళిపోయారు @@ -4562,6 +4595,7 @@ సందేశం అదృశ్యమవుతున్న సందేశాలు యాప్ సెక్యూరిటీ + ఇటీవలి జాబితాలో మరియు యాప్ లోపల స్క్రీన్‌షాట్‌లను బ్లాక్ చేయండి Signal సందేశాలు మరియు కాల్స్, ఎల్లప్పుడూ రిలే కాల్స్, మరియు సీల్ చేయబడ్డ సెండర్ కొత్త చాట్‌ల కొరకు డిఫాల్ట్ టైమర్ @@ -4618,11 +4652,14 @@ మీడియా నాణ్యత పంపిన మీడియా నాణ్యత అధిక నాణ్యత కలిగిన మీడియాను పంపడం వల్ల మరింత డేటా ఉపయోగించబడుతుంది. + అధికం + ప్రామాణికం కాల్స్ + ఆటో కస్టమ్ రంగులను ఉపయోగించండి చాట్ రంగు @@ -4834,6 +4871,7 @@ చిత్రాన్ని తీసుకోండి ఒక ఫోటోని ఎంచుకోండి ఛాయాచిత్రం + వచనం భద్రపరుచు అవతార్ తీసివేయండి @@ -4894,7 +4932,7 @@ ఒక సందేశాన్ని జోడించండి ప్రత్యుత్తరమును జోడించండి వీరికి పంపండి - సందేశాన్ని ఒక్కసారి చూడండి + View once media ఒకటి లేదా అంతకంటే ఎక్కువ ఐటమ్‌లు చాలా పొడవుగా ఉన్నాయి ఒకటి లేదా అంతకంటే ఎక్కువ ఐటమ్‌లు చెల్లుబాటు కావు. చాలా ఎక్కువగా ఐటమ్‌లు ఎంచుకోబడ్డాయి @@ -5487,7 +5525,7 @@ %1$s %2$s మీరు - + %1$sకు%2$s స్పంధించు @@ -6704,5 +6742,11 @@ గమనిక సవరించండి + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 7dc4fca4eb..4ba017a517 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -412,6 +412,7 @@ เข้าร่วม + เต็ม เกิดข้อผิดพลาดในการส่งสื่อ @@ -597,6 +598,7 @@ ยกเลิกเก็บถาวร ลบ เลือกทั้งหมด + เลือกไว้ %1$d @@ -845,6 +847,7 @@ ส่ง %1$d คำเชิญแล้ว + คุณไม่สามารถเพิ่ม %1$s เข้ากลุ่มนี้โดยอัตโนมัติ \n\nพวกเขาได้ถูกเชิญให้เข้ากลุ่มและจะไม่เห็นข้อความในกลุ่มจนกว่าพวกเขาจะตอบรับ คุณไม่สามารถเพิ่มผู้ใช้เหล่านี้เข้ากลุ่มนี้โดยอัตโนมัติ \n\nพวกเขาได้ถูกเชิญให้เข้ากลุ่มและจะไม่เห็นข้อความในกลุ่มจนกว่าพวกเขาจะตอบรับ @@ -1009,7 +1012,9 @@ แก้ไขชื่อและภาพ กลุ่มแบบเก่า + นี่คือกลุ่มแบบเก่า ความสามารถเช่น ระบบผู้ดูแลกลุ่ม จะใช้ได้เฉพาะในกลุ่มแบบใหม่ + นี่คือกลุ่มแบบเก่า เพื่อเข้าถึงความสามารถใหม่ เช่นการ @กล่าวถึง และระบบผู้ดูแลกลุ่ม กลุ่มแบบเก่านี้ไม่สามารถปรับรุ่นให้เป็นกลุ่มแบบใหม่ได้ เพราะมีขนาดใหญ่เกินไป ขนาดกลุ่มที่ใหญ่ที่สุดคือ %1$d ปรับรุ่นกลุ่มนี้ @@ -1227,6 +1232,7 @@ ลบ + เลือกไว้ %1$d (%2$s) @@ -1290,17 +1296,13 @@ คุณได้ออกจากกลุ่ม คุณได้ปรับปรุงกลุ่ม กลุ่มถูกปรับปรุงแล้ว - + การโทรด้วยเสียงขาออก - + วิดีโอคอลขาออก - - การโทรด้วยเสียงที่ปลายสายไม่ได้รับ - - วิดีโอคอลที่ปลายสายไม่ได้รับ - + การโทรด้วยเสียงขาเข้า - + วิดีโอคอลขาเข้า การโทรด้วยเสียงที่ไม่ได้รับ @@ -1310,10 +1312,6 @@ สายโทรเสียงที่ไม่ได้รับระหว่างใช้โปรไฟล์การแจ้งเตือน วิดีโอคอลที่ไม่ได้รับระหว่างใช้โปรไฟล์การแจ้งเตือน - - คุณปฏิเสธการโทรด้วยเสียง - - คุณปฏิเสธวิดีโอคอล %1$s · %2$s %1$s ได้ปรับปรุงกลุ่ม @@ -1514,28 +1512,53 @@ %1$s สามารถรับการชำระเงินได้แล้ว - %1$s ได้เริ่มการโทรแบบกลุ่ม %2$s - คุณเริ่มการโทรกลุ่ม · %1$s - %1$s อยู่ในการโทรแบบกลุ่ม %2$s - คุณอยู่ในการโทรแบบกลุ่ม %1$s - %1$s และ %2$s ได้อยู่ในการโทรแบบกลุ่ม %3$s - โทรกลุ่ม · %1$s - - %1$s ได้เริ่มการโทรแบบกลุ่ม - คุณได้เริ่มโทรแบบกลุ่ม - %1$s อยู่ในการโทรแบบกลุ่ม - คุณอยู่ในการโทรแบบกลุ่ม - %1$s และ %2$s ได้อยู่ในการโทรแบบกลุ่ม - โทรกลุ่ม + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s คุณ - - %1$s %2$s และ %3$d รวมถึงคนอื่นๆ อยู่ในการโทรแบบกลุ่ม %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s %2$s และ %3$d รวมถึงคนอื่นๆ อยู่ในการโทรแบบกลุ่ม + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ ไม่สามารถขอรหัสยืนยันได้ โปรดตรวจสอบการเชื่อมต่อเครือข่ายและลองอีกครั้ง ฟอร์แมทหมายเลขที่ไม่มีมาตรฐาน + ตัวเลขที่คุณป้อน (%1$s) เป็นฟอร์แมทที่ไม่ได้มาตรฐาน\n\n คุณต้องการใส่ %2$sหรือเปล่า Signal Android - ฟอร์แมทหมายเลขโทรศัพท์ @@ -2622,6 +2646,8 @@ กำลังโหลด เรียนรู้เพิ่มเติม เข้าร่วมการโทร + + Call back กลับไปที่สาย ห้องเต็ม เชิญเพื่อน @@ -2686,6 +2712,7 @@ ออกจากสาย บุคคลดังต่อไปนี้อาจติดตั้งแอปใหม่หรือเปลี่ยนอุปกรณ์ กรุณาตรวจยืนยันหมายเลฃความปลอดภัยของคุณกับเขาเพื่อให้แน่ใจถึงความเป็นส่วนตัว ดู + ถูกตรวจยืนยันก่อนหน้า @@ -2992,6 +3019,7 @@ ค่าเริ่มต้น สูง + สูงสุด @@ -3349,6 +3377,7 @@ ส่งถึง %1$s คุณบน %1$s เมื่อ %2$s %1$s บน %2$s เมื่อ %3$s + ถึง จาก รายละเอียดธุรกรรม รวมถึงจำนวนเงินและเวลาของธุรกรรมเป็นส่วนหนึ่งของ MobileCoin Ledger @@ -3411,6 +3440,7 @@ ยืนยันการจ่ายเงิน ค่าธรรมเนียมเครือข่าย ประมาณ %1$s + ถึง จำนวนรวม ยอด: %1$s @@ -4006,6 +4036,7 @@ คัดลอกไปคลิปบอร์ดแล้ว ผู้ดูแล + อนุมัติ ปฏิเสธ @@ -4033,6 +4064,7 @@ ข้อความเสียง %1$s + %1$s ถึง %2$s @@ -4077,6 +4109,7 @@ %1$s และ %2$s ได้เข้าร่วมกลุ่ม %1$s %2$s และ %3$s ได้เข้าร่วมกลุ่ม %1$s %2$s และอีก %3$d คน ได้เข้าร่วมกลุ่ม + %1$s ออกจากกลุ่ม %1$s และ %2$s ออกจากกลุ่ม %1$s %2$s และ %3$s ได้ออกจากกลุ่ม @@ -4452,6 +4485,7 @@ การส่งข้อความ ข้อความที่ลบตัวเอง ความปลอดภัยของแอป + ไม่อนุญาตให้ถ่ายภาพหน้าจอในรายการสนทนาล่าสุดและภายในแอป Signal ส่งข้อความและโทร ถ่ายทอดการโทร และผู้ส่งที่ถูกปกปิด ค่าเริ่มต้นของตัวจับเวลาสำหรับแชทใหม่ @@ -4508,11 +4542,14 @@ คุณภาพของสื่อ ส่งคุณภาพสื่อแล้ว การส่งสื่อคุณภาพสูงจะใช้ข้อมูลมากขึ้น + สูง + มาตรฐาน การโทร + อัตโนมัติ ใช้สีที่กำหนดเอง สีแชท @@ -4721,6 +4758,7 @@ ถ่ายภาพ เลือกรูปภาพ รูปถ่าย + ข้อความ บันทึก ล้างอวตาร @@ -4777,7 +4815,7 @@ เพิ่มข้อความ เพิ่มการตอบกลับ ส่งไปให้ - ดูข้อความครั้งเดียว + View once media มีรายการที่ใหญ่เกินไปอย่างน้อยหนึ่งรายการ มีรายการที่ไม่ถูกต้องอย่างน้อยหนึ่งรายการ เลือกหลายรายการมากเกินไป @@ -5365,7 +5403,7 @@ %1$s %2$s คุณ - + %1$s ถึง %2$s ตอบกลับ @@ -6558,5 +6596,11 @@ แก้ไขโน้ต + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 6e62a2b1fd..eb4b519208 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -415,6 +415,7 @@ Mag-join + Full Error sending media @@ -613,6 +614,7 @@ I-unarchive Burahin Piliin lahat + %1$d selected %1$d selected @@ -864,6 +866,7 @@ Pinadala ang invite Pinadala ang %1$d invites + Si \"%1$s\" ay hindi mo automatic na maidadagdag sa grupong ito. Siya ay naimbitahang mag-join, at hindi makakakita ng anumang group messages hanggang i-accept niya ito. Ang users na ito ay hindi mo automatic na maidadagdag sa grupong ito. Sila ay naimbitahan mag-join, at hindi makakakita ng anumang group messages hanggang i-accept nila ito. @@ -1044,7 +1047,9 @@ I-edit ang pangalan at picture Legacy Group + Ito\'y isang Legacy Group. Ang features tulad ng group admins ay available lamang sa New Groups. + Ito\'y isang Legacy Group. Para magka-access sa new features tulad ng @mentions at admins, Ang Legacy Group na ito\'y hindi pwedeng i-upgrade sa New Group dahil masyado itong malaki. Ang maximum na group size ay %1$d. i-upgrade ang group na ito. @@ -1271,6 +1276,7 @@ Burahin + %1$d selected (%2$s) %1$d selected (%2$s) @@ -1335,17 +1341,13 @@ Nakaalis ka na sa grupo. In-update mo ang grupo. Ang group na ito\'y updated na. - + Outgoing voice call - + Outgoing video call - - \'Di nasagot na voice call - - \'Di nasagot na video call - + Incoming voice call - + Incoming video call Missed voice call @@ -1355,10 +1357,6 @@ May hindi nasagot na voice call habang naka-on ang notification profile May hindi nasagot na video call habang naka-on ang notification profile - - Nag-decline ka sa voice call - - Nag-decline ka sa video call %1$s · %2$s In-update ni %1$s ang grupo. @@ -1567,30 +1565,55 @@ Makakatanggap na si %1$s ng Payments - Sinimulan ni %1$s ang group call · %2$s - Nagsimula ka ng group call · %1$s - Si %1$s ay nasa group call · %2$s - Ikaw ay kasama sa group call · %1$s - Sila %1$s at %2$s ay nasa group call · %3$s - Group call · %1$s - - Sinimulan ni %1$s ang group call - Sinimulan mo ang group call - Si %1$s ay nasa group call - Ikaw ay kasama sa group call - Sila %1$s at %2$s ay nasa group call - Group call + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Ikaw - - Sila %1$s, %2$s, at %3$d pa ay nasa group call · %4$s - Sila %1$s, %2$s, at %3$d na iba pa ay nasa group call · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - Sila %1$s, %2$s, at %3$d pa ay nasa group call - Sila %1$s, %2$s, at %3$d pa ay nasa group call + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Hindi makapag-request ng verification code. Paki-check ng iyong network connection at subukan ulit. Non-standard number format + Ang number na nilagay mo (%1$s) ay mukhang isang non-standard format.\n\nDid you mean %2$s? Signal Android - Phone Number Format @@ -2709,6 +2733,8 @@ Naglo-load Matuto pa Mag-join sa call + + Call back Return to call Puno na ang call Imbitahan ang mga kaibigan @@ -2773,6 +2799,7 @@ Leave call Maaaring nag-reinstall o nagpalit ng devices ang mga nabanggit na tao. I-verify ang safety number mo sa kanila para masiguro ang iyong privacy. Tingnan + Previous verified @@ -3086,6 +3113,7 @@ Default Mataas + Pinakasagad @@ -3446,6 +3474,7 @@ Sent to %1$s You on %1$s at %2$s %1$s on %2$s at %3$s + To From Ang transaction details gaya ng payment amount at oras ng transaction ay parte ng MobileCoin Ledger. @@ -3508,6 +3537,7 @@ I-confirm ang payment Network fee Estimated %1$s + To Total amount Balance: %1$s @@ -4112,6 +4142,7 @@ Copied to clipboard Admin + Approve I-deny @@ -4139,6 +4170,7 @@ Voice message · %1$s + %1$s hanggang %2$s @@ -4185,6 +4217,7 @@ %1$s and %2$s joined %1$s, %2$s, and %3$s joined %1$s, %2$s, and %3$d others joined + %1$s left %1$s and %2$s left %1$s, %2$s, and %3$s left @@ -4562,6 +4595,7 @@ Messaging Mga naglalahong mensahe App security + I-block ang screenshots sa recents list at sa loob ng app Signal messages and calls, always relay calls, and sealed sender Default timer para sa new chats @@ -4618,11 +4652,14 @@ Media quality Sent media quality Ang pag-send ng high quality media will use more data. + Mataas + Standard Mga Tawag + Auto Use custom colors Chat color @@ -4834,6 +4871,7 @@ Take a picture Pumili ng photo Larawan + Text I-save I-clear ang avatar @@ -4894,7 +4932,7 @@ Mag-add ng message Mag-add ng reply I-send kay - View message once + View once media Ang isa o higit pang files were too large Ang isa o higit pang files were invalid Too many items selected @@ -5487,7 +5525,7 @@ %1$s %2$s Ikaw - + %1$s hanggang %2$s Tugon @@ -6680,7 +6718,7 @@ Nickname - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Ang nicknames at notes ay nakatago sa Signal at end-to-end encrypted. Ikaw lang ang makakakita ng mga ito. First name @@ -6694,9 +6732,9 @@ I-save - Delete? + Gusto mo ba itong burahin? - This will permanently delete any nickname and note you’ve set. + Permanenteng buburahin nito ang inilagay mong nickname at note. @@ -6704,5 +6742,11 @@ I-edit ang note + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index eb0e583f50..0e0afd4ebb 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -415,6 +415,7 @@ Katıl + Dolu İçerik gönderilemedi @@ -613,6 +614,7 @@ Çıkart Sil Hepsini seç + %1$d seçili %1$d seçili @@ -864,6 +866,7 @@ Davet gönderildi %1$d davet gönderildi + \"%1$s\" sizin tarafınızdan bu gruba otomatik olarak eklenemez.\n\nKatılmaları için davet gönderildi ve daveti kabul edene kadar hiçbir grup iletisini görmeyecekler. Bu kullanıcılar otomatik olarak bu gruba eklenemez.\n\nKatılmaları için davet gönderildi ve daveti kabul edene kadar hiçbir grup iletisini görmeyecekler. @@ -1044,7 +1047,9 @@ Adı ve resmi düzenleyin Eski Grup + Bu bir Eski Gruptur. Grup yöneticisi gibi özellikler sadece Yeni Gruplarda mevcuttur. + Bu bir Eski Gruptur. Bahsedilmeler ve yöneticiler gibi özelliklere erişmek için, Bu eski grup, boyutu çok büyük olduğu için Yeni Gruba yükseltilemiyor. Maksimum grup boyutu %1$d. bu grubu yükseltin. @@ -1271,6 +1276,7 @@ Sil + %1$d seçili (%2$s) %1$d seçili (%2$s) @@ -1335,17 +1341,13 @@ Gruptan ayrıldınız. Grubu güncellediniz. Grup güncellendi - + Giden sesli arama - + Giden görüntülü arama - - Cevapsız sesli arama - - Cevapsız görüntülü arama - + Gelen sesli arama - + Gelen görüntülü arama Cevapsız sesli arama @@ -1355,10 +1357,6 @@ Bildirim profili açıkken cevapsız sesli arama Bildirim profili açıkken cevapsız görüntülü arama - - Sesli aramayı reddettin - - Görüntülü aramayı reddettin %1$s · %2$s %1$s grubu güncelledi. @@ -1567,30 +1565,55 @@ %1$s artık Ödemeleri kabul edebilir - %1$s grup araması başlattı · %2$s - Bir grup araması başlattın · %1$s - %1$s grup görüşmesinde· %2$s - Grup görüşmesindesiniz · %1$s - %1$s ve %2$s grup görüşmesinde · %3$s - Grup araması · %1$s - - %1$s grup araması başlattı - Bir grup görüşmesi başlattınız - %1$s grup görüşmesinde - Grup görüşmesindesiniz - %1$s ve %2$s grup görüşmesinde - Grup görüşmesi + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Siz - - %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde · %4$s - %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde · %4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde - %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ Doğrulama kodu istenemiyor. Lütfen ağ bağlantını kontrol edip tekrar dene. Standart olmayan numara formatı + Girdiğin numara (%1$s) standart olmayan bir formatta gibi görünüyor.\n\n%2$s mi demek istedin? Signal Android - Telefon Numarası Formatı @@ -2709,6 +2733,8 @@ Yükleniyor Dahasını öğrenin Aramaya katıl + + Call back Aramaya dön Arama dolu Arkadaşlarını davet et @@ -2773,6 +2799,7 @@ Aramadan ayrıl Aşağıdaki kişiler cihaz değiştirmiş veya yeniden yükleme yapmış olabilir. Gizliliğinizden emin olmak için güvenlik numaranızı doğrulayın. Görüntüle + Daha önce doğrulandı @@ -3086,6 +3113,7 @@ Varsayılan Yüksek + En Yüksek @@ -3446,6 +3474,7 @@ %1$s\'ye gönderildi Siz %1$s tarihinde saat %2$s \'de %2$starihinde saat %3$s\'te %1$s + Alıcı Gönderen Ödeme miktarı ve işlem zamanını da içeren işlem ayrıntıları MobileCoin hesap defterinin bir parçasıdır. @@ -3508,6 +3537,7 @@ Ödemeyi onayla Ağ ücreti Tahmini %1$s + Alıcı Toplam tutar Bakiye: %1$s @@ -4112,6 +4142,7 @@ Panoya kopyalandı Yönetici + Onayla Reddet @@ -4139,6 +4170,7 @@ Sesli ileti · %1$s + %1$s --> %2$s @@ -4185,6 +4217,7 @@ %1$s ve %2$s katıldı %1$s, %2$s ve %3$s katıldı %1$s, %2$s ve %3$d diğer kişi katıldı + %1$s ayrıldı %1$s ve %2$s ayrıldı %1$s, %2$s ve %3$s ayrıldı @@ -4562,6 +4595,7 @@ Yazışma Kaybolan iletiler Uygulama güvenliği + Son kullanılanlar listesinde ve uygulama içinde ekran görüntüsü alınmasını engelle Signal iletileri ve aramaları, arama aktarma ve gizli gönderici Yeni sohbetler için varsayılan zamanlayıcı @@ -4618,11 +4652,14 @@ İçerik kalitesi Gönderilen içerik kalitesi Yüksek kalitede içerik göndermek daha fazla veri kullanacaktır. + Yüksek + Standart Aramalar + Otomatik Özel renkleri kullan Sohbet rengi @@ -4834,6 +4871,7 @@ Fotoğraf çekin Fotoğraf seçin Fotoğraf + Yazı Kaydet Avatarı sil @@ -4894,7 +4932,7 @@ İleti ekle Yanıt ekle Gönder: - Tek görümlük ileti + View once media Bir veya daha fazla öğe çok büyük Bir veya daha fazla öğe çok geçersiz Çok fazla öğe seçildi @@ -5487,7 +5525,7 @@ %1$s %2$s Siz - + %1$s --> %2$s Cevapla @@ -6704,5 +6742,11 @@ Notu düzenle + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index fc8e63e105..b249b7d42b 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -412,6 +412,7 @@ قوشۇل + توشتى ۋاسىتە ئەۋەتىش خاتالىقى @@ -597,6 +598,7 @@ ئارخىپسىزلاش ئۆچۈرۈش ھەممىنى تاللاش + %1$d تاللانغان @@ -845,6 +847,7 @@ %1$d تەكلىپنامە يوللاندى + «%1$s» بۇ گۇرۇپپىغا سىز تەرىپىڭىزدىن ئاپتوماتىك قوشۇلمايدۇ. \n\nئۇلار قاتنىشىشقا تەكلىپ قىلىندى، ۋە ئۇلار تەكلىپنى قوبۇل قىلغۇچە گۇرۇپپا ئۇچۇرلىرىنى كۆرەلمەيدۇ. بۇ ئەزالار بۇ گۇرۇپپىغا سىز تەرىپىڭىزدىن ئاپتوماتىك قوشۇلمايدۇ. \n\nئۇلار قاتنىشىشقا تەكلىپ قىلىندى، ۋە ئۇلار تەكلىپنى قوبۇل قىلغۇچە گۇرۇپپا ئۇچۇرلىرىنى كۆرەلمەيدۇ. @@ -1009,7 +1012,9 @@ ئات ۋە باش سۈرەت تەھرىر كونا گۇرۇپپا + بۇ كونا گۇرۇپپا، گۇرۇپپا باشقۇرغۇچىلىرىغا ئوخشاش ئىقتىدارلار يېڭى گۇرۇپپىدىلا ئىشلەيدۇ. + بۇ كونا گۇرۇپپا. @ئاتاش ۋە باشقۇرغۇچىلارغا ئوخشاش ئىقتىدارنى ئىشلىتىشتە، بۇ كونا گۇرۇپپىنى يېڭى گۇرۇپپىغا يۈكسەلتەلمەيدۇ چۈنكى ئادەم بەك كۆپ. يۈكسەلتىشنىڭ ئەزا سان چېكى %1$d . بۇ گۇرۇپپىنى يۈكسەلتىدۇ. @@ -1227,6 +1232,7 @@ ئۆچۈرۈش + %1$d تاللانغان (%2$s) @@ -1290,17 +1296,13 @@ سىز گۇرۇپپىدىن ئايرىلدىڭىز. گۇرۇپپىنى يېڭىلىدىڭىز. بۇ گۇرۇپپا يېڭىلاندى. - + ئۇرۇلغان ئاۋازلىق چاقىرىق - + قوبۇل قىلىنغان ۋىدېيولۇق چاقىرىق - - جاۋابسىز ئاۋازلىق چاقىرىق - - جاۋابسىز ۋىدېيولۇق چاقىرىق - + كەلگەن ئاۋازلىق چاقىرىق - + كەلگەن ۋىدېيولۇق چاقىرىق ئېلىنمىغان ئاۋازلىق چاقىرىق @@ -1310,10 +1312,6 @@ ئۇقتۇرۇش ئارخىپى ئوچۇق ۋاقىتتا رەت قىلىنغان ئاۋازلىق چاقىرىق ئۇقتۇرۇش ئارخىپى ئوچۇق ۋاقىتتا رەت قىلىنغان ۋىدېيولۇق چاقىرىق - - بىر ئاۋازلىق چاقىرىقنى رەت قىلدىڭىز - - بىر ۋىدېيولۇق چاقىرىقنى رەت قىلدىڭىز %1$s · %2$s %1$s گۇرۇپپىنى يېڭىلىدى. @@ -1514,28 +1512,53 @@ %1$s ئەمدى پۇل قوبۇل قىلالايدۇ - %1$s گۇرۇپپىغا چاقىرىشىنى باشلىدى. %2$s - سىز گۇرۇپپا چاقىرىقى باشلىدىڭىز · %1$s - %1$s گۇرۇپپا چاقىرىقىدا. %2$s - سىز گۇرۇپپا چاقىرىقىدا. %1$s - %1$s ۋە %2$s گۇرۇپپا چاقىرىقىدا. %3$s - گۇرۇپپا چاقىرىشى. %1$s - - %1$s گۇرۇپپا چاقىرىقىنى باشلىدى. - سىز گۇرۇپپا چاقىرىقىنى باشلىدىڭىز - %1$s گۇرۇپپا چاقىرىقىدا - سىز بۇ گۇرۇپپا چاقىرىقىدا - %1$s ۋە %2$s گۇرۇپپا چاقىرىقىدا. - گۇرۇپپا چاقىرىقى + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s سىز - - %1$s، %2$s ۋە باشقا %3$d ئەزا گۇرۇپپا چاقىرىقى · «%4$s» دا + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s، %2$s ۋە باشقا %3$d ئەزا گۇرۇپپا چاقىرىقىدا + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ دەلىللەش كودى تەلەپ قىلالمىدى. تور ئۇلىنىشىنى تەكشۈرۈپ قايتا سىناڭ. ئۆلچەملىك بولمىغان نومۇر فورماتى + سىز كىرگۈزگەن سان (%1$s) ئۆلچەملىك بولمىغان فورماتتىكى ساندەك قىلىدۇ.\n\n%2$s دېمەكچىمۇ؟ Signal Android - تېلېفون نومۇرى فورماتى @@ -2622,6 +2646,8 @@ يۈكلەۋاتىدۇ كۆپرەك ئۆگىنىش چاقىرىققا قېتىل + + Call back چاقىرىققا قايت چاقىرىق توشتى دوستلىرىڭىزنى تەكلىپ قىلىڭ @@ -2686,6 +2712,7 @@ چاقىرىقتىن ئايرىل تۆۋەندىكى كىشىلەر قايتا قاچىلىغان ياكى ئۈسكىنىسىنى ئالماشتۇرغاندەك قىلىدۇ. مەخپىيەتلىكنى ساقلاش ئۈچۈن ئۇلار بىلەن ئاراڭدىكى بىخەتەرلىك نومۇرىنى دەلىللە. كۆرسەت + ئىلگىرى دەلىللەنگەن @@ -2992,6 +3019,7 @@ كۆڭۈلدىكى يۇقىرى + ئەڭ يۇقىرى @@ -3349,6 +3377,7 @@ %1$s غا يوللاندى سىز%1$s نىڭ ئۈستىدە%2$s دە ‫%1$s %2$s نىڭ ئۈستىدە %3$s دە + كىمگە كىمدىن چىقىم سوممىسى ۋە مۇئامىلە ۋاقتىنى ئۆز ئىچىگە ئالغان مۇئامىلە تەپسىلاتلىرى MobileCoin ھېسابات دەپتىرىنىڭ بىر قىسمى. @@ -3411,6 +3440,7 @@ چىقىمنى جەزىملە تور ھەققى مۆلچەرلەندى%1$s + كىمگە جەمئىي سومما قالدۇق: %1$s @@ -4006,6 +4036,7 @@ چاپلاش تاختىسىغا كۆچۈردى باشقۇرغۇچى + تەستىقلا رەت قىلىش @@ -4033,6 +4064,7 @@ ئاۋازلىق ئۇچۇر· %1$s + %1$s دىن%2$s غىچە @@ -4077,6 +4109,7 @@ %1$s بىلەن%2$s قېتىلدى %1$s، %2$s ۋە%3$s قېتىلدى %1$s، %2$s ۋە%3$d باشقىلار قېتىلدى + %1$s ئايرىلدى %1$s ۋە%2$s ئايرىلدى %1$s، %2$s ۋە%3$s ئايرىلدى @@ -4452,6 +4485,7 @@ ئۇچۇرلىشىش غايىب ئۇچۇرلار ئەپ بىخەتەرلىكى + خاتىرىلەش تىزىملىكى ۋە ئەپ ئىچىدە ئېكران كەسمە رەسىمگە ئېلىشنى چەكلەيدۇ Signal ئۇچۇر ۋە چاقىرىقنى ھەمىشە چاقىرىق ۋە يوشۇرۇن ئەۋەتكۈچىگە ئۇلاپ يەتكۈزىدۇ يېڭى پاراڭلار ئۈچۈن سۈكۈتتىكى ۋاقىت خاتىرىلىگۈچ @@ -4508,11 +4542,14 @@ ۋاسىتە سۈپىتى ۋاسىتە سۈپىتىنى ئەۋەت يۇقىرى سۈپەتلىك ۋاسىتە يوللىسىڭىز كۆپرەك مەلۇمات ئىشلىتىسىز + يۇقىرى + ئۆلچەملىك چاقىرىشلار + ئاپتوماتىك ئىختىيارىچە رەڭ ئىشلەت پاراڭ رەڭگى @@ -4721,6 +4758,7 @@ رەسىم تارت بىر سۈرەت تاللا سۈرەت + تېكىست ساقلا سىمانى تازىلا @@ -4777,7 +4815,7 @@ بىز ئۇچۇر قوش جاۋاپ قوش تاپشۇرۇۋالغۇچى - بىرلا كۆرسەت ئۇچۇر + View once media بىر ياكى كۆپ تۈرنىڭ ھەجىمى بەك چوڭ بىر ياكى كۆپ تۈر ئىناۋەتسىز تاللانغان تۈر زىيادە كۆپ @@ -5365,7 +5403,7 @@ %1$s %2$s سىز - + %1$s دىن%2$s غىچە جاۋاب @@ -6534,7 +6572,7 @@ لەقەم - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + لەقەم ۋە خاتىرىلەر Signal ئۇچتىن ئۇچقا مەخپىيلەشتۈرۈلگەن ھالدا ساقلىنىدۇ. ئۇلارنى پەقەت سىز كۆرەلەيسىز. ئات @@ -6548,9 +6586,9 @@ ساقلاش - Delete? + ئۆچۈرەمسىز؟ - This will permanently delete any nickname and note you’ve set. + بۇ مەشغۇلات سىز تەڭشىگەن بارلىق لەقەم ۋە خاتىرىلەرنى مەڭگۈلۈك ئۆچۈرىدۇ. @@ -6558,5 +6596,11 @@ خاتىرە تەھرىرلەش + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 7feb176076..6577393862 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -421,6 +421,7 @@ Приєднатись + Переповнено Помилка відправлення медіа @@ -645,6 +646,7 @@ Розархівувати Видалити Вибрати все + %1$d обрано %1$d обрано @@ -902,6 +904,7 @@ Надіслано %1$d запрошень Надіслано %1$d запрошення + Ви не можете автоматично додати \"%1$s\" в цю групу.\n\nЙому (їй) було відправлено запрошення приєднатися, і він (-а) не побачить повідомлення в групі, поки не прийме запрошення. Ви не можете автоматично додати цих користувачів до цієї групи.\n\nЇм було відправлено запрошення приєднатися, і вони не побачать повідомлення в групі, поки не приймуть запрошення. @@ -1114,7 +1117,9 @@ Редагувати назву та картинку Спадкована Група + Це Застаріла Група. Такі функції, як адміністратори груп, доступні тільки для Нових Груп. + Це Застаріла Група. Щоб отримати доступ до таких функцій, як @згадування і адміністратори груп, Ця Застаріла Група не може бути оновлена до Нової Групи, тому що вона занадто велика. Максимальний розмір групи це - %1$d. оновити цю групу. @@ -1359,6 +1364,7 @@ Видалити + %1$d обрав (%2$s) %1$d обрав (%2$s) @@ -1425,17 +1431,13 @@ Ви покинули групу. Ви оновили групу. Групу оновлено. - + Вихідний голосовий виклик - + Вихідний відеовиклик - - Голосовий виклик без відповіді - - Відеовиклик без відповіді - + Вхідний голосовий виклик - + Вхідний відеовиклик Пропущений голосовий виклик @@ -1445,10 +1447,6 @@ Пропущено голосовий виклик під час увімкненого профілю сповіщень Пропущено відеовиклик під час увімкненого профілю сповіщень - - Ви відхилили голосовий виклик - - Ви відхилили відеовиклик %1$s · %2$s %1$s оновив групу. @@ -1673,34 +1671,59 @@ %1$s тепер може приймати платежі - %1$s розпочав груповий дзвінок · %2$s - Ви розпочали груповий виклик · %1$s - %1$s бере участь у груповому дзвінку · %2$s - Ви у груповому дзвінку · %1$s - %1$s і %2$s бере участь у груповому дзвінку · %3$s - Груповий дзвінок · %1$s - - %1$s розпочав груповий дзвінок - Ви розпочали груповий дзвінок - %1$s бере участь у груповому дзвінку - Ви у груповому дзвінку - %1$s і %2$s беруть участь у груповому дзвінку - Груповий дзвінок + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$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, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$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, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2221,6 +2244,7 @@ Не вдається надіслати запит на код підтвердження. Перевірте підключення до мережі та спробуйте знову. Нестандартний формат номеру + Введене число (%1$s) виглядає нестандартним.\n\nВи мали на увазі %2$s? Signal Android — Формат телефонних номерів @@ -2883,6 +2907,8 @@ Завантаження Дізнатися більше Приєднатися до дзвінка + + Call back Повернутись до виклику Дзвінок повний Запросити друзів @@ -2947,6 +2973,7 @@ Покинути дзвінок Можливо, ці користувачі перевстановили застосунок або замінили пристрої. Перевірте коди безпеки для чатів з цими користувачами, щоб упевнитись у конфіденційності спілкування. Детальніше + Попереднє підтверджено @@ -3274,6 +3301,7 @@ Типово Великий + Максимум @@ -3640,6 +3668,7 @@ Надіслано до %1$s Ви %1$s в %2$s %1$s%2$s в %3$s + Кому Від Деталі транзакції, зокрема сума платежу і час транзакції, є частиною особового рахунку MobileCoin. @@ -3702,6 +3731,7 @@ Підтвердити платіж Комісія мережі Приблизно %1$s + Кому Загальна сума Баланс: %1$s @@ -4324,6 +4354,7 @@ Скопійовано до буфера обміну Адміністратор + Прийняти Відхилити @@ -4351,6 +4382,7 @@ Голосове повідомлення · %1$s + %1$s до %2$s @@ -4401,6 +4433,7 @@ %1$s та %2$s приєднались %1$s, %2$s і %3$s приєднались %1$s, %2$s та %3$d інші приєднались + Залишив розмову: %1$s %1$s та %2$s покинули розмову %1$s,%2$s та %3$s покинули розмову @@ -4782,6 +4815,7 @@ Листування Зникаючі повідомлення Безпека програми + Блокувати знімки екрану в списку недавніх і в застосунку Повідомлення і виклики в Signal, завжди ретранслювати виклики, захищений відправник Таймер за умовчанням для нових чатів @@ -4838,11 +4872,14 @@ Якість медіа Якість відправлених медіа Відправлення медіа високої якості споживатиме більше даних. + Великий + Стандартне Виклики + Авто Використовуйте власні кольори Колір чату @@ -5060,6 +5097,7 @@ Зробити фото Вибрати фото Світлина + Опис Зберегти Витерти фото профілю @@ -5128,7 +5166,7 @@ Додати повідомлення Додати відповідь Надіслати - Одноразове повідомлення + View once media Один або більше елементів були завеликими Один або кілька елементів були недійсними Обрано забагато елементів @@ -5731,7 +5769,7 @@ %1$s %2$s Ви - + %1$s до %2$s Відповісти @@ -5934,11 +5972,11 @@ Нова індивідуальна історія - Можуть побачити лише окремі особи + Можуть бачити лише конкретні користувачі Групова історія - Поділитись у групі + Для користувачів уже створеної групи Обрати групи @@ -6203,11 +6241,11 @@ - Оновлення історій автоматично зникають через 24 години. Оберіть глядачів вашої історії або створіть нові історії для конкретних глядачів або груп. + Оновлення історій автоматично зникають через 24 години. Оберіть глядачів своєї історії або створіть нові історії для конкретних глядачів і груп. Вимкнути історії - Якщо вимкнути історії, ви не зможете ні публікувати, ні переглядати історії. + Якщо історії вимкнути, ви не зможете їх ні публікувати, ні переглядати. Увімкнути історії @@ -6972,7 +7010,7 @@ Умовне ім\'я - Nicknames & notes are stored with Signal and end-to-end encrypted. They are only visible to you. + Умовні імена й примітки зберігаються в Signal і захищені наскрізним шифруванням. Їх бачите лише ви. Ім\'я @@ -6986,9 +7024,9 @@ Зберегти - Delete? + Видалити? - This will permanently delete any nickname and note you’ve set. + Умовне ім\'я і примітку буде видалено остаточно. @@ -6996,5 +7034,11 @@ Змінити примітку + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index d6dbd1e4a0..e7f3ab8ae2 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -415,6 +415,7 @@ شامل ہوں + بھرا ہوا میڈیا بھیجنے میں خامی @@ -613,6 +614,7 @@ ان آرکائیو حذف کریں سب منتخب کریں + %1$d منتخب کیا گیا %1$d منتخب کیا گیا @@ -864,6 +866,7 @@ انوائٹ چلا گیا %1$d انوائٹس چلے گئے + \"%1$s\" آپ کے ذریعہ خود بخود اس گروپ میں شامل نہیں ہوسکتے ہیں۔ \\ n y n ان میں شامل ہونے کی دعوت دی گئی ہے ، اور جب تک وہ قبول نہیں کریں گے کوئی گروپ پیغامات نہیں دیکھیں گے۔ ان صارفین کو آپ کے ذریعہ خود بخود اس گروپ میں شامل نہیں کیا جاسکتا۔ \\ n \\ n انہیں گروپ میں شامل ہونے کے لئے مدعو کیا گیا ہے ، اور جب تک وہ قبول نہیں کریں گے کوئی گروپ پیغامات نہیں دیکھیں گے۔ @@ -1044,7 +1047,9 @@ نام اور تصویر میں ترمیم کریں لیگیسی گروپ + یہ لیگیسی گروپ ہے۔ گروپ منتظمین جیسی خصوصیات صرف نئے گروپس کیلئے دستیاب ہیں۔ + یہ لیگیسی گروپ ہے۔ @ ذکر اور منتظمین جیسی نئی خصوصیات تک رسائی حاصل کرنے کیلئے ، اس لیگیسی گروپ کو نئے گروپ میں اپ گریڈ نہیں کیا جاسکتا کیونکہ یہ بہت بڑا ہے۔ زیادہ سے زیادہ گروپ سائز %1$d ہے۔ اس گروپ کو اپ گریڈ کریں۔ @@ -1271,6 +1276,7 @@ حذف کریں + %1$d نے (%2$s) منتخب کیا %1$d نے (%2$s) منتخب کیا @@ -1335,17 +1341,13 @@ آپ نے گروپ چھوڑدیا ہے۔ آپ نے گروپ کی تجدید کی۔ گروپ کو اپ ڈیٹ کیا گیا تھا۔ - + آؤٹ گوئنگ وائس کال - + آؤٹ گوئنگ ویڈیو کال - - غیر جواب شدہ وائس کال - - غیر جواب شدہ ویڈیو کال - + ان کمنگ وائس کال - + ان کمنگ ویڈیو کال مسڈ وائس کال @@ -1355,10 +1357,6 @@ اطلاعات کی پروفائل آن ہونے کے دوران مسڈ وائس کال اطلاعات کی پروفائل آن ہونے کے دوران مسڈ ویڈیو کال - - آپ نے وائس کال مسترد کر دی - - آپ نے ویڈیو کال مسترد کردی %1$s · %2$s %1$sگروپ کی تجدید کی۔ @@ -1567,30 +1565,55 @@ %1$s اب پیمنٹس قبول کر سکتا ہے - %1$s نے ایک گروپ کال شروع کی.%2$s - آپ نے گروپ کال کا آغاز کیا ہے · %1$s - %1$s گروپ کال میں ہے ·%2$s - آپ گروپ کال میں ہیں ·%1$s - %1$s اور %2$s گروپ کال میں ہیں ·%3$s - گروپ کال .%1$s - - %1$s نے ایک گروپ کال شروع کی - آپ نے ایک گروپ کال شروع کی - %1$s گروپ کال میں ہے. - آپ گروپ کال میں ہیں - %1$s اور %2$s گروپ کال میں ہیں - گروپ کال + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s آپ - - %1$s،%2$s ، اور %3$d دوسرے گروپ کال میں شامل ہیں.%4$s - %1$s،%2$s ، اور%3$d دیگر گروپ کال میں ہیں ·%4$s + + + %1$s, %2$s, and %3$d other are in the group call · %4$s + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s،%2$s ، اور %3$d دوسرے گروپ کال میں شامل ہیں - %1$s،%2$s ، اور %3$d دوسرے گروپ کال میں شامل ہیں + + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call @@ -2063,6 +2086,7 @@ تصدیقی کوڈ کی درخواست کرنے سے قاصر ہیں۔ براہ کرم نیٹ ورک کنکشن چیک کریں اور دوبارہ کوشش کریں۔ غیر معیاری ہندسوں کی ترتیب + آپ کا درج کردہ نمبر (%1$s) غیر معیاری فارمیٹ لگ رہا ہے۔\n\nکیا آپ کا مطلب %2$s ہے؟ Signal Android - فون نمبر کا فارمیٹ @@ -2709,6 +2733,8 @@ لوڈ ہو رہا ہے مزید پڑھیں کال میں شامل ہوں + + Call back کال پر واپس جائیں کال پوری ہے دوستوں کو مدعو کریں @@ -2773,6 +2799,7 @@ کال چھوڑو مندرجہ ذیل لوگوں نے انسٹال یا ان میں تبدیلیاں کی ہیں۔ رازداری کو یقینی بنانے کے لیئے ان کے ساتھ اپنے حفاظتی نمبر کی تصدیق کریں۔ دیکھیں + پچھلی تصدیق شدہ @@ -3086,6 +3113,7 @@ پہلے سے طے شدہ اونچا + زیادہ سے زیادہ @@ -3446,6 +3474,7 @@ %1$s کو بھیجا آپ پر %1$s پر%2$s %1$sپر%2$sپر%3$s + کے لئے کی طرف سے ٹرانزیکشن کی تفصیلات بشمول پیمنٹ کی رقم اور لین دین کا وقت، موبائل کوائن لیجر کا حصہ ہیں۔ @@ -3508,6 +3537,7 @@ ادائیگی کی تصدیق کریں نیٹ ورک کی فیس تخمینہ %1$s + کے لئے کل رقم بقیہ: %1$s @@ -4112,6 +4142,7 @@ کلپ بورڈ کو کاپی کریں ایڈمن + منظور کریں انکار کرنا @@ -4139,6 +4170,7 @@ آڈیو پیغام · %1$s + %1$sسے%2$s @@ -4185,6 +4217,7 @@ %1$sاور %2$s شامل ہو گئے %1$s,%2$sاور%3$s شامل ہو گئے %1$s،%2$s اور%3$d دوسرے شامل ہوئے + %1$sچلے گئے %1$s اور %2$s چلے گئے %1$s،%2$s اور%3$s چلے گئے @@ -4562,6 +4595,7 @@ پیغام رسانی پیغامات غائب ہو رہے ہیں ایپ سیکیورٹی + حالیہ فہرست میں اور ایپ کے اندر اسکرین شاٹس کو بلاک کریں Signal میسجز اور کالز ، ہمیشہ ریلے کالز اور مہر بند مرسل کو نئی چیٹس کے لیے ڈیفالٹ ٹائمر @@ -4618,11 +4652,14 @@ میڈیا کوالٹی بھیجے گئے میڈیا کی کوالٹی اعلیٰ کوالٹی کے میڈیا کو بھیجنے میں مزید ڈیٹا استعمال ہو گا۔ + اونچا + معیار کالز + آٹو حسب ضرورت رنگ استعمال کریں چیٹ کا رنگ @@ -4834,6 +4871,7 @@ تصویر کھینچنا تصویر منتخب کریں تصویر + متن محفوظ کریں واضح اوتار @@ -4894,7 +4932,7 @@ پیغام شامل کریں جواب شامل کریں بھیجیں بنام - پیغام ایک بار دیکھیں + View once media ایک یا زائد آئٹمز بہت بڑی تھیں ایک یا زائد آئٹمز غلط تھیں بہت زیادہ آئٹمز منتخب کیے گئے ہیں @@ -5487,7 +5525,7 @@ %1$s%2$s آپ - + %1$sسے%2$s جواب دیں @@ -6704,5 +6742,11 @@ نوٹ میں ترمیم کریں + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index b595dc8b88..e941ceb082 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -412,6 +412,7 @@ Tham gia + Đầy Lỗi gửi tệp đa phương tiện @@ -597,6 +598,7 @@ Bỏ lưu trữ Xóa Chọn tất cả + %1$d đã chọn @@ -845,6 +847,7 @@ Đã gửi %1$d lời mời + \"%1$s\" không thể được tự động thêm vào nhóm bởi bạn.\n\nHọ đã được mời tham gia và sẽ không thấy tin nhắn trong nhóm cho đến khi họ chấp nhận. Những người dùng này không thể được tự động thêm vào nhóm bởi bạn.\n\nHọ đã được mời tham gia và sẽ không thấy tin nhắn trong nhóm cho đến khi họ chấp nhận. @@ -1009,7 +1012,9 @@ Sửa tên và ảnh Nhóm Cũ + Đây là Nhóm Cũ. Các tính năng như quản trị viên nhóm chỉ có trong Nhóm Mới. + Đây là Nhóm Cũ. Để truy cập các tính năng mới như @nhắc tên và quản trị viên, Nhóm Cũ này không thể được nâng cấp thành Nhóm Mới vì nhóm quá lớn. Kích cỡ nhóm tối đa là %1$d. nâng cấp nhóm này. @@ -1227,6 +1232,7 @@ Xóa + %1$dđã chọn (%2$s) @@ -1290,17 +1296,13 @@ Bạn đã rời nhóm. Bạn đã cập nhật nhóm. Nhóm đã được cập nhật. - + Cuộc gọi thoại đi - + Cuộc gọi video đi - - Cuộc gọi thoại không được trả lời - - Cuộc gọi video không được trả lời - + Cuộc gọi thoại đến - + Cuộc gọi video đến Cuộc gọi thoại bị nhỡ @@ -1310,10 +1312,6 @@ Cuộc gọi thoại bị nhỡ khi cấu hình thông báo được bật Cuộc gọi video bị nhỡ khi cấu hình thông báo được bật - - Bạn đã từ chối cuộc gọi thoại - - Bạn đã từ chối cuộc gọi video %1$s · %2$s %1$s đã cập nhật nhóm. @@ -1514,28 +1512,53 @@ %1$s giờ đã có thể nhận Thanh toán - %1$s đã bắt đầu cuộc gọi nhóm · %2$s - Bạn đã bắt đầu cuộc gọi nhóm · %1$s - %1$s đang trong cuộc gọi nhóm · %2$s - Bạn đang trong cuộc gọi nhóm · %1$s - %1$s và %2$s đang trong cuộc gọi nhóm · %3$s - Cuộc gọi nhóm · %1$s - - %1$s đã bắt đầu cuộc gọi nhóm - Bạn đã bắt đầu cuộc gọi nhóm - %1$s đang trong cuộc gọi nhóm - Bạn đang trong cuộc gọi nhóm - %1$s và %2$s đang trong cuộc gọi nhóm - Cuộc gọi nhóm + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s Bạn - - %1$s, %2$s, và %3$d người khác đang trong cuộc gọi nhóm · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, và %3$d người khác đang trong cuộc gọi nhóm + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ Không thể yêu cầu mã xác minh. Vui lòng kiểm tra kết nối mạng của bạn và thử lại. Định dạng số không chuẩn + Số mà bạn nhập vào (%1$s) không đúng. /n/n có phải ý bạn là %2$s? Signal Android - Định dạng số điện thoại @@ -2622,6 +2646,8 @@ Đang tải… Tìm hiểu thêm Tham gia cuộc gọi + + Call back Quay trở lại cuộc gọi Cuộc gọi đã đầy Mời bạn bè @@ -2686,6 +2712,7 @@ Rời cuộc gọi Những người này có thể đã cài lại ứng dụng hoặc thay đổi thiết bị. Vui lòng xác minh lại mã số an toàn để đảm bảo riêng tư. Xem + Đã từng được xác minh @@ -2992,6 +3019,7 @@ Mặc định Cao + Cao nhất @@ -3349,6 +3377,7 @@ Được gửi tới %1$s Bạn ở %1$s tại %2$s %1$s ở %2$s tại %3$s + Đến Từ Chi tiết giao dịch bao gồm số tiền thanh toán và thời gian giao dịch là một phần của Sổ cái MobileCoin. @@ -3411,6 +3440,7 @@ Xác nhận thanh toán Phía mạng lưới Ước tính %1$s + Đến Tổng cộng Số dư: %1$s @@ -4006,6 +4036,7 @@ Đã sao chép vào clipboard Quản trị viên + Đồng ý Từ chôi @@ -4033,6 +4064,7 @@ Tin nhắn thoại · %1$s + %1$s đến %2$s @@ -4077,6 +4109,7 @@ %1$s và %2$s đã tham gia %1$s, %2$s và %3$s đã tham gia %1$s, %2$s và %3$d người khác đã tham gia + Còn lại %1$s Còn lại %1$s và %2$s Còn lại %1$s, %2$s và %3$s @@ -4452,6 +4485,7 @@ Nhắn tin Tin nhắn tự hủy Bảo mật ứng dụng + Chặn chụp màn hình trong danh sách gần đây và bên trong ứng dụng Tin nhắn và cuộc gọi Signal, luôn luôn chuyển tiếp cuộc gọi, và người gửi được niêm phong Đồng hồ mặc định cho tất cả cuộc trò chuyện mới @@ -4508,11 +4542,14 @@ Chất lượng tập tin đa phương tiện Chất lượng tập tin đa phương tiện đã gửi Việc gửi tập tin đa phương tiện chất lượng cao sẽ tốn nhiều lưu lượng hơn. + Cao + Tiêu chuẩn Cuộc gọi + Tự động Sử dụng màu tuỳ chỉnh Màu cuộc trò chuyện @@ -4721,6 +4758,7 @@ Chụp hình Chọn hình Ảnh + Tin nhắn Lưu Xóa ảnh đại diện @@ -4777,7 +4815,7 @@ Thêm vào một tin nhắn Thêm trả lời Gửi đến - Hiển thị tin nhắn xem một lần + View once media Có một hoặc nhiều tập tin quá nặng Có một hoặc nhiều tập tin bị lỗi Bạn chọn quá nhiều tập tin @@ -5365,7 +5403,7 @@ %1$s%2$s Bạn - + %1$s đến %2$s Trả lời @@ -6558,5 +6596,11 @@ Sửa ghi chú + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index 3c44797bd9..dd2da3ac98 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -412,6 +412,7 @@ 摻埋我 + 爆棚 多媒體檔案傳送唔到 @@ -597,6 +598,7 @@ 取消封存 刪除 全部揀晒 + 揀選咗 %1$d 個 @@ -845,6 +847,7 @@ %1$d 個邀請已經送出 + 您冇得自動將「%1$s」加入呢個谷度。\n\n已經邀請咗佢加入,喺佢應承之前,唔會見到個谷嘅任何訊息住。 您冇得自動將呢啲使用者加入呢個谷度。\n\n已經邀請咗佢哋加入,喺佢哋應承之前,都唔會見到個谷嘅任何訊息住。 @@ -1009,7 +1012,9 @@ 改名同相 舊版谷 + 呢個係舊版谷。個谷嘅話事人等功能只有新版谷先用得。 + 呢個係舊版谷。若要用新功能,例如 @點名講 同埋個谷嘅話事人功能, 呢個舊版谷冇得升級做新版谷,因為人數太多。谷嘅人數上限係 %1$d 人。 升級呢個谷。 @@ -1227,6 +1232,7 @@ 刪除 + 揀選咗 %1$d 個 (%2$s) @@ -1290,17 +1296,13 @@ 您現已退谷。 您更新咗呢個谷。 個谷更新咗。 - + 打出嘅語音通話 - + 打出去嘅視像通話 - - 未接嘅語音通話 - - 未接嘅視像通話 - + 語音通話來電 - + 打入嚟嘅視像通話 未接嘅語音通話 @@ -1310,10 +1312,6 @@ 通知設定檔開啟時未接語音通話 通知設定檔開啟時未接視像通話 - - 你拒絕咗一個語音通話 - - 你拒絕咗一個視像通話 %1$s · %2$s %1$s 已更新呢個谷。 @@ -1514,28 +1512,53 @@ %1$s 而家可以接受付款 - %1$s 已發起成谷通話 · %2$s - 你開始咗一個群組通話 · %1$s - %1$s 嚟到成谷通話 · %2$s - 您嚟到成谷通話 · %1$s - %1$s 同 %2$s 嚟到成谷通話 · %3$s - 成谷通話 · %1$s - - %1$s 已發起成谷通話 - 你開始咗一個群組通話 - %1$s 嚟到成谷通話 - 您嚟到成谷通話 - %1$s 同 %2$s 嚟到成谷通話 - 成谷通話 + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s - - %1$s、%2$s 同其餘 %3$d 人嚟到成谷通話 · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s、%2$s 同其餘 %3$d 人嚟到成谷通話 + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ 驗證碼要求失敗。請檢查你嘅網絡連線,然後再試多次啦。 非標準冧把格式 + 您打嗰個冧把 (%1$s) 似乎唔係標準格式。\n\n您係咪話 %2$s? Signal Android - 電話冧把格式 @@ -2622,6 +2646,8 @@ 載入緊 講多啲畀我聽 加入通話 + + Call back 返回通話 通話已滿座 誠邀好友 @@ -2686,6 +2712,7 @@ 退出通話 下列嘅人可能已重新裝過 Signal 或者換咗部機。同佢哋驗證下您嘅安全碼,可以確保私隱。 睇下 + 先前驗證咗 @@ -2992,6 +3019,7 @@ 預設 + 最高 @@ -3349,6 +3377,7 @@ 畀咗 %1$s 您,喺 %1$s,%2$s 傳送 %1$s,喺 %2$s,%3$s 傳送 + 收款人: 付款人: 交易詳情包括付款金額同交易時間,係 MobileCoin Ledger 嘅一部份。 @@ -3411,6 +3440,7 @@ 確認付款 網絡費 估算 %1$s + 收款人: 總數金額 結餘:%1$s @@ -4006,6 +4036,7 @@ 複製咗去剪貼簿 話事人 + 批准 拒絕 @@ -4033,6 +4064,7 @@ 語音訊息 · %1$s + %1$s 畀 %2$s @@ -4077,6 +4109,7 @@ %1$s 同 %2$s 已加入 %1$s、%2$s 同 %3$s 已加入 %1$s、%2$s 同另外 %3$d 人已加入 + %1$s 已退出 %1$s 同 %2$s 已退出 %1$s、%2$s 同 %3$s 已退出 @@ -4452,6 +4485,7 @@ 發訊息 過眼雲煙訊息 App 保安措施 + 封鎖最近清單同埋應用程式入面嘅螢幕截圖 Signal 訊息同通話、一律轉駁通話同封密發送人 為新嘅傾偈預設倒數限期 @@ -4508,11 +4542,14 @@ 多媒體檔案嘅畫質 發送多媒體檔案嘅畫質 發送高畫質嘅多媒體檔案會用多啲數據。 + + 標準 通話 + 自動 用自訂嘅顏色 傾偈顏色 @@ -4721,6 +4758,7 @@ 影返幅相 揀張相 揀相 + 文字 儲存 清走個頭像 @@ -4777,7 +4815,7 @@ 寫返句訊息 寫返句回覆 傳送至 - 流聲掠影訊息 + View once media 有啲項目大得滯 有啲項目無效 揀選嘅項目多得滯 @@ -5365,7 +5403,7 @@ %1$s %2$s - + %1$s 放送 %2$s 回覆 @@ -6558,5 +6596,11 @@ 編輯備註 + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 9c943758c5..c428e1b0f9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -412,6 +412,7 @@ 加入 + 已满 媒体发送失败 @@ -597,6 +598,7 @@ 取消存档 删除 选择全部 + 已选择 %1$d @@ -845,6 +847,7 @@ 已发送%1$d个邀请 + “%1$s”不能直接被您邀请入群。\n\n对方已收到入群邀请,但在他同意入群前他将无法看到群消息。 这些用户不能直接被你邀请入群组。\n\n他们已经收到入群邀请,但在他们同意之前无法收到来自该群组的任何消息。 @@ -1009,7 +1012,9 @@ 编辑昵称和头像 旧版群组 + 这是个旧版群组,管理员等功能支持新版群组。 + 这是一个旧版群组,要是使用@提醒和管理员功能, 由于人数太多,此旧版群组无法升级成新版群组。升级要求人数在%1$d以下。 升级此群组。 @@ -1227,6 +1232,7 @@ 删除 + %1$d 已选择(%2$s) @@ -1290,17 +1296,13 @@ 您已离开该群组。 您已更新该群组 群组已更新。 - + 拨出语音通话 - + 拨出视频通话 - - 未接语音通话 - - 未接视频通话 - + 语音来电 - + 视频来电 未接语音来电 @@ -1310,10 +1312,6 @@ 语音通话由于开启通知配置而未接通 视频通话由于开启通知配置而未接通 - - 您拒接了语音通话 - - 您拒接了视频通话 %1$s · %2$s %1$s 已更新该群组。 @@ -1514,28 +1512,53 @@ %1$s现在可以接受付款啦 - %1$s发起了群组通话 · %2$s - 您发起了一个群组通话 · %1$s - %1$s 在群通话中 · %2$s - 您在群通话中 · %1$s - %1$s 和 %2$s 在群通话中 · %3$s - 群组通话 · %1$s - - %1$s发起了群通话 - 您发起了一个群组通话 - %1$s 在群通话中 - 您在此群组通话中 - %1$s 和 %2$s 在群通话中 - 群组通话 + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s - - %1$s,%2$s 和其他%3$d位成员在群组通话中 · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s,%2$s 和其他%3$d位成员在群组通话中 + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ 无法请求验证码。请检查互联网连接并重试。 非标准号码格式 + 您输入的号码(%1$s)似乎是非标准格式。\n\n您要输入的是 %2$s 吗? Signal Android - 手机号码格式 @@ -2622,6 +2646,8 @@ 正在加载 了解更多 加入通话 + + Call back 回到通话 通话人数已满 邀请好友 @@ -2686,6 +2712,7 @@ 离开通话 以下用户可能重新安装了 Signal 或更换了设备,请重新与之验证安全码以保护您的隐私。 查看 + 已验证 @@ -2992,6 +3019,7 @@ 默认 + 最大 @@ -3349,6 +3377,7 @@ 发送至 %1$s 您在 %1$s 于 %2$s %1$s 在 %2$s 于 %3$s + 来自 交易详情(包括付款金额和交易时间)是 MobileCoin 账簿的一部分。 @@ -3411,6 +3440,7 @@ 确认付款 网络费 估计 %1$s + 总金额 余额:%1$s @@ -4006,6 +4036,7 @@ 已复制到剪切板 管理员 + 批准 拒绝 @@ -4033,6 +4064,7 @@ 语音消息 · %1$s + %1$s 至 %2$s @@ -4077,6 +4109,7 @@ %1$s与%2$s已加入 %1$s、%2$s和 %3$s已加入 %1$s、%2$s 及其它 %3$d 位用户已加入 + %1$s 已离开 %1$s和%2$s已离开 %1$s、%2$s和%3$s已离开 @@ -4452,6 +4485,7 @@ 消息传输 限时消息 应用安全 + 阻止在最近聊天和应用内进行截图 Signal 消息和通话,总是转发通话以及密封发送人 新聊天的默认计时器 @@ -4508,11 +4542,14 @@ 媒体质量 已发送媒体质量 发送高质量媒体将会使用更多数据。 + + 标准 通话 + 自动 使用自定义颜色 聊天颜色 @@ -4721,6 +4758,7 @@ 拍照 选择照片 图片 + 文本 保存 清除头像 @@ -4777,7 +4815,7 @@ 添加消息 添加回复 发送给 - 一次性消息 + View once media 一个或多个项目过大 一个或多个项目无效 选择的项目过多 @@ -5365,7 +5403,7 @@ %1$s %2$s - + %1$s 至 %2$s 回复 @@ -6558,5 +6596,11 @@ 编辑备注 + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 3879e77556..55bfc7d731 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -412,6 +412,7 @@ 加入 + 已滿 傳送媒體時發生錯誤 @@ -597,6 +598,7 @@ 解除封存 刪除 全選 + %1$d 個已選取 @@ -845,6 +847,7 @@ %1$d 個邀請已送出 + 您不可自動將「%1$s」加入此群組。\n\n對方已獲邀加入,而且在對方接受之前,不會看見任何群組訊息。 您不可自動將這些使用者加入此群組。\n\n他們已獲邀加入群組,而且各人在接受之前,不會看見任何群組訊息。 @@ -1009,7 +1012,9 @@ 編輯名稱和圖片 舊版群組 + 這是「舊版群組」。諸如群組管理員的功能僅限「新版群組」可用。 + 這是「舊版群組」。若要存取新功能,諸如 @提及 和管理員, 此「舊版群組」無法升級至「新版群組」,因為人數太多。群組人數上限為 %1$d 人。 升級此群組。 @@ -1227,6 +1232,7 @@ 刪除 + %1$d 個已選取 (%2$s) @@ -1290,17 +1296,13 @@ 您已退出此群組。 您已更新此群組。 此群組已更新。 - + 撥打語音通話 - + 撥打視訊通話 - - 未接的語音通話 - - 未接的視訊通話 - + 語音通話來電 - + 視訊通話來電 未接的語音通話 @@ -1310,10 +1312,6 @@ 通知設定檔開啟時有未接語音通話 通知設定檔開啟時未接視訊通話 - - 你拒絕了一個語音通話 - - 你拒絕了一個視訊通話 %1$s · %2$s %1$s 已更新此群組。 @@ -1514,28 +1512,53 @@ %1$s 現在可以接受付款 - %1$s 開展了群組通話 · %2$s - 你發起了群組通話 · %1$s - %1$s 加入了群組通話 · %2$s - 您現已在群組通話中 · %1$s - %1$s 和 %2$s 現已在群組通話中 · %3$s - 群組通話 · %1$s - - %1$s 已開始群組通話 - 你發起了群組通話 - %1$s 加入了群組通話 - 您加入了群組通話 - %1$s 和 %2$s 加入了群組通話 - 群組通話 + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s - - %1$s、%2$s 和另 %3$d 人現已在群組通話中 · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s、%2$s 和另 %3$d 人現已在群組通話中 + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ 無法要求驗證碼。請檢查你的網路連線,然後再試一次。 非標準號碼格式 + 您所輸入的號碼 (%1$s) 似乎並非標準格式。\n\n您是指 %2$s 嗎? Signal Android - 電話號碼格式 @@ -2622,6 +2646,8 @@ 正在載入 了解更多 加入通話 + + Call back 返回通話 通話已滿座 邀請好友 @@ -2686,6 +2712,7 @@ 退出通話 下列的人或已重新安裝或更換裝置。請驗證您與各人的安全碼,以確保私隱。 檢視 + 先前已驗證 @@ -2992,6 +3019,7 @@ 預設值 + 最高 @@ -3349,6 +3377,7 @@ 傳送給 %1$s 您,在 %1$s 於 %2$s %1$s,在 %2$s 於 %3$s + 收款人: 付款人: 付款金額及交易時間等交易詳情屬於 MobileCoin 帳目的一部分。 @@ -3411,6 +3440,7 @@ 確認付款 網絡費用 估算 %1$s + 收款人: 總金額 餘額:%1$s @@ -4006,6 +4036,7 @@ 已複製至剪貼簿 管理員 + 核准 拒絕 @@ -4033,6 +4064,7 @@ 語音訊息 · %1$s + 由 %1$s 發給 %2$s @@ -4077,6 +4109,7 @@ %1$s 和 %2$s 已加入 %1$s、%2$s 和 %3$s 已加入 %1$s、%2$s 和另 %3$d 人已加入 + %1$s 已退出 %1$s 和 %2$s 已退出 %1$s、%2$s 和 %3$s 已退出 @@ -4452,6 +4485,7 @@ 傳訊 限時訊息 應用程式安全性 + 封鎖最近的清單以及此應用程式中的畫面截圖 Signal 訊息與通話、一律轉送通話和密封寄件人 新聊天的預設計時器 @@ -4508,11 +4542,14 @@ 媒體的品質 送出媒體的品質 傳送高品質的媒體將會使用較多數據。 + 高品質 + 標準 通話 + 自動 使用自訂色彩 聊天色彩 @@ -4721,6 +4758,7 @@ 拍照 選取照片 照片 + 文字 儲存 清除頭像 @@ -4777,7 +4815,7 @@ 新增訊息 新增回覆 傳送給 - 閱後即焚訊息 + View once media 部分項目太大 部分項目無效 已選取太多項目 @@ -5365,7 +5403,7 @@ %1$s %2$s - + %1$s 至 %2$s 回覆 @@ -6558,5 +6596,11 @@ 編輯備註 + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 5696f5ee50..ec4c1833c3 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -412,6 +412,7 @@ 加入 + 已滿 傳送媒體檔時出錯 @@ -597,6 +598,7 @@ 解除封存 刪除 全選 + %1$d 已選擇 @@ -845,6 +847,7 @@ %1$d 個邀請已送出 + 「%1$s」不能由你自動新增到此群組中。\n\n他們已獲邀請加入,並且在他們接受之前不會看到任何群組訊息。 你無法將這些使用者自動新增到此群組。\n\n他們已被邀請加入群組,並且在他們接受之前不會看到任何群組訊息。 @@ -1009,7 +1012,9 @@ 編輯名稱及圖片 舊版群組 + 這是一個舊版群組。群組管理員之類的功能僅適用於新群組。 + 這是一個舊版群組。 要連結@提及和管理員等新功能, 這個舊版群組太大,因此無法升級為新版群組。 最大群組大小為%1$d。 升級此群組。 @@ -1227,6 +1232,7 @@ 刪除 + %1$d 已選擇 (%2$s) @@ -1290,17 +1296,13 @@ 您已經離開了此群組。 你已更新群組。 該群組已更新。 - + 撥打語音通話 - + 撥出視訊電話 - - 未接的語音通話 - - 未接的視訊電話 - + 語音通話來電 - + 視訊電話來電 未接的語音通話 @@ -1310,10 +1312,6 @@ 通知設定檔開啟時有未接語音通話 通知設定檔開啟時未接視訊通話 - - 你拒絕了一個語音通話 - - 你已拒絕一通視訊電話 %1$s · %2$s %1$s 更新了群組。 @@ -1514,28 +1512,53 @@ %1$s 現在可以接受付款 - %1$s 已開始群組通話 · %2$s - 你發起了群組通話 · %1$s - %1$s 在群組通話中 · %2$s - 你正在群組通話中 · %1$s - %1$s 和 %2$s 在群組通話中 · %3$s - 群組通話 · %1$s - - %1$s 發起了群組通話 - 你發起了群組通話 - %1$s 在群組通話中 - 你正在群組通話中 - %1$s 及 %2$s 在群組通話中 - 群組通話 + + %1$s is in the call · %2$s + + You are in the call · %1$s + + %1$s and %2$s are in the call · %3$s + + %1$s is in the call + + You are in the call + + %1$s and %2$s are in the call + + The video call has ended + + The video call has ended · %1$s + + Missed video call + + Missed video call · %1$s + + Incoming video call + + Incoming video call · %1$s + + Outgoing video call + + Outgoing video call · %1$s + + You started a video call + + You started a video call · %1$s + + %1$s started a video call + + %1$s started a video call · %2$s - - %1$s、%2$s 和另外 %3$d 人在群組通話中 · %4$s + + + %1$s, %2$s, and %3$d others are in the group call · %4$s - - %1$s, %2$s, 及 %3$d 及其他人在群組通話中 + + + %1$s, %2$s, and %3$d others are in the group call @@ -1984,6 +2007,7 @@ 無法要求驗證碼。請檢查你的網路連線,然後再試一次。 非標準數字格式 + 你輸入的數字 (%1$s) 似乎是非標準格式。\n\n你的意思是 %2$s嗎 ? Signal Android - 電話號碼格式 @@ -2622,6 +2646,8 @@ 載入中 了解更多 加入通話 + + Call back 返回通話 通話人數已滿 邀請好友 @@ -2686,6 +2712,7 @@ 離開通話 下列的人可能已重新安裝或更換了裝置。請驗證你與他們的安全碼,以確保隱私。 檢視 + 先前已驗證 @@ -2992,6 +3019,7 @@ 預設 + 最高 @@ -3349,6 +3377,7 @@ 傳送給 %1$s 你在%1$s 在 %2$s %1$s 在 %2$s 在 %3$s + 來自 包括付款金額和交易時間在內的交易詳細資訊是MobileCoin Ledger的一部分。 @@ -3411,6 +3440,7 @@ 確認付款 網路費 估計 %1$s + 總金額 餘額: %1$s @@ -4006,6 +4036,7 @@ 已複製到剪貼簿 管理員 + 批准 拒絕 @@ -4033,6 +4064,7 @@ 語音訊息 · %1$s + %1$s 傳送給 %2$s @@ -4077,6 +4109,7 @@ %1$s 和 %2$s 已加入 %1$s, %2$s 和 %3$s已加入 %1$s, %2$s 和 %3$d 及其他人已加入 + %1$s 離開了 %1$s 和 %2$s 已離開 %1$s, %2$s 和 %3$s 已離開 @@ -4452,6 +4485,7 @@ 傳訊 自動銷毀訊息 應用程式安全性 + 在對話清單或應用程式內停用畫面擷取功能 Signal 訊息和電話,永遠轉發通話和密封發件人 新聊天的預設計時器 @@ -4508,11 +4542,14 @@ 媒體檔品質 已傳送媒體檔品質 傳送高畫質的媒體檔將會使用較多數據。 + + 標準 通話 + 自動 使用自定義顏色 聊天的顏色 @@ -4721,6 +4758,7 @@ 拍照 選取照片 照片 + 文字 儲存 清除頭像 @@ -4777,7 +4815,7 @@ 新增訊息 新增回覆 傳送給 - 查看一次訊息 + View once media 一件或多件項目容量太大 一項或多項無效 選擇的項目太多 @@ -5365,7 +5403,7 @@ %1$s %2$s - + %1$s 傳送給 %2$s 回覆 @@ -6558,5 +6596,11 @@ 編輯備註 + + + Restoring backup… + + Downloading backup data… + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 203d2c432f..bf629496d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -866,7 +866,7 @@ Invitation sent %d invitations sent - + “%1$s” can’t be automatically added to this group by you.\n\nThey’ve been invited to join, and won’t see any group messages until they accept. These users can’t be automatically added to this group by you.\n\nThey’ve been invited to join the group, and won’t see any group messages until they accept. @@ -1597,9 +1597,9 @@ You started a video call You started a video call · %1$s - + %1$s started a video call - + %1$s started a video call · %2$s You diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index 5f1d9b88fe..4ca12e906a 100644 --- a/app/static-ips.gradle.kts +++ b/app/static-ips.gradle.kts @@ -7,4 +7,3 @@ rootProject.extra["sfu_ips"] = """new String[]{"34.36.104.134"}""" 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"}""" -rootProject.extra["key_backup_ips"] = """new String[]{"240.0.0.1"}""" From 03845eabaf1a864ec9011a70d3304dfda46af403 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 18 Apr 2024 16:44:32 -0400 Subject: [PATCH 039/113] Bump version to 7.5.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 7dfac31b6e..43a77b4d4a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1410 -val canonicalVersionName = "7.4.2" +val canonicalVersionCode = 1411 +val canonicalVersionName = "7.5.0" val postFixSize = 100 val abiPostFix: Map = mapOf( From 42aeceffe2c395d1e8bf10b5db6f500e690a46a1 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 22 Apr 2024 16:32:27 -0400 Subject: [PATCH 040/113] Revert full usage of ActiveCallManager. --- app/src/main/AndroidManifest.xml | 6 + .../service/webrtc/AndroidCallConnection.kt | 8 +- .../service/webrtc/WebRtcCallService.java | 467 ++++++++++++++++++ .../service/webrtc/WebRtcInteractor.java | 30 +- .../securesms/util/FeatureFlags.java | 7 + .../webrtc/CallNotificationBuilder.java | 7 +- 6 files changed, 503 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0f55e9c3a6..a74e94c4d5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1113,6 +1113,12 @@ android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:exported="false"/> + + CallNotificationBuilder.getCallInProgressNotification(this, type, Recipient.resolved(id), isVideoCall, false)) + .subscribeOn(Schedulers.from(singleThreadExecutor)) + .observeOn(AndroidSchedulers.mainThread()) + .filter(unused -> requestTime == lastNotificationRequestTime && !stopping) + .subscribe(notification -> { + lastNotification = notification; + startForegroundCompat(lastNotificationId, lastNotification); + }); + } + } + + private synchronized void startForegroundCompat(int notificationId, Notification notification) { + if (stopping) { + return; + } + + if (Build.VERSION.SDK_INT >= 30) { + startForeground(notificationId, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE); + } else { + startForeground(notificationId, notification); + } + } + + private synchronized void stop() { + stopping = true; + stopForeground(true); + stopSelf(); + } + + private void registerUncaughtExceptionHandler() { + uncaughtExceptionHandlerManager = new UncaughtExceptionHandlerManager(); + uncaughtExceptionHandlerManager.registerHandler(new ProximityLockRelease(callManager.getLockManager())); + } + + private void registerNetworkReceiver() { + if (networkReceiver == null) { + networkReceiver = new NetworkReceiver(); + + registerReceiver(networkReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + } + } + + private void unregisterNetworkReceiver() { + if (networkReceiver != null) { + unregisterReceiver(networkReceiver); + + networkReceiver = null; + } + } + + public void registerPowerButtonReceiver() { + if (!AndroidTelecomUtil.getTelecomSupported() && powerButtonReceiver == null) { + powerButtonReceiver = new PowerButtonReceiver(); + + registerReceiver(powerButtonReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); + } + } + + public void unregisterPowerButtonReceiver() { + if (powerButtonReceiver != null) { + unregisterReceiver(powerButtonReceiver); + + powerButtonReceiver = null; + } + } + + @Override + public @Nullable IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onAudioDeviceChanged(@NonNull SignalAudioManager.AudioDevice activeDevice, @NonNull Set availableDevices) { + callManager.onAudioDeviceChanged(activeDevice, availableDevices); + } + + @Override + public void onBluetoothPermissionDenied() { + callManager.onBluetoothPermissionDenied(); + } + + public static PendingIntent getServicePendingIntent(@NonNull Context context, @NonNull Intent intent) { + return Build.VERSION.SDK_INT >= 26 ? PendingIntent.getForegroundService(context, 0, intent, PendingIntentFlags.mutable()) + : PendingIntent.getService(context, 0, intent, PendingIntentFlags.mutable()); + } + + @SuppressWarnings("deprecation") + private class HangUpRtcOnPstnCallAnsweredListener extends PhoneStateListener { + @Override + public void onCallStateChanged(int state, @NonNull String phoneNumber) { + super.onCallStateChanged(state, phoneNumber); + if (state == TelephonyManager.CALL_STATE_OFFHOOK) { + hangup(); + Log.i(TAG, "Device phone call ended Signal call."); + } + } + + private void hangup() { + callManager.localHangup(); + } + } + + /** + * Periodically request the web socket stay open if we are doing anything call related. + */ + private class WebSocketKeepAliveTask implements Runnable { + private boolean keepRunning = false; + + @MainThread + public void start() { + if (!keepRunning) { + keepRunning = true; + run(); + } + } + + @MainThread + public void stop() { + keepRunning = false; + ThreadUtil.cancelRunnableOnMain(webSocketKeepAliveTask); + ApplicationDependencies.getIncomingMessageObserver().removeKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN); + } + + @MainThread + @Override + public void run() { + if (keepRunning) { + ApplicationDependencies.getIncomingMessageObserver().registerKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN); + ThreadUtil.runOnMainDelayed(this, REQUEST_WEBSOCKET_STAY_OPEN_DELAY); + } + } + } + + private static class NetworkReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); + + ApplicationDependencies.getSignalCallManager().networkChange(activeNetworkInfo != null && activeNetworkInfo.isConnected()); + ApplicationDependencies.getSignalCallManager().dataModeUpdate(); + } + } + + private static class PowerButtonReceiver extends BroadcastReceiver { + @Override + public void onReceive(@NonNull Context context, @NonNull Intent intent) { + if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { + ApplicationDependencies.getSignalCallManager().screenOff(); + } + } + } + + private static class ProximityLockRelease implements Thread.UncaughtExceptionHandler { + private final LockManager lockManager; + + private ProximityLockRelease(@NonNull LockManager lockManager) { + this.lockManager = lockManager; + } + + @Override + public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) { + Log.i(TAG, "Uncaught exception - releasing proximity lock", throwable); + lockManager.updatePhoneState(LockManager.PhoneState.IDLE); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java index 219c3431cd..2e5d5d248b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java @@ -95,11 +95,11 @@ void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String } void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer, boolean isVideoCall) { - ActiveCallManager.update(context, type, remotePeer.getRecipient().getId(), isVideoCall); + WebRtcCallService.update(context, type, remotePeer.getRecipient().getId(), isVideoCall); } void setCallInProgressNotification(int type, @NonNull Recipient recipient, boolean isVideoCall) { - ActiveCallManager.update(context, type, recipient.getId(), isVideoCall); + WebRtcCallService.update(context, type, recipient.getId(), isVideoCall); } void retrieveTurnServers(@NonNull RemotePeer remotePeer) { @@ -107,7 +107,7 @@ void retrieveTurnServers(@NonNull RemotePeer remotePeer) { } void stopForegroundService() { - ActiveCallManager.stop(); + WebRtcCallService.stop(context); } void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer) { @@ -127,51 +127,51 @@ boolean startWebRtcCallActivityIfPossible() { } void registerPowerButtonReceiver() { - ActiveCallManager.changePowerButtonReceiver(context, true); + WebRtcCallService.changePowerButtonReceiver(context, true); } void unregisterPowerButtonReceiver() { - ActiveCallManager.changePowerButtonReceiver(context, false); + WebRtcCallService.changePowerButtonReceiver(context, false); } void silenceIncomingRinger() { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SilenceIncomingRinger()); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SilenceIncomingRinger()); } void initializeAudioForCall() { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Initialize()); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Initialize()); } void startIncomingRinger(@Nullable Uri ringtoneUri, boolean vibrate) { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.StartIncomingRinger(ringtoneUri, vibrate)); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.StartIncomingRinger(ringtoneUri, vibrate)); } void startOutgoingRinger() { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.StartOutgoingRinger()); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.StartOutgoingRinger()); } void stopAudio(boolean playDisconnect) { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Stop(playDisconnect)); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Stop(playDisconnect)); } void startAudioCommunication() { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Start()); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Start()); } public void setUserAudioDevice(@Nullable RecipientId recipientId, @NonNull SignalAudioManager.ChosenAudioDeviceIdentifier userDevice) { if (userDevice.isLegacy()) { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDeviceLegacy().ordinal(), false)); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDeviceLegacy().ordinal(), false)); } else { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDevice31(), true)); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDevice31(), true)); } } public void setDefaultAudioDevice(@NonNull RecipientId recipientId, @NonNull SignalAudioManager.AudioDevice userDevice, boolean clearUserEarpieceSelection) { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); } public void playStateChangeUp() { - ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); } void peekGroupCallForRingingCheck(@NonNull GroupCallRingCheckInfo groupCallRingCheckInfo) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 82e8e028f0..e99380aa9c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -116,6 +116,7 @@ public final class FeatureFlags { private static final String CALLING_REACTIONS = "android.calling.reactions"; private static final String NOTIFICATION_THUMBNAIL_BLOCKLIST = "android.notificationThumbnailProductBlocklist"; private static final String CALLING_RAISE_HAND = "android.calling.raiseHand"; + private static final String USE_ACTIVE_CALL_MANAGER = "android.calling.useActiveCallManager.5"; private static final String GIF_SEARCH = "global.gifSearch"; private static final String AUDIO_REMUXING = "android.media.audioRemux.1"; private static final String VIDEO_RECORD_1X_ZOOM = "android.media.videoCaptureDefaultZoom"; @@ -197,6 +198,7 @@ public final class FeatureFlags { CALLING_REACTIONS, NOTIFICATION_THUMBNAIL_BLOCKLIST, CALLING_RAISE_HAND, + USE_ACTIVE_CALL_MANAGER, GIF_SEARCH, AUDIO_REMUXING, VIDEO_RECORD_1X_ZOOM, @@ -693,6 +695,11 @@ public static String notificationThumbnailProductBlocklist() { return getString(NOTIFICATION_THUMBNAIL_BLOCKLIST, ""); } + /** Whether or not to use active call manager instead of WebRtcCallService. */ + public static boolean useActiveCallManager() { + return getBoolean(USE_ACTIVE_CALL_MANAGER, false); + } + /** Whether the in-app GIF search is available for use. */ public static boolean gifSearchAvailable() { return getBoolean(GIF_SEARCH, true); diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java index a6cc907b87..43df76007f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java @@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.service.webrtc.ActiveCallManager; +import org.thoughtcrime.securesms.service.webrtc.WebRtcCallService; import org.thoughtcrime.securesms.util.ConversationUtil; /** @@ -114,7 +115,7 @@ public static Notification getCallInProgressNotification( if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forIncomingCall( person, - ActiveCallManager.denyCallIntent(context), + WebRtcCallService.denyCallIntent(context), getActivityPendingIntent(context, isVideoCall ? LaunchCallScreenIntentState.VIDEO : LaunchCallScreenIntentState.AUDIO) ).setIsVideo(isVideoCall)); } @@ -122,7 +123,7 @@ public static Notification getCallInProgressNotification( return builder.build(); } else if (type == TYPE_OUTGOING_RINGING) { builder.setContentText(context.getString(R.string.NotificationBarManager__establishing_signal_call)); - builder.addAction(getServiceNotificationAction(context, ActiveCallManager.hangupIntent(context), R.drawable.symbol_phone_down_fill_24, R.string.NotificationBarManager__cancel_call)); + builder.addAction(getServiceNotificationAction(context, WebRtcCallService.hangupIntent(context), R.drawable.symbol_phone_down_fill_24, R.string.NotificationBarManager__cancel_call)); return builder.build(); } else { builder.setContentText(getOngoingCallContentText(context, recipient, isVideoCall)); @@ -138,7 +139,7 @@ public static Notification getCallInProgressNotification( if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forOngoingCall( person, - ActiveCallManager.hangupIntent(context) + WebRtcCallService.hangupIntent(context) ).setIsVideo(isVideoCall)); } From db27204084a1033c22d8f60d36726c1e476f2f2f Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 22 Apr 2024 14:19:16 -0400 Subject: [PATCH 041/113] Validate pni signature message. --- .../api/messages/EnvelopeContentValidator.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt index db240092af..d73a137700 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt @@ -1,5 +1,6 @@ package org.whispersystems.signalservice.api.messages +import okio.ByteString import org.signal.libsignal.protocol.message.DecryptionErrorMessage import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage import org.signal.libsignal.zkgroup.InvalidInputException @@ -13,6 +14,7 @@ import org.whispersystems.signalservice.internal.push.DataMessage import org.whispersystems.signalservice.internal.push.EditMessage import org.whispersystems.signalservice.internal.push.Envelope import org.whispersystems.signalservice.internal.push.GroupContextV2 +import org.whispersystems.signalservice.internal.push.PniSignatureMessage import org.whispersystems.signalservice.internal.push.ReceiptMessage import org.whispersystems.signalservice.internal.push.StoryMessage import org.whispersystems.signalservice.internal.push.SyncMessage @@ -43,6 +45,10 @@ object EnvelopeContentValidator { validateSenderKeyDistributionMessage(content.senderKeyDistributionMessage.toByteArray())?.let { return it } } + if (content.pniSignatureMessage != null) { + validatePniSignatureMessage(content.pniSignatureMessage)?.let { return it } + } + // Reminder: envelope.destinationServiceId was already validated since we need that for decryption return when { @@ -255,6 +261,18 @@ object EnvelopeContentValidator { } } + private fun validatePniSignatureMessage(pniSignatureMessage: PniSignatureMessage): Result? { + if (pniSignatureMessage.pni.isNullOrInvalidPni()) { + return Result.Invalid("[PniSignatureMessage] Invalid PNI") + } + + if (pniSignatureMessage.signature == null) { + return Result.Invalid("[PniSignatureMessage] Signature is null") + } + + return null + } + private fun validateStoryMessage(storyMessage: StoryMessage): Result { if (storyMessage.group != null) { validateGroupContextV2(storyMessage.group, "[StoryMessage]")?.let { return it } @@ -328,6 +346,11 @@ object EnvelopeContentValidator { return parsed == null || parsed.isUnknown } + private fun ByteString?.isNullOrInvalidPni(): Boolean { + val parsed = ServiceId.PNI.parseOrNull(this?.toByteArray()) + return parsed == null || parsed.isUnknown + } + private fun Content?.meetsStoryFlagCriteria(): Boolean { return when { this == null -> false From 716bc1f5e7b20cba1d8a094241926abe19110af3 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 22 Apr 2024 16:52:02 -0400 Subject: [PATCH 042/113] Cleanup dangling domain reference. --- build-logic/plugins/src/main/java/translations.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build-logic/plugins/src/main/java/translations.gradle b/build-logic/plugins/src/main/java/translations.gradle index 0f6a0c0f56..06a3658ebb 100644 --- a/build-logic/plugins/src/main/java/translations.gradle +++ b/build-logic/plugins/src/main/java/translations.gradle @@ -117,7 +117,6 @@ task resolveStaticIps { rootProject.extra["content_proxy_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("contentproxy.signal.org")}\"\"\" rootProject.extra["svr2_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("svr2.signal.org")}\"\"\" rootProject.extra["cdsi_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdsi.signal.org")}\"\"\" - rootProject.extra["key_backup_ips"] = "\"\"${staticIpResolver.resolveToBuildConfig("api.backup.signal.org")}\"\"\" """.stripIndent().trim() + "\n" } } From dc35261e000d6b252f269116f19877af8ecb4c23 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 22 Apr 2024 16:56:39 -0400 Subject: [PATCH 043/113] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 52 +++--- app/src/main/res/values-ar/strings.xml | 68 ++++---- app/src/main/res/values-az/strings.xml | 20 +-- app/src/main/res/values-bg/strings.xml | 52 +++--- app/src/main/res/values-bn/strings.xml | 52 +++--- app/src/main/res/values-bs/strings.xml | 60 +++---- app/src/main/res/values-ca/strings.xml | 52 +++--- app/src/main/res/values-cs/strings.xml | 60 +++---- app/src/main/res/values-da/strings.xml | 20 +-- app/src/main/res/values-de/strings.xml | 52 +++--- app/src/main/res/values-el/strings.xml | 52 +++--- app/src/main/res/values-es/strings.xml | 52 +++--- app/src/main/res/values-et/strings.xml | 52 +++--- app/src/main/res/values-eu/strings.xml | 52 +++--- app/src/main/res/values-fa/strings.xml | 20 +-- app/src/main/res/values-fi/strings.xml | 52 +++--- app/src/main/res/values-fr/strings.xml | 178 ++++++++++----------- app/src/main/res/values-ga/strings.xml | 32 ++-- app/src/main/res/values-gl/strings.xml | 52 +++--- app/src/main/res/values-gu/strings.xml | 52 +++--- app/src/main/res/values-hi/strings.xml | 52 +++--- app/src/main/res/values-hr/strings.xml | 60 +++---- app/src/main/res/values-hu/strings.xml | 52 +++--- app/src/main/res/values-in/strings.xml | 48 +++--- app/src/main/res/values-it/strings.xml | 52 +++--- app/src/main/res/values-iw/strings.xml | 28 ++-- app/src/main/res/values-ja/strings.xml | 48 +++--- app/src/main/res/values-ka/strings.xml | 52 +++--- app/src/main/res/values-kk/strings.xml | 52 +++--- 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 | 48 +++--- app/src/main/res/values-ky/strings.xml | 14 +- app/src/main/res/values-lt/strings.xml | 60 +++---- app/src/main/res/values-lv/strings.xml | 56 +++---- app/src/main/res/values-mk/strings.xml | 52 +++--- app/src/main/res/values-ml/strings.xml | 52 +++--- app/src/main/res/values-mr/strings.xml | 52 +++--- app/src/main/res/values-ms/strings.xml | 48 +++--- app/src/main/res/values-my/strings.xml | 48 +++--- app/src/main/res/values-nb/strings.xml | 52 +++--- app/src/main/res/values-nl/strings.xml | 20 +-- app/src/main/res/values-pa/strings.xml | 52 +++--- app/src/main/res/values-pl/strings.xml | 28 ++-- app/src/main/res/values-pt-rBR/strings.xml | 20 +-- app/src/main/res/values-pt/strings.xml | 52 +++--- app/src/main/res/values-ro/strings.xml | 56 +++---- app/src/main/res/values-ru/strings.xml | 60 +++---- app/src/main/res/values-sk/strings.xml | 60 +++---- app/src/main/res/values-sl/strings.xml | 60 +++---- app/src/main/res/values-sq/strings.xml | 52 +++--- app/src/main/res/values-sr/strings.xml | 52 +++--- app/src/main/res/values-sv/strings.xml | 52 +++--- app/src/main/res/values-sw/strings.xml | 52 +++--- app/src/main/res/values-ta/strings.xml | 52 +++--- app/src/main/res/values-te/strings.xml | 52 +++--- app/src/main/res/values-th/strings.xml | 48 +++--- app/src/main/res/values-tl/strings.xml | 10 +- app/src/main/res/values-tr/strings.xml | 20 +-- app/src/main/res/values-ug/strings.xml | 16 +- app/src/main/res/values-uk/strings.xml | 78 ++++----- app/src/main/res/values-ur/strings.xml | 52 +++--- app/src/main/res/values-vi/strings.xml | 48 +++--- app/src/main/res/values-yue/strings.xml | 48 +++--- app/src/main/res/values-zh-rCN/strings.xml | 48 +++--- app/src/main/res/values-zh-rHK/strings.xml | 48 +++--- app/src/main/res/values-zh-rTW/strings.xml | 48 +++--- app/static-ips.gradle.kts | 4 +- 68 files changed, 1640 insertions(+), 1640 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 2dd57d264b..eb875e87a4 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s is in die oproep · %2$s - You are in the call · %1$s + Jy is in die oproep · %1$s - %1$s and %2$s are in the call · %3$s + %1$s en %2$s is in die oproep · %3$s - %1$s is in the call + %1$s is in die oproep - You are in the call + Jy is in die oproep - %1$s and %2$s are in the call + %1$s en %2$s is in die oproep - The video call has ended + Die video-oproep is beëindig - The video call has ended · %1$s + Die video-oproep is beëindig · %1$s - Missed video call + Gemiste video-oproep - Missed video call · %1$s + Gemiste video-oproep %1$s - Incoming video call + Inkomende video-oproep - Incoming video call · %1$s + Inkomende video-oproep · %1$s - Outgoing video call + Uitgaande video-oproep - Outgoing video call · %1$s + Uitgaande video-oproep · %1$s - You started a video call + Jy het \'n video-oproep begin - You started a video call · %1$s + Jy het \'n video-oproep begin · %1$s - %1$s started a video call + %1$s het \'n video-oproep begin - %1$s started a video call · %2$s + %1$s het \'n video-oproep begin · %2$s Jy - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s en %3$d ander is in die groepoproep · %4$s + %1$s, %2$s en %3$d ander is in die groepoproep · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s en %3$d ander is in die groepoproep + %1$s, %2$s en %3$d ander is in die groepoproep @@ -2734,7 +2734,7 @@ Vind meer uit Sluit aan by oproep - Call back + Bel terug Keer terug na oproep Oproep is vol Nooi vriende uit @@ -4932,7 +4932,7 @@ Voeg ’n boodskap toe Voeg ’n antwoord toe Stuur na - View once media + Eenkeerkyk-media Een of meer items was te groot Een of meer items was ongeldig Te veel items gekies @@ -6744,9 +6744,9 @@ - Restoring backup… + Herstel tans rugsteun… - Downloading backup data… + Laai tans rugsteundata af… diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index b65cd08e3d..d8f89a0d42 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1778,62 +1778,62 @@ - %1$s is in the call · %2$s + %1$s موجود في المكالمة · %2$s - You are in the call · %1$s + أنت موجود في المكالمة · %1$s - %1$s and %2$s are in the call · %3$s + %1$s و%2$s موجودان في المكالمة · %3$s - %1$s is in the call + %1$s موجود في المكالمة - You are in the call + أنت موجود في المكالمة - %1$s and %2$s are in the call + %1$s و%2$s موجودان في المكالمة - The video call has ended + انتهت مكالمة الفيديو - The video call has ended · %1$s + انتهت مكالمة الفيديو · %1$s - Missed video call + مكالمة فيديو فائتة - Missed video call · %1$s + مكالمة بالصورة فائتة · %1$s - Incoming video call + مكالمة فيديو واردة - Incoming video call · %1$s + مكالمة فيديو واردة · %1$s - Outgoing video call + مكالمة فيديو صادرة - Outgoing video call · %1$s + مكالمة فيديو صادرة · %1$s - You started a video call + بدأت مكالمة فيديو - You started a video call · %1$s + بدأت مكالمة فيديو · %1$s - %1$s started a video call + %1$s بدأ مكالمة فيديو - %1$s started a video call · %2$s + %1$s بدأ مكالمة فيديو · %2$s أنت - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %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 - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + الأعضاء %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 آخرون موجودون في المكالمة الجماعية @@ -3082,7 +3082,7 @@ لمعرفة المزيد الانضمام للمكالمة - Call back + إعادة الاتصال العودة للمكالمة المكالمة ممتلئة دعوة الأصدقاء @@ -5400,7 +5400,7 @@ إضافة نص إضافة رد إرسال إلى - View once media + وسائط للمُشاهدة مرة واحدة عنصر واحد أو أكثر كان كبيرًا جدًا عنصر واحد أو أكثر غير صالح تم تحديد عدد كبير جدًا من العناصر @@ -7328,9 +7328,9 @@ - Restoring backup… + تجري استعادة النسخة الاحتياطية… - Downloading backup data… + يَجري تنزيل البيانات الاحتياطية… diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index ca9166c594..ead4bc2837 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -1582,15 +1582,15 @@ The video call has ended · %1$s - Missed video call + Buraxılmış video zəng - Missed video call · %1$s + Cavabsız görüntülü zəng · %1$s - Incoming video call + Gələn video zəng Incoming video call · %1$s - Outgoing video call + Gedən video zəng Outgoing video call · %1$s @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s və digər %3$d nəfər qrup zəngindədir · %4$s + %1$s, %2$s və digər %3$d nəfər qrup zəngindədir · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s və digər %3$d nəfər qrup zəngindədir + %1$s, %2$s və digər %3$d nəfər qrup zəngindədir @@ -2734,7 +2734,7 @@ Daha ətraflı Zəngə qoşul - Call back + Geri zəng Zəngə qayıt Zəng dolub Dostlarını dəvət et @@ -4932,7 +4932,7 @@ Bir mesaj əlavə et Bir cavab əlavə et Göndər - View once media + View Once Media Bir və ya daha çox element çox böyük idi Bir və ya daha çox element etibarsız idi Həddindən çox element seçildi diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 65f40103a9..78c678fa06 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s е в разговора · %2$s - You are in the call · %1$s + Вие сте в разговора · %1$s - %1$s and %2$s are in the call · %3$s + %1$s и %2$s са в разговора · %3$s - %1$s is in the call + %1$s е в разговора - You are in the call + Вие сте в разговора - %1$s and %2$s are in the call + %1$s и %2$s са в разговора - The video call has ended + Видео обаждането приключи - The video call has ended · %1$s + Видео обаждането приключи · %1$s - Missed video call + Пропуснато видео повикване - Missed video call · %1$s + Пропуснато видео обаждане преди · %1$s - Incoming video call + Входящо видео повикване - Incoming video call · %1$s + Входящо видео обаждане · %1$s - Outgoing video call + Изходящо видео повикване - Outgoing video call · %1$s + Изходящо видео обаждане · %1$s - You started a video call + Започнахте видео обаждане - You started a video call · %1$s + Започнахте видео обаждане · %1$s - %1$s started a video call + %1$s започна видео обаждане - %1$s started a video call · %2$s + %1$s започна видео обаждане · %2$s Вие - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s и %3$d друг са в груповия разговор · %4$s + %1$s, %2$s и %3$d други са в груповия разговор · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s и %3$d друг са в груповия разговор + %1$s, %2$s и %3$d други са в груповия разговор @@ -2734,7 +2734,7 @@ Научете повече Присъединете се към разговор - Call back + Обратно повикване Върнете се към разговора Обаждането е пълно Покани приятели @@ -4932,7 +4932,7 @@ Добави съобщение Добавяне на отговор Изпращане до - View once media + Мултимедия за еднократно гледане Един или повече елементи бяха твърде големи Един или повече елементи бяха невалидни Избрани са твърде много елементи @@ -6744,9 +6744,9 @@ - Restoring backup… + Възстановяване на резервно копие… - Downloading backup data… + Изтегляне на данни от резервно копие… diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index d393ea0d68..042ffe47b4 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s কলে আছেন · %2$s - You are in the call · %1$s + আপনি কলে আছেন · %1$s - %1$s and %2$s are in the call · %3$s + %1$s এবং %2$s কলে আছেন · %3$s - %1$s is in the call + %1$s কলে আছেন - You are in the call + আপনি কলে আছেন - %1$s and %2$s are in the call + %1$s ও %2$s কলে আছেন - The video call has ended + ভিডিও কল শেষ হয়েছে - The video call has ended · %1$s + ভিডিও কল শেষ হয়েছে · %1$s - Missed video call + মিসড ভিডিও কল - Missed video call · %1$s + ভিডিও কল মিস করেছেন। %1$s - Incoming video call + ভিডিও কল আসছে - Incoming video call · %1$s + ভিডিও কল এসেছে · %1$s - Outgoing video call + ভিডিও কল যাচ্ছে - Outgoing video call · %1$s + ভিডিও কল যাচ্ছে · %1$s - You started a video call + আপনি একটি ভিডিও কল শুরু করেছেন - You started a video call · %1$s + আপনি একটি ভিডিও কল শুরু করেছেন · %1$s - %1$s started a video call + %1$s একটি ভিডিও কল শুরু করেছেন - %1$s started a video call · %2$s + %1$s একটি ভিডিও কল শুরু করেছেন · %2$s আপনি - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, এবং %3$d অন্যান্যরা গ্রুপ কল এ আছেন। %4$s + %1$s, %2$s, এবং%3$d অন্যান্যরা গ্রুপ কল এ আছেন। %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, ও %3$d জন গ্রুপ কলটিতে আছে + %1$s, %2$s ও আরও %3$d জন গ্রুপ কলটিতে আছেন @@ -2734,7 +2734,7 @@ আরও জানুন কলে যোগ দিন - Call back + ফিরতি কল করুন কলে ফেরত যান কলটিতে জায়গা নেই বন্ধুদের আমন্ত্রণ @@ -4932,7 +4932,7 @@ একটি বার্তা যোগ করুন একটি জবাব যোগ করুন পাঠান - View once media + একবার-দেখার উপযোগী মিডিয়া এক বা একাধিক আইটেম খুব বড় ছিল৷ এক বা একাধিক আইটেম বাতিল ছিল অনেকগুলি আইটেম নির্বাচন করা হয়েছে @@ -6744,9 +6744,9 @@ - Restoring backup… + ব্যাকআপ পুনর্বহাল করা হচ্ছে… - Downloading backup data… + ব্যাকআপ ডেটা ডাউনলোড করা হচ্ছে… diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 0c0b216a0b..a7ddf84c45 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s je u pozivu · %2$s - You are in the call · %1$s + Vi ste u pozivu · %1$s - %1$s and %2$s are in the call · %3$s + %1$s i %2$s su u pozivu · %3$s - %1$s is in the call + %1$s je u pozivu - You are in the call + Vi ste u pozivu - %1$s and %2$s are in the call + %1$s i %2$s učestvuju u ovom pozivu - The video call has ended + Video poziv je završen - The video call has ended · %1$s + Video poziv je završen · %1$s - Missed video call + Propušteni videopoziv - Missed video call · %1$s + Propušten videopoziv · %1$s - Incoming video call + Dolazni video poziv - Incoming video call · %1$s + Dolazni video poziv · %1$s - Outgoing video call + Odlazni video poziv - Outgoing video call · %1$s + Odlazni video poziv · %1$s - You started a video call + Pokrenuli ste video poziv - You started a video call · %1$s + Pokrenuli ste video poziv · %1$s - %1$s started a video call + %1$s je započeo/la video poziv - %1$s started a video call · %2$s + %1$s je započeo/la video poziv · %2$s Vi - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s i %3$d druga osoba učestvuje u grupnom pozivu · %4$s + %1$s, %2$s i %3$d druge osobe učestvuju u grupnom pozivu · %4$s + %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu · %4$s + %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s i %3$d druga osoba učestvuje u grupnom pozivu + %1$s, %2$s i %3$d druge osobe učestvuju u grupnom pozivu + %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu + %1$s, %2$s i %3$d drugih osoba učestvuje u grupnom pozivu @@ -2908,7 +2908,7 @@ Saznajte više Pridruži se pozivu - Call back + Povratni poziv Vrati se pozivu Poziv je popunjen Pozovi prijatelje @@ -5166,7 +5166,7 @@ Napiši poruku Odgovor Pošalji za - View once media + Mediji koji se mogu pogledati jednom Jedna ili više datoteka su prevelike Jedna ili više datoteka nisu validne Označeno je previše datoteka @@ -7036,9 +7036,9 @@ - Restoring backup… + Vraćanje sigurnosne kopije… - Downloading backup data… + Preuzimanje sigurnosne kopije podataka… diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 2827bfab87..0da94aba50 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s és a la trucada · %2$s - You are in the call · %1$s + Ets a la trucada · %1$s - %1$s and %2$s are in the call · %3$s + %1$s i %2$s són a la trucada · %3$s - %1$s is in the call + %1$s és a la trucada - You are in the call + Ets a la trucada - %1$s and %2$s are in the call + %1$s i %2$s són a la trucada - The video call has ended + La videotrucada ha finalitzat - The video call has ended · %1$s + La videotrucada ha finalitzat · %1$s - Missed video call + Videotrucada perduda - Missed video call · %1$s + Trucada de vídeo perduda · %1$s - Incoming video call + Videotrucada entrant - Incoming video call · %1$s + Videotrucada entrant · %1$s - Outgoing video call + Videotrucada sortint - Outgoing video call · %1$s + Videotrucada realitzada · %1$s - You started a video call + Has iniciat una videotrucada - You started a video call · %1$s + Has iniciat una videotrucada · %1$s - %1$s started a video call + %1$s ha iniciat una videotrucada - %1$s started a video call · %2$s + %1$s ha iniciat una videotrucada · %2$s Vós - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s i %3$d més són a la trucada de grup · %4$s + %1$s, %2$s i %3$d més són a la trucada de grup · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s i %3$d més són a la trucada de grup. + %1$s, %2$s i %3$d més són a la trucada de grup. @@ -2734,7 +2734,7 @@ Més informació Afegeix-me a la trucada - Call back + Torna la trucada Torna a la trucada La trucada és plena Convideu-hi amistats @@ -4932,7 +4932,7 @@ Afegiu-hi un missatge Afegiu-hi una resposta Enviar a - View once media + Contingut d\'una sola visualització Un o més elements eren massa grossos. Un o més elements no eren vàlids. Massa elements seleccionats @@ -6744,9 +6744,9 @@ - Restoring backup… + Restaurant la còpia de seguretat… - Downloading backup data… + S\'està descarregant la còpia de seguretat… diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 3d04178ef8..9d23caa65d 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s se účastní hovoru · %2$s - You are in the call · %1$s + Účastníte se hovoru · %1$s - %1$s and %2$s are in the call · %3$s + %1$s a %2$s se účastní hovoru · %3$s - %1$s is in the call + %1$s se účastní hovoru - You are in the call + Účastníte se hovoru - %1$s and %2$s are in the call + %1$s a %2$s se účastní hovoru - The video call has ended + Videohovor skončil - The video call has ended · %1$s + Videohovor skončil · %1$s - Missed video call + Zmeškaný videohovor - Missed video call · %1$s + Zmeškaný videohovor · %1$s - Incoming video call + Příchozí videohovor - Incoming video call · %1$s + Příchozí videohovor · %1$s - Outgoing video call + Odchozí videohovor - Outgoing video call · %1$s + Odchozí videohovor · %1$s - You started a video call + Zahájili jste videohovor - You started a video call · %1$s + Zahájili jste videohovor · %1$s - %1$s started a video call + Uživatel %1$s zahájil videohovor - %1$s started a video call · %2$s + Uživatel %1$s zahájil videohovor · %2$s Vy - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s a %3$d další se účastní skupinového hovoru · %4$s + %1$s, %2$s a %3$d další se účastní skupinového hovoru · %4$s + %1$s, %2$s a %3$d dalších se účastní skupinového hovoru · %4$s + %1$s, %2$s a %3$d dalších se účastní skupinového hovoru · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s a %3$d další se účastní skupinového hovoru + %1$s, %2$s a %3$d dalších se účastní skupinového hovoru + %1$s, %2$s a %3$d dalších se účastní skupinového hovoru + %1$s, %2$s a %3$d dalších se účastní skupinového hovoru @@ -2908,7 +2908,7 @@ Zjistit více Připojit se k hovoru - Call back + Zavolat zpět Vrátit se k hovoru Hovor je plný Pozvat přátele @@ -5166,7 +5166,7 @@ Přidat zprávu Přidat odpověď Odeslat uživateli - View once media + Média, která lze zobrazit jen jednou Jeden nebo více souborů je moc velkých Jeden nebo více souborů je neplatných Příliš mnoho vybraných položek @@ -7036,9 +7036,9 @@ - Restoring backup… + Obnovování zálohy… - Downloading backup data… + Stahování záložních dat… diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 8306b72af6..9c6b0c596d 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1582,15 +1582,15 @@ The video call has ended · %1$s - Missed video call + Ubesvaret videoopkald - Missed video call · %1$s + Ubesvaret videoopkald · %1$s - Incoming video call + Indgående videoopkald Incoming video call · %1$s - Outgoing video call + Udgående videoopkald Outgoing video call · %1$s @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, og %3$d andre deltager i gruppesamtalen · %4$s + %1$s, %2$s, og %3$d andre er med i gruppeopkaldet · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, og %3$d andre deltager i gruppesamtalen + %1$s, %2$s, og %3$d andre er med i gruppeopkaldet @@ -2734,7 +2734,7 @@ Læs mere Deltag i opkald - Call back + Ring tilbage Tilbage til opkald Opkald er fuld Inviter venner @@ -4932,7 +4932,7 @@ Tilføj en besked Tilføj et svar Send til - View once media + Medier, som kan ses én gang Et eller flere emner var for store Et eller flere emner var ugyldige Der er valgt for mange emner diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c682bb5197..acabc274be 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s nimmt am Anruf teil · %2$s - You are in the call · %1$s + Du nimmst am Anruf teil · %1$s - %1$s and %2$s are in the call · %3$s + %1$s und %2$s nehmen am Anruf teil · %3$s - %1$s is in the call + %1$s nimmt am Anruf teil - You are in the call + Du nimmst am Anruf teil - %1$s and %2$s are in the call + %1$s und %2$s nehmen am Anruf teil - The video call has ended + Der Videoanruf wurde beendet - The video call has ended · %1$s + Der Videoanruf wurde beendet · %1$s - Missed video call + Verpasster Videoanruf - Missed video call · %1$s + Verpasster Videoanruf · %1$s - Incoming video call + Eingehender Videoanruf - Incoming video call · %1$s + Eingehender Videoanruf · %1$s - Outgoing video call + Ausgehender Videoanruf - Outgoing video call · %1$s + Ausgehender Videoanruf · %1$s - You started a video call + Du hast einen Videoanruf gestartet - You started a video call · %1$s + Du hast einen Videoanruf gestartet · %1$s - %1$s started a video call + %1$s hat einen Videoanruf gestartet - %1$s started a video call · %2$s + %1$s hat einen Videoanruf gestartet · %2$s Du - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s und %3$d weitere Person nehmen am Gruppenanruf teil · %4$s + %1$s, %2$s und %3$d weitere Personen nehmen am Gruppenanruf teil · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s und %3$d weitere Person nehmen am Gruppenanruf teil + %1$s, %2$s und %3$d weitere Personen nehmen am Gruppenanruf teil @@ -2734,7 +2734,7 @@ Mehr erfahren Anruf beitreten - Call back + Rückruf Zurück zum Anruf Anruf ist voll Freunde einladen @@ -4932,7 +4932,7 @@ Füg eine Nachricht hinzu Antwort hinzufügen Senden an - View once media + Medien zur Einmalansicht Mindestens ein Element war zu groß Mindestens ein Element war ungültig Zu viele Elemente ausgewählt @@ -6744,9 +6744,9 @@ - Restoring backup… + Datensicherung wiederherstellen… - Downloading backup data… + Sicherungsdaten werden heruntergeladen… diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 17e186f78a..02fcab0d3f 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + Ο/Η %1$s είναι στην κλήση · %2$s - You are in the call · %1$s + Είσαι στην κλήση · %1$s - %1$s and %2$s are in the call · %3$s + Οι %1$s και %2$s είναι στην κλήση · %3$s - %1$s is in the call + Ο/Η %1$s είναι στην κλήση - You are in the call + Είσαι στην κλήση - %1$s and %2$s are in the call + Οι %1$s και %2$s είναι στην κλήση - The video call has ended + Η βιντεοκλήση τελείωσε - The video call has ended · %1$s + Η βιντεοκλήση τελείωσε · %1$s - Missed video call + Αναπάντητη βιντεοκλήση - Missed video call · %1$s + Αναπάντητη βιντεοκλήση · %1$s - Incoming video call + Εισερχόμενη βιντεοκλήση - Incoming video call · %1$s + Εισερχόμενη βιντεοκλήση · %1$s - Outgoing video call + Εξερχόμενη βιντεοκλήση - Outgoing video call · %1$s + Εξερχόμενη βιντεοκλήση · %1$s - You started a video call + Ξεκίνησες μια βιντεοκλήση - You started a video call · %1$s + Ξεκίνησες μια βιντεοκλήση · %1$s - %1$s started a video call + Ο/Η %1$s ξεκίνησε μια βιντεοκλήση - %1$s started a video call · %2$s + Ο/Η %1$s ξεκίνησε μια βιντεοκλήση · %2$s Εσύ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + Οι %1$s, %2$s και %3$d ακόμα είναι στην κλήση · %4$s + Οι %1$s, %2$s και %3$d ακόμα είναι στην ομαδική κλήση · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + Οι %1$s, %2$s και %3$d ακόμα είναι στην ομαδική κλήση + Οι %1$s, %2$s και %3$d ακόμα είναι στην ομαδική κλήση @@ -2734,7 +2734,7 @@ Μάθε περισσότερα Είσοδος στην κλήση - Call back + Επανάκληση Επιστροφή στην κλήση Η κλήση είναι γεμάτη Πρόσκληση φίλων @@ -4932,7 +4932,7 @@ Προσθήκη μηνύματος Προσθήκη απάντησης Αποστολή σε - View once media + Πολυμέσα μιας μόνο προβολής Ένα ή περισσότερα αντικείμενα ήταν πολύ μεγάλα Ένα ή περισσότερα αντικείμενα δεν ήταν έγκυρα Επιλέχθηκαν πάρα πολλά αντικείμενα @@ -6744,9 +6744,9 @@ - Restoring backup… + Επαναφορά αντίγραφου ασφαλείας… - Downloading backup data… + Λήψη δεδομένων αντιγράφου ασφαλείας… diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2a0faff43c..35589f0f3d 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s participa en la llamada · %2$s - You are in the call · %1$s + Participas en la llamada · %1$s - %1$s and %2$s are in the call · %3$s + %1$s y %2$s participan en la llamada · %3$s - %1$s is in the call + %1$s participa en la llamada - You are in the call + Participas en la llamada - %1$s and %2$s are in the call + %1$s y %2$s participan en la llamada - The video call has ended + La videollamada ha finalizado - The video call has ended · %1$s + La videollamada ha finalizado · %1$s - Missed video call + Videollamada perdida - Missed video call · %1$s + Videollamada perdida · %1$s - Incoming video call + Videollamada entrante - Incoming video call · %1$s + Videollamada entrante · %1$s - Outgoing video call + Videollamada realizada - Outgoing video call · %1$s + Videollamada realizada · %1$s - You started a video call + Iniciaste una videollamada - You started a video call · %1$s + Iniciaste una videollamada · %1$s - %1$s started a video call + %1$s inició una videollamada - %1$s started a video call · %2$s + %1$s inició una videollamada · %2$s - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s y %3$d persona más participan en la llamada · %4$s + %1$s, %2$s y %3$d personas más participan en la llamada · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s y %3$d persona más participan en la llamada + %1$s, %2$s y %3$d personas más participan en la llamada @@ -2734,7 +2734,7 @@ Saber más Unirse a la llamada - Call back + Devolver llamada Volver a la llamada La llamada está llena Invitar personas @@ -4932,7 +4932,7 @@ Añadir mensaje Añadir respuesta Enviar a - View once media + Contenido para ver solo una vez Uno o más elementos son demasiado grandes Uno o más elementos no son válidos Demasiados elementos seleccionados @@ -6744,9 +6744,9 @@ - Restoring backup… + Recuperando copia de seguridad… - Downloading backup data… + Descargando copia de seguridad… diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index a6443e1d33..c8f579ddeb 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s on kõnes · %2$s - You are in the call · %1$s + Sa oled kõnes · %1$s - %1$s and %2$s are in the call · %3$s + %1$s ja %2$s on kõnes · %3$s - %1$s is in the call + %1$s on kõnes - You are in the call + Sa oled kõnes - %1$s and %2$s are in the call + %1$s ja %2$s on kõnes - The video call has ended + Videokõne on lõppenud - The video call has ended · %1$s + Videokõne on lõppenud · %1$s - Missed video call + Vastamata videokõne - Missed video call · %1$s + Vastamata videokõne · %1$s - Incoming video call + Sissetulev videokõne - Incoming video call · %1$s + Sissetulev videokõne · %1$s - Outgoing video call + Väljuv videokõne - Outgoing video call · %1$s + Väljuv videokõne · %1$s - You started a video call + Sa alustasid videokõnet - You started a video call · %1$s + Sa alustasid videokõnet · %1$s - %1$s started a video call + %1$s alustas videokõnet - %1$s started a video call · %2$s + %1$s alustas videokõnet · %2$s Sina - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s ja %3$d teine on grupikõnes · %4$s + %1$s, %2$s ja %3$d teist on grupikõnes · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s ja %3$d teine on grupikõnes + %1$s, %2$s ja %3$d teist on grupikõnes @@ -2734,7 +2734,7 @@ Rohkem infot Liitu kõnega - Call back + Helista tagasi Pöördu kõnesse tagasi Kõne on täis Kutsu sõpru @@ -4932,7 +4932,7 @@ Lisa sõnum Lisa vastus Vali saaja - View once media + Ühekordselt vaadatav meediasisu Üks või mitu üksust olid liiga suured Üks või mitu üksust olid kehtetud Liiga palju elemente valitud @@ -6744,9 +6744,9 @@ - Restoring backup… + Varukoopia taastamine … - Downloading backup data… + Varukoopia andmete allalaadimine … diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 7e5645604f..1d1fd0e929 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s deian dago · %2$s - You are in the call · %1$s + Deian zaude · %1$s - %1$s and %2$s are in the call · %3$s + %1$s eta %2$s deian daude · %3$s - %1$s is in the call + %1$s deian dago - You are in the call + Deian zaude - %1$s and %2$s are in the call + %1$s eta %2$s deian daude - The video call has ended + Bideodeia amaitu da - The video call has ended · %1$s + Bideodeia amaitu da · %1$s - Missed video call + Bideodei galdua - Missed video call · %1$s + Bideodei galdua · %1$s - Incoming video call + Sarrerako bideodeia - Incoming video call · %1$s + Sarrerako bideodeia · %1$s - Outgoing video call + Irteerako bideodeia - Outgoing video call · %1$s + Irteerako bideodeia · %1$s - You started a video call + Bideodeia hasi duzu - You started a video call · %1$s + Bideodeia hasi duzu · %1$s - %1$s started a video call + %1$s erabiltzaileak bideodeia hasi du - %1$s started a video call · %2$s + %1$s erabiltzaileak bideodeia hasi du · %2$s Zu - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s eta beste %3$d talde deian daude %4$s + %1$s, %2$s eta beste %3$d talde-deian daude %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s eta beste %3$d talde-deian daude + %1$s, %2$s eta beste %3$d talde-deian daude @@ -2734,7 +2734,7 @@ Gehiago jakin Deian sartu - Call back + Itzuli deia Deira itzuli Deia beteta dago Gonbidatu lagunak @@ -4932,7 +4932,7 @@ Gehitu mezu bat Gehitu erantzun bat Bidali honi: - View once media + Behin ikusteko multimedia-elementua Elementu bat edo gehiago handiegiak dira Elementu bat edo gehiago handiegiak dira Elementu gehiegi hautatu dira @@ -6744,9 +6744,9 @@ - Restoring backup… + Babeskopiak leheneratzen… - Downloading backup data… + Babeskopietako datuak deskargatzen… diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 73323554ac..f0fd1303cd 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -1582,15 +1582,15 @@ The video call has ended · %1$s - Missed video call + تماس تصویری ازدست‌رفته - Missed video call · %1$s + تماس تصویری از دست رفته · %1$s - Incoming video call + تماس تصویری دریافتی Incoming video call · %1$s - Outgoing video call + تماس تصویری خروجی Outgoing video call · %1$s @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند · %4$s + %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند + %1$s، %2$s و %3$d نفر دیگر در تماس گروهی هستند @@ -2734,7 +2734,7 @@ بیشتر یاد بگیرید پیوستن به تماس - Call back + تماس بازگشت به تماس ظرفیت تماس تکمیل است دعوت دوستان @@ -4932,7 +4932,7 @@ افزودن یک پیام یک پاسخ اضافه کنید ارسال به - View once media + رسانه با قابلیت یک‌بار مشاهده یک یا چند مورد بسیار بزرگ بودند یک یا چند مورد نامعتبر بودند موارد بسیار زیادی انتخاب شده است diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 9f28e1b4d1..2fc8a2e4ed 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s on puhelussa · %2$s - You are in the call · %1$s + Olet puhelussa · %1$s - %1$s and %2$s are in the call · %3$s + %1$s ja %2$s ovat puhelussa · %3$s - %1$s is in the call + %1$s on puhelussa - You are in the call + Olet puhelussa - %1$s and %2$s are in the call + %1$s ja %2$s ovat puhelussa - The video call has ended + Videopuhelu on päättynyt - The video call has ended · %1$s + Videopuhelu on päättynyt · %1$s - Missed video call + Vastaamaton videopuhelu - Missed video call · %1$s + Vastaamaton videopuhelu · %1$s - Incoming video call + Saapuva videopuhelu - Incoming video call · %1$s + Saapuva videopuhelu · %1$s - Outgoing video call + Lähtevä videopuhelu - Outgoing video call · %1$s + Soitettu videopuhelu · %1$s - You started a video call + Aloitit videopuhelun - You started a video call · %1$s + Aloitit videopuhelun · %1$s - %1$s started a video call + %1$s aloitti videopuhelun - %1$s started a video call · %2$s + %1$s aloitti videopuhelun · %2$s Sinä - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s ja %3$d muu ovat ryhmäpuhelussa · %4$s + %1$s, %2$s ja %3$d muuta ovat ryhmäpuhelussa · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s ja %3$d muu ovat ryhmäpuhelussa + %1$s, %2$s ja %3$d muuta ovat ryhmäpuhelussa @@ -2734,7 +2734,7 @@ Lue lisää Liity puheluun - Call back + Soita takaisin Palaa puheluun Puhelu on täynnä Kutsu ystäviä @@ -4932,7 +4932,7 @@ Lisää viesti Lisää vastaus Lähetä henkilölle - View once media + Kerran katsottava media Yksi tai useampi valinnoista olivat liian suuria Yksi tai useampi valinnoista olivat virheellisiä Liikaa valintoja @@ -6744,9 +6744,9 @@ - Restoring backup… + Palautetaan varmuuskopiota… - Downloading backup data… + Ladataan varmuuskopion tietoja… diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ac7563e0d0..f294cdba39 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1295,8 +1295,8 @@ Me le rappeler plus tard - Confirmer votre NIP Signal - Nous vous demanderons de temps en temps de confirmer votre NIP afin que vous vous en souveniez. + Confirmer votre code PIN Signal + Nous vous demanderons occasionnellement de confirmer votre code PIN pour vous aider à le mémoriser. Confirmer le PIN Commençons Nouveau groupe @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s a rejoint l’appel · %2$s - You are in the call · %1$s + Vous avez rejoint l’appel · %1$s - %1$s and %2$s are in the call · %3$s + %1$s et %2$s ont rejoint l’appel · %3$s - %1$s is in the call + %1$s a rejoint l’appel - You are in the call + Vous avez rejoint l’appel - %1$s and %2$s are in the call + %1$s et %2$s ont rejoint l’appel - The video call has ended + L’appel vidéo est terminé - The video call has ended · %1$s + L’appel vidéo est terminé · %1$s - Missed video call + Appel vidéo manqué - Missed video call · %1$s + Appel vidéo manqué – %1$s - Incoming video call + Appel vidéo entrant - Incoming video call · %1$s + Appel vidéo entrant · %1$s - Outgoing video call + Appel vidéo sortant - Outgoing video call · %1$s + Appel vidéo sortant · %1$s - You started a video call + Vous avez démarré un appel vidéo - You started a video call · %1$s + Vous avez démarré un appel vidéo · %1$s - %1$s started a video call + %1$s a démarré un appel vidéo - %1$s started a video call · %2$s + %1$s a démarré un appel vidéo · %2$s Vous - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s et %3$d autre personne participent à l’appel de groupe · %4$s + %1$s, %2$s et %3$d autres personnes participent à l’appel de groupe · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s et %3$d autre personne participent à l’appel de groupe + %1$s, %2$s et %3$d autres personnes participent à l’appel de groupe @@ -1728,11 +1728,11 @@ La version des services Google Play installée ne fonctionne pas correctement. Veuillez réinstaller les Services Google Play et réessayer. - Le NIP est erroné - Ignorer la saisie du NIP ? + Code PIN incorrect. + Ne pas saisir de code PIN ? Besoin d’aide ? - Votre NIP est un code à %1$d chiffres ou plus que vous avez créé et qui peut être numérique ou alphanumérique. Si vous avez oublié votre NIP, vous pouvez en créer un nouveau. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. - Si vous ne vous souvenez pas de votre NIP, vous pouvez en créer un nouveau. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. + Votre code PIN est un code numérique ou alphanumérique que vous avez créé et qui comporte %1$d caractères ou plus.\n\n Si vous l’avez oublié, vous pouvez en créer un nouveau. Vous pourrez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. + Si vous ne vous souvenez pas de votre code PIN, vous pouvez en créer un nouveau. Vous pourrez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. Créer un nouveau PIN Contacter l’assistance Annuler @@ -1741,11 +1741,11 @@ Il vous reste %1$d essai. Si vous épuisez le nombre limite d’essais, vous pouvez créer un nouveau PIN. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. Il vous reste %1$d essais. Si vous épuisez le nombre limite d’essais, vous pouvez créer un nouveau PIN. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. - Inscription à Signal – Besoin d’aide avec le NIP sur Android + Inscription à Signal – Besoin d’aide : code PIN sous Android - Créer votre NIP - Vous avez épuisé tous vos essais de NIP, mais vous pouvez encore accéder à votre compte Signal en créant un nouveau NIP. Afin de protéger vos données personnelles et votre sécurité, votre compte sera restauré sans informations de profil enregistrées ni préférences. + Créer un code PIN + Vous avez dépassé le nombre de tentatives autorisées. Pour accéder à votre compte Signal, vous devez créer un nouveau code PIN. Afin de protéger vos données personnelles et votre sécurité, votre compte sera restauré sans informations de profil, ni paramètres enregistrés. Créer un nouveau PIN @@ -2132,7 +2132,7 @@ Activer le blocage d’inscription ? Désactiver le blocage d’inscription ? - Si vous oubliez votre NIP Signal lors d’une nouvelle inscription à Signal, vous ne pourrez pas accéder à votre compte pendant sept jours. + Si vous ne vous souvenez plus de votre code PIN au moment de vous réinscrire à Signal, vous ne pourrez pas accéder à votre compte pendant sept jours. Activer Désactiver @@ -2174,12 +2174,12 @@ Plus - Le NIP a été confirmé avec succès. Nous vous le rappellerons plus tard. - Le NIP a été confirmé avec succès. Nous vous le rappellerons demain. - Le NIP a été confirmé avec succès. Nous vous le rappellerons dans quelques jours. - Le NIP a été confirmé avec succès. Nous vous le rappellerons dans une semaine. - Le NIP a été confirmé avec succès. Nous vous le rappellerons dans quelques semaines. - Le NIP a été confirmé avec succès. Nous vous le rappellerons dans un mois. + Vous avez bien confirmé votre code PIN. Nous vous le redemanderons plus tard. + Vous avez bien confirmé votre code•PIN. Nous vous le redemanderons demain. + Vous avez bien confirmé votre code•PIN. Nous vous le redemanderons dans quelques jours. + Vous avez bien confirmé votre code•PIN. Nous vous le redemanderons dans une semaine. + Vous avez bien confirmé votre code•PIN. Nous vous le redemanderons dans quelques semaines. + Vous avez bien confirmé votre code•PIN. Nous vous le redemanderons dans un mois. Image @@ -2734,7 +2734,7 @@ En savoir plus Me joindre à l’appel - Call back + Rappeler Revenir à l’appel L’appel est complet Inviter des amis @@ -3220,9 +3220,9 @@ Graphique illustrant l’endroit où l’icône de l’application de remplacement sera visible. Désactiver le code PIN - Activer le NIP + Activer le code PIN La désactivation du code PIN entraînera la perte de toutes vos données lors de votre réinscription sur Signal, à moins de procéder à une sauvegarde et une restauration manuelles. Vous ne pouvez pas activer le blocage d’inscription si le code PIN est désactivé. - Les NIP font en sorte que les informations sont enregistrées chiffrées dans Signal afin que vous seul puissiez y accéder. Votre profil, vos paramètres et vos contacts seront restaurés quand vous réinstallerez Signal. Vous n’aurez pas besoin de votre NIP pour ouvrir l’appli. + Avec un code PIN, les informations stockées dans Signal sont chiffrées, et vous seul y avez accès. Votre profil, vos contacts et vos paramètres sont restaurés si vous réinstallez Signal. Vous n’avez pas besoin du code PIN pour ouvrir l’appli. Valeur par défaut du système Langue Appels et messages Signal @@ -3732,9 +3732,9 @@ Suivant - Créer un NIP alphanumérique + Créer un code PIN alphanumérique - Créer un NIP numérique + Créer un code PIN numérique @@ -3746,42 +3746,42 @@ Votre PIN doit comporter au moins %1$d chiffre Votre PIN doit comporter au moins %1$d chiffres - Créer un nouveau NIP + Créer un nouveau code PIN Vous pouvez changer de code PIN tant que cet appareil est enregistré. - Créer votre NIP + Créer un code PIN Les PIN peuvent vous aider à restaurer votre compte et garder vos données chiffrées avec Signal. - Choisissez une NIP plus robuste + Choisissez un code PIN plus complexe - Les NIP ne correspondent pas. Veuillez réessayez. + Les codes PIN ne correspondent pas. Veuillez réessayez. Saisissez à nouveau le PIN que vous venez de créer. - Confirmez votre NIP - Échec de création du NIP - Votre NIP n’a pas été enregistré. Nous vous inviterons à en créer un plus tard. - Le NIP a été créé. + Confirmez votre code PIN + Impossible de créer le code PIN. + Votre code PIN n’a pas été enregistré. Nous vous inviterons à en créer un plus tard. + Code PIN créé. Saisissez de nouveau votre PIN. - Création du NIP… + Création du code PIN… - Nous présentons les NIP - Les NIP font en sorte que les informations sont enregistrées chiffrées dans Signal afin que vous seul puissiez y accéder. Votre profil, vos paramètres et vos contacts seront restaurés quand vous réinstallerez Signal. Vous n’aurez pas besoin de votre NIP pour ouvrir l’appli. + Lancement des codes PIN + Avec un code PIN, les informations stockées dans Signal sont chiffrées, et vous seul y avez accès. Votre profil, vos contacts et vos paramètres sont restaurés si vous réinstallez Signal. Vous n’avez pas besoin du code PIN pour ouvrir l’appli. En savoir plus Blocage d’inscription = PIN Le blocage d’inscription fait peau neuve : il dépend désormais du code PIN et gagne en efficacité. Mettez-le à jour. - Mettre le NIP à jour - Créer votre NIP + Mettre le code PIN à jour + Créer un code PIN En savoir plus sur les codes PIN Désactiver le code PIN - Saisissez votre NIP Signal - Pour vous aider à mémoriser votre NIP, nous vous demanderons de le saisir régulièrement. Nous vous le demanderons de moins en moins souvent. + Saisissez votre code PIN Signal + Pour vous aider à mémoriser votre code PIN, nous vous demanderons de le saisir régulièrement. Nous vous le demanderons moins souvent au fil du temps. Ignorer Envoyer - Avez-vous oublié votre NIP ? - Le NIP est erroné. Veuillez réessayer. + Code PIN oublié ? + Code PIN incorrect. Veuillez réessayer. Le compte est verrouillé @@ -3792,17 +3792,17 @@ Saisissez votre PIN - Saisissez le NIP que vous avez créé pour votre compte. Il est différent de votre code de confirmation reçu par texto. + Saisissez le code PIN que vous avez créé pour votre compte. Il ne s’agit pas du code de confirmation reçu par SMS. Saisissez le PIN choisi pour votre compte. Changer de clavier - Le NIP est erroné. Veuillez réessayer. - Avez-vous oublié votre NIP ? - Le NIP est erroné - Avez-vous oublié votre NIP ? + Code PIN incorrect. Veuillez réessayer. + Code PIN oublié ? + Code PIN incorrect. + Code•PIN oublié ? Il vous reste peu d’essais - Inscription à Signal – Besoin d’aide avec le NIP sur Android (NIP v2) + Inscription à Signal – Besoin d’aide : code PIN sous Android (PIN v2) Pour des raisons de sécurité et de protection de vos données personnelles, votre PIN ne peut pas être récupéré. Si vous avez oublié votre PIN, vous pourrez reconfirmer votre numéro par texto après %1$d jour d’inactivité. Dans ce cas, votre compte sera effacé et toutes ses données supprimées. @@ -3833,9 +3833,9 @@ %1$s recevra une invitation par message. Vous pourrez l’appeler une fois votre invitation acceptée. - Créer un NIP - Les NIP font en sorte que les informations sont enregistrées chiffrées dans Signal. - Créer un NIP + Créer un code PIN + Avec un code PIN, les informations stockées dans Signal sont chiffrées. + Créer un code PIN @@ -3949,21 +3949,21 @@ Verrou d’écran Verrouiller l’accès à Signal avec le verrou d’écran d’Android ou avec une empreinte Délai d’inactivité avant verrouillage de l’écran - NIP Signal - Créer un NIP + Code PIN Signal + Créer un code PIN Changer de code PIN Rappels de code PIN Désactiver Confirmer le PIN - Confirmer votre NIP Signal - Assurez-vous de mémoriser votre NIP ou de le conserver à l’abri, car il ne peut pas être récupéré. Si vous oubliez votre NIP, vous risquez de perdre des données lors de la réinscription de votre compte Signal. - Le NIP est erroné. Veuillez réessayer. + Confirmer votre code PIN Signal + Mémorisez bien votre code PIN ou gardez-le en lieu sûr, car il est impossible de le récupérer. En cas d’oubli, vous risquez de perdre des données lorsque vous réinscrirez votre compte Signal. + Code PIN incorrect. Veuillez réessayer. Impossible d’activer le blocage d’inscription. Impossible de désactiver le blocage d’inscription. Aucun Blocage d’inscription Saisissez le PIN du blocage d’inscription - Votre NIP comporte au moins %1$d chiffres ou caractères + Votre code PIN comporte au moins %1$d chiffres ou caractères Trop d’essais Trop grand nombre de tentatives de saisie du code PIN. Veuillez réessayer dans un jour. Vous avez fait trop d’essais. Veuillez réessayer plus tard. @@ -4338,9 +4338,9 @@ La phrase de récupération est un autre moyen de restaurer votre compte de paiement. Enregistrer votre phrase - Mettez votre NIP à jour - Si votre solde est élevé, vous pouvez opter pour un NIP alphanumérique afin de renforcer la protection de votre compte. - Mettre le NIP à jour + Mettez à jour votre code PIN + Si votre solde est élevé, vous pouvez opter pour un code PIN alphanumérique afin de renforcer la protection de votre compte. + Mettre à jour le code PIN @@ -4365,8 +4365,8 @@ Enregistrer une phrase de récupération Saisissez la phrase de récupération - Après avoir réinstallé Signal, votre solde sera restauré automatiquement si vous confirmez votre PIN Signal. Vous pouvez aussi restaurer votre solde grâce à une phrase de récupération, une phrase de %1$d mot qui vous est propre. Notez-la et conservez-la dans un endroit sûr. - Après avoir réinstallé Signal, votre solde sera restauré automatiquement si vous confirmez votre PIN Signal. Vous pouvez aussi restaurer votre solde grâce à une phrase de récupération, une phrase de %1$d mots qui vous est propre. Notez-la et conservez-la dans un endroit sûr. + Votre solde est automatiquement restauré lorsque vous réinstallez Signal et confirmez votre code PIN Signal. Vous pouvez aussi le restaurer grâce à une phrase de récupération unique comportant %1$d mot. Notez-la et conservez-la en lieu sûr. + Votre solde est automatiquement restauré lorsque vous réinstallez Signal et confirmez votre code PIN Signal. Vous pouvez aussi le restaurer grâce à une phrase de récupération unique comportant %1$d mots. Notez-la et conservez-la en lieu sûr. Le solde de votre compte est disponible Il est temps d\'enregistrer votre phrase de récupération, une clé de 24 mots que vous pouvez utiliser pour rétablir votre solde. @@ -4541,14 +4541,14 @@ Modifier le numéro - Signal changer numéro – Besoin d’aide avec le NIP sur Android (NIP v2) + Changement de numéro Signal – Besoin d’aide : code PIN sous Android (PIN v2) - Les NIP ne correspondent pas - Le NIP associé à votre nouveau numéro est différent de celui associé à votre ancien. Voulez-vous conserver votre ancien NIP ou le mettre à jour ? - Conserver l’ancien NIP - Mettre le NIP à jour - Conserver l’ancien NIP ? + Les codes PIN ne correspondent pas. + Le code PIN associé à votre nouveau numéro est différent de celui associé à votre ancien numéro. Voulez-vous conserver l’ancien code PIN ou le mettre à jour ? + Conserver l’ancien code PIN + Mettre à jour le code PIN + Conserver l’ancien code PIN ? @@ -4932,7 +4932,7 @@ Ajouter un message Ajouter une réponse Envoyer à - View once media + Média à vue unique Un élément ou plus étaient trop grands Un élément ou plus étaient invalides Trop d’éléments sélectionnés @@ -6744,9 +6744,9 @@ - Restoring backup… + Restauration de la sauvegarde… - Downloading backup data… + Téléchargement des données sauvegardées… diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 4c3c86191e..b9c64d93f2 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -1741,15 +1741,15 @@ The video call has ended · %1$s - Missed video call + Físghlao caillte - Missed video call · %1$s + Físghlao caillte · %1$s - Incoming video call + Físghlao isteach Incoming video call · %1$s - Outgoing video call + Físghlao amach Outgoing video call · %1$s @@ -1765,20 +1765,20 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa · %4$s + Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa · %4$s + Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa · %4$s + Tá %1$s, %2$s agus %3$d nduine eile sa ghlao grúpa · %4$s + Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa + Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa + Tá %1$s, %2$s agus %3$d dhuine eile sa ghlao grúpa + Tá %1$s, %2$s agus %3$d nduine eile sa ghlao grúpa + Tá %1$s, %2$s agus %3$d duine eile sa ghlao grúpa @@ -2995,7 +2995,7 @@ Foghlaim tuilleadh Téigh isteach sa ghlao - Call back + Glaoigh ar ais Return To Call Call is Full Tabhair cuirí do chairde @@ -5283,7 +5283,7 @@ Cuir teachtaireacht leis Cuir freagra leis Seol chuig - View once media + Meáin Amhairc Aonuaire Bhí mír amháin nó níos mó rómhór Bhí mír amháin nó níos mó neamhbhailí Roghnaíodh an iomarca nithe diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 5ccd40b4a2..bb7b6d1fb6 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s está na chamada · %2$s - You are in the call · %1$s + Estás na chamada · %1$s - %1$s and %2$s are in the call · %3$s + %1$s e %2$s están na chamada · %3$s - %1$s is in the call + %1$s está na chamada - You are in the call + Estás na chamada - %1$s and %2$s are in the call + %1$s e %2$s están na chamada - The video call has ended + A videochamada rematou - The video call has ended · %1$s + A videochamada rematou · %1$s - Missed video call + Chamada de vídeo perdida - Missed video call · %1$s + Chamada perdida de vídeo · %1$s - Incoming video call + Chamada de vídeo entrante - Incoming video call · %1$s + Videochamada entrante · %1$s - Outgoing video call + Chamada de vídeo saínte - Outgoing video call · %1$s + Videochamada saínte · %1$s - You started a video call + Iniciaches unha videochamada - You started a video call · %1$s + Iniciaches unha videochamada · %1$s - %1$s started a video call + %1$s iniciou unha videochamada - %1$s started a video call · %2$s + %1$s iniciou unha videochamada · %2$s Ti - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s e %3$d máis están na chamada en grupo · %4$s + %1$s, %2$se %3$d máis están na chamada en grupo · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s e %3$d máis están na chamada en grupo + %1$s, %2$s e %3$d máis están na chamada en grupo @@ -2734,7 +2734,7 @@ Saber máis Unirse á chamada - Call back + Devolver chamada Volver chamar A chamada está completa Convidar amizades @@ -4932,7 +4932,7 @@ Engadir mensaxe Engadir unha resposta Enviar a - View once media + Arquivo dunha soa visualización Un ou máis elementos son demasiado grandes Un ou máis elementos son incorrectos Demasiados elementos seleccionados @@ -6744,9 +6744,9 @@ - Restoring backup… + Restaurando copia de seguranza… - Downloading backup data… + Descargando copia de seguranza… diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 103ed249a1..b898918bb2 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s કૉલમાં છે · %2$s - You are in the call · %1$s + તમે કૉલમાં છો · %1$s - %1$s and %2$s are in the call · %3$s + %1$s અને %2$s કૉલમાં છે · %3$s - %1$s is in the call + %1$s કૉલમાં છે - You are in the call + તમે કૉલમાં છો - %1$s and %2$s are in the call + %1$s અને %2$s કૉલમાં છે - The video call has ended + વીડિયો કૉલ સમાપ્ત થયો છે - The video call has ended · %1$s + વીડિયો કૉલ સમાપ્ત થયો છે · %1$s - Missed video call + મિસ્ડ વિડિયો કૉલ - Missed video call · %1$s + મિસ્ડ વિડિયો કૉલ · %1$s - Incoming video call + ઈનકમિંગ વિડિયો કૉલ - Incoming video call · %1$s + ઇનકમિંગ વીડિયો કૉલ · %1$s - Outgoing video call + આઉટગોઈંગ વિડિયો કૉલ - Outgoing video call · %1$s + આઉટગોઇંગ વીડિયો કૉલ · %1$s - You started a video call + તમે વીડિયો કૉલ શરૂ કર્યો - You started a video call · %1$s + તમે વીડિયો કૉલ શરૂ કર્યો · %1$s - %1$s started a video call + %1$sએ વીડિયો કૉલ શરૂ કર્યો - %1$s started a video call · %2$s + %1$sએ વીડિયો કૉલ શરૂ કર્યો · %2$s તમે - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, અને %3$d અન્યો આ કૉલમાં છે%4$s + %1$s, %2$s, અને %3$d અન્યો આ ગ્રુપ કૉલમાં છે%4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, અને %3$d અન્યો આ કૉલમાં છે + %1$s, %2$s, અને %3$d અન્યો આ ગ્રુપ કૉલમાં છે @@ -2734,7 +2734,7 @@ વધુ શીખો કૉલમાં જોડાઓ - Call back + પાછો કૉલ કરો કૉલ પર પાછા જાઓ કૉલ પૂર્ણ છે મિત્રોને આમંત્રિત કરો @@ -4932,7 +4932,7 @@ મેસેજ ઉમેરો જવાબ ઉમેરો આમને મોકલો - View once media + એકવાર જોઈ શકાય તેવા મીડિયા એક અથવા વધુ વસ્તુઓ બહુ મોટી હતી એક અથવા વધુ વસ્તુઓ અમાન્ય હતી બહુ બધી વસ્તુઓ પસંદ કરી @@ -6744,9 +6744,9 @@ - Restoring backup… + બેકઅપ રિસ્ટોર કરી રહ્યાં છીએ… - Downloading backup data… + બેકઅપ ડેટા ડાઉનલોડ કરી રહ્યાં છીએ… diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index a8eed9722d..0005cd472c 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s कॉल में है · %2$s - You are in the call · %1$s + आप कॉल में हैं · %1$s - %1$s and %2$s are in the call · %3$s + %1$s और %2$s कॉल में हैं · %3$s - %1$s is in the call + %1$s कॉल में है - You are in the call + आप कॉल में हैं - %1$s and %2$s are in the call + %1$s और %2$s कॉल में हैं - The video call has ended + वीडियो कॉल समाप्त हो गया है - The video call has ended · %1$s + वीडियो कॉल समाप्त हो गया है · %1$s - Missed video call + छूटी हुई वीडियो कॉल - Missed video call · %1$s + मिस हुई वीडियो कॉल · %1$s - Incoming video call + इनकमिंग वीडियो कॉल - Incoming video call · %1$s + इनकमिंग वीडियो कॉल %1$s - Outgoing video call + आउटगोइंग वीडियो कॉल - Outgoing video call · %1$s + आउटगोइंग वीडियो कॉल %1$s - You started a video call + आपने वीडियो कॉल शुरू किया - You started a video call · %1$s + आपने वीडियो कॉल शुरू किया %1$s - %1$s started a video call + %1$s ने वीडियो कॉल शुरू किया - %1$s started a video call · %2$s + %1$s ने वीडियो कॉल शुरू किया · %2$s आप - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं · %4$s + %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं + %1$s, %2$s, और %3$d अन्य लोग ग्रुप कॉल में हैं @@ -2734,7 +2734,7 @@ अधिक जानें कॉल से जुड़ें - Call back + वापस कॉल करें कॉल पर वापस जाएँ कॉल पूर्ण है मित्रों को आमंत्रित करें @@ -4932,7 +4932,7 @@ एक मेसेज शामिल करें एक जवाब शामिल करें को भेजें - View once media + एक बार मीडिया देखें एक या उससे ज़्यादा आइटम बहुत बड़े थे एक या अधिक आइटम अमान्य थे बहुत ज़्यादा आइटम चुने गए @@ -6744,9 +6744,9 @@ - Restoring backup… + बैकअप पुनः निर्मित हो रहा है… - Downloading backup data… + बैकअप डाटा डाउनलोड हो रहा है… diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index bdd0b8c611..312b3b0998 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s sudjeluje u ovom pozivu · %2$s - You are in the call · %1$s + Pridružili ste se pozivu · %1$s - %1$s and %2$s are in the call · %3$s + %1$s i %2$s sudjeluju u ovom pozivu · %3$s - %1$s is in the call + %1$s sudjeluje u ovom pozivu - You are in the call + Vi sudjelujete u ovom pozivu - %1$s and %2$s are in the call + %1$s i %2$s sudjeluju u ovom pozivu - The video call has ended + Videopoziv je završen - The video call has ended · %1$s + Videopoziv je završen · %1$s - Missed video call + Propušteni videopoziv - Missed video call · %1$s + Propušteni videopoziv · %1$s - Incoming video call + Dolazni videopoziv - Incoming video call · %1$s + Dolazni videopoziv · %1$s - Outgoing video call + Odlazni videopoziv - Outgoing video call · %1$s + Odlazni videopoziv · %1$s - You started a video call + Započeli ste videopoziv - You started a video call · %1$s + Započeli ste videopoziv · %1$s - %1$s started a video call + %1$s započinje videopoziv - %1$s started a video call · %2$s + %1$s započinje videopoziv · %2$s Vi - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s i još %3$d osoba su na ovom grupnom pozivu · %4$s + %1$s, %2$s i još %3$d osobe su na ovom grupnom pozivu · %4$s + %1$s, %2$s i još %3$d osoba je na ovom grupnom pozivu · %4$s + %1$s, %2$s i još %3$d osoba je na ovom grupnom pozivu · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s i još %3$d osoba su na ovom grupnom pozivu + %1$s, %2$s i još %3$d osobe su na ovom grupnom pozivu + %1$s, %2$s i još %3$d osoba je na ovom grupnom pozivu + %1$s, %2$s i još %3$d osoba je na ovom grupnom pozivu @@ -2908,7 +2908,7 @@ Saznajte više Pridruži se pozivu - Call back + Uzvrati poziv Povratak na poziv Poziv je pun Pozovi prijatelje @@ -5166,7 +5166,7 @@ Dodaj poruku Dodajte odgovor Pošalji - View once media + Medijski zapis koji nestaje nakon prikaza Jedna ili više stavki bile su prevelike Jedna ili više stavki bile su nevažeće Odabrano je previše stavki @@ -7036,9 +7036,9 @@ - Restoring backup… + Vraćanje podataka iz sigurnosne kopije… - Downloading backup data… + Preuzimanje sigurnosne kopije podataka… diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 4589ce4577..9f5b388cfd 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s részt vesz a hívásban · %2$s - You are in the call · %1$s + Részt veszel a hívásban · %1$s - %1$s and %2$s are in the call · %3$s + %1$s és %2$s részt vesznek a hívásban · %3$s - %1$s is in the call + %1$s részt vesz a hívásban - You are in the call + Részt veszel a hívásban - %1$s and %2$s are in the call + %1$s és %2$s részt vesznek a hívásban - The video call has ended + A videohívás véget ért - The video call has ended · %1$s + A videohívás véget ért · %1$s - Missed video call + Nem fogadott videóhívás - Missed video call · %1$s + Nem fogadott videóhívás · %1$s - Incoming video call + Bejövő videóhívás - Incoming video call · %1$s + Bejövő videohívás · %1$s - Outgoing video call + Kimenő videóhívás - Outgoing video call · %1$s + Kimenő videohívás · %1$s - You started a video call + Videohívást indítottál - You started a video call · %1$s + Videohívást indítottál · %1$s - %1$s started a video call + %1$s videohívást indított - %1$s started a video call · %2$s + %1$s videohívást indított · %2$s Te - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s és %3$d másik személy csoporthívásban van · %4$s + %1$s, %2$s és %3$d másik személy csoporthívásban van · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s és %3$d másik személy csoporthívásban van + %1$s, %2$s és %3$d másik személy csoporthívásban van @@ -2734,7 +2734,7 @@ Tudj meg többet! Belépés a hívásba - Call back + Visszahívás Vissza a híváshoz A hívás betelt Barátok meghívása @@ -4932,7 +4932,7 @@ Szöveg hozzáadása Válasz hozzáadása Címzett - View once media + Egyszer megjelenő médiafájl megtekintése Egy vagy több elem túl nagy méretű Egy vagy több elem érvénytelen formátumú Túl sok kijelölt elem @@ -6744,9 +6744,9 @@ - Restoring backup… + Biztonsági mentés visszaállítása… - Downloading backup data… + Biztonsági mentés adatainak letöltése… diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index cc7ae1f80a..eeead0a658 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s ada dalam panggilan · %2$s - You are in the call · %1$s + Anda ada dalam panggilan · %1$s - %1$s and %2$s are in the call · %3$s + %1$s dan %2$s ada dalam panggilan · %3$s - %1$s is in the call + %1$s ada dalam panggilan - You are in the call + Anda ada dalam panggilan - %1$s and %2$s are in the call + %1$s dan %2$s ada dalam panggilan - The video call has ended + Panggilan video telah berakhir - The video call has ended · %1$s + Panggilan video telah berakhir · %1$s - Missed video call + Panggilan video tidak terjawab - Missed video call · %1$s + Panggilan video tidak terjawab · %1$s - Incoming video call + Panggilan video masuk - Incoming video call · %1$s + Panggilan video masuk · %1$s - Outgoing video call + Panggilan video keluar - Outgoing video call · %1$s + Panggilan video keluar · %1$s - You started a video call + Anda memulai panggilan video - You started a video call · %1$s + Anda memulai panggilan video · %1$s - %1$s started a video call + %1$s memulai panggilan video - %1$s started a video call · %2$s + %1$s memulai panggilan video · %2$s Anda - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, dan %3$d lainnya berada di panggilan grup. %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, dan %3$d lainnya berada di panggilan grup @@ -2647,7 +2647,7 @@ Pelajari lebih lanjut Bergabung panggilan - Call back + Panggil Ulang Kembali ke panggilan Panggilan penuh Undang teman @@ -4815,7 +4815,7 @@ Tambah pesan Tambah balasan Kirim ke - View once media + Media sekali lihat Ukuran satu atau beberapa media terlalu besar Satu atau beberapa media salah Terlalu banyak media dipilih @@ -6598,9 +6598,9 @@ - Restoring backup… + Memulihkan cadangan … - Downloading backup data… + Mengunduh data cadangan … diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 25b2b896ba..f4d672a0bd 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s è in chiamata · %2$s - You are in the call · %1$s + Sei in chiamata · %1$s - %1$s and %2$s are in the call · %3$s + %1$s e %2$s sono in chiamata · %3$s - %1$s is in the call + %1$s è in chiamata - You are in the call + Sei in chiamata - %1$s and %2$s are in the call + %1$s e %2$s sono in chiamata - The video call has ended + Videochiamata terminata - The video call has ended · %1$s + Videochiamata terminata · %1$s - Missed video call + Videochiamata persa - Missed video call · %1$s + Videochiamata persa · %1$s - Incoming video call + Videochiamata in arrivo - Incoming video call · %1$s + Videochiamata in entrata · %1$s - Outgoing video call + Videochiamata in uscita - Outgoing video call · %1$s + Videochiamata in uscita · %1$s - You started a video call + Hai iniziato una videochiamata - You started a video call · %1$s + Hai iniziato una videochiamata · %1$s - %1$s started a video call + %1$s ha iniziato una videochiamata - %1$s started a video call · %2$s + %1$s ha iniziato una videochiamata · %2$s Tu - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s e %3$d altra persona sono nella chiamata di gruppo · %4$s + %1$s, %2$s e %3$d altre persone sono nella chiamata di gruppo · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s e %3$d altra persona sono nella chiamata di gruppo + %1$s, %2$s e %3$d altre persone sono nella chiamata di gruppo @@ -2734,7 +2734,7 @@ Scopri di più Unisciti alla chiamata - Call back + Richiama Torna alla chiamata La chiamata è piena Invita amici @@ -4932,7 +4932,7 @@ Aggiungi un messaggio Aggiungi una risposta Invia a - View once media + Media visibile una sola volta Uno o più elementi erano troppo grandi Uno o più elementi non erano validi Troppi elementi selezionati @@ -6744,9 +6744,9 @@ - Restoring backup… + Ripristino backup in corso… - Downloading backup data… + Download dati di backup… diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 378816fe65..4674eb5072 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1688,15 +1688,15 @@ The video call has ended · %1$s - Missed video call + שיחת וידאו שלא נענתה - Missed video call · %1$s + שיחת וידאו שלא נענתה · %1$s - Incoming video call + שיחת וידאו נכנסת Incoming video call · %1$s - Outgoing video call + שיחת וידאו יוצאת Outgoing video call · %1$s @@ -1712,18 +1712,18 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %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, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s ועוד %3$d אחר בשיחה הקבוצתית + %1$s, %2$s ועוד %3$d אחרים בשיחה הקבוצתית + %1$s, %2$s ועוד %3$d אחרים בשיחה הקבוצתית + %1$s, %2$s ועוד %3$d אחרים בשיחה הקבוצתית @@ -2908,7 +2908,7 @@ למד עוד הצטרף לשיחה - Call back + חייג חזרה חזור לשיחה השיחה מלאה הזמן חברים @@ -5166,7 +5166,7 @@ הוסף הודעה הוסף תשובה שליחה אל - View once media + מדיה לצפייה חד פעמית פריט אחד או יותר היו יותר מדי גדולים פריט אחד או יותר היו בלתי תקפים יותר מדי פריטים נבחרו diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ec4ec0c06f..bf834d6b76 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$sは通話中です・%2$s - You are in the call · %1$s + あなたは通話中です・%1$s - %1$s and %2$s are in the call · %3$s + %1$sと%2$sが通話中です・%3$s - %1$s is in the call + %1$sは通話中です - You are in the call + あなたは通話中です - %1$s and %2$s are in the call + %1$sと%2$sが通話中です - The video call has ended + ビデオ通話が終了しました - The video call has ended · %1$s + ビデオ通話が終了しました・%1$s - Missed video call + ビデオ通話着信あり - Missed video call · %1$s + ビデオ通話着信あり · %1$s - Incoming video call + ビデオ通話着信 - Incoming video call · %1$s + ビデオ通話着信・%1$s - Outgoing video call + ビデオ通話発信 - Outgoing video call · %1$s + ビデオ通話発信・%1$s - You started a video call + ビデオ通話を開始しました - You started a video call · %1$s + ビデオ通話を開始しました・%1$s - %1$s started a video call + %1$sはビデオ通話を開始しました - %1$s started a video call · %2$s + %1$sはビデオ通話を開始しました・%2$s あなた - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s、%2$sほか%3$d名がグループ通話中です · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s、%2$sほか%3$d名がグループ通話中です @@ -2647,7 +2647,7 @@ 詳しく見る 通話に参加する - Call back + 折り返す 通話に戻る 満席です 友達を招待する @@ -4815,7 +4815,7 @@ メッセージを追加してください 返信を追加 宛先 - View once media + 使い捨てメディア 1つ以上のアイテムのサイズが大きすぎます 1つ以上のアイテムが不正です 選択されたアイテムが多すぎます @@ -6598,9 +6598,9 @@ - Restoring backup… + バックアップを復元しています… - Downloading backup data… + バックアップデータをダウンロードしています… diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 8443443513..83c6b446bf 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s არის ამ ზარზე · %2$s - You are in the call · %1$s + შენ ხარ ამ ზარზე · %1$s - %1$s and %2$s are in the call · %3$s + %1$s და %2$s ამ ზარზე არიან · %3$s - %1$s is in the call + %1$s ამ ზარზეა - You are in the call + ამ ზარზე ხარ - %1$s and %2$s are in the call + %1$s და %2$s ამ ზარზე არიან - The video call has ended + ეს ვიდეო ზარი დასრულდა - The video call has ended · %1$s + ეს ვიდეო ზარი დასრულდა · %1$s - Missed video call + გამოტოვებული ვიდეო ზარი - Missed video call · %1$s + გამოტოვებული ვიდეო ზარი · %1$s - Incoming video call + შემომავალი ვიდეო ზარი - Incoming video call · %1$s + შემომავალი ვიდეო ზარი · %1$s - Outgoing video call + გამავალი ვიდეო ზარი - Outgoing video call · %1$s + გამავალი ვიდეო ზარი · %1$s - You started a video call + შენ წამოიწყე ვიდეო ზარი - You started a video call · %1$s + შენ წამოიწყე ვიდეო ზარი · %1$s - %1$s started a video call + %1$s-მა ვიდეო ზარი წამოიწყო - %1$s started a video call · %2$s + %1$s-მა ვიდეო ზარი წამოიწყო · %2$s შენ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარში არიან · %4$s + %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარზე არიან · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარში არიან + %1$s, %2$s, და %3$d სხვა ჯგუფურ ზარზე არიან @@ -2734,7 +2734,7 @@ გაიგე მეტი ზარზე შესვლა - Call back + გადარეკვა ზარზე დაბრუნება ზარი გადავსებულია მოიწვიე მეგობრები @@ -4932,7 +4932,7 @@ შეტყობინების დამატება პასუხის დამატება გაეგზავნოთ ჩამოთვლილებს - View once media + ერთხელ სანახავი მედია-ფაილი ერთი ან მეტი ელემენტი ზედმეტად დიდი იყო ერთი ან მეტი ელემენტი არავალიდური იყო მონიშნულია ზედმეტი ელემენტი @@ -6744,9 +6744,9 @@ - Restoring backup… + მიმდინარეობს სარეზერვო მონაცემების აღდგენა… - Downloading backup data… + მიმდინარეობს სარეზერვო მონაცემების გადმოწერა… diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 3e5c58810e..7a96275924 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s байланыста · %2$s - You are in the call · %1$s + Сіз байланыстасыз · %1$s - %1$s and %2$s are in the call · %3$s + %1$s және %2$s байланыста · %3$s - %1$s is in the call + %1$s байланыста - You are in the call + Сіз байланыстасыз - %1$s and %2$s are in the call + %1$s және %2$s байланыста - The video call has ended + Видеоқоңырау аяқталды - The video call has ended · %1$s + Видеоқоңырау аяқталды · %1$s - Missed video call + Қабылданбаған видеоқоңырау - Missed video call · %1$s + Өткізіп алған видеоқоңырау · %1$s - Incoming video call + Кіріс видеоқоңырау - Incoming video call · %1$s + Сізге видеоқоңырау шалынды · %1$s - Outgoing video call + Шығыс видеоқоңырау - Outgoing video call · %1$s + Сіз видеоқоңырау шалдыңыз · %1$s - You started a video call + Видеоқоңырауды бастадыңыз - You started a video call · %1$s + Видеоқоңырау бастадыңыз · %1$s - %1$s started a video call + %1$s видеоқоңырау бастады - %1$s started a video call · %2$s + %1$s видеоқоңырау бастады · %2$s Сіз - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр · %4$s + %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр + %1$s, %2$s және тағы %3$d адам топтық қоңырауға қатысып отыр @@ -2734,7 +2734,7 @@ Толық ақпарат Қоңырауға қосылу - Call back + Кері қоңырау шалу Қоңырауға оралу Қоңырауға бұдан артық адам қосыла алмайды Достарды шақыру @@ -4932,7 +4932,7 @@ Хат қосу Жауап қосу Алушы - View once media + Бір рет көрілетін мультимедиа Бір немесе бірнеше элемент өте үлкен болды Бір немесе бірнеше элемент дұрыс болмады Тым көп элемент таңдалды @@ -6744,9 +6744,9 @@ - Restoring backup… + Резервтік дерек қалпына келтірілуде… - Downloading backup data… + Резервтік дерек жүктеп алынуда… diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index 3e80fdaca5..30d8d5863c 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s កំពុងនៅក្នុងការហៅ · %2$s - You are in the call · %1$s + អ្នកកំពុងនៅក្នុងការហៅ · %1$s - %1$s and %2$s are in the call · %3$s + %1$s និង %2$s កំពុងនៅក្នុងការហៅ · %3$s - %1$s is in the call + %1$s កំពុងនៅក្នុងការហៅ - You are in the call + អ្នកកំពុងនៅក្នុងការហៅ - %1$s and %2$s are in the call + %1$s និង %2$s កំពុងនៅក្នុងការហៅនេះ - The video call has ended + ការហៅជាវីដេអូបានបញ្ចប់ - The video call has ended · %1$s + ការហៅជាវីដេអូបានបញ្ចប់ · %1$s - Missed video call + ការហៅជាវីដេអូខកមិនបានទទួល - Missed video call · %1$s + ខកខានការហៅជាវីដេអូ · %1$s - Incoming video call + ការហៅចូលជាវីដេអូ - Incoming video call · %1$s + ការហៅចូលជាវីដេអូ · %1$s - Outgoing video call + ការហៅចេញជាវីដេអូ - Outgoing video call · %1$s + ការហៅចេញជាវីដេអូ · %1$s - You started a video call + អ្នកបានចាប់ផ្តើមការហៅជាវីដេអូ - You started a video call · %1$s + អ្នកបានចាប់ផ្តើមការហៅជាវីដេអូ · %1$s - %1$s started a video call + %1$s បានចាប់ផ្តើមការហៅជាវីដេអូ - %1$s started a video call · %2$s + %1$s បានចាប់ផ្តើមការហៅជាវីដេអូ · %2$s អ្នក - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, និង%3$d ផ្សេងទៀតនៅក្នុងការហៅជាក្រុម · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, និង%3$d ផ្សេងទៀតនៅក្នុងការហៅជាក្រុម @@ -2647,7 +2647,7 @@ សិក្សាបន្ថែម ចូលរួមការហៅ - Call back + ហៅទៅវិញ ត្រឡប់ទៅការហៅ ការហៅពេញ អញ្ជើញមិត្តភក្តិ @@ -4815,7 +4815,7 @@ បន្ថែមសារមួយ បន្ថែមការឆ្លើយតប ផ្ញើទៅ - View once media + មេឌៀមើលបានតែម្តង ធាតុមួយ ឬច្រើនមានទំហំធំពេក មានធាតុមួយ ឬច្រើនមិនត្រឹមត្រូវ ធាតុដែលបានជ្រើសរើសច្រើនពេក @@ -6598,9 +6598,9 @@ - Restoring backup… + កំពុងស្ដារការបម្រុងទុក… - Downloading backup data… + កំពុងទាញយកទិន្នន័យបម្រុងទុក… diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index b8dc3f67bf..ef4f737e55 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -23,7 +23,7 @@ ಅಳಿಸಿ ದಯವಿಟ್ಟು ನಿರೀಕ್ಷಿಸಿ… ಉಳಿಸಿ - ಸ್ವಯಂ ಟಿಪ್ಪಣಿ + ಸ್ವಯಂ ಬಳಕೆಗೆ ಟಿಪ್ಪಣಿ ಹವಾಮಾನ @@ -42,16 +42,16 @@ ನೀವು ಇನ್ನೂ ಪಾಸ್‌ಫ್ರೇಸ್ ಹೊಂದಿಸಿಲ್ಲ! - ಪಾಸ್‌ಫ್ರೇಸ್‌ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ + ಪಾಸ್‌ಫ್ರೇಸ್‌ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುವುದೇ? ಇದು Signal ಮತ್ತು ಸಂದೇಶ ಅಧಿಸೂಚನೆಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ಅನ್ಲಾಕ್ ಮಾಡುತ್ತದೆ. ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಸರ್ವರ್‌ ಗೆ ಸಂಪರ್ಕಿಸುವಾಗ ದೋಷ! ನೋಂದಣಿ ಲಾಕ್ ಆಗಿಸಲು PINಗಳು ಬೇಕು. PINಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು, ದಯವಿಟ್ಟು ಮೊದಲು ನೋಂದಣಿ ಲಾಕ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ. - ಪಿನ್ ರಚಿಸಲಾಗಿದೆ. - ಪಿನ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. - ಪೇಮೆಂಟ್ಸ್ ರಿಕವರಿ ಪದಗುಚ್ಛವನ್ನು ರೆಕಾರ್ಡ್‌ ಮಾಡಿ + PIN ರಚಿಸಲಾಗಿದೆ. + PIN ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. + ಪಾವತಿಗಳನ್ನು ಮರೆಪಡೆಯುವ ಪದಗುಚ್ಛವನ್ನು ರೆಕಾರ್ಡ್‌ ಮಾಡಿ ಪದಗುಚ್ಛವನ್ನು ರೆಕಾರ್ಡ್‌ ಮಾಡಿ - ನಿಮ್ಮ PIN ಅನ್ನು ನೀವು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುವ‌ ಮೊದಲು, ನಿಮ್ಮ ಪೇಮೆಂಟ್ಸ್ ರಿಕವರಿ ಪದಗುಚ್ಛವನ್ನು ರೆಕಾರ್ಡ್‌ ಮಾಡಿ ನಿಮ್ಮ ಪೇಮೆಂಟ್ಸ್ ಖಾತೆಯ ರಿಕವರಿ ಮಾಡುವುದನ್ನು ನೀವು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಬೇಕು. + ನಿಮ್ಮ PIN ಅನ್ನು ನೀವು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುವ‌ ಮೊದಲು, ನಿಮ್ಮ ಪಾವತಿಗಳನ್ನು ಮರೆಪಡೆಯುವ ಪದಗುಚ್ಛವನ್ನು ರೆಕಾರ್ಡ್‌ ಮಾಡಿ ನಿಮ್ಮ ಪೇಮೆಂಟ್ಸ್ ಖಾತೆಯ ರಿಕವರಿ ಮಾಡುವುದನ್ನು ನೀವು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಬೇಕು. @@ -126,7 +126,7 @@ ಕಾರ್ಡ್ ಸಂಖ್ಯೆ - MM/YY + ತಿತಿ/ವವ CVV @@ -173,7 +173,7 @@ ನಿರ್ಬಂಧವು Signal ಅಪ್‌ಡೇಟ್‌ಗಳು ಮತ್ತು ಸುದ್ದಿಯನ್ನು ಪಡೆಯುತ್ತದೆ. - ಪುನರಾರಂಭವು Signal ಅಪ್‌ಡೇಟ್‌ಗಳು ಮತ್ತು ಸುದ್ದಿಯನ್ನು ಪಡೆಯುತ್ತದೆ. + Signal ಅಪ್‌ಡೇಟ್‌ಗಳು ಮತ್ತು ಸುದ್ದಿ ಪಡೆಯುವುದನ್ನು ಮತ್ತೆ ಆರಂಭಿಸಿ. %1$sನಿರ್ಬಂಧ ತೆಗೆಯಬೇಕೇ? ನಿರ್ಬಂಧಿಸಿ ನಿರ್ಬಂಧಿಸಿ ಹಾಗು ತೊರೆಯಿರಿ @@ -1582,15 +1582,15 @@ The video call has ended · %1$s - Missed video call + ಮಿಸ್ಡ್ ವೀಡಿಯೊ ಕಾಲ್ - Missed video call · %1$s + ಮಿಸ್ಡ್ ವೀಡಿಯೋ ಕಾಲ್ · %1$s - Incoming video call + ಒಳಬರುವ ವೀಡಿಯೊ ಕಾಲ್ Incoming video call · %1$s - Outgoing video call + ಹೊರಹೋಗುವ ವೀಡಿಯೊ ಕಾಲ್ Outgoing video call · %1$s @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ · %4$s + %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ + %1$s, %2$s ಮತ್ತು %3$d ಇತರರು ಗ್ರೂಪ್‌ ಕಾಲ್‌ನಲ್ಲಿದ್ದಾರೆ @@ -2734,7 +2734,7 @@ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ ಕರೆಗೆ ಸೇರಿ - Call back + ಹಿಂತಿರುಗಿ ಕರೆ ಮಾಡಿ ಕರೆಗೆ ಹಿಂತಿರುಗಿ ಕರೆ ಭರ್ತಿಯಾಗಿದೆ ಸ್ನೇಹಿತರನ್ನು ಆಹ್ವಾನಿಸಿ @@ -4932,7 +4932,7 @@ ಮೆಸೇಜ್ ಸೇರಿಸಿ ಉತ್ತರ ಸೇರಿಸಿ ಇವರಿಗೆ ಕಳುಹಿಸಿ - View once media + View Once Media ಒಂದು ಅಥವಾ ಹೆಚ್ಚು ಐಟಂಗಳು ತೀರಾ ದೊಡ್ಡದಾಗಿವೆ ಒಂದು ಅಥವಾ ಹೆಚ್ಚು ಐಟಂಗಳು ಅಮಾನ್ಯವಾಗಿವೆ ತುಂಬಾ ಐಟಂಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 854916a849..e2fd08eef2 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s 님이 통화에 참여했습니다 · %2$s - You are in the call · %1$s + 통화에 참여했습니다 · %1$s - %1$s and %2$s are in the call · %3$s + %1$s 님과 %2$s 님이 통화에 참여했습니다 · %3$s - %1$s is in the call + %1$s 님이 통화에 참여했습니다 - You are in the call + 통화에 참여했습니다 - %1$s and %2$s are in the call + %1$s 님과 %2$s 님이 통화에 참여했습니다 - The video call has ended + 화상 통화를 종료했습니다 - The video call has ended · %1$s + 화상 통화를 종료했습니다 · %1$s - Missed video call + 부재중 영상 통화 - Missed video call · %1$s + 부재중 영상 통화 %1$s - Incoming video call + 수신 영상 통화 - Incoming video call · %1$s + 화상 통화 수신 중 · %1$s - Outgoing video call + 발신 영상 통화 - Outgoing video call · %1$s + 화상 통화 발신 중 · %1$s - You started a video call + 화상 통화를 시작했습니다 - You started a video call · %1$s + 화상 통화를 시작했습니다 · %1$s - %1$s started a video call + %1$s 님이 화상 통화를 시작했습니다 - %1$s started a video call · %2$s + %1$s 님이 화상 통화를 시작했습니다 · %2$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s 외 %3$d명이 그룹 통화 중입니다(%4$s). - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s 외 %3$d명이 그룹 통화 중입니다. @@ -2647,7 +2647,7 @@ 더 알아보기 통화 참여 - Call back + 다시 걸기 통화로 돌아가기 통화 참가자 수 최대 친구 초대 @@ -4815,7 +4815,7 @@ 메시지 추가 답장 추가 받는 사람 - View once media + 한 번만 볼 수 있는 미디어 하나 이상의 항목이 너무 큽니다. 하나 이상의 항목이 유효하지 않습니다. 항목을 너무 많이 선택했습니다. @@ -6598,9 +6598,9 @@ - Restoring backup… + 백업을 복원하는 중… - Downloading backup data… + 백업 데이터를 다운로드하는 중… diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index f43f06e44b..1e98e36950 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -1529,15 +1529,15 @@ The video call has ended · %1$s - Missed video call + Кабыл алынбаган аудио чалуу - Missed video call · %1$s + Жооп берилбеген видео чалуу · %1$s - Incoming video call + Келүүчү аудио чалуу Incoming video call · %1$s - Outgoing video call + Чыгуучу аудио чалуу Outgoing video call · %1$s @@ -1553,12 +1553,12 @@ - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s жана дагы %3$d топтук чалууда · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s жана дагы %3$d топтук чалууда @@ -2647,7 +2647,7 @@ Кененирээк маалымат Чалууга кошулуу - Call back + Кайра чалуу Чалууга кайтуу Орун жок Досторду чакыруу diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index f9ce637e60..6ce6feb61f 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s yra skambutyje · %2$s - You are in the call · %1$s + Dalyvaujate skambutyje · %1$s - %1$s and %2$s are in the call · %3$s + %1$s ir %2$s yra skambutyje · %3$s - %1$s is in the call + %1$s yra skambutyje - You are in the call + Dalyvaujate skambutyje - %1$s and %2$s are in the call + %1$s ir %2$s yra skambutyje - The video call has ended + Vaizdo skambutis baigtas - The video call has ended · %1$s + Vaizdo skambutis baigtas · %1$s - Missed video call + Praleistas vaizdo skambutis - Missed video call · %1$s + Praleistas vaizdo skambutis · %1$s - Incoming video call + Gaunamasis vaizdo skambutis - Incoming video call · %1$s + Gaunamasis vaizdo skambutis · %1$s - Outgoing video call + Išsiunčiamasis vaizdo skambutis - Outgoing video call · %1$s + Išsiunčiamasis vaizdo skambutis · %1$s - You started a video call + Pradėjote vaizdo skambutį - You started a video call · %1$s + Pradėjote vaizdo skambutį · %1$s - %1$s started a video call + %1$s pradėjo vaizdo skambutį - %1$s started a video call · %2$s + %1$s pradėjo vaizdo skambutį · %2$s Jūs - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje · %4$s + %1$s, %2$s ir dar %3$d žmonės yra grupės skambutyje · %4$s + %1$s, %2$s ir dar %3$d žmonių yra grupės skambutyje · %4$s + %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje + %1$s, %2$s ir dar %3$d žmonės yra grupės skambutyje + %1$s, %2$s ir dar %3$d žmonių yra grupės skambutyje + %1$s, %2$s ir dar %3$d žmogus yra grupės skambutyje @@ -2908,7 +2908,7 @@ Sužinoti daugiau Prisijungti prie skambučio - Call back + Atskambinti Grįžti į skambutį Skambutis yra pilnas Pakviesti draugus @@ -5166,7 +5166,7 @@ Pridėti žinutę Pridėti atsakymą Kam siųsti - View once media + Medija, kurią galima peržiūrėti vieną kartą Vienas ar daugiau elementų buvo per dideli Vienas ar daugiau elementų buvo neteisingi Pažymėta per daug elementų @@ -7036,9 +7036,9 @@ - Restoring backup… + Atkuriama atsarginė kopija… - Downloading backup data… + Atsiunčiami atsarginės kopijos duomenys… diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index d4d4d456df..5da86da11b 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -1619,56 +1619,56 @@ - %1$s is in the call · %2$s + %1$s piedalās zvanā · %2$s - You are in the call · %1$s + Jūs piedalāties zvanā · %1$s - %1$s and %2$s are in the call · %3$s + %1$s un %2$s piedalās zvanā · %3$s - %1$s is in the call + %1$s piedalās zvanā - You are in the call + Jūs piedalāties zvanā - %1$s and %2$s are in the call + %1$s un %2$s piedalās zvanā - The video call has ended + Video zvans ir beidzies - The video call has ended · %1$s + Video zvans ir beidzies · %1$s - Missed video call + Neatbildēts videozvans - Missed video call · %1$s + Neatbildēts videozvans · %1$s - Incoming video call + Ienākošs videozvans - Incoming video call · %1$s + Ienākošais video zvans · %1$s - Outgoing video call + Izejošs videozvans - Outgoing video call · %1$s + Izejošais video zvans · %1$s - You started a video call + Jūs uzsākāt video zvanu - You started a video call · %1$s + Jūs uzsākāt video zvanu · %1$s - %1$s started a video call + %1$s uzsāka video zvanu - %1$s started a video call · %2$s + %1$s uzsāka video zvanu · %2$s Jūs - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s un %3$d cits ir grupas zvanā · %4$s + %1$s, %2$s un %3$d cits ir grupas zvanā · %4$s + %1$s, %2$s un %3$d citi ir grupas zvanā · %4$s - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s un %3$d cits ir grupas zvanā + %1$s, %2$s un %3$d cits ir grupas zvanā + %1$s, %2$s un %3$d citi ir grupas zvanā @@ -2821,7 +2821,7 @@ Lasīt vairāk Pievienoties zvanam - Call back + Atzvanīt Atgriezties pie zvana Zvana dalībnieku limits izsmelts Uzaicināt draugus @@ -5049,7 +5049,7 @@ Pievienojiet ziņu Pievienot atbildi Sūtīt - View once media + Vienreiz skatāma multivide Viens vai vairāki vienumi bija pārāk lieli Viens vai vairāki vienumi bija nederīgi Pārāk daudz izvēlētu vienumu @@ -6890,9 +6890,9 @@ - Restoring backup… + Atjauno rezerves kopijas datus… - Downloading backup data… + Lejupielādē rezerves kopijas datus… diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 163b62e8ac..e560fec4d3 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s е на повикот · %2$s - You are in the call · %1$s + Вие сте на повикот · %1$s - %1$s and %2$s are in the call · %3$s + %1$s и %2$s се на повикот · %3$s - %1$s is in the call + %1$s е на повикот - You are in the call + Вие сте на повикот - %1$s and %2$s are in the call + %1$s и %2$s се на овој повик - The video call has ended + Видео повикот заврши - The video call has ended · %1$s + Видео повикот заврши · %1$s - Missed video call + Пропуштен видео повик - Missed video call · %1$s + Испуштен видео повик · %1$s - Incoming video call + Дојдовен видео повик - Incoming video call · %1$s + Дојдовен видео повик · %1$s - Outgoing video call + Појдовен видео повик - Outgoing video call · %1$s + Појдовен видео повик · %1$s - You started a video call + Започнавте видео повик - You started a video call · %1$s + Започнавте видео повик · %1$s - %1$s started a video call + %1$s започна видео повик - %1$s started a video call · %2$s + %1$s започна видео повик · %2$s Вие - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, и %3$d друг е во групниот повик · %4$s + %1$s, %2$s, и %3$d други се во групниот повик · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, и %3$d друг е во групниот повик + %1$s, %2$s, и %3$d други се во групниот повик @@ -2734,7 +2734,7 @@ Дознајте повеќе Приклучи се на повикот - Call back + Возврати повик Врати се на повикот Повикот е полн Покани пријатели @@ -4932,7 +4932,7 @@ Додај порака Додај одговор Испрати на - View once media + Еднократно видлива медиумска датотека Еден или повеќе предмети беа премногу големи Еден или повеќе предмети беа невалидни Премногу избрани предмети @@ -6744,9 +6744,9 @@ - Restoring backup… + Се враќа резервната копија… - Downloading backup data… + Се преземаат податоците од резервната копија… diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 848c56f0ad..f53c3eb944 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s കോളിലാണ് · %2$s - You are in the call · %1$s + നിങ്ങൾ കോളിലാണ് · %1$s - %1$s and %2$s are in the call · %3$s + %1$s , %2$s എന്നിവർ കോളിലാണ് · %3$s - %1$s is in the call + %1$s കോളിലാണ് - You are in the call + നിങ്ങൾ കോളിലാണ് - %1$s and %2$s are in the call + %1$s, %2$s എന്നിവർ കോളിലാണ് - The video call has ended + വീഡിയോ കോൾ അവസാനിച്ചു - The video call has ended · %1$s + വീഡിയോ കോൾ അവസാനിച്ചു · %1$s - Missed video call + മിസ്‌ഡ് വീഡിയോ കോൾ - Missed video call · %1$s + നഷ്‌ടമായ വീഡിയോ കോൾ · %1$s - Incoming video call + ഇൻകമിംഗ് വീഡിയോ കോൾ - Incoming video call · %1$s + ഇൻകമിംഗ് വീഡിയോ കോൾ · %1$s - Outgoing video call + ഔട്ട്ഗോയിംഗ് വീഡിയോ കോൾ - Outgoing video call · %1$s + ഔട്ട്ഗോയിംഗ് വീഡിയോ കോൾ · %1$s - You started a video call + നിങ്ങൾ ഒരു വീഡിയോ കോൾ ആരംഭിച്ചു - You started a video call · %1$s + നിങ്ങൾ ഒരു വീഡിയോ കോൾ ആരംഭിച്ചു · %1$s - %1$s started a video call + %1$s ഒരു വീഡിയോ കോൾ ആരംഭിച്ചു - %1$s started a video call · %2$s + %1$s ഒരു വീഡിയോ കോൾ ആരംഭിച്ചു · %2$s നിങ്ങൾ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, പിന്നെ %3$d ആളും ഗ്രൂപ്പ് കോളിലാണ് · %4$s + %1$s, %2$s, പിന്നെ %3$d ആളുകളും ഈ കോളിലുണ്ട് · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s - ഉം, %2$s - ഉം പിന്നെ %3$d ആളും ഈ കോളിലുണ്ട് + %1$s, %2$s, പിന്നെ %3$d ആളുകളും ഈ കോളിലുണ്ട് @@ -2734,7 +2734,7 @@ കൂടുതൽ അറിയുക കോളിൽ ചേരുക - Call back + തിരികെ വിളിക്കുക കോളിലേക്ക് മടങ്ങുക കോൾ നിറഞ്ഞു സുഹൃത്തുക്കളെ ക്ഷണിക്കുക @@ -4932,7 +4932,7 @@ ഒരു സന്ദേശം ചേര്‍ക്കുക ഒരു മറുപടി ചേർക്കുക ഇനിപ്പറയുന്നയാൾക്ക് അയയ്ക്കുക - View once media + ഒറ്റത്തവണ വീഡിയോ കാണുക ഒന്നോ അതിലധികമോ ഇനങ്ങൾ വളരെ വലുതായിരുന്നു ഒന്നോ അതിലധികമോ ഇനങ്ങൾ അസാധുവാണ് വളരെയധികം ഇനങ്ങൾ തിരഞ്ഞെടുത്തു @@ -6744,9 +6744,9 @@ - Restoring backup… + ബാക്കപ്പ് പുനഃസ്ഥാപിക്കുന്നു… - Downloading backup data… + ബാക്കപ്പ് ഡാറ്റ ഡൗൺലോഡ് ചെയ്യുന്നു… diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index caf830e8fd..0560e77efd 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s कॉल मध्ये आहे · %2$s - You are in the call · %1$s + आपण कॉलमध्ये आहात · %1$s - %1$s and %2$s are in the call · %3$s + %1$s आणि %2$s कॉलमध्ये आहेत · %3$s - %1$s is in the call + %1$s कॉल मध्ये आहेत - You are in the call + आपण कॉलमध्ये आहात - %1$s and %2$s are in the call + %1$s आणि %2$s कॉलमध्ये आहेत - The video call has ended + व्हिडिओ कॉल समाप्त झाला आहे - The video call has ended · %1$s + व्हिडिओ कॉल समाप्त झाला आहे · %1$s - Missed video call + चूकवलेला व्हिडिओ कॉल - Missed video call · %1$s + सुटलेला व्हिडिओ कॉल · %1$s - Incoming video call + येणारा व्हिडिओ कॉल - Incoming video call · %1$s + येणारा व्हिडिओ कॉल · %1$s - Outgoing video call + बाहेर जाणारा व्हिडिओ कॉल - Outgoing video call · %1$s + जाणारा व्हिडिओ कॉल · %1$s - You started a video call + आपण व्हिडिओ कॉल सुरु केला - You started a video call · %1$s + आपण व्हिडिओ कॉल सुरु केला · %1$s - %1$s started a video call + %1$s ने व्हिडिओ कॉल सुरु केला - %1$s started a video call · %2$s + %1$s ने व्हिडिओ कॉल सुरु केला · %2$s आपण - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहे · %4$s + %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहेत · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहेत + %1$s, %2$s, आणि %3$d इतर व्यक्ती गट कॉलमध्ये आहेत @@ -2734,7 +2734,7 @@ अधिक जाणा कॉलमध्ये सामील व्हा - Call back + परत कॉल करा कॉलवर परत या कॉल पूर्ण आहे मित्रांना आमंत्रित करा @@ -4932,7 +4932,7 @@ संदेश जोडा प्रत्युत्तर जोडा यांना पाठवा - View once media + एकदा मिडिया पहा एक किंवा अधिक आयटम खूप मोठे होते एक किंवा अधिक आयटम अवैध होते जास्त आयटम निवडले @@ -6744,9 +6744,9 @@ - Restoring backup… + बॅकअप पुनर्स्थापित करत आहे… - Downloading backup data… + बॅकअप डेटा डाऊनलोड करत आहे… diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 68fae3eb74..6f5389a3f3 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s dalam panggilan · %2$s - You are in the call · %1$s + Anda dalam panggilan · %1$s - %1$s and %2$s are in the call · %3$s + %1$s dan %2$s sedang dalam panggilan · %3$s - %1$s is in the call + %1$s dalam panggilan - You are in the call + Anda dalam panggilan - %1$s and %2$s are in the call + %1$s dan %2$s sedang dalam panggilan ini - The video call has ended + Panggilan video telah tamat - The video call has ended · %1$s + Panggilan video telah tamat · %1$s - Missed video call + Panggilan video terlepas - Missed video call · %1$s + Panggilan video tidak dijawab · %1$s - Incoming video call + Panggilan video masuk - Incoming video call · %1$s + Panggilan video masuk · %1$s - Outgoing video call + Panggilan video keluar - Outgoing video call · %1$s + Panggilan video keluar · %1$s - You started a video call + Anda memulakan panggilan video - You started a video call · %1$s + Anda memulakan panggilan video · %1$s - %1$s started a video call + %1$s memulakan panggilan video - %1$s started a video call · %2$s + %1$s memulakan panggilan video · %2$s Anda - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, dan %3$d orang yang lain berada dalam panggilan kumpulan · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, dan %3$d orang yang lain berada dalam panggilan kumpulan @@ -2647,7 +2647,7 @@ Ketahui lebih lanjut Sertai panggilan - Call back + Panggilan Balik Kembali ke panggilan Panggilan penuh Jemput rakan @@ -4815,7 +4815,7 @@ Tambah mesej Tambah balasan Hantar kepada - View once media + Media lihat sekali Saiz satu atau lebih item terlalu besar Satu atau lebih item tidak sah Terlalu banyak item dipilih @@ -6598,9 +6598,9 @@ - Restoring backup… + Memulihkan sandaran… - Downloading backup data… + Memuat turun data sandaran… diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 98ba3116ad..a8a37a84ed 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s သည် ကောလ်ထဲတွင် ရှိနေသည် · %2$s - You are in the call · %1$s + သင်သည် ကောလ်ထဲတွင် ရှိနေသည် · %1$s - %1$s and %2$s are in the call · %3$s + %1$s နှင့် %2$s သည် ကောလ်ထဲ တွင်ရှိနေသည် · %3$s - %1$s is in the call + %1$s သည် ကောလ်ထဲတွင် ရှိနေသည် - You are in the call + သင်သည် ကောလ်ထဲတွင် ရှိနေသည် - %1$s and %2$s are in the call + %1$s နှင့် %2$s သည် ကောလ်ထဲတွင် ရှိနေသည် - The video call has ended + ဗီဒီယိုကောလ်ခေါ်ဆိုမှု ပြီးဆုံးသွားပါပြီ - The video call has ended · %1$s + ဗီဒီယိုကောလ်ခေါ်ဆိုမှု ပြီးဆုံးသွားပါပြီ · %1$s - Missed video call + လွတ်သွားသော ဗီဒီယိုကောလ် - Missed video call · %1$s + လွဲချော်ခဲ့သော ဗီဒီယိုခေါ်ဆိုမှု · %1$s - Incoming video call + အဝင် ဗီဒီယိုကောလ် - Incoming video call · %1$s + အဝင်ဗီဒီယိုကောလ် · %1$s - Outgoing video call + အထွက် ဗီဒီယိုကောလ် - Outgoing video call · %1$s + အထွက်ဗီဒီယိုကောလ် · %1$s - You started a video call + သင်သည် ဗီဒီယိုကောလ်ခေါ်ဆိုမှု စတင်ခဲ့သည် - You started a video call · %1$s + သင်သည် ဗီဒီယိုကောလ်ခေါ်ဆိုမှုကို စတင်ခဲ့သည် · %1$s - %1$s started a video call + %1$s ဗီဒီယိုကောလ်ခေါ်ဆိုမှုကို စတင်ခဲ့သည် - %1$s started a video call · %2$s + %1$s ဗီဒီယိုကောလ်ခေါ်ဆိုမှုကို စတင်ခဲ့သည် · %2$s သင် - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်များသည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိပါသည် · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်များသည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိပါသည် @@ -2647,7 +2647,7 @@ ထပ်မံလေ့လာရန် ခေါ်ဆိုမှုထဲဝင်မယ် - Call back + ပြန်ခေါ်မည် ခေါ်ဆိုမှုသို့ပြန်သွားသည် ခေါ်ဆိုမှုထဲ လူပြည့်နေပါပြီ သူငယ်ချင်းများကို ဖိတ်ခေါ်ပါ @@ -4815,7 +4815,7 @@ မက်ဆေ့ချ် ပေါင်းထည့်ရန် ပြန်စာ ပေါင်းထည့်ရန် ဖော်ပြပါသို့ ပို့ရန် - View once media + တစ်ခါကြည့် မီဒီယာ တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော ဖိုင်များသည် ကြီးလွန်းနေပါသည် တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော ဖိုင်များသည် အကျုံးမဝင်ပါ ဖိုင်များစွာကို ရွေးထားပါသည် @@ -6598,9 +6598,9 @@ - Restoring backup… + ဘက်ခ်အပ်မှ ပြန်လည်ရယူနေသည်… - Downloading backup data… + ဘက်ခ်အပ် ဒေတာများကို ဒေါင်းလုဒ်လုပ်နေသည်… diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 11f574993d..133975bc68 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s er med i samtalen · %2$s - You are in the call · %1$s + Du er med i samtalen · %1$s - %1$s and %2$s are in the call · %3$s + %1$s og %2$s er med i samtalen · %3$s - %1$s is in the call + %1$s er med i samtalen - You are in the call + Du er med i samtalen - %1$s and %2$s are in the call + %1$s og %2$s er med i samtalen - The video call has ended + Videosamtalen er avsluttet - The video call has ended · %1$s + Videosamtalen er avsluttet · %1$s - Missed video call + Tapt videoanrop - Missed video call · %1$s + Ubesvart videoanrop · %1$s - Incoming video call + Innkommende videoanrop - Incoming video call · %1$s + Innkommende videoanrop · %1$s - Outgoing video call + Utgående videoanrop - Outgoing video call · %1$s + Utgående videoanrop · %1$s - You started a video call + Du startet en videosamtale - You started a video call · %1$s + Du startet en videosamtale · %1$s - %1$s started a video call + %1$s startet en videosamtale - %1$s started a video call · %2$s + %1$s startet en videosamtale · %2$s Deg - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, og %3$d annen er i gruppesamtalen · %4$s + %1$s, %2$s, og %3$d andre er i gruppesamtalen · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, og %3$d annen er i gruppesamtalen + %1$s, %2$s, og %3$d andre er i gruppesamtalen @@ -2734,7 +2734,7 @@ Lær mer Bli med i samtale - Call back + Ring tilbake Gå tilbake til samtale Samtalen er full Inviter venner @@ -4932,7 +4932,7 @@ Legg til en melding Legg til et svar Send til - View once media + Mediefiler som vises én gang Elementene er for store Elementene er ugyldige For mange valgte elementer @@ -6744,9 +6744,9 @@ - Restoring backup… + Gjenoppretter sikkerhetskopi … - Downloading backup data… + Laster ned sikkerhetskopi … diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 317c304e78..9a57d85815 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1582,15 +1582,15 @@ The video call has ended · %1$s - Missed video call + Gemiste video-oproep - Missed video call · %1$s + Video-oproep gemist · %1$s - Incoming video call + Inkomende video-oproep Incoming video call · %1$s - Outgoing video call + Uitgaande video-oproep Outgoing video call · %1$s @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s en %3$d ander zijn in deze groepsoproep aanwezig · %4$s + %1$s, %2$s en %3$d anderen zijn in deze groepsoproep aanwezig · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s en %3$d ander zijn in deze groepsoproep aanwezig + %1$s, %2$s en %3$d anderen zijn in deze groepsoproep aanwezig @@ -2734,7 +2734,7 @@ Meer lezen Aan oproep deelnemen - Call back + Terugbellen Naar de oproep terugkeren De oproep is vol Vrienden uitnodigen @@ -4932,7 +4932,7 @@ Berichttekst toevoegen Een reactie toevoegen Versturen naar - View once media + Eenmaligeweergave-media Een of meerdere items zijn te groot Een of meerdere items zijn ongeldig Te veel items geselecteerd diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 4a51f7cfa6..d51515e769 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s ਕਾਲ ਵਿੱਚ ਹਨ · %2$s - You are in the call · %1$s + ਤੁਸੀਂ ਕਾਲ ਵਿੱਚ ਹੋ · %1$s - %1$s and %2$s are in the call · %3$s + %1$s ਅਤੇ %2$s ਕਾਲ ਵਿੱਚ ਹਨ · %3$s - %1$s is in the call + %1$s ਕਾਲ ਵਿੱਚ ਹਨ - You are in the call + ਤੁਸੀਂ ਕਾਲ ਵਿੱਚ ਹੋ - %1$s and %2$s are in the call + %1$s ਅਤੇ %2$s ਕਾਲ ਵਿੱਚ ਹਨ - The video call has ended + ਵੀਡੀਓ ਕਾਲ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ - The video call has ended · %1$s + ਵੀਡੀਓ ਕਾਲ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ · %1$s - Missed video call + ਵੀਡੀਓ ਕਾਲ ਮਿਸ ਹੋਈ - Missed video call · %1$s + ਵੀਡੀਓ ਕਾਲ ਮਿਸ ਹੋਈ · %1$s - Incoming video call + ਇਨਕਮਿੰਗ ਵੀਡੀਓ ਕਾਲ - Incoming video call · %1$s + ਇਨਕਮਿੰਗ ਵੀਡੀਓ ਕਾਲ · %1$s - Outgoing video call + ਆਊਟਗੋਇੰਗ ਵੀਡੀਓ ਕਾਲ - Outgoing video call · %1$s + ਆਊਟਗੋਇੰਗ ਵੀਡੀਓ ਕਾਲ · %1$s - You started a video call + ਤੁਸੀਂ ਵੀਡੀਓ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ - You started a video call · %1$s + ਤੁਸੀਂ ਵੀਡੀਓ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ · %1$s - %1$s started a video call + %1$s ਨੇ ਵੀਡੀਓ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ - %1$s started a video call · %2$s + %1$s ਨੇ ਵੀਡੀਓ ਕਾਲ ਸ਼ੁਰੂ ਕੀਤੀ · %2$s ਤੁਸੀਂ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, ਅਤੇ %3$d ਹੋਰ ਵਿਅਕਤੀ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹੈ · %4$s + %1$s, %2$s, ਅਤੇ %3$d ਹੋਰ ਵਿਅਕਤੀ ਕਾਲ ਵਿੱਚ ਹਨ · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, ਅਤੇ %3$d ਹੋਰ ਵਿਅਕਤੀ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹੈ + %1$s, %2$s, ਅਤੇ %3$d ਹੋਰ ਵਿਅਕਤੀ ਗਰੁੱਪ ਕਾਲ ਵਿੱਚ ਹਨ @@ -2734,7 +2734,7 @@ ਹੋਰ ਜਾਣੋ ਕਾਲ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ - Call back + ਵਾਪਸ ਕਾਲ ਕਰੋ ਕਾਲ ’ਤੇ ਵਾਪਸ ਜਾਓ ਕਾਲ ਭਰੀ ਹੋਈ ਹੈ ਦੋਸਤਾਂ ਨੂੰ ਸੱਦਾ ਦਿਓ @@ -4932,7 +4932,7 @@ ਸੁਨੇਹਾ ਜੋੜੋ ਜਵਾਬ ਜੋੜੋ ਇਹਨਾਂ ਨੂੰ ਭੇਜੋ - View once media + ਮੀਡੀਆ ਜੋ ਇੱਕ ਵਾਰ ਦੇਖਿਆ ਜਾ ਸਕਦਾ ਹੈ ਇੱਕ ਜਾਂ ਵੱਧ ਚੀਜ਼ਾਂ ਬਹੁਤ ਵੱਡੀਆਂ ਸਨ ਇੱਕ ਜਾਂ ਵੱਧ ਆਈਟਮਾਂ ਵਾਜਬ ਨਹੀਂ ਹਨ ਬਹੁਤ ਵੱਧ ਚੀਜ਼ਾਂ ਚੁਣੀਆਂ ਗਈਆਂ @@ -6744,9 +6744,9 @@ - Restoring backup… + ਬੈਕਅੱਪ ਰੀਸਟੋਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ… - Downloading backup data… + ਬੈਕਅੱਪ ਡਾਟਾ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ… diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 1fb555f90b..82a081277a 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1688,15 +1688,15 @@ The video call has ended · %1$s - Missed video call + Nieodebrane połączenie wideo - Missed video call · %1$s + Nieodebrane połączenie wideo · %1$s - Incoming video call + Przychodzące połączenie wideo Incoming video call · %1$s - Outgoing video call + Wychodzące połączenie wideo Outgoing video call · %1$s @@ -1712,18 +1712,18 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s i %3$d inny uczestniczy w rozmowie · %4$s + %1$s, %2$s i %3$d innych uczestniczy w rozmowie · %4$s + %1$s, %2$s i %3$d innych uczestniczy w rozmowie · %4$s + %1$s, %2$s i %3$d innych uczestniczy w rozmowie · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s i %3$d inny uczestniczy w rozmowie + %1$s, %2$s i %3$d innych uczestniczy w rozmowie + %1$s, %2$s i %3$d innych uczestniczy w rozmowie + %1$s, %2$s i %3$d innych uczestniczy w rozmowie @@ -2908,7 +2908,7 @@ Dowiedz się więcej Dołącz do rozmowy - Call back + Oddzwoń Wróć do rozmowy Połączenie jest pełne Zaproś znajomych @@ -5166,7 +5166,7 @@ Dodaj wiadomość Dodaj odpowiedź Wyślij do - View once media + Multimedia jednorazowe Co najmniej jeden element był zbyt duży Co najmniej jeden element był nieprawidłowy Zbyt wiele wybranych elementów diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 8d5f3ec620..9ed653be4f 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1582,15 +1582,15 @@ The video call has ended · %1$s - Missed video call + Chamada de vídeo perdida - Missed video call · %1$s + Chamada de vídeo perdida · %1$s - Incoming video call + Chamada de vídeo recebida Incoming video call · %1$s - Outgoing video call + Chamada de vídeo efetuada Outgoing video call · %1$s @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s e mais %3$d pessoa estão na ligação em grupo · %4$s + %1$s, %2$s e mais %3$d pessoas estão na ligação em grupo · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s e mais %3$d pessoa estão na ligação em grupo + %1$s, %2$s e mais %3$d pessoas estão na ligação em grupo @@ -2734,7 +2734,7 @@ Saiba mais Participar da chamada - Call back + Retornar a chamada Voltar para a chamada A chamada está lotada Convidar amigos(as) @@ -4932,7 +4932,7 @@ Adicionar uma mensagem Responder Enviar para - View once media + Ver mídia temporária Um ou mais itens são muito grandes Um ou mais itens são inválidos Muitos itens foram selecionados diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 56048cebf0..88aec03d2c 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s está na chamada · %2$s - You are in the call · %1$s + Está na chamada · %1$s - %1$s and %2$s are in the call · %3$s + %1$s e %2$s estão na chamada · %3$s - %1$s is in the call + %1$s está na chamada - You are in the call + Está na chamada - %1$s and %2$s are in the call + %1$s e %2$s estão nesta chamada - The video call has ended + A videochamada terminou - The video call has ended · %1$s + A videochamada terminou · %1$s - Missed video call + Videochamada perdida - Missed video call · %1$s + Chamada de vídeo perdida · %1$s - Incoming video call + A receber videochamada - Incoming video call · %1$s + A receber videochamada · %1$s - Outgoing video call + A efetuar videochamada - Outgoing video call · %1$s + A efetuar videochamada · %1$s - You started a video call + Iniciou uma videochamada - You started a video call · %1$s + Iniciou uma videochamada · %1$s - %1$s started a video call + %1$s iniciou uma videochamada - %1$s started a video call · %2$s + %1$s iniciou uma videochamada · %2$s Você - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, e %3$d outro estão na chamada de grupo · %4$s + %1$s, %2$s, e outros %3$d estão na chamada de grupo · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, e %3$d outro estão na chamada de grupo + %1$s, %2$s, e outros %3$d estão na chamada de grupo @@ -2734,7 +2734,7 @@ Saber mais Entrar na chamada - Call back + Ligar de volta Regressar à chamada A chamada encontra-se completa Convidar amigos @@ -4932,7 +4932,7 @@ Adicionar uma mensagem Adicionar uma resposta Enviar para - View once media + Ver multimédia de visualização única Um ou mais itens são demasiado grandes Um ou mais itens são inválidos Demasiados itens selecionados @@ -6744,9 +6744,9 @@ - Restoring backup… + A restaurar cópia de segurança… - Downloading backup data… + A descarregar dados da cópia de segurança… diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 2b68e8604b..befa79123a 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1619,56 +1619,56 @@ - %1$s is in the call · %2$s + %1$s este în apel · %2$s - You are in the call · %1$s + Ești în apel · %1$s - %1$s and %2$s are in the call · %3$s + %1$s și %2$s sunt în apel · %3$s - %1$s is in the call + %1$s este în apel - You are in the call + Tu ești în apel - %1$s and %2$s are in the call + %1$s și %2$s sunt în acest apel - The video call has ended + Apelul video s-a încheiat - The video call has ended · %1$s + Apelul video s-a încheiat · %1$s - Missed video call + Apel video nepreluat - Missed video call · %1$s + Apel video nepreluat · %1$s - Incoming video call + Apel de intrare video - Incoming video call · %1$s + Apel de intrare video · %1$s - Outgoing video call + Apel de ieșire video - Outgoing video call · %1$s + Apel de ieșire video · %1$s - You started a video call + Ai început un apel video - You started a video call · %1$s + Ai început un apel video · %1$s - %1$s started a video call + %1$s a început un apel video - %1$s started a video call · %2$s + %1$s a început un apel video · %2$s Tu - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s și %3$d sunt în apelul de grup · %4$s + %1$s, %2$s și alte %3$d persoane sunt în apelul de grup · %4$s + %1$s, %2$s și alte %3$d persoane sunt în apelul de grup · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s și %3$d sunt în apelul de grup + %1$s, %2$s și alte %3$d persoane sunt în apelul de grup + %1$s, %2$s și alte %3$d persoane sunt în apelul de grup @@ -2821,7 +2821,7 @@ Află mai multe Alătură-te apelului - Call back + Apelează înapoi Întoarcere la apel Numărul maxim de participanți a fost atins Invită prieteni @@ -5049,7 +5049,7 @@ Adaugă un mesaj Adaugă un răspuns Trimite către - View once media + Mijloc media vizibil o singură dată Unul sau mai multe elemente au fost prea mari Unul sau mai multe elemente au fost invalide Prea multe elemente selectate @@ -6890,9 +6890,9 @@ - Restoring backup… + Se restaurează copia de rezervă… - Downloading backup data… + Se descarcă datele din copia de rezervă… diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 83f2f912cb..b9787e3e44 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s участвует в звонке · %2$s - You are in the call · %1$s + Вы участвуете в звонке · %1$s - %1$s and %2$s are in the call · %3$s + %1$s и %2$s участвуют в звонке · %3$s - %1$s is in the call + %1$s участвует в звонке - You are in the call + Вы участвуете в звонке - %1$s and %2$s are in the call + %1$s и %2$s участвуют в звонке - The video call has ended + Видеозвонок был завершён - The video call has ended · %1$s + Видеозвонок был завершён · %1$s - Missed video call + Пропущенный видеозвонок - Missed video call · %1$s + Пропущенный видеозвонок · %1$s - Incoming video call + Входящий видеозвонок - Incoming video call · %1$s + Входящий видеозвонок · %1$s - Outgoing video call + Исходящий видеозвонок - Outgoing video call · %1$s + Исходящий видеозвонок · %1$s - You started a video call + Вы начали видеозвонок - You started a video call · %1$s + Вы начали видеозвонок · %1$s - %1$s started a video call + %1$s начал(а) видеозвонок - %1$s started a video call · %2$s + %1$s начал(а) видеозвонок · %2$s Вы - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %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, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s и ещё %3$d человек в групповом звонке + %1$s, %2$s и ещё %3$d человека в групповом звонке + %1$s, %2$s и ещё %3$d человек в групповом звонке + %1$s, %2$s и ещё %3$d человек в групповом звонке @@ -2908,7 +2908,7 @@ Узнать больше Присоединиться к звонку - Call back + Перезвонить Вернуться к звонку Звонок заполнен Пригласить друзей @@ -5166,7 +5166,7 @@ Добавьте сообщение Добавьте ответ Отправить - View once media + Просмотреть одноразовые медиа Один или несколько элементов были слишком большими Один или несколько элементов были недействительными Выбрано слишком много элементов @@ -7036,9 +7036,9 @@ - Restoring backup… + Восстановление резервной копии… - Downloading backup data… + Скачиваем резервную копию… diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 655913e374..14489968f3 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s sa zúčastňuje hovoru · %2$s - You are in the call · %1$s + Zúčastňujete sa hovoru · %1$s - %1$s and %2$s are in the call · %3$s + %1$s a %2$s sa zúčastňujú hovoru · %3$s - %1$s is in the call + %1$s sa zúčastňuje hovoru - You are in the call + Zúčastňujete sa hovoru - %1$s and %2$s are in the call + %1$s a %2$s sa zúčastňujú hovoru - The video call has ended + Videohovor sa skončil - The video call has ended · %1$s + Videohovor sa skončil · %1$s - Missed video call + Zmeškaný videohovor - Missed video call · %1$s + Zmeškaný videohovor · %1$s - Incoming video call + Prichádzajúci videohovor - Incoming video call · %1$s + Prichádzajúci videohovor · %1$s - Outgoing video call + Odchádzajúci videohovor - Outgoing video call · %1$s + Odchádzajúci videohovor · %1$s - You started a video call + Začali ste videohovor - You started a video call · %1$s + Začali ste videohovor · %1$s - %1$s started a video call + Používateľ %1$s začal videohovor - %1$s started a video call · %2$s + Používateľ %1$s začal videohovor · %2$s Vy - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore + %1$s, %2$s a %3$d ďalší sú v skupinovom hovore @@ -2908,7 +2908,7 @@ Dozvedieť sa viac Pripojiť sa k hovoru - Call back + Zavolať späť Vrátiť sa k hovoru Hovor je plne obsadený Pozvať priateľov @@ -5166,7 +5166,7 @@ Pridajte správu Pridať odpoveď Odoslať - View once media + Médiá na jedno pozretie Jedna, alebo viac položiek bolo príliš veľkých Jedna, alebo viac položiek je neplatných Je vybratých príliš veľa položiek @@ -7036,9 +7036,9 @@ - Restoring backup… + Obnovuje sa záloha… - Downloading backup data… + Sťahujú sa zálohované údaje… diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index f2a53c94b8..ba88c31f5f 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s je v klicu · %2$s - You are in the call · %1$s + Ste v klicu · %1$s - %1$s and %2$s are in the call · %3$s + %1$s in %2$s sta v klicu · %3$s - %1$s is in the call + %1$s je v klicu - You are in the call + Ste v klicu - %1$s and %2$s are in the call + %1$s in %2$s sta v klicu - The video call has ended + Video klic se je končal - The video call has ended · %1$s + Video klic se je končal · %1$s - Missed video call + Zgrešen video klic - Missed video call · %1$s + Zamujen video klic · %1$s - Incoming video call + Dohodni video klic - Incoming video call · %1$s + Dohodni video klic · %1$s - Outgoing video call + Odhodni video klic - Outgoing video call · %1$s + Odhodni video klic · %1$s - You started a video call + Začeli ste video klic - You started a video call · %1$s + Začeli ste video klic · %1$s - %1$s started a video call + %1$s je začel_a video klic - %1$s started a video call · %2$s + %1$s je začel_a video klic · %2$s Vi - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, in še %3$d drug_a so v skupinskem klicu · %4$s + %1$s, %2$s, in še %3$d druga_i so v skupinskem klicu · %4$s + %1$s, %2$s, in še %3$d drugi_e so v skupinskem klicu · %4$s + %1$s, %2$s in še %3$d drugih je v skupinskem klicu · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, in še %3$d drug_a so v skupinskem klicu + %1$s, %2$s, in še %3$d druga_i so v skupinskem klicu + %1$s, %2$s in še %3$d drugi_e so v skupinskem klicu + %1$s, %2$s in še %3$d drugih je v skupinskem klicu @@ -2908,7 +2908,7 @@ Izvedite več Pridruži se klicu - Call back + Pokliči nazaj Zopet se pridruži klicu Klic je polno zaseden Vabilo prijateljem_icam @@ -5166,7 +5166,7 @@ Dodaj sporočilo Dodaj odgovor Poslano uporabniku_ci - View once media + Datoteka za enkratni ogled En ali več predmetov je bilo prevelikih En ali več predmetov je bilo neveljavnih Preveč izbranih predmetov @@ -7036,9 +7036,9 @@ - Restoring backup… + Obnavljanje podatkov iz varnostne kopije … - Downloading backup data… + Prenos varnostne kopije … diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 4a1a5067a9..5c73bf125b 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s është në telefonatë · %2$s - You are in the call · %1$s + Je në telefonatë · %1$s - %1$s and %2$s are in the call · %3$s + %1$s dhe %2$s janë në telefonatë · %3$s - %1$s is in the call + %1$s është në telefonatë - You are in the call + Je në telefonatë - %1$s and %2$s are in the call + %1$s dhe %2$s janë në telefonatë - The video call has ended + Thirrja me video ka përfunduar - The video call has ended · %1$s + Thirrja me video ka përfunduar · %1$s - Missed video call + Thirrje video e humbur - Missed video call · %1$s + Thirrje video e humbur · %1$s - Incoming video call + Thirrje video hyrëse - Incoming video call · %1$s + Thirrje hyrëse me video · %1$s - Outgoing video call + Thirrje video dalëse - Outgoing video call · %1$s + Thirrje dalëse me video · %1$s - You started a video call + Ti fillove një thirrje me video - You started a video call · %1$s + Ti fillove një thirrje me video · %1$s - %1$s started a video call + %1$s filloi një thirrje me video - %1$s started a video call · %2$s + %1$s filloi një thirrje me video · %2$s Ju - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, dhe %3$d tjetër gjenden te thirrja e grupit · %4$s + %1$s, %2$s, dhe %3$d të tjerë gjenden te thirrja e grupit · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, dhe %3$d tjetër gjenden te thirrja e grupit + %1$s, %2$s, dhe %3$d të tjerë gjenden te thirrja e grupit @@ -2734,7 +2734,7 @@ Mësoni më tepër Hyni në thirrje - Call back + Ktheji Thirrjen Kthehuni te thirrja Thirrja është plot Ftoni shokë @@ -4932,7 +4932,7 @@ Shtoni një mesazh Shtoni një përgjigje Dërguar - View once media + Media që mund të shihet vetëm një herë Një ose më tepër objekte qenë shumë të mëdhenj Një ose më tepër objekte qenë të pavlefshëm Shumë objekte të përzgjedhur @@ -6744,9 +6744,9 @@ - Restoring backup… + Rezervimi po ribëhet… - Downloading backup data… + Po shkarkohen të dhënat rezervë… diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index a57bfb83d6..3f19b1e087 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s је у позиву · %2$s - You are in the call · %1$s + Ви сте у позиву · %1$s - %1$s and %2$s are in the call · %3$s + %1$s и %2$s су у позиву · %3$s - %1$s is in the call + %1$s је у позиву - You are in the call + Ви сте у позиву - %1$s and %2$s are in the call + %1$s и %2$s су у позиву - The video call has ended + Видео-позив је завршен - The video call has ended · %1$s + Видео-позив је завршен · %1$s - Missed video call + Пропуштен видео позив - Missed video call · %1$s + пропуштен видео позив од · %1$s - Incoming video call + Долазни видео позив - Incoming video call · %1$s + Долазни видео-позив · %1$s - Outgoing video call + Одлазни видео позив - Outgoing video call · %1$s + Одлазни видео-позив · %1$s - You started a video call + Започели сте видео-позив - You started a video call · %1$s + Започели сте видео-позив · %1$s - %1$s started a video call + %1$s је започео/ла видео-позив - %1$s started a video call · %2$s + %1$s је започео/ла видео-позив · %2$s Ви - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + У групном позиву су %1$s, %2$s и још %3$d особа · %4$s + У групном позиву су %1$s, %2$s и још њих %3$d · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + У групном позиву су %1$s, %2$s и још %3$d особа + У групном позиву су %1$s, %2$s и још њих %3$d @@ -2734,7 +2734,7 @@ Сазнајте више Укључите се у позив - Call back + Позови Вратите се на позив Позив је пун Позовите пријатеље @@ -4932,7 +4932,7 @@ Напишите поруку Напишите одговор Пошаљи кориснику - View once media + Једнократни медиј Једна или више ставки су превелике Једна или више ставки су неважеће Изабрано је превише ставки @@ -6744,9 +6744,9 @@ - Restoring backup… + Враћање резервне копије… - Downloading backup data… + Преузимање података резервне копије… diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index ecb0a347b4..fed5af6ce9 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s är i samtalet · %2$s - You are in the call · %1$s + Du är i samtalet · %1$s - %1$s and %2$s are in the call · %3$s + %1$s och %2$s är i samtalet · %3$s - %1$s is in the call + %1$s är i samtalet - You are in the call + Du är i samtalet - %1$s and %2$s are in the call + %1$s och %2$s är i samtalet - The video call has ended + Videosamtalet har avslutats - The video call has ended · %1$s + Videosamtalet har avslutats · %1$s - Missed video call + Missat videosamtal - Missed video call · %1$s + Missat videosamtal · %1$s - Incoming video call + Inkommande videosamtal - Incoming video call · %1$s + Inkommande videosamtal · %1$s - Outgoing video call + Utgående videosamtal - Outgoing video call · %1$s + Utgående videosamtal · %1$s - You started a video call + Du startade ett videosamtal - You started a video call · %1$s + Du startade ett videosamtal · %1$s - %1$s started a video call + %1$s startade ett videosamtal - %1$s started a video call · %2$s + %1$s startade ett videosamtal · %2$s Du - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s och %3$d andra är i gruppsamtalet · %4$s + %1$s, %2$s och %3$d andra är i gruppsamtalet · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s och %3$d andra är i gruppsamtalet + %1$s, %2$s och %3$d andra är i gruppsamtalet @@ -2734,7 +2734,7 @@ Läs mer Gå med i samtalet - Call back + Ring tillbaka Återgå till samtal Samtalet är fullt Bjud in vänner @@ -4932,7 +4932,7 @@ Lägg till ett meddelande Lägg till ett svar Skicka till - View once media + Tillfällig mediefil Ett eller flera objekt var för stora Ett eller flera objekt var ogiltiga För många objekt valda @@ -6744,9 +6744,9 @@ - Restoring backup… + Återställer säkerhetskopia … - Downloading backup data… + Laddar ner säkerhetskopieringsdata … diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 1453d23cf3..fddbc5b91d 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s ako kwenye mazungumzo· %2$s - You are in the call · %1$s + Uko kwenye mazungumzo · %1$s - %1$s and %2$s are in the call · %3$s + %1$s na %2$s wako kwenye mazungumzo · %3$s - %1$s is in the call + %1$s ako kwenye mazungumzo - You are in the call + Uko kwenye mazungumzo - %1$s and %2$s are in the call + %1$s na %2$s wako kwenye mazungumzo - The video call has ended + Simu ya video imekwisha - The video call has ended · %1$s + Simu ya video imekwisha · %1$s - Missed video call + Simu ya video fifi - Missed video call · %1$s + Umekosa simu ya video · %1$s - Incoming video call + Simu ya video inayopigwa - Incoming video call · %1$s + Simu ya video inayoingia · %1$s - Outgoing video call + Simu ya video uliyopiga - Outgoing video call · %1$s + Simu ya video inayotoka · %1$s - You started a video call + Umepiga simu ya video - You started a video call · %1$s + Umepiga simu ya video · %1$s - %1$s started a video call + %1$s amepiga simu ya video - %1$s started a video call · %2$s + %1$s amepiga simu ya video · %2$s Wewe - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s na%3$d mwingine yuko kwenye mazungumzo ya simu ya kikundi · %4$s + %1$s, %2$s na %3$dwako kwenye mazungumzo ya simu ya kikundi · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s,na %3$d mwingine yuko kwenye mazungumzo ya simu ya kikundi + %1$s, %2$s na %3$d wengine wako kwenye mazungumzo ya simu ya kikundi @@ -2734,7 +2734,7 @@ Jifunze zaidi Jiunge na mazungumzo ya simu - Call back + Rudisha Simu Rudi kwenye mazungumzo ya simu Mazungumzo ya simu yamejaa Alika marafiki @@ -4932,7 +4932,7 @@ Ongeza ujumbe Ongeza jibu Tuma kwa - View once media + Tazama mara moja Kitu kimoja au zaidi vilikuwa vikubwa sana Kitu kimoja au zaidi vilikuwa batili Vitu vingi zaidi vimechaguliwa @@ -6744,9 +6744,9 @@ - Restoring backup… + Inarejesha chelezo… - Downloading backup data… + Inapakua data chelezo… diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 28122fb4c0..e33c457209 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s என்பவர் %2$s நேரத்தில் அழைப்பில் உள்ளார் - You are in the call · %1$s + நீங்கள் %1$s நேரத்தில் அழைப்பில் உள்ளீர்கள் - %1$s and %2$s are in the call · %3$s + %1$s மற்றும் %2$s ஆகியோ %3$s நேரத்தில் அழைப்பில் உள்ளனர் - %1$s is in the call + %1$s அழைப்பில் உள்ளார் - You are in the call + நீங்கள் அழைப்பில் உள்ளீர்கள் - %1$s and %2$s are in the call + %1$s மற்றும் %2$s ஆகியோர் அழைப்பில் உள்ளனர் - The video call has ended + வீடியோ அழைப்பு முடிந்தது - The video call has ended · %1$s + வீடியோ அழைப்பு %1$s முடிந்தது - Missed video call + தவறவிட்ட வீடியோ அழைப்பு - Missed video call · %1$s + தவறவிட்ட வீடியோ அழைப்பு . %1$s - Incoming video call + உள்வரும் வீடியோ அழைப்பு - Incoming video call · %1$s + %1$s நேரத்தில் இன்கமிங் வீடியோ அழைப்பு - Outgoing video call + வெளிச்செல்லும் வீடியோ அழைப்பு - Outgoing video call · %1$s + %1$s நேரத்தில் அவுட்கோயிங் வீடியோ அழைப்பு - You started a video call + நீங்கள் ஒரு வீடியோ அழைப்பைத் தொடங்கியுள்ளீர்கள் - You started a video call · %1$s + நீங்கள் %1$s நேரத்தில் ஒரு வீடியோ அழைப்பைத் தொடங்கியுள்ளீர்கள் - %1$s started a video call + %1$s என்பவர் ஒரு வீடியோ அழைப்பைத் தொடங்கினார் - %1$s started a video call · %2$s + %1$s என்பவர் %2$s நேரத்தில் ஒரு வீடியோ அழைப்பைத் தொடங்கினார் நீங்கள் - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s,. மற்றும் கூடுதலாக%3$d குழு உறுப்பினர் அழைப்பில் உள்ளனர் %4$s + %1$s, %2$s மற்றும் கூடுதலாக%3$d குழு உறுப்பினர்கள் அழைப்பில் உள்ளனர் %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, மற்றும் %3$dகூடுதலாக குழு உறுப்பினர்கள் அழைப்பில் உள்ளனர் + %1$s, %2$s, மற்றும் கூடுதலாக %3$d குழு உறுப்பினர்கள் அழைப்பில் உள்ளனர் @@ -2734,7 +2734,7 @@ மேலும் அறிக அழைப்பில் சேரவும் - Call back + மீண்டும் அழைக்கவும் அழைப்புக்குத் திரும்பு அழைப்பு நிரம்பியுள்ளது நண்பர்களை அழை @@ -4932,7 +4932,7 @@ கூட்டு ஒரு செய்தி ஒரு பதிலைச் சேர்க்கவும் இவருக்கு அனுப்பு - View once media + ஒரு முறை பார்க்கக்கூடிய மீடியா ஒன்று அல்லது அதற்கு மேற்பட்ட பொருட்கள் மிகப் பெரியதாக இருந்தன ஒன்று அல்லது அதற்கு மேற்பட்ட பொருட்கள் செல்லாதவை பல பொருட்கள் தேர்ந்தெடுக்கப்பட்டுள்ளன @@ -6744,9 +6744,9 @@ - Restoring backup… + காப்புப்பிரதியை மீட்டெடுக்கிறது… - Downloading backup data… + காப்புப்பிரதி தரவைப் பதிவிறக்குகிறது… diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 36a934a7e3..1891f4127d 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s కాల్‌లో ఉన్నారు · %2$s - You are in the call · %1$s + మీరు కాల్‌లో ఉన్నారు · %1$s - %1$s and %2$s are in the call · %3$s + %1$s మరియు %2$s కాల్‌లో ఉన్నారు · %3$s - %1$s is in the call + %1$s కాల్‌లో ఉన్నారు - You are in the call + మీరు కాల్‌లో ఉన్నారు - %1$s and %2$s are in the call + %1$s మరియు %2$s కాల్‌లో ఉన్నారు - The video call has ended + వీడియో కాల్ ముగిసింది - The video call has ended · %1$s + వీడియో కాల్ ముగిసింది · %1$s - Missed video call + మిస్డ్ వీడియో కాల్ - Missed video call · %1$s + తప్పిన వీడియో కాల్ . %1$s - Incoming video call + ఇన్‌కమింగ్ వీడియో కాల్ - Incoming video call · %1$s + ఇన్‌కమింగ్ వీడియో కాల్ · %1$s - Outgoing video call + అవుట్‌గోయింగ్ వీడియో కాల్ - Outgoing video call · %1$s + అవుట్‌గోయింగ్ వీడియో కాల్ · %1$s - You started a video call + మీరు వీడియో కాల్ ప్రారంభించారు - You started a video call · %1$s + మీరు వీడియో కాల్ ప్రారంభించారు · %1$s - %1$s started a video call + %1$s వీడియో కాల్ ప్రారంభించారు - %1$s started a video call · %2$s + %1$s వీడియో కాల్ ప్రారంభించారు · %2$s మీరు - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s మరియు %3$d ఇతర ఈ కాల్‌లో ఉన్నారు · %4$s + %1$s, %2$s, మరియు %3$d ఇతరులు ఈ సమూహ కాల్‌లో ఉన్నారు · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s మరియు %3$d ఇతర ఈ సమూహ కాల్‌లో ఉన్నారు + %1$s, %2$s, మరియు %3$d ఇతరులు ఈ సమూహ కాల్‌లో ఉన్నారు @@ -2734,7 +2734,7 @@ ఇంకా నేర్చుకో కాల్‌లో చేరండి - Call back + తిరిగి కాల్ చేయండి కాల్‌కు తిరిగి వెళ్ళు కాల్ నిండింది స్నేహితులను ఆహ్వానించండి @@ -4932,7 +4932,7 @@ ఒక సందేశాన్ని జోడించండి ప్రత్యుత్తరమును జోడించండి వీరికి పంపండి - View once media + ఒక్కసారి వీక్షించదగిన మీడియా ఒకటి లేదా అంతకంటే ఎక్కువ ఐటమ్‌లు చాలా పొడవుగా ఉన్నాయి ఒకటి లేదా అంతకంటే ఎక్కువ ఐటమ్‌లు చెల్లుబాటు కావు. చాలా ఎక్కువగా ఐటమ్‌లు ఎంచుకోబడ్డాయి @@ -6744,9 +6744,9 @@ - Restoring backup… + బ్యాకప్ పునరుద్ధరించబడుతోంది… - Downloading backup data… + బ్యాకప్ డేటాను డౌన్‌లోడ్ చేస్తోంది… diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 4ba017a517..6803ec89d3 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s อยู่ในสาย · %2$s - You are in the call · %1$s + คุณอยู่ในสาย · %1$s - %1$s and %2$s are in the call · %3$s + %1$s และ %2$s อยู่ในสาย · %3$s - %1$s is in the call + %1$s อยู่ในสาย - You are in the call + คุณอยู่ในสาย - %1$s and %2$s are in the call + %1$s และ %2$s อยู่ในสาย - The video call has ended + วิดีโอคอลสิ้นสุดแล้ว - The video call has ended · %1$s + วิดีโอคอลสิ้นสุดแล้ว · %1$s - Missed video call + วิดีโอคอลที่ไม่ได้รับ - Missed video call · %1$s + โทรวิดีโอที่พลาด · %1$s - Incoming video call + วิดีโอคอลขาเข้า - Incoming video call · %1$s + สายวิดีโอคอลเรียกเข้า · %1$s - Outgoing video call + วิดีโอคอลขาออก - Outgoing video call · %1$s + สายวิดีโอคอลโทรออก · %1$s - You started a video call + คุณเริ่มวิดีโอคอล - You started a video call · %1$s + คุณเริ่มวิดีโอคอล · %1$s - %1$s started a video call + %1$s เริ่มวิดีโอคอล - %1$s started a video call · %2$s + %1$s เริ่มวิดีโอคอล · %2$s คุณ - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s %2$s และ %3$d รวมถึงคนอื่นๆ อยู่ในการโทรแบบกลุ่ม %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s %2$s และ %3$d รวมถึงคนอื่นๆ อยู่ในการโทรแบบกลุ่ม @@ -2647,7 +2647,7 @@ เรียนรู้เพิ่มเติม เข้าร่วมการโทร - Call back + โทรกลับ กลับไปที่สาย ห้องเต็ม เชิญเพื่อน @@ -4815,7 +4815,7 @@ เพิ่มข้อความ เพิ่มการตอบกลับ ส่งไปให้ - View once media + ไฟล์สื่อแบบที่ดูได้ครั้งเดียว มีรายการที่ใหญ่เกินไปอย่างน้อยหนึ่งรายการ มีรายการที่ไม่ถูกต้องอย่างน้อยหนึ่งรายการ เลือกหลายรายการมากเกินไป @@ -6598,9 +6598,9 @@ - Restoring backup… + กำลังกู้คืนข้อมูลสำรอง… - Downloading backup data… + กำลังดาวน์โหลดข้อมูลสำรอง… diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index eb4b519208..1c7a77399b 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + Sila %1$s, %2$s, at %3$d pa ay nasa group call · %4$s + Sila %1$s, %2$s, at %3$d na iba pa ay nasa group call · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + Sila %1$s, %2$s, at %3$d pa ay nasa group call + Sila %1$s, %2$s, at %3$d pa ay nasa group call @@ -2734,7 +2734,7 @@ Matuto pa Mag-join sa call - Call back + Tawagan Pabalik Return to call Puno na ang call Imbitahan ang mga kaibigan diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 0e0afd4ebb..80c313591a 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1582,15 +1582,15 @@ The video call has ended · %1$s - Missed video call + Cevapsız görüntülü arama - Missed video call · %1$s + Yanıtsız görüntülü arama · %1$s - Incoming video call + Gelen görüntülü arama Incoming video call · %1$s - Outgoing video call + Giden görüntülü arama Outgoing video call · %1$s @@ -1606,14 +1606,14 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde · %4$s + %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde · %4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde + %1$s, %2$s, ve %3$d diğer kişi grup görüşmesinde @@ -2734,7 +2734,7 @@ Dahasını öğrenin Aramaya katıl - Call back + Geri Ara Aramaya dön Arama dolu Arkadaşlarını davet et @@ -4932,7 +4932,7 @@ İleti ekle Yanıt ekle Gönder: - View once media + Bir Kez Görüntülenen Medya Bir veya daha fazla öğe çok büyük Bir veya daha fazla öğe çok geçersiz Çok fazla öğe seçildi diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index b249b7d42b..8e451478e6 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -1529,15 +1529,15 @@ The video call has ended · %1$s - Missed video call + سۆزلەشمىگەن سىن چاقىرىش - Missed video call · %1$s + سۆزلەشمىگەن سىن چاقىرىش. %1$s - Incoming video call + كەلگەن ۋىدېيولۇق چاقىرىق Incoming video call · %1$s - Outgoing video call + قوبۇل قىلىنغان ۋىدېيولۇق چاقىرىق Outgoing video call · %1$s @@ -1553,12 +1553,12 @@ - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s، %2$s ۋە باشقا %3$d ئەزا گۇرۇپپا چاقىرىقى · «%4$s» دا - %1$s, %2$s, and %3$d others are in the group call + %1$s، %2$s ۋە باشقا %3$d ئەزا گۇرۇپپا چاقىرىقىدا @@ -2647,7 +2647,7 @@ كۆپرەك ئۆگىنىش چاقىرىققا قېتىل - Call back + قايتۇرۇپ چاقىر چاقىرىققا قايت چاقىرىق توشتى دوستلىرىڭىزنى تەكلىپ قىلىڭ @@ -4815,7 +4815,7 @@ بىز ئۇچۇر قوش جاۋاپ قوش تاپشۇرۇۋالغۇچى - View once media + بىرلا قېتىم كۆرۈلىدىغان مېدىيا بىر ياكى كۆپ تۈرنىڭ ھەجىمى بەك چوڭ بىر ياكى كۆپ تۈر ئىناۋەتسىز تاللانغان تۈر زىيادە كۆپ diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 6577393862..4993952204 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -46,7 +46,7 @@ Це назавжди розблокує Signal та сповіщення повідомлень. Вимкнути Помилка підключення до сервера! - PIN-коди, необхідні для блокування реєстрації. Щоб відключити PIN-коди, будь ласка, відключіть блокування реєстрації. + PIN-коди необхідні для блокування реєстрації. Для вимкнення PIN-кодів спершу необхідно вимкнути блокування реєстрації. PIN створений. PIN вимкнений. Записати кодову фразу для платежів @@ -740,8 +740,8 @@ Час резервного копіювання Перевірити парольну фразу резервної копії Випробуйте парольну фразу своєї резервної копії і переконайтеся, що вона збігається - Відключити Блокування Реєстрації? - Відключити + Увімкнути + Вимкнути "Щоб відновити з резервної копії, встановіть нову копію Signal. Відкрийте програму та натисніть \"Відновити резервну копію\", потім виберіть файл резервної копії. %1$s" Дізнатися більше В процесі… @@ -830,8 +830,8 @@ Неназваний пристрій - З\'язано %1$s - Остання активність %1$s + Зв\'язано %1$s + Остання активність: %1$s Сьогодні @@ -1342,10 +1342,10 @@ Видалення Видалення повідомлень… Збираю вкладення… - Сортувати як - Новіші - Старіші - Використання сховища + Сортування + Найновіші + Найдавніші + Розмір Загальне використання сховища Сітка Список @@ -1385,7 +1385,7 @@ Нагадати пізніше - Підтвердіть свій PIN-код Signal + Підтвердьте свій PIN-код Signal Іноді ми будемо просити вас підтвердити PIN-код, щоб ви його не забували. Підтвердити PIN-код Для початку @@ -1688,15 +1688,15 @@ The video call has ended · %1$s - Missed video call + Пропущений відеовиклик - Missed video call · %1$s + Пропущений відеодзвінок · %1$s - Incoming video call + Вхідний відеовиклик Incoming video call · %1$s - Outgoing video call + Вихідний відеовиклик Outgoing video call · %1$s @@ -1712,18 +1712,18 @@ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %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, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, та%3$d інший у цьому груповому дзвінку + %1$s, %2$s, та%3$d інші у цьому груповому дзвінку + %1$s, %2$s, та%3$d інші у цьому груповому дзвінку + %1$s, %2$s, та%3$d інші у цьому груповому дзвінку @@ -2291,10 +2291,10 @@ Включити блокування реєстрації? - Відключити Блокування Реєстрації? + Вимкнути блокування реєстрації? Якщо ви забудете свій PIN-код після повторної реєстрації в Signal, то втратите доступ до свого акаунту на 7 днів. Увімкнути - Відключити + Вимкнути Переглянути фото @@ -2908,7 +2908,7 @@ Дізнатися більше Приєднатися до дзвінка - Call back + Перетелефонувати Повернутись до виклику Дзвінок повний Запросити друзів @@ -2962,7 +2962,7 @@ Не зараз - Відключити + Вимкнути Історія змін коду безпеки @@ -3383,7 +3383,7 @@ Обмеження кількості повідомлень у чаті Зберігати повідомлення Очистити історію повідомлень - Прив\'язані пристрої + Зв\'язані пристрої Світла Темна Вигляд @@ -3516,7 +3516,7 @@ Під\'єднано до проксі З\'єднання не вдалася! Помилка з\'єднання із проксі. Перевірте адресу проксі і спробуйте ще раз. - Помилка з\'єднання із проксі. Ви можете відключити проксі в будь-який час в настройках. + Ви з\'єднані з проксі-сервером. Проксі-сервер можна завжди вимкнути в налаштуваннях. Успіх Не вдалось підключитися Введіть адресу проксі @@ -4076,7 +4076,7 @@ Папка Я записав цю фразу-пароль. Без неї я не зможу відновити резервну копію. Відновити резервну копію - Перенесення або відновлення акаунту + Перенести або відновити акаунт Перенести акаунт Пропустити Резервні копії чату @@ -4096,7 +4096,7 @@ Мітка часу резервної копії: %1$s Увімкнути локальні резервні копії? Увімкнути резервні копії - Підтвердіть, що ви розумієте про що йдеться поставивши галочку + Підтвердьте, що вам усе зрозуміло, поставивши галочку. Видалити резервні копії? Вимкнути та видалити усі локальні резервні копії? Видалити резервні копії @@ -4165,13 +4165,13 @@ Створіть PIN-код Змінити PIN-код Нагадування PIN-коду - Відключити + Вимкнути Підтвердити PIN - Підтвердіть свій PIN-код Signal + Підтвердьте свій PIN-код Signal PIN-код необхідно запам’ятати або надійно зберегти, оскільки його неможливо відновити. Якщо ви забудете свій PIN-код, то можете втратити дані при повторній реєстрації свого акаунту Signal. Неправильний PIN-код. Будь ласка спробуйте ще раз. Не вдалось включити блокування реєстрації. - Не вдалось відключити блокування реєстрації. + Не вдалося вимкнути блокування реєстрації. Немає Блокування доступу Ви повинні ввести Ваш ПІН блокування доступу @@ -4205,7 +4205,7 @@ Не вдалося видалити дані - Перенесення або відновлення акаунту + Перенести або відновити акаунт Якщо ви вже зареєстрували акаунт Signal, то можете перенести або відновити свій акаунт і повідомлення Перенести з пристрою Android Перенести акаунт і повідомлення зі старого пристрою Android. Необхідний доступ до старого пристрою. @@ -4267,7 +4267,7 @@ Wi-Fi На екрані Wi-Fi Direct, видаліть всі групи які запам\'ятались і від\'єднайте будь-які запрошені або з\'єднані пристрої. Екран Wi-Fi Direct - Спробуйте відключити і включити Wi-Fi на обох пристроях. + Спробуйте вимкнути і ввімкнути Wi-Fi на обох пристроях. Переконайтеся, що обидва пристрої знаходяться в режимі переносу. Перейти на сторінку підтримки Спробувати знову @@ -4557,7 +4557,7 @@ Збережіть свою фразу Оновити PIN-код - Якщо ви маєте високий баланс, пропонуємо створити літерно-цифровий PIN-код для більшої безпеки вашого облікового запису. + Якщо ви маєте високий баланс, пропонуємо створити літерно-цифровий PIN-код для більшої безпеки вашого акаунту. Оновити PIN-код @@ -4567,7 +4567,7 @@ Вимкнути гаманець Ваш баланс - Перш ніж відключити платежі, ми рекомендуємо перевести кошти в інший гаманець. Інакше кошти залишаться у вашому гаманці, прив\'язаному до Signal, якщо ви знову активуєте платежі. + Перед вимкненням платежів рекомендуємо вивести кошти в інший гаманець. Якщо ви цього не зробите, то в разі повторної активації платежів кошти залишаться у вашому гаманці, зв\'язаному із Signal. Перевести кошти, що залишились на балансі Вимкнути без переводу коштів Вимкнути @@ -5166,7 +5166,7 @@ Додати повідомлення Додати відповідь Надіслати - View once media + Переглянути одноразові медіафайли Один або більше елементів були завеликими Один або кілька елементів були недійсними Обрано забагато елементів @@ -5790,7 +5790,7 @@ Вилучити глядача? - %1$s все одно зможе переглядати цей пост, але не бачитиме майбутніх постів, якими ви поділитесь у сторі %2$s. + Користувач %1$s ще зможе бачити цей вміст, але вже не побачить вмісту історії «%2$s», яким ви поділитесь у майбутньому. Вилучити глядача diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index e7f3ab8ae2..18227f2c6a 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -1566,54 +1566,54 @@ - %1$s is in the call · %2$s + %1$s کال میں ہے · %2$s - You are in the call · %1$s + آپ کال میں ہیں · %1$s - %1$s and %2$s are in the call · %3$s + %1$s اور %2$s کال میں ہیں · %3$s - %1$s is in the call + %1$s کال میں ہے - You are in the call + آپ کال میں ہیں - %1$s and %2$s are in the call + %1$s اور%2$s کال میں ہیں - The video call has ended + ویڈیو کال ختم ہو چکی ہے - The video call has ended · %1$s + ویڈیو کال ختم ہو چکی ہے · %1$s - Missed video call + مسڈ ویڈیو کال - Missed video call · %1$s + مس ویڈیو کال · %1$s - Incoming video call + ان کمنگ ویڈیو کال - Incoming video call · %1$s + ان کمنگ ویڈیو کال · %1$s - Outgoing video call + آؤٹ گوئنگ ویڈیو کال - Outgoing video call · %1$s + آؤٹ گوئنگ ویڈیو کال · %1$s - You started a video call + آپ نے ایک ویڈیو کال شروع کی - You started a video call · %1$s + آپ نے ایک ویڈیو کال شروع کی · %1$s - %1$s started a video call + %1$s نے ایک ویڈیو کال شروع کی - %1$s started a video call · %2$s + %1$s نے ایک ویڈیو کال شروع کی · %2$s آپ - %1$s, %2$s, and %3$d other are in the group call · %4$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s،%2$s ، اور %3$d دوسرے گروپ کال میں شامل ہیں.%4$s + %1$s،%2$s ، اور%3$d دیگر گروپ کال میں ہیں ·%4$s - %1$s, %2$s, and %3$d other are in the group call - %1$s, %2$s, and %3$d others are in the group call + %1$s،%2$s ، اور %3$d دوسرے گروپ کال میں شامل ہیں + %1$s،%2$s ، اور %3$d دوسرے گروپ کال میں شامل ہیں @@ -2734,7 +2734,7 @@ مزید پڑھیں کال میں شامل ہوں - Call back + واپسی کال کریں کال پر واپس جائیں کال پوری ہے دوستوں کو مدعو کریں @@ -4932,7 +4932,7 @@ پیغام شامل کریں جواب شامل کریں بھیجیں بنام - View once media + ایک مرتبہ دیکھا جانے والا میڈیا ایک یا زائد آئٹمز بہت بڑی تھیں ایک یا زائد آئٹمز غلط تھیں بہت زیادہ آئٹمز منتخب کیے گئے ہیں @@ -6744,9 +6744,9 @@ - Restoring backup… + بیک اپ بحال کیا جا رہا ہے… - Downloading backup data… + بیک اپ ڈیٹا ڈاؤن لوڈ ہو رہا ہے… diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index e941ceb082..9b3888da9b 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s đang trong cuộc gọi · %2$s - You are in the call · %1$s + Bạn đang trong cuộc gọi · %1$s - %1$s and %2$s are in the call · %3$s + %1$s và %2$s đang trong cuộc gọi · %3$s - %1$s is in the call + %1$s đang trong cuộc gọi - You are in the call + Bạn đang trong cuộc gọi - %1$s and %2$s are in the call + %1$s và %2$s đang trong cuộc gọi - The video call has ended + Cuộc gọi video đã kết thúc - The video call has ended · %1$s + Cuộc gọi video đã kết thúc · %1$s - Missed video call + Cuộc gọi video bị nhỡ - Missed video call · %1$s + Cuộc gọi video nhỡ · %1$s - Incoming video call + Cuộc gọi video đến - Incoming video call · %1$s + Cuộc gọi video đến · %1$s - Outgoing video call + Cuộc gọi video đi - Outgoing video call · %1$s + Cuộc gọi video đi · %1$s - You started a video call + Bạn đã bắt đầu cuộc gọi video - You started a video call · %1$s + Bạn đã bắt đầu cuộc gọi video · %1$s - %1$s started a video call + %1$s đã bắt đầu cuộc gọi video - %1$s started a video call · %2$s + %1$s đã bắt đầu cuộc gọi video · %2$s Bạn - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s, %2$s, và %3$d người khác đang trong cuộc gọi nhóm · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, và %3$d người khác đang trong cuộc gọi nhóm @@ -2647,7 +2647,7 @@ Tìm hiểu thêm Tham gia cuộc gọi - Call back + Gọi lại Quay trở lại cuộc gọi Cuộc gọi đã đầy Mời bạn bè @@ -4815,7 +4815,7 @@ Thêm vào một tin nhắn Thêm trả lời Gửi đến - View once media + Đa phương tiện xem một lần Có một hoặc nhiều tập tin quá nặng Có một hoặc nhiều tập tin bị lỗi Bạn chọn quá nhiều tập tin @@ -6598,9 +6598,9 @@ - Restoring backup… + Đang khôi phục bản sao lưu… - Downloading backup data… + Đang tải dữ liệu sao lưu… diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index dd2da3ac98..5fec9c0254 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s 喺通話入面 · %2$s - You are in the call · %1$s + 你喺通話入面 · %1$s - %1$s and %2$s are in the call · %3$s + %1$s 同 %2$s 喺通話入面 · %3$s - %1$s is in the call + %1$s 喺通話入面 - You are in the call + 你喺通話入面 - %1$s and %2$s are in the call + %1$s 同 %2$s 喺通話入面 - The video call has ended + 呢個視像通話已經完咗 - The video call has ended · %1$s + 呢個視像通話已經完咗 · %1$s - Missed video call + 未接嘅視像通話 - Missed video call · %1$s + 未接嘅視像通話 · %1$s - Incoming video call + 打入嚟嘅視像通話 - Incoming video call · %1$s + 視像通話來電· %1$s - Outgoing video call + 打出去嘅視像通話 - Outgoing video call · %1$s + 打出嘅視像通話· %1$s - You started a video call + 你開始咗視像通話 - You started a video call · %1$s + 你開始咗視像通話 · %1$s - %1$s started a video call + %1$s 開始咗視像通話 - %1$s started a video call · %2$s + %1$s 開始咗視像通話 · %2$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s、%2$s 同其餘 %3$d 人嚟到成谷通話 · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s、%2$s 同其餘 %3$d 人嚟到成谷通話 @@ -2647,7 +2647,7 @@ 講多啲畀我聽 加入通話 - Call back + 打返俾佢 返回通話 通話已滿座 誠邀好友 @@ -4815,7 +4815,7 @@ 寫返句訊息 寫返句回覆 傳送至 - View once media + 閱後即焚媒體 有啲項目大得滯 有啲項目無效 揀選嘅項目多得滯 @@ -6598,9 +6598,9 @@ - Restoring backup… + 還原緊備份… - Downloading backup data… + 下載緊備份資料… diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c428e1b0f9..66f450c9cc 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s在通话中 · %2$s - You are in the call · %1$s + 你在通话中 · %1$s - %1$s and %2$s are in the call · %3$s + %1$s和%2$s在通话中 · %3$s - %1$s is in the call + %1$s在通话中 - You are in the call + 你在通话中 - %1$s and %2$s are in the call + %1$s和%2$s在通话中 - The video call has ended + 视频通话已结束 - The video call has ended · %1$s + 视频通话已结束 · %1$s - Missed video call + 未接视频来电 - Missed video call · %1$s + 未接视频来电 · %1$s - Incoming video call + 视频来电 - Incoming video call · %1$s + 视频来电 · %1$s - Outgoing video call + 拨出视频通话 - Outgoing video call · %1$s + 拨出视频通话 · %1$s - You started a video call + 你发起了视频通话 - You started a video call · %1$s + 你发起了视频通话 · %1$s - %1$s started a video call + %1$s发起了视频通话 - %1$s started a video call · %2$s + %1$s发起了视频通话 · %2$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s,%2$s 和其他%3$d位成员在群组通话中 · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s,%2$s 和其他%3$d位成员在群组通话中 @@ -2647,7 +2647,7 @@ 了解更多 加入通话 - Call back + 回拨 回到通话 通话人数已满 邀请好友 @@ -4815,7 +4815,7 @@ 添加消息 添加回复 发送给 - View once media + 阅后即焚媒体 一个或多个项目过大 一个或多个项目无效 选择的项目过多 @@ -6598,9 +6598,9 @@ - Restoring backup… + 正在恢复备份… - Downloading backup data… + 正在下载备份数据… diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 55bfc7d731..21f8d5539d 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s 現已在通話中 · %2$s - You are in the call · %1$s + 你現已在通話中 · %1$s - %1$s and %2$s are in the call · %3$s + %1$s 和 %2$s 現已在通話中 · %3$s - %1$s is in the call + %1$s 現已在通話中 - You are in the call + 你現已在通話中 - %1$s and %2$s are in the call + %1$s 和 %2$s 現已在通話中 - The video call has ended + 視訊通話已結束 - The video call has ended · %1$s + 視訊通話已結束 · %1$s - Missed video call + 未接的視訊通話 - Missed video call · %1$s + 已錯過視像通話 · %1$s - Incoming video call + 視訊通話來電 - Incoming video call · %1$s + 視訊通話來電 · %1$s - Outgoing video call + 撥打視訊通話 - Outgoing video call · %1$s + 撥打視訊通話 · %1$s - You started a video call + 你開始了視訊通話 - You started a video call · %1$s + 你開始了視訊通話 · %1$s - %1$s started a video call + %1$s 開始了視訊通話 - %1$s started a video call · %2$s + %1$s 開始了視訊通話 · %2$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s、%2$s 和另 %3$d 人現已在群組通話中 · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s、%2$s 和另 %3$d 人現已在群組通話中 @@ -2647,7 +2647,7 @@ 了解更多 加入通話 - Call back + 回撥 返回通話 通話已滿座 邀請好友 @@ -4815,7 +4815,7 @@ 新增訊息 新增回覆 傳送給 - View once media + 閱後即焚媒體檔案 部分項目太大 部分項目無效 已選取太多項目 @@ -6598,9 +6598,9 @@ - Restoring backup… + 正在還原備份… - Downloading backup data… + 正在下載備份資料… diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index ec4c1833c3..5eee03d2fb 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1513,52 +1513,52 @@ - %1$s is in the call · %2$s + %1$s 現已在通話中 · %2$s - You are in the call · %1$s + 你現已在通話中 · %1$s - %1$s and %2$s are in the call · %3$s + %1$s 和 %2$s 現已在通話中 · %3$s - %1$s is in the call + %1$s 現已在通話中 - You are in the call + 你現已在通話中 - %1$s and %2$s are in the call + %1$s 和 %2$s 現已在通話中 - The video call has ended + 視訊通話已結束 - The video call has ended · %1$s + 視訊通話已結束 · %1$s - Missed video call + 錯過視訊電話 - Missed video call · %1$s + 錯過的視訊電話· %1$s - Incoming video call + 視訊電話來電 - Incoming video call · %1$s + 視訊通話來電 · %1$s - Outgoing video call + 撥出視訊電話 - Outgoing video call · %1$s + 撥打視訊通話 · %1$s - You started a video call + 你開始了視訊通話 - You started a video call · %1$s + 你開始了視訊通話 · %1$s - %1$s started a video call + %1$s 開始了視訊通話 - %1$s started a video call · %2$s + %1$s 開始了視訊通話 · %2$s - %1$s, %2$s, and %3$d others are in the group call · %4$s + %1$s、%2$s 和另外 %3$d 人在群組通話中 · %4$s - %1$s, %2$s, and %3$d others are in the group call + %1$s, %2$s, 及 %3$d 及其他人在群組通話中 @@ -2647,7 +2647,7 @@ 了解更多 加入通話 - Call back + 回撥 返回通話 通話人數已滿 邀請好友 @@ -4815,7 +4815,7 @@ 新增訊息 新增回覆 傳送給 - View once media + 一次性多媒體內容 一件或多件項目容量太大 一項或多項無效 選擇的項目太多 @@ -6598,9 +6598,9 @@ - Restoring backup… + 正在還原備份… - Downloading backup data… + 正在下載備份資料… diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index 4ca12e906a..c662a4619e 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[]{"172.253.62.121"}""" -rootProject.extra["cdn_ips"] = """new String[]{"18.160.18.106","18.160.18.114","18.160.18.42","18.160.18.45"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.251.40.179"}""" +rootProject.extra["cdn_ips"] = """new String[]{"18.238.49.106","18.238.49.6","18.238.49.66","18.238.49.90"}""" 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.36.104.134"}""" From a9ea3854d22d9faa128a35e829cc0d8fb32e97ff Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 22 Apr 2024 17:06:18 -0400 Subject: [PATCH 044/113] Bump version to 7.5.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 43a77b4d4a..008d90fc2f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1411 -val canonicalVersionName = "7.5.0" +val canonicalVersionCode = 1412 +val canonicalVersionName = "7.5.1" val postFixSize = 100 val abiPostFix: Map = mapOf( From 1fa53cfcb853c57d2515c1dcefbd67e8f77b5971 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Tue, 23 Apr 2024 10:22:01 -0400 Subject: [PATCH 045/113] Prevent crash on attachment delete while voice note system tone is playing. --- .../voice/VoiceNotePlaybackService.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java index a628098137..8ed7ba2dec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java @@ -34,6 +34,7 @@ import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.database.DatabaseObserver; import org.thoughtcrime.securesms.database.MessageTable; @@ -207,19 +208,31 @@ private void onAttachmentDeleted() { Log.d(TAG, "Current item is null or playback properties are null."); return; } - final Uri currentUi = currentItem.playbackProperties.uri; - final DatabaseAttachment attachment = SignalDatabase.attachments().getAttachment(new PartUriParser(currentUi).getPartId()); - if (attachment == null) { - player.stop(); - int playingIndex = player.getCurrentMediaItemIndex(); - player.removeMediaItem(playingIndex); - Log.d(TAG, "Currently playing item removed."); - } else { - Log.d(TAG, "Attachment was not null, therefore not deleted, therefore no action taken."); + + final Uri currentlyPlayingUri = currentItem.playbackProperties.uri; + + if (currentlyPlayingUri == VoiceNoteMediaItemFactory.NEXT_URI || currentlyPlayingUri == VoiceNoteMediaItemFactory.END_URI) { + Log.v(TAG, "Attachment deleted while voice note service was playing a system tone."); + } + + try { + final AttachmentId partId = new PartUriParser(currentlyPlayingUri).getPartId(); + final DatabaseAttachment attachment = SignalDatabase.attachments().getAttachment(partId); + if (attachment == null) { + player.stop(); + int playingIndex = player.getCurrentMediaItemIndex(); + player.removeMediaItem(playingIndex); + Log.d(TAG, "Currently playing item removed."); + } else { + Log.d(TAG, "Attachment was not null, therefore not deleted, therefore no action taken."); + } + } catch (NumberFormatException ex) { + Log.w(TAG, "Could not parse currently playing URI into an attachmentId.", ex); } } }); } + /** * Some devices, such as the ASUS Zenfone 8, erroneously report multiple broadcast receivers for {@value Intent#ACTION_MEDIA_BUTTON} in the package manager. * This triggers a failure within the {@link MediaSession} initialization and throws an {@link IllegalStateException}. From 0536628da33e0ab59319c1610b9e25ded1881e05 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 10:44:09 -0400 Subject: [PATCH 046/113] Stagger app wake ups due to analyze database alarm. --- .../securesms/service/AnalyzeDatabaseAlarmListener.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/AnalyzeDatabaseAlarmListener.kt b/app/src/main/java/org/thoughtcrime/securesms/service/AnalyzeDatabaseAlarmListener.kt index 9414b40726..779b662e81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/AnalyzeDatabaseAlarmListener.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/AnalyzeDatabaseAlarmListener.kt @@ -5,6 +5,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobs.AnalyzeDatabaseJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.util.toMillis +import java.security.SecureRandom import java.time.LocalDateTime /** @@ -43,12 +44,13 @@ class AnalyzeDatabaseAlarmListener : PersistentAlarmManagerListener() { } private fun getNextTime(): Long { + val random = SecureRandom() return LocalDateTime .now() .plusDays(1) - .withHour(3) - .withMinute(0) - .withSecond(0) + .withHour(2 + random.nextInt(3)) + .withMinute(random.nextInt(60)) + .withSecond(random.nextInt(60)) .toMillis() } } From c811bdcffa6ed1946711b74a459fcde375df14a5 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 11:26:36 -0400 Subject: [PATCH 047/113] Fix benchmark test messages. --- .../java/org/signal/benchmark/setup/TestMessages.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/benchmark/java/org/signal/benchmark/setup/TestMessages.kt b/app/src/benchmark/java/org/signal/benchmark/setup/TestMessages.kt index 85eec172cd..8651bff5b0 100644 --- a/app/src/benchmark/java/org/signal/benchmark/setup/TestMessages.kt +++ b/app/src/benchmark/java/org/signal/benchmark/setup/TestMessages.kt @@ -1,5 +1,6 @@ package org.signal.benchmark.setup +import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.PointerAttachment import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.database.MessageType @@ -9,7 +10,6 @@ import org.thoughtcrime.securesms.mms.IncomingMessage import org.thoughtcrime.securesms.mms.OutgoingMessage import org.thoughtcrime.securesms.mms.QuoteModel import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.releasechannel.ReleaseChannel import org.whispersystems.signalservice.api.messages.SignalServiceAttachment import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId @@ -144,7 +144,7 @@ object TestMessages { } private fun imageAttachment(): SignalServiceAttachmentPointer { return SignalServiceAttachmentPointer( - ReleaseChannel.CDN_NUMBER, + Cdn.S3.cdnNumber, SignalServiceAttachmentRemoteId.from(""), "image/webp", null, @@ -167,7 +167,7 @@ object TestMessages { private fun voiceAttachment(): SignalServiceAttachmentPointer { return SignalServiceAttachmentPointer( - ReleaseChannel.CDN_NUMBER, + Cdn.S3.cdnNumber, SignalServiceAttachmentRemoteId.from(""), "audio/aac", null, From 87606af29c17a3c0bdda1850bf3a956f5a577be8 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 11:30:56 -0400 Subject: [PATCH 048/113] Update translations and other static files. --- app/src/main/res/values-az/strings.xml | 34 +++++------ app/src/main/res/values-da/strings.xml | 34 +++++------ app/src/main/res/values-fa/strings.xml | 34 +++++------ app/src/main/res/values-ga/strings.xml | 34 +++++------ app/src/main/res/values-in/strings.xml | 6 +- app/src/main/res/values-iw/strings.xml | 32 +++++------ app/src/main/res/values-kn/strings.xml | 42 +++++++------- app/src/main/res/values-ky/strings.xml | 34 +++++------ app/src/main/res/values-nl/strings.xml | 34 +++++------ app/src/main/res/values-pl/strings.xml | 32 +++++------ app/src/main/res/values-pt-rBR/strings.xml | 34 +++++------ app/src/main/res/values-tl/strings.xml | 66 +++++++++++----------- app/src/main/res/values-tr/strings.xml | 34 +++++------ app/src/main/res/values-ug/strings.xml | 32 +++++------ app/src/main/res/values-uk/strings.xml | 56 +++++++++--------- app/static-ips.gradle.kts | 4 +- 16 files changed, 271 insertions(+), 271 deletions(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index ead4bc2837..7458806506 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -1566,21 +1566,21 @@ - %1$s is in the call · %2$s + %1$s zəngə qoşulub · %2$s - You are in the call · %1$s + Zəngə qoşuldunuz · %1$s - %1$s and %2$s are in the call · %3$s + %1$s və %2$s zəngə qoşuldular · %3$s - %1$s is in the call + %1$s zəngə qoşuldu - You are in the call + Siz zəngə qoşuldunuz - %1$s and %2$s are in the call + %1$s və %2$s zəngə qoşuldular - The video call has ended + Video zəng başa çatdı - The video call has ended · %1$s + Video zəng · %1$s başa çatdı Buraxılmış video zəng @@ -1588,19 +1588,19 @@ Gələn video zəng - Incoming video call · %1$s + Gələn video zəng · %1$s Gedən video zəng - Outgoing video call · %1$s + Gedən video zəng · %1$s - You started a video call + Video zəngə başladınız - You started a video call · %1$s + Video zəngə başladınız · %1$s - %1$s started a video call + %1$s video zəngə başladı - %1$s started a video call · %2$s + %1$s video zəngə başladı · %2$s Siz @@ -4932,7 +4932,7 @@ Bir mesaj əlavə et Bir cavab əlavə et Göndər - View Once Media + Bir dəfə göstərilən media faylı Bir və ya daha çox element çox böyük idi Bir və ya daha çox element etibarsız idi Həddindən çox element seçildi @@ -6744,9 +6744,9 @@ - Restoring backup… + Ehtiyat nüsxə bərpa olunur… - Downloading backup data… + Ehtiyat nüsxə verilənləri endirilir… diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 9c6b0c596d..098c920a32 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1566,21 +1566,21 @@ - %1$s is in the call · %2$s + %1$s er med i opkaldet · %2$s - You are in the call · %1$s + Du er med i opkaldet · %1$s - %1$s and %2$s are in the call · %3$s + %1$s og %2$s er med i opkaldet · %3$s - %1$s is in the call + %1$s er med i opkaldet - You are in the call + Du er med i opkaldet - %1$s and %2$s are in the call + %1$s og %2$s er med i opkaldet - The video call has ended + Videoopkaldet er afsluttet - The video call has ended · %1$s + Videoopkaldet er afsluttet · %1$s Ubesvaret videoopkald @@ -1588,19 +1588,19 @@ Indgående videoopkald - Incoming video call · %1$s + Indgående videoopkald · %1$s Udgående videoopkald - Outgoing video call · %1$s + Udgående videoopkald · %1$s - You started a video call + Du startede et videoopkald - You started a video call · %1$s + Du startede et videoopkald · %1$s - %1$s started a video call + %1$s startede et videoopkald - %1$s started a video call · %2$s + %1$s startede et videoopkald · %2$s Dig @@ -4932,7 +4932,7 @@ Tilføj en besked Tilføj et svar Send til - Medier, som kan ses én gang + Se medier én gang Et eller flere emner var for store Et eller flere emner var ugyldige Der er valgt for mange emner @@ -6744,9 +6744,9 @@ - Restoring backup… + Gendannelse af sikkerhedskopi… - Downloading backup data… + Downloader sikkerhedskopidata… diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index f0fd1303cd..13624afbf3 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -1566,21 +1566,21 @@ - %1$s is in the call · %2$s + ‏%1$s در این تماس است · %2$s - You are in the call · %1$s + شما در این تماس هستید · %1$s - %1$s and %2$s are in the call · %3$s + ‏%1$s و %2$s در این تماس هستند · %3$s - %1$s is in the call + ‏%1$s در این تماس است - You are in the call + شما در این تماس هستید - %1$s and %2$s are in the call + ‏%1$s و %2$s در این تماس هستند - The video call has ended + تماس تصویری پایان یافت - The video call has ended · %1$s + تماس تصویری پایان یافت · %1$s تماس تصویری ازدست‌رفته @@ -1588,19 +1588,19 @@ تماس تصویری دریافتی - Incoming video call · %1$s + تماس تصویری دریافتی · %1$s تماس تصویری خروجی - Outgoing video call · %1$s + تماس تصویری خروجی · %1$s - You started a video call + یک تماس تصویری شروع کردید - You started a video call · %1$s + یک تماس تصویری شروع کردید · %1$s - %1$s started a video call + ‏%1$s یک تماس تصویری شروع کرد - %1$s started a video call · %2$s + ‏%1$s یک تماس تصویری شروع کرد · %2$s شما @@ -4932,7 +4932,7 @@ افزودن یک پیام یک پاسخ اضافه کنید ارسال به - رسانه با قابلیت یک‌بار مشاهده + فایل رسانه با قابلیت یک‌بار مشاهده یک یا چند مورد بسیار بزرگ بودند یک یا چند مورد نامعتبر بودند موارد بسیار زیادی انتخاب شده است @@ -6744,9 +6744,9 @@ - Restoring backup… + در حال بازیابی پشتیبان… - Downloading backup data… + در حال بارگیری داده‌های پشتیبان… diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index b9c64d93f2..bfd46de2ad 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -1725,21 +1725,21 @@ - %1$s is in the call · %2$s + Tá %1$s sa ghlao · %2$s - You are in the call · %1$s + Tá tusa sa ghlao · %1$s - %1$s and %2$s are in the call · %3$s + Tá %1$s agus %2$s sa ghlao · %3$s - %1$s is in the call + Tá %1$s sa ghlao - You are in the call + Tá tusa sa ghlao - %1$s and %2$s are in the call + Tá %1$s agus %2$s sa ghlao - The video call has ended + Tá an físghlao críochnaithe - The video call has ended · %1$s + Tá an físghlao críochnaithe · %1$s Físghlao caillte @@ -1747,19 +1747,19 @@ Físghlao isteach - Incoming video call · %1$s + Físghlao isteach · %1$s Físghlao amach - Outgoing video call · %1$s + Físghlao amach · %1$s - You started a video call + Chuir tú tús le físghlao - You started a video call · %1$s + Chuir tú tús le físghlao · %1$s - %1$s started a video call + Chuir %1$s tús le físghlao - %1$s started a video call · %2$s + Chuir %1$s tús le físghlao · %2$s Tusa @@ -5283,7 +5283,7 @@ Cuir teachtaireacht leis Cuir freagra leis Seol chuig - Meáin Amhairc Aonuaire + Meáin amhairc aonuaire Bhí mír amháin nó níos mó rómhór Bhí mír amháin nó níos mó neamhbhailí Roghnaíodh an iomarca nithe @@ -7182,9 +7182,9 @@ - Restoring backup… + Cúltaca á chur ar ais… - Downloading backup data… + Sonraí cúltaca á n-íoslódáil… diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index eeead0a658..32f285e1ae 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1553,12 +1553,12 @@ - %1$s, %2$s, dan %3$d lainnya berada di panggilan grup. %4$s + %1$s, %2$s, dan %3$d lainnya ada dalam panggilan grup · %4$s - %1$s, %2$s, dan %3$d lainnya berada di panggilan grup + %1$s, %2$s, dan %3$d lainnya ada dalam panggilan grup @@ -2647,7 +2647,7 @@ Pelajari lebih lanjut Bergabung panggilan - Panggil Ulang + Panggil ulang Kembali ke panggilan Panggilan penuh Undang teman diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 4674eb5072..850a9d5eb2 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1672,21 +1672,21 @@ - %1$s is in the call · %2$s + %1$s בשיחה הזו · %2$s - You are in the call · %1$s + את/ה בשיחה הזו · %1$s - %1$s and %2$s are in the call · %3$s + %1$s וגם %2$s בשיחה הזו · %3$s - %1$s is in the call + %1$s בשיחה הזו - You are in the call + את/ה בשיחה הזו - %1$s and %2$s are in the call + %1$s וגם %2$s בשיחה זו - The video call has ended + שיחת הווידאו הסתיימה - The video call has ended · %1$s + שיחת הווידאו הסתיימה · %1$s שיחת וידאו שלא נענתה @@ -1694,19 +1694,19 @@ שיחת וידאו נכנסת - Incoming video call · %1$s + שיחת וידאו נכנסת · %1$s שיחת וידאו יוצאת - Outgoing video call · %1$s + שיחת וידאו יוצאת · %1$s - You started a video call + התחלת שיחת וידאו - You started a video call · %1$s + התחלת שיחת וידאו · %1$s - %1$s started a video call + %1$s התחיל/ה שיחת וידאו - %1$s started a video call · %2$s + %1$s התחיל/ה שיחת וידאו · %2$s את/ה @@ -7036,9 +7036,9 @@ - Restoring backup… + משחזרים גיבוי… - Downloading backup data… + מורידים נתוני גיבוי… diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index ef4f737e55..1c89373a33 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1566,41 +1566,41 @@ - %1$s is in the call · %2$s + %1$s ಅವರು ಕರೆಯಲ್ಲಿದ್ದಾರೆ · %2$s - You are in the call · %1$s + ನೀವು ಕರೆಯಲ್ಲಿದ್ದೀರಿ · %1$s - %1$s and %2$s are in the call · %3$s + %1$s ಮತ್ತು %2$s ಅವರು ಕರೆಯಲ್ಲಿದ್ದಾರೆ · %3$s - %1$s is in the call + %1$s ಅವರು ಕರೆಯಲ್ಲಿದ್ದಾರೆ - You are in the call + ನೀವು ಕರೆಯಲ್ಲಿದ್ದೀರಿ - %1$s and %2$s are in the call + %1$s ಮತ್ತು %2$s ಕರೆಯಲ್ಲಿದ್ದಾರೆ - The video call has ended + ವೀಡಿಯೊ ಕರೆ ಮುಕ್ತಾಯವಾಗಿದೆ - The video call has ended · %1$s + ವೀಡಿಯೊ ಕರೆ ಮುಕ್ತಾಯವಾಗಿದೆ · %1$s - ಮಿಸ್ಡ್ ವೀಡಿಯೊ ಕಾಲ್ + ಮಿಸ್ಡ್ ವೀಡಿಯೊ ಕರೆ - ಮಿಸ್ಡ್ ವೀಡಿಯೋ ಕಾಲ್ · %1$s + ಮಿಸ್ಡ್ ವೀಡಿಯೊ ಕರೆ · %1$s - ಒಳಬರುವ ವೀಡಿಯೊ ಕಾಲ್ + ಒಳಬರುವ ವೀಡಿಯೊ ಕರೆ - Incoming video call · %1$s + ಒಳಬರುವ ವೀಡಿಯೊ ಕರೆ · %1$s - ಹೊರಹೋಗುವ ವೀಡಿಯೊ ಕಾಲ್ + ಹೊರಹೋಗುವ ವೀಡಿಯೊ ಕರೆ - Outgoing video call · %1$s + ಹೊರಹೋಗುವ ವೀಡಿಯೊ ಕರೆ · %1$s - You started a video call + ನೀವು ವೀಡಿಯೊ ಕರೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ್ದೀರಿ - You started a video call · %1$s + ನೀವು ವೀಡಿಯೊ ಕರೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ್ದೀರಿ · %1$s - %1$s started a video call + %1$s ಅವರು ವೀಡಿಯೊ ಕರೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ್ದಾರೆ - %1$s started a video call · %2$s + %1$s ಅವರು ವೀಡಿಯೊ ಕರೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ್ದಾರೆ · %2$s ನೀವು @@ -4932,7 +4932,7 @@ ಮೆಸೇಜ್ ಸೇರಿಸಿ ಉತ್ತರ ಸೇರಿಸಿ ಇವರಿಗೆ ಕಳುಹಿಸಿ - View Once Media + ಒಮ್ಮೆ ವೀಕ್ಷಿಸಬಹುದಾದ ಮೀಡಿಯಾ ಒಂದು ಅಥವಾ ಹೆಚ್ಚು ಐಟಂಗಳು ತೀರಾ ದೊಡ್ಡದಾಗಿವೆ ಒಂದು ಅಥವಾ ಹೆಚ್ಚು ಐಟಂಗಳು ಅಮಾನ್ಯವಾಗಿವೆ ತುಂಬಾ ಐಟಂಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ @@ -6744,9 +6744,9 @@ - Restoring backup… + ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲಾಗುತ್ತಿದೆ… - Downloading backup data… + ಬ್ಯಾಕಪ್ ಡೇಟಾವನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ… diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 1e98e36950..a8878ea351 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -1513,21 +1513,21 @@ - %1$s is in the call · %2$s + %1$s сүйлөшүп жатат · %2$s - You are in the call · %1$s + Сүйлөшүп жатасыз · %1$s - %1$s and %2$s are in the call · %3$s + %1$s менен %2$s сүйлөшүп жатышат · %3$s - %1$s is in the call + %1$s сүйлөшүп жатат - You are in the call + Сүйлөшүп жатасыз - %1$s and %2$s are in the call + %1$s менен %2$s сүйлөшүп жатат - The video call has ended + Видео чалуу бүттү - The video call has ended · %1$s + Видео чалуу бүттү · %1$s Кабыл алынбаган аудио чалуу @@ -1535,19 +1535,19 @@ Келүүчү аудио чалуу - Incoming video call · %1$s + Бирөө видео режиминде чалды · %1$s Чыгуучу аудио чалуу - Outgoing video call · %1$s + Бирөөгө видео режиминде чалдыңыз · %1$s - You started a video call + Видео режиминде чалып баштадыңыз - You started a video call · %1$s + Видео режиминде чалып баштадыңыз · %1$s - %1$s started a video call + %1$s видео режиминде чалып баштады - %1$s started a video call · %2$s + %1$s видео режиминде чалып баштады · %2$s Сиз @@ -4815,7 +4815,7 @@ Билдирүү кошуу Жооп кошуу Кимге - View once media + Бир жолу көрүлүүчү MMS Бир же бир нече нерсе өтө чоң болуп чыкты Бир же бир нече нерсе жараксыз Өтө көп нерсе тандалды @@ -6598,9 +6598,9 @@ - Restoring backup… + Камдык көчүрмөлөр калыбына келтирилүүдө… - Downloading backup data… + Камдык көчүрмөлөр жүктөлүп алынууда… diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 9a57d85815..e53d09058d 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1566,41 +1566,41 @@ - %1$s is in the call · %2$s + %1$s is aanwezig in de oproep · %2$s - You are in the call · %1$s + Jij bent aanwezig in de oproep · %1$s - %1$s and %2$s are in the call · %3$s + %1$s en %2$s zijn aanwezig in de oproep · %3$s - %1$s is in the call + %1$s is aanwezig in de oproep - You are in the call + Jij bent aanwezig in de oproep - %1$s and %2$s are in the call + %1$s en %2$s zijn aanwezig in de oproep - The video call has ended + De video-oproep is beëindigd - The video call has ended · %1$s + De video-oproep is beëindigd · %1$s Gemiste video-oproep - Video-oproep gemist · %1$s + Gemiste video-oproep · %1$s Inkomende video-oproep - Incoming video call · %1$s + Inkomende video-oproep · %1$s Uitgaande video-oproep - Outgoing video call · %1$s + Uitgaande video-oproep · %1$s - You started a video call + Je bent een video-oproep gestart - You started a video call · %1$s + Je bent een video-oproep gestart · %1$s - %1$s started a video call + %1$s is een video-oproep gestart - %1$s started a video call · %2$s + %1$s is een video-oproep gestart · %2$s Jij @@ -6744,9 +6744,9 @@ - Restoring backup… + Back-upgegevens aan het terugzetten… - Downloading backup data… + Back-upgegevens aan het downloaden… diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 82a081277a..86dd383bda 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1672,21 +1672,21 @@ - %1$s is in the call · %2$s + %1$s uczestniczy w rozmowie · %2$s - You are in the call · %1$s + Uczestniczysz w rozmowie · %1$s - %1$s and %2$s are in the call · %3$s + %1$s i %2$s uczestniczą w rozmowie · %3$s - %1$s is in the call + %1$s uczestniczy w rozmowie - You are in the call + Uczestniczysz w rozmowie - %1$s and %2$s are in the call + %1$s i %2$s uczestniczą w rozmowie - The video call has ended + Połączenie wideo zostało zakończone - The video call has ended · %1$s + Połączenie wideo zostało zakończone · %1$s Nieodebrane połączenie wideo @@ -1694,19 +1694,19 @@ Przychodzące połączenie wideo - Incoming video call · %1$s + Przychodzące połączenie wideo · %1$s Wychodzące połączenie wideo - Outgoing video call · %1$s + Wychodzące połączenie wideo · %1$s - You started a video call + Połączenie wideo rozpoczęte przez Ciebie - You started a video call · %1$s + Połączenie wideo rozpoczęte przez Ciebie · %1$s - %1$s started a video call + Połączenie wideo rozpoczęte przez %1$s - %1$s started a video call · %2$s + Połączenie wideo rozpoczęte przez %1$s · %2$s Ty @@ -7036,9 +7036,9 @@ - Restoring backup… + Przywracanie kopii zapasowej… - Downloading backup data… + Pobieranie danych kopii zapasowej… diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 9ed653be4f..156a665915 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1566,21 +1566,21 @@ - %1$s is in the call · %2$s + %1$s está na chamada · %2$s - You are in the call · %1$s + Você está na chamada · %1$s - %1$s and %2$s are in the call · %3$s + %1$s e %2$s estão na chamada · %3$s - %1$s is in the call + %1$s está na chamada - You are in the call + Você está na chamada - %1$s and %2$s are in the call + %1$s e %2$s estão na chamada - The video call has ended + A chamada de vídeo foi encerrada - The video call has ended · %1$s + A chamada de vídeo foi encerrada · %1$s Chamada de vídeo perdida @@ -1588,19 +1588,19 @@ Chamada de vídeo recebida - Incoming video call · %1$s + Chamada de vídeo recebida · %1$s Chamada de vídeo efetuada - Outgoing video call · %1$s + Chamada de vídeo efetuada · %1$s - You started a video call + Você iniciou uma chamada de vídeo - You started a video call · %1$s + Você iniciou uma chamada de vídeo · %1$s - %1$s started a video call + %1$s iniciou uma chamada de vídeo - %1$s started a video call · %2$s + %1$s iniciou uma chamada de vídeo · %2$s Você @@ -4932,7 +4932,7 @@ Adicionar uma mensagem Responder Enviar para - Ver mídia temporária + Mídia temporária Um ou mais itens são muito grandes Um ou mais itens são inválidos Muitos itens foram selecionados @@ -6744,9 +6744,9 @@ - Restoring backup… + Restaurando backup… - Downloading backup data… + Baixando dados de backup… diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 1c7a77399b..047c7ebe3b 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -43,13 +43,13 @@ Hindi ka pa nagset ng passphrase! I-disable ang passphrase? - Permanente nitong ia-unlock ang Signal at mga notipikasyong pangmensahe. + Permanente nitong ia-unlock ang Signal at ang message notifications. I-disable Nagka-error sa pagkonekta sa server! Kinailangan ang mga PIN para sa registration lock. Para mag-disable ng PIN, paki disable muna ang registration lock. Nalikha na ang PIN. Naka-disable ang PIN. - I-record ang payments recovery phase + I-record ang payments recovery phrase I-record ang phrase Bago mo ma-disable ang iyong PIN, kinakailangan mong i-record ang payments recovery phrase mo para masiguradong mare-recover mo ang iyong payments account. @@ -72,13 +72,13 @@ (audio) (video) (lokasyon) - (tugon) + (reply) (Voice message) Gallery File - Kontak + Kontakin Lokasyon Kailangan ng Signal ng pahintulot upang maipakita ang iyong mga larawan at video. Bigyan ng Access @@ -86,9 +86,9 @@ Walang mahanap na app para makapili ng media. - Kailangan ng Signal ng pahintulot sa Storage upang makapaglakip ng mga larawan, video, o audio, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Mga Pahintulot\", at i-enable ang \"Storage\". - Kailangan ng Signal ng pahintulot sa Mga Kontak upang makapaglakip ng impormasyon ng kontak, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"mga Pahintulot\", at i-enable ang \"Mga Kontak\". - Kailangan ng Signal ng pahintulot sa Lokasyon upang makapaglakip ng lokasyon, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Mga Pahintulot\", at i-enable ang \"Lokasyon\". + Kailangan ng Signal ng pahintulot sa Storage upang makapag-attach ng photos, videos, o audio, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Permissions\", at i-enable ang \"Storage\". + Kailangan ng Signal ng pahintulot sa Contacts upang makapag-attach ng contact information, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Permissions\", at i-enable ang \"Contacts\". + Kailangan ng Signal ng pahintulot sa Lokasyon upang makapag-attach ng lokasyon, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Permissions\", at i-enable ang \"Location\". Hindi pa na-activate ni %1$s ang Payments @@ -100,10 +100,10 @@ Nag-a-upload ng media… - Nagko-compress ng video… + Kino-compressa ng video… - Tinitignan ang messages… + Chine-check ang messages… @@ -115,7 +115,7 @@ Hindi makakatawag o makakapag-send sa \'yo ng messages ang blocked users. Walang blocked users Gusto mo bang i-block ang user na ito? - Hindi na makakatawag o makakapag-send ng messages si \"%1$s\" sa\'yo. + Hindi na makakatawag o makakapag-send ng messages sa \'yo si \"%1$s\". I-block @@ -163,9 +163,9 @@ I-block ang %1$s? Hindi ka na makatatanggap ng messages o updates mula sa group, at hindi ka na rin pwedeng i-add ng members ulit sa group na ito. Hindi ka na maaaring i-add ulit ng members sa groupong ito. - Ang mga kasapi ng grupong ito ay maaaring isali kang muli sa grupong ito. + Maaari ka ulit isali sa grupong ito ng members ng grupong ito. - Pwede niyo nang i-message at tawagan ang isa\'t isa at ang pangalan at photo mo ay ibabahagi rin sa kanya. + Pwede niyo nang i-message at tawagan ang isa\'t isa at ibabahagi rin sa kanya ang pangalan at photo mo. Pwede niyo nang i-message ang isa\'t isa. Hindi makakatawag o makakapag-send ng messages sa \'yo ang mga binlock mong tao. @@ -178,13 +178,13 @@ I-block I-block at Umalis - Iulat at harangin + I-report at i-block - Gusto mong iulat bilang spam? + I-report bilang spam? - Iulat bilang spam + I-report bilang spam - Aabisuhan ang signal na maaaring nagpapadala ng spam ang taong ito. Hindi nababasa ng Signal ang nilalaman ng anumang mga chat. + Ino-notify ang Signal na maaaring nagpapadala ng spam ang taong ito. Hindi nababasa ng Signal ang nilalaman ng anumang chats. Ang Signal ay aabisuhan na si %1$s, na siyang nag-imbita sa iyo sa grupong ito, ay maaaring nagpapadala ng spam. Hindi nababasa ng Signal ang nilalaman ng anumang chat. @@ -201,10 +201,10 @@ - Hindi suportado sa iyong device ang video recording + Hindi suportado ng iyong device ang video recording - I-tap para sa larawan, huwag bitiwan para video + I-tap para sa photo, i-hold para sa video Kunan Magpalit ng Camera Buksan ang gallery @@ -1566,21 +1566,21 @@ - %1$s is in the call · %2$s + Si %1$s ay nasa call · %2$s - You are in the call · %1$s + Ikaw ay nasa call · %1$s - %1$s and %2$s are in the call · %3$s + Sila %1$s at %2$s ay nasa call · %3$s - %1$s is in the call + Si %1$s ay nasa call - You are in the call + Ikaw ay nasa call - %1$s and %2$s are in the call + Sila %1$s at %2$s ay nasa call - The video call has ended + Tapos na ang video call - The video call has ended · %1$s + Tapos na ang video call · %1$s Missed video call @@ -1594,13 +1594,13 @@ Outgoing video call · %1$s - You started a video call + Nagsimula ka ng video call - You started a video call · %1$s + Nagsimula ka ng video call · %1$s - %1$s started a video call + Si %1$s ay nagsimula ng video call - %1$s started a video call · %2$s + Si %1$s ay nagsimula ng video call · %2$s Ikaw @@ -4932,7 +4932,7 @@ Mag-add ng message Mag-add ng reply I-send kay - View once media + View-once media Ang isa o higit pang files were too large Ang isa o higit pang files were invalid Too many items selected @@ -6744,9 +6744,9 @@ - Restoring backup… + Nire-restore ang backup… - Downloading backup data… + Dina-download ang backup data… diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 80c313591a..b6094645fa 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1566,21 +1566,21 @@ - %1$s is in the call · %2$s + %1$s aramada · %2$s - You are in the call · %1$s + Aramadasın · %1$s - %1$s and %2$s are in the call · %3$s + %1$s ve %2$s aramada · %3$s - %1$s is in the call + %1$s aramada - You are in the call + Aramadasın - %1$s and %2$s are in the call + %1$s ve %2$s aramada - The video call has ended + Görüntülü arama sona erdi - The video call has ended · %1$s + Görüntülü arama sona erdi · %1$s Cevapsız görüntülü arama @@ -1588,19 +1588,19 @@ Gelen görüntülü arama - Incoming video call · %1$s + Gelen görüntülü arama · %1$s Giden görüntülü arama - Outgoing video call · %1$s + Giden görüntülü arama · %1$s - You started a video call + Bir görüntülü arama başlattın - You started a video call · %1$s + Bir görüntülü arama başlattın · %1$s - %1$s started a video call + %1$s bir görüntülü arama başlattı - %1$s started a video call · %2$s + %1$s bir görüntülü arama başlattı · %2$s Siz @@ -4932,7 +4932,7 @@ İleti ekle Yanıt ekle Gönder: - Bir Kez Görüntülenen Medya + Bir kez görünür medya Bir veya daha fazla öğe çok büyük Bir veya daha fazla öğe çok geçersiz Çok fazla öğe seçildi @@ -6744,9 +6744,9 @@ - Restoring backup… + Yedekleme geri yükleniyor… - Downloading backup data… + Yedekleme verileri indiriliyor… diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 8e451478e6..2b85f4d866 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -1513,21 +1513,21 @@ - %1$s is in the call · %2$s + %1$s چاقىرىقتا · %2$s - You are in the call · %1$s + سىز چاقىرىقتا · %1$s - %1$s and %2$s are in the call · %3$s + %1$s ۋە %2$s چاقىرىقتا · %3$s - %1$s is in the call + %1$s چاقىرىقتا - You are in the call + سىز چاقىرىقتا - %1$s and %2$s are in the call + %1$s ۋە %2$s چاقىرىقتا - The video call has ended + ۋىدېيولۇق چاقىرىق ئاخىرلاشتى - The video call has ended · %1$s + ۋىدېيولۇق چاقىرىق ئاخىرلاشتى · %1$s سۆزلەشمىگەن سىن چاقىرىش @@ -1535,19 +1535,19 @@ كەلگەن ۋىدېيولۇق چاقىرىق - Incoming video call · %1$s + كەلگەن ۋىدېيولۇق چاقىرىق · %1$s قوبۇل قىلىنغان ۋىدېيولۇق چاقىرىق - Outgoing video call · %1$s + ئۇرۇلغان ۋىدېيولۇق چاقىرىق · %1$s - You started a video call + بىر ۋىدېيولۇق چاقىرىق باشلىدىڭىز - You started a video call · %1$s + بىر ۋىدېيولۇق چاقىرىق باشلىدىڭىز · %1$s - %1$s started a video call + %1$s ۋىدېيولۇق چاقىرىق باشلىدى - %1$s started a video call · %2$s + %1$s ۋىدېيولۇق چاقىرىق باشلىدى · %2$s سىز @@ -6598,9 +6598,9 @@ - Restoring backup… + زاپاسنى ئەسلىگە قايتۇرۇۋاتىدۇ… - Downloading backup data… + زاپاس سانلىق مەلۇماتنى چۈشۈرۈۋاتىدۇ… diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 4993952204..8b6a78749e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1451,7 +1451,7 @@ %1$s · %2$s %1$s оновив групу. %1$s приєднався до Signal! - Ви заборонили зникаючі повідомлення + Ви вимкнули зникаючі повідомлення %1$s вимкнув зникаючі повідомлення. Ви встановлюєте час зникнення повідомлення на %1$s. %1$sвстановлює час зникання повідомлень на %2$s. @@ -1672,58 +1672,58 @@ - %1$s is in the call · %2$s + %1$s бере участь у виклику · %2$s - You are in the call · %1$s + Ви берете участь у виклику · %1$s - %1$s and %2$s are in the call · %3$s + %1$s і %2$s беруть участь у виклику · %3$s - %1$s is in the call + %1$s бере участь у виклику - You are in the call + Ви берете участь у виклику - %1$s and %2$s are in the call + %1$s і %2$s беруть участь у виклику - The video call has ended + Відеовиклик завершено - The video call has ended · %1$s + Відеовиклик завершено · %1$s Пропущений відеовиклик - Пропущений відеодзвінок · %1$s + Пропущений відеовиклик · %1$s Вхідний відеовиклик - Incoming video call · %1$s + Вхідний відеовиклик · %1$s Вихідний відеовиклик - Outgoing video call · %1$s + Вихідний відеовиклик · %1$s - You started a video call + Ви почали відеовиклик - You started a video call · %1$s + Ви почали відеовиклик · %1$s - %1$s started a video call + Користувач %1$s почав відеовиклик - %1$s started a video call · %2$s + Користувач %1$s почав відеовиклик · %2$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 + %1$s,%2$s і ще %3$d користувачів беруть участь у груповому виклику · %4$s + %1$s, %2$s і ще %3$d користувача беруть участь у груповому виклику · %4$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 користувача беруть участь у груповому виклику @@ -2908,7 +2908,7 @@ Дізнатися більше Приєднатися до дзвінка - Перетелефонувати + Передзвонити Повернутись до виклику Дзвінок повний Запросити друзів @@ -5166,7 +5166,7 @@ Додати повідомлення Додати відповідь Надіслати - Переглянути одноразові медіафайли + Одноразовий медіафайл Один або більше елементів були завеликими Один або кілька елементів були недійсними Обрано забагато елементів @@ -7036,9 +7036,9 @@ - Restoring backup… + Відновлення резервної копії… - Downloading backup data… + Завантаження резервної копії… diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index c662a4619e..9c00a899a8 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.179"}""" -rootProject.extra["cdn_ips"] = """new String[]{"18.238.49.106","18.238.49.6","18.238.49.66","18.238.49.90"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.250.80.115"}""" +rootProject.extra["cdn_ips"] = """new String[]{"18.238.55.2","18.238.55.54","18.238.55.7","18.238.55.78"}""" 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.36.104.134"}""" From f053ebbd519f3fb585a5bb9c20129d7105b9187f Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 11:36:15 -0400 Subject: [PATCH 049/113] Update baseline profile. --- app/src/main/baseline-prof.txt | 1676 ++++++++++++++++++-------------- 1 file changed, 945 insertions(+), 731 deletions(-) diff --git a/app/src/main/baseline-prof.txt b/app/src/main/baseline-prof.txt index ac7d08d66f..5319b7b893 100644 --- a/app/src/main/baseline-prof.txt +++ b/app/src/main/baseline-prof.txt @@ -1,168 +1,47 @@ HPLandroidx/appcompat/widget/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HPLandroidx/core/view/ViewGroupKt$descendants$1;->(Landroid/view/ViewGroup;Lkotlin/coroutines/Continuation;)V HPLandroidx/core/view/ViewGroupKt$descendants$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; HPLandroidx/customview/poolingcontainer/PoolingContainer;->callPoolingContainerOnRelease(Landroid/view/View;)V -HPLandroidx/customview/poolingcontainer/PoolingContainer;->getPoolingContainerListenerHolder(Landroid/view/View;)Landroidx/customview/poolingcontainer/PoolingContainerListenerHolder; -HPLandroidx/customview/poolingcontainer/PoolingContainerListenerHolder;->()V HPLandroidx/fragment/app/FragmentManager;->saveAllStateInternal()Landroid/os/Bundle; HPLandroidx/fragment/app/FragmentStateManager;->saveState()Landroid/os/Bundle; HPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areItemsTheSame(II)Z -HPLandroidx/recyclerview/widget/BatchingListUpdateCallback;->onChanged(IILjava/lang/Object;)V -HPLandroidx/recyclerview/widget/DiffUtil;->backward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; HPLandroidx/recyclerview/widget/DiffUtil;->forward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; HPLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->buildAdapterChangeFlagsForAnimations(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)I -HPLandroidx/recyclerview/widget/RecyclerView;->animateChange(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;ZZ)V HPLandroidx/recyclerview/widget/RecyclerView;->viewRangeUpdate(IILjava/lang/Object;)V HPLandroidx/recyclerview/widget/ViewInfoStore;->addToPreLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)V -HPLandroidx/recyclerview/widget/ViewInfoStore;->isDisappearing(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Z HPLandroidx/recyclerview/widget/ViewInfoStore;->popFromLayoutStep(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;I)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; HPLandroidx/savedstate/SavedStateRegistry;->performSave(Landroid/os/Bundle;)V +HPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I +HPLio/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable;->subscribeActual(Lio/reactivex/rxjava3/core/MaybeObserver;)V HPLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver;->innerSuccess(Lio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver$InnerObserver;Ljava/lang/Object;)V HPLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver;->onNext(Ljava/lang/Object;)V HPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->removeFirst()V -HPLj$/time/ZonedDateTime;->(Lj$/time/LocalDateTime;Lj$/time/ZoneId;Lj$/time/ZoneOffset;)V -HPLj$/time/ZonedDateTime;->m(JILj$/time/ZoneId;)Lj$/time/ZonedDateTime; -HPLj$/time/ZonedDateTime;->n(Lj$/time/Instant;Lj$/time/ZoneId;)Lj$/time/ZonedDateTime; -HPLj$/time/ZonedDateTime;->toLocalDate()Lj$/time/LocalDate; -HPLkotlin/sequences/SequencesKt__SequenceBuilderKt;->sequence(Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; -HPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSql()Ljava/lang/String; -HPLokio/Buffer;->write(Lokio/Buffer;J)V -HPLokio/OutputStreamSink;->write(Lokio/Buffer;J)V +HPLkotlin/sequences/SequenceBuilderIterator;->yieldAll(Ljava/util/Iterator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +HPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getCount()I +HPLnet/zetetic/database/sqlcipher/SQLiteCursor;->setWindow(Landroid/database/CursorWindow;)V HPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda0;->rejectedExecution(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V HPLorg/signal/core/util/concurrent/SignalExecutors;->lambda$newCachedBoundedExecutor$1(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V -HPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->build()Lorg/signal/core/util/tracing/DebugAnnotation; -HPLorg/signal/core/util/tracing/DebugAnnotation;->(Ljava/lang/Long;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/Long;Lorg/signal/core/util/tracing/DebugAnnotation$NestedValue;Lokio/ByteString;)V -HPLorg/signal/core/util/tracing/TracePacket$Builder;->()V -HPLorg/signal/libsignal/protocol/ecc/ECPublicKey;->equals(Ljava/lang/Object;)Z +HPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V HPLorg/signal/paging/FixedSizePagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->applyCorners()V -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->applyCornersForSizeClass2()V -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->getCells()[Lorg/thoughtcrime/securesms/components/ThumbnailView; -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setCellBackgroundColor(I)V -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setRadii(IIII)V -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setRelativeRadii(Lorg/thoughtcrime/securesms/components/ThumbnailView;IIII)V -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setSlide(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/mms/Slide;IZ)V -HPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setSlides(Lcom/bumptech/glide/RequestManager;Ljava/util/List;Z)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setClickable(Z)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setConversationColor(I)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setCorners(IIII)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setFocusable(Z)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setImageResource(Lcom/bumptech/glide/RequestManager;Ljava/util/List;ZZ)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setMaximumThumbnailHeight(I)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setMinimumThumbnailWidth(I)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setOnLongClickListener(Landroid/view/View$OnLongClickListener;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setThumbnailBounds([I)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->showThumbnailView()V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->(ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIII)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->copy$default(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState; -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->copy(ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIII)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState; -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->(FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIII)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->copy$default(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIIIILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->copy(FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIII)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;Lorg/thoughtcrime/securesms/util/views/Stub;)V -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->copy$default(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;ILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState; -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->copy(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState; -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->getAlbumViewState()Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState; -HPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->getThumbnailViewState()Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; -HPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->isListCommitted()Z -HPLorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport2;->m([Ljava/lang/Object;)Ljava/util/List; -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->onMeasure(II)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setBounds(IIII)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setClickable(Z)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setFocusable(Z)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setImageResource(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/mms/Slide;ZZII)Lorg/signal/core/util/concurrent/ListenableFuture; -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setRadii(IIII)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V -HPLorg/thoughtcrime/securesms/components/ThumbnailView;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->getTransferState(Ljava/util/List;)I -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->toString()Ljava/lang/String; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->(Z)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->(Z)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->deriveMode(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->isUpdateToExistingSet(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;Ljava/util/List;)Z -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setCancelClickListener(Landroid/view/View$OnClickListener;)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setClickable(Z)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setFocusable(Z)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setShowSecondaryText(Z)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setSlides(Ljava/util/List;)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setTransferClickListener(Landroid/view/View$OnClickListener;)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setVisible(Z)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->updateState(Lkotlin/jvm/functions/Function1;)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->(ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZ)V -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->copy$default(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->copy(ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZ)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->toString()Ljava/lang/String; -HPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->(Landroid/content/Context;Landroid/util/AttributeSet;II)V -HPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setTitle(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Runnable;)Ljava/lang/String; -HPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->updateOutlineVisibility()V HPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->animateChange(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)Z HPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->animatePersistence(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)Z -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->bindPayloadsIfAvailable()Z -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getBindable()Lorg/thoughtcrime/securesms/BindableConversationItem; -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getColorizerProjections(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/ProjectionList; -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getNextMessage()Lj$/util/Optional; -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getPreviousMessage()Lj$/util/Optional; -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)V -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)V HPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeConversationThreadUi$8;->invoke()Ljava/lang/Boolean; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->doAfterFirstRender()V HPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getReminder$lambda$10(Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;Lorg/thoughtcrime/securesms/database/model/GroupRecord;)Lj$/util/Optional; -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getRequestReviewState$lambda$15(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getRequestReviewState(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;)Lio/reactivex/rxjava3/core/Single; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$13;->apply(Lj$/util/Optional;)Lio/reactivex/rxjava3/core/MaybeSource; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Lorg/thoughtcrime/securesms/recipients/Recipient;)Ljava/lang/Boolean; -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)Lio/reactivex/rxjava3/core/SingleSource; HPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->equals(Ljava/lang/Object;)Z HPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->draw(Landroid/graphics/Canvas;)V -HPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getChatColors()Lorg/thoughtcrime/securesms/conversation/colors/ChatColors; -HPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getOutline(Landroid/graphics/Outline;)V -HPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setCorners([F)V -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isEndOfMessageCluster(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->setMessageShape(Lorg/thoughtcrime/securesms/database/model/MessageRecord;ZI)Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->bind(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)V -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getColorizerProjections(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/ProjectionList; -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateBodyBubbleDrawable(Landroid/view/ViewGroup;)V -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateChatColorsDrawable(Landroid/view/ViewGroup;)V -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->linkifyMessageBody(Landroid/text/Spannable;)V -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentBody()V -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSender()V -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getBodyTextColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->getFooterWidth()I -HPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->onPostMeasure()Z -HPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->(ZZJJIZ)V +HPLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->equals(Ljava/lang/Object;)Z +HPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z HPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$unregisterObserver$17(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -HPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->trace(Ljava/lang/String;Ljava/lang/Runnable;)V HPLorg/thoughtcrime/securesms/database/model/IdentityRecord;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->update(Landroid/text/TextPaint;)V -HPLorg/thoughtcrime/securesms/fonts/SignalSymbols;->getSpannedString(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;)Ljava/lang/CharSequence; -HPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler;->getCurrentHolder(I)Lorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder; -HPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler;->updateVideoDisplayPositionAndSize(Landroidx/recyclerview/widget/RecyclerView;Lorg/thoughtcrime/securesms/giph/mp4/GiphyMp4Playable;)V +HPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->equals(Ljava/lang/Object;)Z HPLorg/thoughtcrime/securesms/mms/Slide;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V -HPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->removeForeverObserver(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V +HPLorg/thoughtcrime/securesms/profiles/ProfileName;->equals(Ljava/lang/Object;)Z +HPLorg/thoughtcrime/securesms/recipients/Recipient;->hasSameContent(Lorg/thoughtcrime/securesms/recipients/Recipient;)Z HPLorg/thoughtcrime/securesms/util/BubbleUtil;->canBubble(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Long;)Z -HPLorg/thoughtcrime/securesms/util/Projection$Corners;->(FFFF)V -HPLorg/thoughtcrime/securesms/util/Projection$Corners;->toRadii()[F -HPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areContentsTheSame(Ljava/lang/Object;Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->setPayload(Ljava/util/List;)V HSPLandroid/support/v4/media/MediaBrowserCompat$MediaBrowserImplApi21$$ExternalSyntheticThrowCCEIfNotNull0;->m(Ljava/lang/Object;)V HSPLandroid/support/v4/media/session/IMediaSession$Stub;->()V HSPLandroid/support/v4/media/session/MediaControllerCompat$MediaControllerImplApi21;->(Landroid/content/Context;Landroid/support/v4/media/session/MediaSessionCompat$Token;)V @@ -649,6 +528,7 @@ HSPLandroidx/appcompat/widget/ActionMenuView;->setOverflowReserved(Z)V HSPLandroidx/appcompat/widget/ActionMenuView;->setPopupTheme(I)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->(Landroid/view/View;)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->applySupportBackgroundTint()V +HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->loadFromAttributes(Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->onSetBackgroundDrawable(Landroid/graphics/drawable/Drawable;)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->onSetBackgroundResource(I)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->setInternalBackgroundTint(Landroid/content/res/ColorStateList;)V @@ -707,6 +587,7 @@ HSPLandroidx/appcompat/widget/AppCompatEmojiTextHelper;->(Landroid/widget/ HSPLandroidx/appcompat/widget/AppCompatEmojiTextHelper;->getFilters([Landroid/text/InputFilter;)[Landroid/text/InputFilter; HSPLandroidx/appcompat/widget/AppCompatEmojiTextHelper;->loadFromAttributes(Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/AppCompatEmojiTextHelper;->setEnabled(Z)V +HSPLandroidx/appcompat/widget/AppCompatHintHelper;->onCreateInputConnection(Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;Landroid/view/View;)Landroid/view/inputmethod/InputConnection; HSPLandroidx/appcompat/widget/AppCompatImageButton;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/widget/AppCompatImageButton;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/AppCompatImageButton;->drawableStateChanged()V @@ -756,8 +637,10 @@ HSPLandroidx/appcompat/widget/AppCompatTextHelper;->onSetCompoundDrawables()V HSPLandroidx/appcompat/widget/AppCompatTextHelper;->onSetTextAppearance(Landroid/content/Context;I)V HSPLandroidx/appcompat/widget/AppCompatTextHelper;->populateSurroundingTextIfNeeded(Landroid/widget/TextView;Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;)V HSPLandroidx/appcompat/widget/AppCompatTextHelper;->setCompoundDrawables(Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;)V +HSPLandroidx/appcompat/widget/AppCompatTextHelper;->updateTypefaceAndStyle(Landroid/content/Context;Landroidx/appcompat/widget/TintTypedArray;)V HSPLandroidx/appcompat/widget/AppCompatTextView;->(Landroid/content/Context;)V HSPLandroidx/appcompat/widget/AppCompatTextView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLandroidx/appcompat/widget/AppCompatTextView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/AppCompatTextView;->consumeTextFutureAndSetBlocking()V HSPLandroidx/appcompat/widget/AppCompatTextView;->drawableStateChanged()V HSPLandroidx/appcompat/widget/AppCompatTextView;->getEmojiTextViewHelper()Landroidx/appcompat/widget/AppCompatEmojiTextHelper; @@ -785,6 +668,7 @@ HSPLandroidx/appcompat/widget/AppCompatTextViewAutoSizeHelper$Impl;->()V HSPLandroidx/appcompat/widget/AppCompatTextViewAutoSizeHelper;->()V HSPLandroidx/appcompat/widget/AppCompatTextViewAutoSizeHelper;->(Landroid/widget/TextView;)V HSPLandroidx/appcompat/widget/AppCompatTextViewAutoSizeHelper;->getAutoSizeTextType()I +HSPLandroidx/appcompat/widget/AppCompatTextViewAutoSizeHelper;->loadFromAttributes(Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/AppCompatTextViewAutoSizeHelper;->supportsAutoSizeText()Z HSPLandroidx/appcompat/widget/ContentFrameLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/widget/ContentFrameLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V @@ -1207,7 +1091,6 @@ HSPLandroidx/compose/ui/text/platform/extensions/LocaleListHelperMethods$$Extern HSPLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper$$ExternalSyntheticBackportWithForwarding0;->m(Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->()V HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->(Landroidx/constraintlayout/core/ArrayRow;Landroidx/constraintlayout/core/Cache;)V -HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->add(Landroidx/constraintlayout/core/SolverVariable;FZ)V HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->clear()V HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->contains(Landroidx/constraintlayout/core/SolverVariable;)Z HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->divideByAmount(F)V @@ -1216,6 +1099,8 @@ HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getCurrentSize()I HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getVariable(I)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getVariableValue(I)F HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->invert()V +HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->remove(Landroidx/constraintlayout/core/SolverVariable;Z)F +HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->use(Landroidx/constraintlayout/core/ArrayRow;Z)F HSPLandroidx/constraintlayout/core/ArrayRow;->(Landroidx/constraintlayout/core/Cache;)V HSPLandroidx/constraintlayout/core/ArrayRow;->addError(Landroidx/constraintlayout/core/LinearSystem;I)Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/ArrayRow;->addSingleError(Landroidx/constraintlayout/core/SolverVariable;I)Landroidx/constraintlayout/core/ArrayRow; @@ -1230,18 +1115,18 @@ HSPLandroidx/constraintlayout/core/ArrayRow;->createRowLowerThan(Landroidx/const HSPLandroidx/constraintlayout/core/ArrayRow;->ensurePositiveConstant()V HSPLandroidx/constraintlayout/core/ArrayRow;->getKey()Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/ArrayRow;->hasKeyVariable()Z +HSPLandroidx/constraintlayout/core/ArrayRow;->hasVariable(Landroidx/constraintlayout/core/SolverVariable;)Z HSPLandroidx/constraintlayout/core/ArrayRow;->isEmpty()Z HSPLandroidx/constraintlayout/core/ArrayRow;->isNew(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/LinearSystem;)Z HSPLandroidx/constraintlayout/core/ArrayRow;->pivot(Landroidx/constraintlayout/core/SolverVariable;)V HSPLandroidx/constraintlayout/core/ArrayRow;->reset()V HSPLandroidx/constraintlayout/core/ArrayRow;->updateFromFinalVariable(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/SolverVariable;Z)V +HSPLandroidx/constraintlayout/core/ArrayRow;->updateFromRow(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/ArrayRow;Z)V HSPLandroidx/constraintlayout/core/Cache;->()V HSPLandroidx/constraintlayout/core/LinearSystem;->()V HSPLandroidx/constraintlayout/core/LinearSystem;->()V -HSPLandroidx/constraintlayout/core/LinearSystem;->acquireSolverVariable(Landroidx/constraintlayout/core/SolverVariable$Type;Ljava/lang/String;)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/LinearSystem;->addCentering(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;IFLandroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)V HSPLandroidx/constraintlayout/core/LinearSystem;->addConstraint(Landroidx/constraintlayout/core/ArrayRow;)V -HSPLandroidx/constraintlayout/core/LinearSystem;->addEquality(Landroidx/constraintlayout/core/SolverVariable;I)V HSPLandroidx/constraintlayout/core/LinearSystem;->addEquality(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/LinearSystem;->addGreaterBarrier(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;IZ)V HSPLandroidx/constraintlayout/core/LinearSystem;->addGreaterThan(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)V @@ -1253,7 +1138,6 @@ HSPLandroidx/constraintlayout/core/LinearSystem;->computeValues()V HSPLandroidx/constraintlayout/core/LinearSystem;->createObjectVariable(Ljava/lang/Object;)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/LinearSystem;->createRow()Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/LinearSystem;->createSlackVariable()Landroidx/constraintlayout/core/SolverVariable; -HSPLandroidx/constraintlayout/core/LinearSystem;->enforceBFS(Landroidx/constraintlayout/core/LinearSystem$Row;)I HSPLandroidx/constraintlayout/core/LinearSystem;->getCache()Landroidx/constraintlayout/core/Cache; HSPLandroidx/constraintlayout/core/LinearSystem;->getMetrics()Landroidx/constraintlayout/core/Metrics; HSPLandroidx/constraintlayout/core/LinearSystem;->getObjectVariableValue(Ljava/lang/Object;)I @@ -1292,6 +1176,7 @@ HSPLandroidx/constraintlayout/core/SolverVariable;->removeFromRow(Landroidx/cons HSPLandroidx/constraintlayout/core/SolverVariable;->reset()V HSPLandroidx/constraintlayout/core/SolverVariable;->setFinalValue(Landroidx/constraintlayout/core/LinearSystem;F)V HSPLandroidx/constraintlayout/core/SolverVariable;->setType(Landroidx/constraintlayout/core/SolverVariable$Type;Ljava/lang/String;)V +HSPLandroidx/constraintlayout/core/SolverVariable;->updateReferencesWithNewDefinition(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/ArrayRow;)V HSPLandroidx/constraintlayout/core/state/WidgetFrame;->()V HSPLandroidx/constraintlayout/core/state/WidgetFrame;->(Landroidx/constraintlayout/core/widgets/ConstraintWidget;)V HSPLandroidx/constraintlayout/core/widgets/Barrier;->()V @@ -1378,6 +1263,7 @@ HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->isVerticalSolvingP HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->markHorizontalSolvingPassDone()V HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->markVerticalSolvingPassDone()V HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->reset()V +HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->resetFinalResolution()V HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->resetSolverVariables(Landroidx/constraintlayout/core/Cache;)V HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->setBaselineDistance(I)V HSPLandroidx/constraintlayout/core/widgets/ConstraintWidget;->setCompanionWidget(Ljava/lang/Object;)V @@ -1485,13 +1371,13 @@ HSPLandroidx/constraintlayout/core/widgets/analyzer/DependencyGraph;->invalidate HSPLandroidx/constraintlayout/core/widgets/analyzer/DependencyGraph;->setMeasurer(Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->()V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->canMeasure(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;)Z -HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->horizontalSolvingPass(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveBarrier(ILandroidx/constraintlayout/core/widgets/Barrier;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;IZ)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveHorizontalCenterConstraints(ILandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveHorizontalMatchConstraint(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveVerticalCenterConstraints(ILandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveVerticalMatchConstraint(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solvingPass(Landroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;)V +HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->verticalSolvingPass(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;)V HSPLandroidx/constraintlayout/widget/Barrier;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/constraintlayout/widget/Barrier;->init(Landroid/util/AttributeSet;)V HSPLandroidx/constraintlayout/widget/Barrier;->resolveRtl(Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V @@ -1515,7 +1401,7 @@ HSPLandroidx/constraintlayout/widget/ConstraintHelper;->validateParams()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$1;->()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams$Table;->()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->resolveLayoutDirection(I)V +HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->validate()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->(Landroidx/constraintlayout/widget/ConstraintLayout;Landroidx/constraintlayout/widget/ConstraintLayout;)V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->captureLayoutInfo(IIIIII)V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->didMeasures()V @@ -1813,6 +1699,10 @@ HSPLandroidx/core/os/TraceCompat;->endSection()V HSPLandroidx/core/os/UserManagerCompat$Api24Impl$$ExternalSyntheticApiModelOutline0;->m(Landroid/os/UserManager;)Z HSPLandroidx/core/os/UserManagerCompat$Api24Impl;->isUserUnlocked(Landroid/content/Context;)Z HSPLandroidx/core/os/UserManagerCompat;->isUserUnlocked(Landroid/content/Context;)Z +HSPLandroidx/core/text/util/LinkifyCompat$$ExternalSyntheticLambda0;->()V +HSPLandroidx/core/text/util/LinkifyCompat;->()V +HSPLandroidx/core/text/util/LinkifyCompat;->addLinks(Landroid/text/Spannable;I)Z +HSPLandroidx/core/text/util/LinkifyCompat;->shouldAddLinksFallbackToFramework()Z HSPLandroidx/core/util/ObjectsCompat$Api19Impl;->equals(Ljava/lang/Object;Ljava/lang/Object;)Z HSPLandroidx/core/util/ObjectsCompat$Api19Impl;->hash([Ljava/lang/Object;)I HSPLandroidx/core/util/ObjectsCompat;->equals(Ljava/lang/Object;Ljava/lang/Object;)Z @@ -2139,6 +2029,11 @@ HSPLandroidx/core/view/accessibility/AccessibilityNodeInfoCompat;->unwrap()Landr HSPLandroidx/core/view/accessibility/AccessibilityNodeInfoCompat;->wrap(Landroid/view/accessibility/AccessibilityNodeInfo;)Landroidx/core/view/accessibility/AccessibilityNodeInfoCompat; HSPLandroidx/core/view/animation/PathInterpolatorCompat$Api21Impl;->createPathInterpolator(FFFF)Landroid/view/animation/Interpolator; HSPLandroidx/core/view/animation/PathInterpolatorCompat;->create(FFFF)Landroid/view/animation/Interpolator; +HSPLandroidx/core/view/inputmethod/EditorInfoCompat$$ExternalSyntheticApiModelOutline0;->m(Landroid/view/inputmethod/EditorInfo;[Ljava/lang/String;)V +HSPLandroidx/core/view/inputmethod/EditorInfoCompat;->()V +HSPLandroidx/core/view/inputmethod/EditorInfoCompat;->setContentMimeTypes(Landroid/view/inputmethod/EditorInfo;[Ljava/lang/String;)V +HSPLandroidx/core/view/inputmethod/InputConnectionCompat$1;->(Landroid/view/inputmethod/InputConnection;ZLandroidx/core/view/inputmethod/InputConnectionCompat$OnCommitContentListener;)V +HSPLandroidx/core/view/inputmethod/InputConnectionCompat;->createWrapper(Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;Landroidx/core/view/inputmethod/InputConnectionCompat$OnCommitContentListener;)Landroid/view/inputmethod/InputConnection; HSPLandroidx/core/widget/ImageViewCompat$Api21Impl;->setImageTintList(Landroid/widget/ImageView;Landroid/content/res/ColorStateList;)V HSPLandroidx/core/widget/ImageViewCompat;->setImageTintList(Landroid/widget/ImageView;Landroid/content/res/ColorStateList;)V HSPLandroidx/core/widget/TextViewCompat$Api16Impl;->getMaxLines(Landroid/widget/TextView;)I @@ -2247,6 +2142,10 @@ HSPLandroidx/emoji2/viewsintegration/EmojiEditableFactory;->()V HSPLandroidx/emoji2/viewsintegration/EmojiEditableFactory;->()V HSPLandroidx/emoji2/viewsintegration/EmojiEditableFactory;->getInstance()Landroid/text/Editable$Factory; HSPLandroidx/emoji2/viewsintegration/EmojiEditableFactory;->newEditable(Ljava/lang/CharSequence;)Landroid/text/Editable; +HSPLandroidx/emoji2/viewsintegration/EmojiInputConnection$EmojiCompatDeleteHelper;->()V +HSPLandroidx/emoji2/viewsintegration/EmojiInputConnection$EmojiCompatDeleteHelper;->updateEditorInfoAttrs(Landroid/view/inputmethod/EditorInfo;)V +HSPLandroidx/emoji2/viewsintegration/EmojiInputConnection;->(Landroid/widget/TextView;Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;)V +HSPLandroidx/emoji2/viewsintegration/EmojiInputConnection;->(Landroid/widget/TextView;Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;Landroidx/emoji2/viewsintegration/EmojiInputConnection$EmojiCompatDeleteHelper;)V HSPLandroidx/emoji2/viewsintegration/EmojiInputFilter;->(Landroid/widget/TextView;)V HSPLandroidx/emoji2/viewsintegration/EmojiInputFilter;->filter(Ljava/lang/CharSequence;IILandroid/text/Spanned;II)Ljava/lang/CharSequence; HSPLandroidx/emoji2/viewsintegration/EmojiKeyListener$EmojiCompatHandleKeyDownHelper;->()V @@ -5169,6 +5068,7 @@ HSPLandroidx/recyclerview/widget/LinearLayoutManager;->getExtraLayoutSpace(Landr HSPLandroidx/recyclerview/widget/LinearLayoutManager;->getReverseLayout()Z HSPLandroidx/recyclerview/widget/LinearLayoutManager;->isAutoMeasureEnabled()Z HSPLandroidx/recyclerview/widget/LinearLayoutManager;->isLayoutRTL()Z +HSPLandroidx/recyclerview/widget/LinearLayoutManager;->layoutChunk(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;Landroidx/recyclerview/widget/LinearLayoutManager$LayoutState;Landroidx/recyclerview/widget/LinearLayoutManager$LayoutChunkResult;)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->layoutForPredictiveAnimations(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;II)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->onAnchorReady(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;Landroidx/recyclerview/widget/LinearLayoutManager$AnchorInfo;I)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->onInitializeAccessibilityEvent(Landroid/view/accessibility/AccessibilityEvent;)V @@ -5658,7 +5558,6 @@ HSPLandroidx/savedstate/SavedStateRegistryController;->performAttach()V HSPLandroidx/savedstate/SavedStateRegistryController;->performRestore(Landroid/os/Bundle;)V HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner;->set(Landroid/view/View;Landroidx/savedstate/SavedStateRegistryOwner;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->(Ljava/lang/String;[Ljava/lang/Object;)V -HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->bind(Landroidx/sqlite/db/SupportSQLiteProgram;ILjava/lang/Object;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->bind(Landroidx/sqlite/db/SupportSQLiteProgram;[Ljava/lang/Object;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->bindTo(Landroidx/sqlite/db/SupportSQLiteProgram;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->getSql()Ljava/lang/String; @@ -5763,11 +5662,13 @@ HSPLcom/airbnb/lottie/LottieCompositionFactory;->rawResCacheKey(Landroid/content HSPLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda0;->(Lcom/airbnb/lottie/LottieDrawable;F)V HSPLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda0;->run(Lcom/airbnb/lottie/LottieComposition;)V HSPLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda9;->(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/KeyPath;Ljava/lang/Object;Lcom/airbnb/lottie/value/LottieValueCallback;)V +HSPLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda9;->run(Lcom/airbnb/lottie/LottieComposition;)V HSPLcom/airbnb/lottie/LottieDrawable$1;->(Lcom/airbnb/lottie/LottieDrawable;)V HSPLcom/airbnb/lottie/LottieDrawable$1;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V HSPLcom/airbnb/lottie/LottieDrawable$OnVisibleAction;->()V HSPLcom/airbnb/lottie/LottieDrawable$OnVisibleAction;->(Ljava/lang/String;I)V HSPLcom/airbnb/lottie/LottieDrawable;->$r8$lambda$7HgNmvtAytyG5A3axzRshxgGqrI(Lcom/airbnb/lottie/LottieDrawable;FLcom/airbnb/lottie/LottieComposition;)V +HSPLcom/airbnb/lottie/LottieDrawable;->$r8$lambda$riFJCWOqfI5iOFlatZRlwc9qv1U(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/KeyPath;Ljava/lang/Object;Lcom/airbnb/lottie/value/LottieValueCallback;Lcom/airbnb/lottie/LottieComposition;)V HSPLcom/airbnb/lottie/LottieDrawable;->()V HSPLcom/airbnb/lottie/LottieDrawable;->access$000(Lcom/airbnb/lottie/LottieDrawable;)Lcom/airbnb/lottie/model/layer/CompositionLayer; HSPLcom/airbnb/lottie/LottieDrawable;->access$100(Lcom/airbnb/lottie/LottieDrawable;)Lcom/airbnb/lottie/utils/LottieValueAnimator; @@ -5785,6 +5686,7 @@ HSPLcom/airbnb/lottie/LottieDrawable;->getOpacity()I HSPLcom/airbnb/lottie/LottieDrawable;->getRenderMode()Lcom/airbnb/lottie/RenderMode; HSPLcom/airbnb/lottie/LottieDrawable;->invalidateSelf()V HSPLcom/airbnb/lottie/LottieDrawable;->isApplyingOpacityToLayersEnabled()Z +HSPLcom/airbnb/lottie/LottieDrawable;->lambda$addValueCallback$14(Lcom/airbnb/lottie/model/KeyPath;Ljava/lang/Object;Lcom/airbnb/lottie/value/LottieValueCallback;Lcom/airbnb/lottie/LottieComposition;)V HSPLcom/airbnb/lottie/LottieDrawable;->lambda$setProgress$13(FLcom/airbnb/lottie/LottieComposition;)V HSPLcom/airbnb/lottie/LottieDrawable;->pauseAnimation()V HSPLcom/airbnb/lottie/LottieDrawable;->resolveKeyPath(Lcom/airbnb/lottie/model/KeyPath;)Ljava/util/List; @@ -6178,7 +6080,6 @@ HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->(Lokio/BufferedSource; HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->beginArray()V HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->beginObject()V HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->close()V -HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->doPeek()I HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->endArray()V HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->endObject()V HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->findName(Ljava/lang/String;Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;)I @@ -6192,7 +6093,6 @@ HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextQuotedValue(Lokio/ByteSt HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextString()Ljava/lang/String; HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peek()Lcom/airbnb/lottie/parser/moshi/JsonReader$Token; HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peekKeyword()I -HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peekNumber()I HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->selectName(Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;)I HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipName()V HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipQuotedValue(Lokio/ByteString;)V @@ -6346,6 +6246,8 @@ HSPLcom/annimon/stream/Stream;->sorted()Lcom/annimon/stream/Stream; HSPLcom/annimon/stream/Stream;->sorted(Ljava/util/Comparator;)Lcom/annimon/stream/Stream; HSPLcom/annimon/stream/Stream;->toList()Ljava/util/List; HSPLcom/annimon/stream/Stream;->withoutNulls()Lcom/annimon/stream/Stream; +HSPLcom/annimon/stream/function/BinaryOperator$Util$2;->(Ljava/util/Comparator;)V +HSPLcom/annimon/stream/function/BinaryOperator$Util;->maxBy(Ljava/util/Comparator;)Lcom/annimon/stream/function/BinaryOperator; HSPLcom/annimon/stream/function/Predicate$Util$4;->(Lcom/annimon/stream/function/Predicate;)V HSPLcom/annimon/stream/function/Predicate$Util$4;->test(Ljava/lang/Object;)Z HSPLcom/annimon/stream/function/Predicate$Util$5;->()V @@ -6372,6 +6274,9 @@ HSPLcom/annimon/stream/iterator/PrimitiveIterator$OfInt;->next()Ljava/lang/Objec HSPLcom/annimon/stream/operator/IntArray;->([I)V HSPLcom/annimon/stream/operator/IntArray;->hasNext()Z HSPLcom/annimon/stream/operator/IntArray;->nextInt()I +HSPLcom/annimon/stream/operator/IntRangeClosed;->(II)V +HSPLcom/annimon/stream/operator/IntRangeClosed;->hasNext()Z +HSPLcom/annimon/stream/operator/IntRangeClosed;->nextInt()I HSPLcom/annimon/stream/operator/ObjArray;->([Ljava/lang/Object;)V HSPLcom/annimon/stream/operator/ObjArray;->hasNext()Z HSPLcom/annimon/stream/operator/ObjArray;->nextIteration()Ljava/lang/Object; @@ -7541,12 +7446,12 @@ HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->(Lcom/fasterxml/jackson/core/io/IOContext;ILjava/io/Reader;Lcom/fasterxml/jackson/core/ObjectCodec;Lcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;)V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->(Lcom/fasterxml/jackson/core/io/IOContext;ILjava/io/Reader;Lcom/fasterxml/jackson/core/ObjectCodec;Lcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;[CIIZ)V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_closeInput()V +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_finishString()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_finishString2()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_loadMore()Z HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchFalse()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchNull()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchTrue()V -HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_nextAfterName()Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parseName2(III)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parseNumber2(ZI)Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_releaseBuffers()V @@ -7555,6 +7460,7 @@ HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipColon()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipColon2(Z)I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipComma(I)I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipString()V +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipWSOrEnd()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipWSOrEnd2()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_updateLocation()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_updateNameLocation()V @@ -7562,6 +7468,8 @@ HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_verifyNoLeadingZero HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->getReadCapabilities()Lcom/fasterxml/jackson/core/util/JacksonFeatureSet; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->getText()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->getValueAsString()Ljava/lang/String; +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->nextFieldName()Ljava/lang/String; +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->nextToken()Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->()V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->(Lcom/fasterxml/jackson/core/io/IOContext;ILcom/fasterxml/jackson/core/ObjectCodec;Ljava/io/OutputStream;C)V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->_flushBuffer()V @@ -7607,6 +7515,7 @@ HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName(IIII)Ljava/l HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName([IIII)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getCurrentLocation()Lcom/fasterxml/jackson/core/JsonLocation; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getReadCapabilities()Lcom/fasterxml/jackson/core/util/JacksonFeatureSet; +HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getText()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->nextFieldName()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->parseEscapedName([IIIII)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->parseLongName(III)Ljava/lang/String; @@ -7619,7 +7528,6 @@ HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_flushBuffer()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_releaseBuffers()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_verifyValueWrite(Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeBinary(Lcom/fasterxml/jackson/core/Base64Variant;[BII)V -HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeFieldName(Lcom/fasterxml/jackson/core/SerializableString;Z)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeFieldName(Ljava/lang/String;Z)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeNull()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeString(Ljava/lang/String;)V @@ -7648,7 +7556,7 @@ HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_calcTertiaryShift(I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_checkNeedForRehash()Z HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_findOffsetForAdd(I)I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_findSecondary(III)Ljava/lang/String; -HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_findSecondary(II[II)Ljava/lang/String; +HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_findSecondary(IIII)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_resizeAndFindOffsetForAdd(I)I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_spilloverStart()I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_verifyLongName([III)Z @@ -7678,7 +7586,6 @@ HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer$TableInfo;->createIn HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->(I)V HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->(Lcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;IILcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer$TableInfo;)V HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_addSymbol([CIIII)Ljava/lang/String; -HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_findSymbol2([CIILcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer$Bucket;)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_hashToIndex(I)I HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_thresholdSize(I)I HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->copyArrays()V @@ -8205,7 +8112,6 @@ HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->_deserializeWithErro HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/lang/Object;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->deserializeFromObject(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->vanillaDeserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/core/JsonToken;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerBase;->()V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerBase;->(Lcom/fasterxml/jackson/databind/deser/BeanDeserializerBuilder;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/deser/impl/BeanPropertyMap;Ljava/util/Map;Ljava/util/Set;ZLjava/util/Set;Z)V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerBase;->_delegateDeserializer()Lcom/fasterxml/jackson/databind/JsonDeserializer; @@ -8520,7 +8426,6 @@ HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->with HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/String; HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->isCachable()Z HSPLcom/fasterxml/jackson/databind/deser/std/UUIDDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/UUIDDeserializer;->()V @@ -10884,6 +10789,10 @@ HSPLcom/google/common/collect/RegularImmutableSet;->()V HSPLcom/google/common/collect/RegularImmutableSet;->([Ljava/lang/Object;I[Ljava/lang/Object;II)V HSPLcom/google/common/collect/RegularImmutableSet;->contains(Ljava/lang/Object;)Z HSPLcom/google/common/collect/RegularImmutableSet;->size()I +HSPLcom/google/common/collect/Sets$2;->(Ljava/util/Set;Ljava/util/Set;)V +HSPLcom/google/common/collect/Sets$2;->isEmpty()Z +HSPLcom/google/common/collect/Sets$SetView;->()V +HSPLcom/google/common/collect/Sets$SetView;->(Lcom/google/common/collect/Sets$1;)V HSPLcom/google/common/collect/Sets;->intersection(Ljava/util/Set;Ljava/util/Set;)Lcom/google/common/collect/Sets$SetView; HSPLcom/google/common/collect/Sets;->newIdentityHashSet()Ljava/util/Set; HSPLcom/google/common/collect/SingletonImmutableSet;->(Ljava/lang/Object;)V @@ -11634,9 +11543,7 @@ HSPLcom/squareup/wire/internal/Internal;->countNonNull(Ljava/lang/Object;Ljava/l HSPLcom/squareup/wire/internal/Internal;->immutableCopyOf(Ljava/lang/String;Ljava/util/List;)Ljava/util/List; HSPLcom/squareup/wire/internal/Internal__InternalKt;->checkElementsNotNull(Ljava/util/List;)V HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;)I -HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)I -HSPLcom/squareup/wire/internal/Internal__InternalKt;->immutableCopyOf(Ljava/lang/String;Ljava/util/List;)Ljava/util/List; HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->callRequireNonNull(Ljava/util/concurrent/Callable;)Lio/reactivex/rxjava3/core/Scheduler; HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->initMainThreadScheduler(Ljava/util/concurrent/Callable;)Lio/reactivex/rxjava3/core/Scheduler; HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->onMainThreadScheduler(Lio/reactivex/rxjava3/core/Scheduler;)Lio/reactivex/rxjava3/core/Scheduler; @@ -11986,9 +11893,7 @@ HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedRepla HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->getHead()Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->leaveTransform(Ljava/lang/Object;)Ljava/lang/Object; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->next(Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->removeFirst()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->replay(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$DefaultUnboundedFactory;->()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscriber;Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->index()Ljava/lang/Object; @@ -12323,9 +12228,7 @@ HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode;->soNext HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode;->spValue(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->()V HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->clear()V -HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->isEmpty()Z HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lpConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; -HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvProducerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->offer(Ljava/lang/Object;)Z HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->poll()Ljava/lang/Object; @@ -12362,6 +12265,7 @@ HSPLio/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue;->soProducerIndex(J HSPLio/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue;->writeToQueue(Ljava/util/concurrent/atomic/AtomicReferenceArray;Ljava/lang/Object;JI)Z HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->()V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->(Ljava/lang/Runnable;Z)V +HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->cancelFuture(Ljava/util/concurrent/Future;)V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->dispose()V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->setFuture(Ljava/util/concurrent/Future;)V HSPLio/reactivex/rxjava3/internal/schedulers/DisposeOnCancel;->(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -12445,6 +12349,9 @@ HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->setOnce(Lja HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->setOnce(Ljava/util/concurrent/atomic/AtomicReference;Lorg/reactivestreams/Subscription;J)Z HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->validate(J)Z HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->validate(Lorg/reactivestreams/Subscription;Lorg/reactivestreams/Subscription;)Z +HSPLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->(I)V +HSPLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->add(Ljava/lang/Object;)V +HSPLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->forEachWhile(Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList$NonThrowingPredicate;)V HSPLio/reactivex/rxjava3/internal/util/ArrayListSupplier;->()V HSPLio/reactivex/rxjava3/internal/util/ArrayListSupplier;->(Ljava/lang/String;I)V HSPLio/reactivex/rxjava3/internal/util/ArrayListSupplier;->asSupplier()Lio/reactivex/rxjava3/functions/Supplier; @@ -12560,6 +12467,7 @@ HSPLio/reactivex/rxjava3/processors/BehaviorProcessor;->subscribeActual(Lorg/rea HSPLio/reactivex/rxjava3/processors/FlowableProcessor;->()V HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->(Lorg/reactivestreams/Subscriber;Lio/reactivex/rxjava3/processors/PublishProcessor;)V HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->isCancelled()Z +HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->onNext(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->request(J)V HSPLio/reactivex/rxjava3/processors/PublishProcessor;->()V HSPLio/reactivex/rxjava3/processors/PublishProcessor;->()V @@ -12687,6 +12595,10 @@ HSPLj$/time/ZoneOffset;->(I)V HSPLj$/time/ZoneOffset;->n()Lj$/time/zone/c; HSPLj$/time/ZoneOffset;->o()I HSPLj$/time/ZoneOffset;->r(I)Lj$/time/ZoneOffset; +HSPLj$/time/ZonedDateTime;->(Lj$/time/LocalDateTime;Lj$/time/ZoneId;Lj$/time/ZoneOffset;)V +HSPLj$/time/ZonedDateTime;->m(JILj$/time/ZoneId;)Lj$/time/ZonedDateTime; +HSPLj$/time/ZonedDateTime;->n(Lj$/time/Instant;Lj$/time/ZoneId;)Lj$/time/ZonedDateTime; +HSPLj$/time/ZonedDateTime;->toLocalDate()Lj$/time/LocalDate; HSPLj$/time/a;->g(JJ)J HSPLj$/time/a;->h(Lsun/misc/Unsafe;Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z HSPLj$/time/a;->i(JJ)J @@ -12865,7 +12777,7 @@ HSPLj$/util/S;->a(Lj$/util/function/Consumer;)V HSPLj$/util/S;->characteristics()I HSPLj$/util/S;->estimateSize()J HSPLj$/util/S;->getExactSizeIfKnown()J -HSPLj$/util/S;->s(Lj$/util/function/Consumer;)Z +HSPLj$/util/S;->p(Lj$/util/function/Consumer;)Z HSPLj$/util/U;->()V HSPLj$/util/V;->()V HSPLj$/util/W;->()V @@ -12875,7 +12787,7 @@ HSPLj$/util/Z;->a(Lj$/util/function/Consumer;)V HSPLj$/util/Z;->characteristics()I HSPLj$/util/Z;->estimateSize()J HSPLj$/util/Z;->getExactSizeIfKnown()J -HSPLj$/util/Z;->s(Lj$/util/function/Consumer;)Z +HSPLj$/util/Z;->p(Lj$/util/function/Consumer;)Z HSPLj$/util/b0;->()V HSPLj$/util/b0;->a(III)V HSPLj$/util/b0;->m([Ljava/lang/Object;II)Lj$/util/Spliterator; @@ -12908,7 +12820,6 @@ HSPLj$/util/concurrent/ConcurrentHashMap;->tabAt([Lj$/util/concurrent/l;I)Lj$/ut HSPLj$/util/concurrent/ConcurrentHashMap;->tableSizeFor(I)I HSPLj$/util/concurrent/ConcurrentHashMap;->transfer([Lj$/util/concurrent/l;[Lj$/util/concurrent/l;)V HSPLj$/util/concurrent/ConcurrentHashMap;->treeifyBin([Lj$/util/concurrent/l;I)V -HSPLj$/util/concurrent/ConcurrentHashMap;->untreeify(Lj$/util/concurrent/l;)Lj$/util/concurrent/l; HSPLj$/util/concurrent/a;->([Lj$/util/concurrent/l;IILj$/util/concurrent/ConcurrentHashMap;)V HSPLj$/util/concurrent/a;->hasNext()Z HSPLj$/util/concurrent/b;->(Lj$/util/concurrent/ConcurrentHashMap;)V @@ -12928,7 +12839,6 @@ HSPLj$/util/concurrent/q;->()V HSPLj$/util/concurrent/q;->(Lj$/util/concurrent/r;)V HSPLj$/util/concurrent/q;->a(Ljava/lang/Object;I)Lj$/util/concurrent/l; HSPLj$/util/concurrent/q;->c(Lj$/util/concurrent/r;Lj$/util/concurrent/r;)Lj$/util/concurrent/r; -HSPLj$/util/concurrent/q;->e()V HSPLj$/util/concurrent/q;->f(ILjava/lang/Object;Ljava/lang/Object;)Lj$/util/concurrent/r; HSPLj$/util/concurrent/q;->h(Lj$/util/concurrent/r;Lj$/util/concurrent/r;)Lj$/util/concurrent/r; HSPLj$/util/concurrent/q;->i(Lj$/util/concurrent/r;Lj$/util/concurrent/r;)Lj$/util/concurrent/r; @@ -12944,27 +12854,36 @@ HSPLj$/util/d;->get(Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/d;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/d;->remove(Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/d;->values()Ljava/util/Collection; +HSPLj$/util/function/b;->(Ljava/util/Comparator;I)V HSPLj$/util/m;->()V HSPLj$/util/m;->j(Lj$/util/Spliterator;)J HSPLj$/util/m;->y(Ljava/lang/Object;Ljava/lang/Object;)Z +HSPLj$/util/stream/A1;->(Lj$/util/stream/V2;Ljava/lang/Object;I)V +HSPLj$/util/stream/A1;->l1()Lj$/util/stream/P1; HSPLj$/util/stream/B2;->(Lj$/util/stream/c;)V HSPLj$/util/stream/B2;->B1(ILj$/util/stream/g2;)Lj$/util/stream/g2; HSPLj$/util/stream/C2;->(Lj$/util/stream/g2;Ljava/util/Comparator;)V -HSPLj$/util/stream/C2;->m()V -HSPLj$/util/stream/C2;->n(J)V +HSPLj$/util/stream/C2;->l()V +HSPLj$/util/stream/C2;->m(J)V HSPLj$/util/stream/Collector$Characteristics;->()V HSPLj$/util/stream/Collector$Characteristics;->(Ljava/lang/String;I)V HSPLj$/util/stream/Collector$Characteristics;->values()[Lj$/util/stream/Collector$Characteristics; HSPLj$/util/stream/Collectors;->()V HSPLj$/util/stream/Collectors;->toList()Lj$/util/stream/Collector; HSPLj$/util/stream/Collectors;->toSet()Lj$/util/stream/Collector; +HSPLj$/util/stream/E1;->(Lj$/util/function/BinaryOperator;)V +HSPLj$/util/stream/E1;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/E1;->get()Ljava/lang/Object; +HSPLj$/util/stream/E1;->l()V +HSPLj$/util/stream/E1;->m(J)V +HSPLj$/util/stream/E1;->q()Z HSPLj$/util/stream/F1;->(Lj$/util/stream/V2;Lj$/util/function/BinaryOperator;Lj$/util/function/BiConsumer;Lj$/util/function/Supplier;Lj$/util/stream/Collector;)V HSPLj$/util/stream/F1;->l1()Lj$/util/stream/P1; HSPLj$/util/stream/F1;->p()I HSPLj$/util/stream/G1;->(Lj$/util/function/Supplier;Lj$/util/function/BiConsumer;Lj$/util/function/BinaryOperator;)V HSPLj$/util/stream/G1;->accept(Ljava/lang/Object;)V -HSPLj$/util/stream/G1;->m()V -HSPLj$/util/stream/G1;->n(J)V +HSPLj$/util/stream/G1;->l()V +HSPLj$/util/stream/G1;->m(J)V HSPLj$/util/stream/H;->(I)V HSPLj$/util/stream/I;->(ZLj$/util/stream/V2;Ljava/lang/Object;Lj$/util/function/Predicate;Lj$/util/stream/b;)V HSPLj$/util/stream/I;->f0(Lj$/util/stream/w0;Lj$/util/Spliterator;)Ljava/lang/Object; @@ -12975,12 +12894,13 @@ HSPLj$/util/stream/L0;->get()Ljava/lang/Object; HSPLj$/util/stream/M;->()V HSPLj$/util/stream/M;->get()Ljava/lang/Object; HSPLj$/util/stream/N;->()V -HSPLj$/util/stream/N;->m()V -HSPLj$/util/stream/N;->n(J)V +HSPLj$/util/stream/N;->l()V +HSPLj$/util/stream/N;->m(J)V HSPLj$/util/stream/Q1;->()V HSPLj$/util/stream/Q1;->get()Ljava/lang/Object; HSPLj$/util/stream/S1;->(Lj$/util/stream/c;Lj$/util/stream/g2;I)V -HSPLj$/util/stream/S1;->n(J)V +HSPLj$/util/stream/S1;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/S1;->m(J)V HSPLj$/util/stream/S2;->(Ljava/util/Map;)V HSPLj$/util/stream/S2;->a(Lj$/util/stream/T2;)V HSPLj$/util/stream/T2;->()V @@ -13000,8 +12920,8 @@ HSPLj$/util/stream/U2;->j(Lj$/util/stream/T2;)Lj$/util/stream/S2; HSPLj$/util/stream/U2;->values()[Lj$/util/stream/U2; HSPLj$/util/stream/U;->(Z)V HSPLj$/util/stream/U;->f0(Lj$/util/stream/w0;Lj$/util/Spliterator;)Ljava/lang/Object; -HSPLj$/util/stream/U;->m()V -HSPLj$/util/stream/U;->n(J)V +HSPLj$/util/stream/U;->l()V +HSPLj$/util/stream/U;->m(J)V HSPLj$/util/stream/U;->p()I HSPLj$/util/stream/V0;->()V HSPLj$/util/stream/V1;->(Lj$/util/Spliterator;IZ)V @@ -13032,7 +12952,7 @@ HSPLj$/util/stream/Z0;->()V HSPLj$/util/stream/b;->(I)V HSPLj$/util/stream/b;->get()Ljava/lang/Object; HSPLj$/util/stream/c2;->(Lj$/util/stream/g2;)V -HSPLj$/util/stream/c2;->m()V +HSPLj$/util/stream/c2;->l()V HSPLj$/util/stream/c2;->q()Z HSPLj$/util/stream/c;->()V HSPLj$/util/stream/c;->(Lj$/util/Spliterator;IZ)V @@ -13044,6 +12964,12 @@ HSPLj$/util/stream/c;->isParallel()Z HSPLj$/util/stream/c;->n1(Lj$/util/Spliterator;Lj$/util/stream/g2;)Lj$/util/stream/g2; HSPLj$/util/stream/c;->o1(Lj$/util/stream/g2;)Lj$/util/stream/g2; HSPLj$/util/stream/c;->q1(Lj$/util/stream/F3;)Ljava/lang/Object; +HSPLj$/util/stream/h2;->(Lj$/util/stream/i2;Lj$/util/stream/g2;)V +HSPLj$/util/stream/h2;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/h2;->m(J)V +HSPLj$/util/stream/h2;->q()Z +HSPLj$/util/stream/i2;->(Lj$/util/stream/c;IJJ)V +HSPLj$/util/stream/i2;->B1(ILj$/util/stream/g2;)Lj$/util/stream/g2; HSPLj$/util/stream/l;->(I)V HSPLj$/util/stream/n;->(Lj$/util/function/Supplier;Lj$/util/function/BiConsumer;Lj$/util/function/BinaryOperator;Lj$/util/function/Function;Ljava/util/Set;)V HSPLj$/util/stream/n;->(Lj$/util/function/Supplier;Lj$/util/function/BiConsumer;Lj$/util/function/BinaryOperator;Ljava/util/Set;)V @@ -13131,14 +13057,13 @@ HSPLkotlin/UnsafeLazyImpl;->(Lkotlin/jvm/functions/Function0;)V HSPLkotlin/UnsafeLazyImpl;->getValue()Ljava/lang/Object; HSPLkotlin/collections/AbstractCollection$toString$1;->(Lkotlin/collections/AbstractCollection;)V HSPLkotlin/collections/AbstractCollection;->()V -HSPLkotlin/collections/AbstractCollection;->contains(Ljava/lang/Object;)Z -HSPLkotlin/collections/AbstractCollection;->isEmpty()Z -HSPLkotlin/collections/AbstractCollection;->size()I HSPLkotlin/collections/AbstractCollection;->toString()Ljava/lang/String; HSPLkotlin/collections/AbstractList$Companion;->()V HSPLkotlin/collections/AbstractList$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLkotlin/collections/AbstractList$Companion;->checkElementIndex$kotlin_stdlib(II)V HSPLkotlin/collections/AbstractList$IteratorImpl;->(Lkotlin/collections/AbstractList;)V +HSPLkotlin/collections/AbstractList$IteratorImpl;->hasNext()Z +HSPLkotlin/collections/AbstractList$IteratorImpl;->next()Ljava/lang/Object; HSPLkotlin/collections/AbstractList;->()V HSPLkotlin/collections/AbstractList;->()V HSPLkotlin/collections/AbstractList;->iterator()Ljava/util/Iterator; @@ -13321,7 +13246,6 @@ HSPLkotlin/collections/CollectionsKt___CollectionsKt;->firstOrNull(Ljava/lang/It HSPLkotlin/collections/CollectionsKt___CollectionsKt;->firstOrNull(Ljava/util/List;)Ljava/lang/Object; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->getOrNull(Ljava/util/List;I)Ljava/lang/Object; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->joinTo$default(Ljava/lang/Iterable;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Appendable; -HSPLkotlin/collections/CollectionsKt___CollectionsKt;->joinTo(Ljava/lang/Iterable;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/Appendable; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->joinToString$default(Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->joinToString(Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->last(Ljava/util/List;)Ljava/lang/Object; @@ -13329,6 +13253,7 @@ HSPLkotlin/collections/CollectionsKt___CollectionsKt;->lastOrNull(Ljava/util/Lis HSPLkotlin/collections/CollectionsKt___CollectionsKt;->maxOrNull(Ljava/lang/Iterable;)Ljava/lang/Comparable; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->minOrNull(Ljava/lang/Iterable;)Ljava/lang/Comparable; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->minus(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/List; +HSPLkotlin/collections/CollectionsKt___CollectionsKt;->plus(Ljava/util/Collection;Ljava/lang/Iterable;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->plus(Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->plus(Ljava/util/Collection;[Ljava/lang/Object;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->reversed(Ljava/lang/Iterable;)Ljava/util/List; @@ -13338,6 +13263,7 @@ HSPLkotlin/collections/CollectionsKt___CollectionsKt;->sortedWith(Ljava/lang/Ite HSPLkotlin/collections/CollectionsKt___CollectionsKt;->sumOfFloat(Ljava/lang/Iterable;)F HSPLkotlin/collections/CollectionsKt___CollectionsKt;->toCollection(Ljava/lang/Iterable;Ljava/util/Collection;)Ljava/util/Collection; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->toIntArray(Ljava/util/Collection;)[I +HSPLkotlin/collections/CollectionsKt___CollectionsKt;->toList(Ljava/lang/Iterable;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->toMutableList(Ljava/lang/Iterable;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->toMutableList(Ljava/util/Collection;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt___CollectionsKt;->toMutableSet(Ljava/lang/Iterable;)Ljava/util/Set; @@ -15192,6 +15118,7 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->()V +HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->(Z)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getContextReceiverTypeCount()I @@ -15206,6 +15133,7 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getOldFlags()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReceiverType()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReturnType()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReturnTypeId()I +HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getSerializedSize()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameter(I)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeParameter; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameterCount()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameterList()Ljava/util/List; @@ -15222,6 +15150,7 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasReturnType( HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasReturnTypeId()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasTypeTable()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->initFields()V +HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->writeTo(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind;->(Ljava/lang/String;III)V @@ -15276,7 +15205,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->()V -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->(Z)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->getContextReceiverTypeCount()I @@ -15688,7 +15616,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmNameResolve HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmNameResolverBase;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmNameResolverBase;->([Ljava/lang/String;Ljava/util/Set;Ljava/util/List;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmNameResolverBase;->getQualifiedClassName(I)Ljava/lang/String; -HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmNameResolverBase;->getString(I)Ljava/lang/String; HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmNameResolverBase;->isLocalClassName(I)Z HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmNameResolverKt;->toExpandedRecordsList(Ljava/util/List;)Ljava/util/List; HSPLkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmProtoBufUtil;->()V @@ -15857,6 +15784,8 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readBytes()Lkot HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readDouble()D HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readEnum()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readFloat()F +HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readMessage(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite$Builder;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V +HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readMessage(Lkotlin/reflect/jvm/internal/impl/protobuf/Parser;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite; HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawByte()B HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawLittleEndian32()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawLittleEndian64()J @@ -15866,6 +15795,7 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawVarint64 HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readSInt64()J HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->recomputeBufferSizeAfterLimit()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->refillBuffer(I)V +HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->tryRefillBuffer(I)Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->(Ljava/io/OutputStream;[B)V HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->computeBoolSize(IZ)I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->computeBoolSizeNoTag(Z)I @@ -15916,8 +15846,9 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->getField(Lkotlin/reflec HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->getSerializedSize()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->getWireFormatForFieldType(Lkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;Z)I HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->hasField(Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet$FieldDescriptorLite;)Z -HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->isInitialized(Ljava/util/Map$Entry;)Z +HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->isInitialized()Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->iterator()Ljava/util/Iterator; +HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->makeImmutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->newFieldSet()Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet; HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->readPrimitiveField(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;Z)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->setField(Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet$FieldDescriptorLite;Ljava/lang/Object;)V @@ -15963,6 +15894,7 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->access$100( HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->makeExtensionsImmutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->newRepeatedGeneratedExtension(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/Internal$EnumLiteMap;ILkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;ZLjava/lang/Class;)Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$GeneratedExtension; HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->newSingularGeneratedExtension(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Ljava/lang/Object;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/Internal$EnumLiteMap;ILkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;Ljava/lang/Class;)Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$GeneratedExtension; +HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->parseUnknownField(Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;I)Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/LazyStringArrayList;->()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/LazyStringArrayList;->()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/LazyStringArrayList;->add(Lkotlin/reflect/jvm/internal/impl/protobuf/ByteString;)V @@ -16007,7 +15939,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->binarySearchInArr HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->checkMutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->ensureEntryArrayMutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->entrySet()Ljava/util/Set; -HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->get(Ljava/lang/Object;)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->getArrayEntryAt(I)Ljava/util/Map$Entry; HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->getNumArrayEntries()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap;->getOverflowEntries()Ljava/lang/Iterable; @@ -16496,6 +16427,7 @@ HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeseria HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->getReceiverParameterAnnotations(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/AnnotatedCallableKind;)Lkotlin/reflect/jvm/internal/impl/descriptors/annotations/Annotations; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->initializeWithCoroutinesExperimentalityStatus(Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/descriptors/DeserializedSimpleFunctionDescriptor;Lkotlin/reflect/jvm/internal/impl/descriptors/ReceiverParameterDescriptor;Lkotlin/reflect/jvm/internal/impl/descriptors/ReceiverParameterDescriptor;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/types/KotlinType;Lkotlin/reflect/jvm/internal/impl/descriptors/Modality;Lkotlin/reflect/jvm/internal/impl/descriptors/DescriptorVisibility;Ljava/util/Map;)V HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadConstructor(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Constructor;Z)Lkotlin/reflect/jvm/internal/impl/descriptors/ClassConstructorDescriptor; +HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadFunction(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;)Lkotlin/reflect/jvm/internal/impl/descriptors/SimpleFunctionDescriptor; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadOldFlags(I)I HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->valueParameters(Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/AnnotatedCallableKind;)Ljava/util/List; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/NameResolverUtilKt;->getClassId(Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;I)Lkotlin/reflect/jvm/internal/impl/name/ClassId; @@ -16534,6 +16466,7 @@ HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeseriali HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->computeLocalClassifierReplacementType(I)Lkotlin/reflect/jvm/internal/impl/types/SimpleType; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->getOwnTypeParameters()Ljava/util/List; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->simpleType$collectAllArguments(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;)Ljava/util/List; +HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->simpleType(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;Z)Lkotlin/reflect/jvm/internal/impl/types/SimpleType; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->toAttributes(Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/descriptors/annotations/Annotations;Lkotlin/reflect/jvm/internal/impl/types/TypeConstructor;Lkotlin/reflect/jvm/internal/impl/descriptors/DeclarationDescriptor;)Lkotlin/reflect/jvm/internal/impl/types/TypeAttributes; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->type(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;)Lkotlin/reflect/jvm/internal/impl/types/KotlinType; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->typeArgument(Lkotlin/reflect/jvm/internal/impl/descriptors/TypeParameterDescriptor;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type$Argument;)Lkotlin/reflect/jvm/internal/impl/types/TypeProjection; @@ -16759,6 +16692,7 @@ HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$KeyWithComp HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$KeyWithComputation;->equals(Ljava/lang/Object;)Z HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$KeyWithComputation;->hashCode()I HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValue;->(Lkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager;Lkotlin/jvm/functions/Function0;)V +HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValue;->invoke()Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValue;->postCompute(Ljava/lang/Object;)V HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValueWithPostCompute;->(Lkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager;Lkotlin/jvm/functions/Function0;)V HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValueWithPostCompute;->invoke()Ljava/lang/Object; @@ -17102,7 +17036,6 @@ HSPLkotlin/sequences/GeneratorSequence;->access$getGetNextValue$p(Lkotlin/sequen HSPLkotlin/sequences/GeneratorSequence;->iterator()Ljava/util/Iterator; HSPLkotlin/sequences/SequenceBuilderIterator;->()V HSPLkotlin/sequences/SequenceBuilderIterator;->getContext()Lkotlin/coroutines/CoroutineContext; -HSPLkotlin/sequences/SequenceBuilderIterator;->hasNext()Z HSPLkotlin/sequences/SequenceBuilderIterator;->next()Ljava/lang/Object; HSPLkotlin/sequences/SequenceBuilderIterator;->resumeWith(Ljava/lang/Object;)V HSPLkotlin/sequences/SequenceBuilderIterator;->setNextStep(Lkotlin/coroutines/Continuation;)V @@ -17156,7 +17089,6 @@ HSPLkotlin/sequences/SequencesKt___SequencesKt;->flatMapIterable(Lkotlin/sequenc HSPLkotlin/sequences/SequencesKt___SequencesKt;->map(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; HSPLkotlin/sequences/SequencesKt___SequencesKt;->mapNotNull(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; HSPLkotlin/sequences/SequencesKt___SequencesKt;->sortedWith(Lkotlin/sequences/Sequence;Ljava/util/Comparator;)Lkotlin/sequences/Sequence; -HSPLkotlin/sequences/SequencesKt___SequencesKt;->toCollection(Lkotlin/sequences/Sequence;Ljava/util/Collection;)Ljava/util/Collection; HSPLkotlin/sequences/SequencesKt___SequencesKt;->toList(Lkotlin/sequences/Sequence;)Ljava/util/List; HSPLkotlin/sequences/SequencesKt___SequencesKt;->toMutableList(Lkotlin/sequences/Sequence;)Ljava/util/List; HSPLkotlin/sequences/TransformingSequence$iterator$1;->(Lkotlin/sequences/TransformingSequence;)V @@ -17209,7 +17141,6 @@ HSPLkotlin/text/StringsKt;->contains$default(Ljava/lang/CharSequence;CZILjava/la HSPLkotlin/text/StringsKt;->contains$default(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt;->contains(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z HSPLkotlin/text/StringsKt;->drop(Ljava/lang/String;I)Ljava/lang/String; -HSPLkotlin/text/StringsKt;->endsWith$default(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange; HSPLkotlin/text/StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I HSPLkotlin/text/StringsKt;->indexOf$default(Ljava/lang/CharSequence;CIZILjava/lang/Object;)I @@ -17239,8 +17170,6 @@ HSPLkotlin/text/StringsKt__IndentKt;->replaceIndent(Ljava/lang/String;Ljava/lang HSPLkotlin/text/StringsKt__IndentKt;->trimIndent(Ljava/lang/String;)Ljava/lang/String; HSPLkotlin/text/StringsKt__StringNumberConversionsKt;->toLongOrNull(Ljava/lang/String;)Ljava/lang/Long; HSPLkotlin/text/StringsKt__StringNumberConversionsKt;->toLongOrNull(Ljava/lang/String;I)Ljava/lang/Long; -HSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith$default(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Z -HSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith(Ljava/lang/String;Ljava/lang/String;Z)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->isBlank(Ljava/lang/CharSequence;)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->regionMatches(Ljava/lang/String;ILjava/lang/String;IIZ)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->replace$default(Ljava/lang/String;CCZILjava/lang/Object;)Ljava/lang/String; @@ -17260,6 +17189,7 @@ HSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence; HSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt__StringsKt;->contains(Ljava/lang/CharSequence;CZ)Z HSPLkotlin/text/StringsKt__StringsKt;->contains(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z +HSPLkotlin/text/StringsKt__StringsKt;->findAnyOf$StringsKt__StringsKt(Ljava/lang/CharSequence;Ljava/util/Collection;IZZ)Lkotlin/Pair; HSPLkotlin/text/StringsKt__StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange; HSPLkotlin/text/StringsKt__StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I HSPLkotlin/text/StringsKt__StringsKt;->indexOf$default(Ljava/lang/CharSequence;CIZILjava/lang/Object;)I @@ -17417,7 +17347,6 @@ HSPLnet/zetetic/database/sqlcipher/CloseGuard;->get()Lnet/zetetic/database/sqlci HSPLnet/zetetic/database/sqlcipher/CloseGuard;->open(Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteClosable;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteClosable;->acquireReference()V -HSPLnet/zetetic/database/sqlcipher/SQLiteClosable;->close()V HSPLnet/zetetic/database/sqlcipher/SQLiteClosable;->releaseReference()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection$Operation;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection$Operation;->(Lnet/zetetic/database/sqlcipher/SQLiteConnection$1;)V @@ -17446,7 +17375,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->canonicalizeSyncMode(Ljava HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->detachCancellationSignal(Landroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->execute(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForChangedRowCount(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)I -HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForCursorWindow(Ljava/lang/String;[Ljava/lang/Object;Landroid/database/CursorWindow;IIZLandroid/os/CancellationSignal;)I HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForLastInsertedRowId(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)J HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForLong(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)J HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForString(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)Ljava/lang/String; @@ -17479,6 +17407,7 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->acquireConnection(Ljava/lang/String;ILandroid/os/CancellationSignal;)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->closeExcessConnectionsAndLogExceptionsLocked()V +HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->finishAcquireConnectionLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnection;I)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->getPriority(I)I HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->markAcquiredConnectionsLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$AcquiredConnectionStatus;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->obtainConnectionWaiterLocked(Ljava/lang/Thread;JIZLjava/lang/String;I)Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$ConnectionWaiter; @@ -17492,7 +17421,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->recycleConnectionWaite HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->releaseConnection(Lnet/zetetic/database/sqlcipher/SQLiteConnection;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->setMaxConnectionPoolSizeLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->throwIfClosedLocked()V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->tryAcquireNonPrimaryConnectionLocked(Ljava/lang/String;I)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->tryAcquirePrimaryConnectionLocked(I)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->waitForConnection(Ljava/lang/String;ILandroid/os/CancellationSignal;)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->wakeConnectionWaitersLocked()V @@ -17502,14 +17430,11 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->(Lnet/zetetic/database/sqlcipher/SQLiteCursorDriver;Ljava/lang/String;Lnet/zetetic/database/sqlcipher/SQLiteQuery;)V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->awc_clearOrCreateWindow(Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->close()V -HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->fillWindow(I)V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->finalize()V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getColumnIndex(Ljava/lang/String;)I HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getColumnNames()[Ljava/lang/String; -HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getCount()I HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->onMove(II)Z -HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->setWindow(Landroid/database/CursorWindow;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase$1;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase$1;->initialValue()Ljava/lang/Object; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase$1;->initialValue()Lnet/zetetic/database/sqlcipher/SQLiteSession; @@ -17529,11 +17454,11 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->executeSql(Ljava/lang/String HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->findEditTable(Ljava/lang/String;)Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getPath()Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getThreadDefaultConnectionFlags(Z)I -HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getThreadSession()Lnet/zetetic/database/sqlcipher/SQLiteSession; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getVersion()I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->hasCodec()Z HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->inTransaction()Z HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->insert(Ljava/lang/String;ILandroid/content/ContentValues;)J +HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->insert(Ljava/lang/String;Ljava/lang/String;Landroid/content/ContentValues;)J HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->insertWithOnConflict(Ljava/lang/String;Ljava/lang/String;Landroid/content/ContentValues;I)J HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->isMainThread()Z HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->isOpen()Z @@ -17559,7 +17484,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->setVersion(I)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->throwIfNotOpenLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->update(Ljava/lang/String;ILandroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/Object;)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->update(Ljava/lang/String;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I -HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->updateWithOnConflict(Ljava/lang/String;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;I)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->(Ljava/lang/String;I[BLnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)V @@ -17580,6 +17504,7 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Lnet/zetetic/database/sqlcipher/SQLiteDatabase$CursorFactory;IILnet/zetetic/database/DatabaseErrorHandler;Lnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;Z)V HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->(Landroid/content/Context;Ljava/lang/String;[BLnet/zetetic/database/sqlcipher/SQLiteDatabase$CursorFactory;IILnet/zetetic/database/DatabaseErrorHandler;Lnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;Z)V HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getBytes(Ljava/lang/String;)[B +HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getDatabaseLocked(Z)Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getReadableDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getWritableDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->onConfigure(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V @@ -17595,8 +17520,10 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getColumnNames()[Ljava/lang/S HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getConnectionFlags()I HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSession()Lnet/zetetic/database/sqlcipher/SQLiteSession; +HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSql()Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->onAllReferencesReleased()V HSPLnet/zetetic/database/sqlcipher/SQLiteQuery;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/lang/String;Landroid/os/CancellationSignal;)V +HSPLnet/zetetic/database/sqlcipher/SQLiteQuery;->fillWindow(Landroid/database/CursorWindow;IIZ)I HSPLnet/zetetic/database/sqlcipher/SQLiteQueryBuilder;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteQueryBuilder;->appendClause(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteQueryBuilder;->appendColumns(Ljava/lang/StringBuilder;[Ljava/lang/String;)V @@ -17605,7 +17532,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteSession$Transaction;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteSession$Transaction;->(Lnet/zetetic/database/sqlcipher/SQLiteSession$1;)V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->(Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->acquireConnection(Ljava/lang/String;ILandroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->beginTransaction(ILnet/zetetic/database/sqlcipher/SQLiteTransactionListener;ILandroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->beginTransactionUnchecked(ILnet/zetetic/database/sqlcipher/SQLiteTransactionListener;ILandroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->endTransaction(Landroid/os/CancellationSignal;)V @@ -17871,8 +17797,6 @@ HSPLokhttp3/RealCall$AsyncCall;->()V HSPLokhttp3/RealCall$AsyncCall;->(Lokhttp3/RealCall;Lokhttp3/Callback;)V HSPLokhttp3/RealCall$AsyncCall;->execute()V HSPLokhttp3/RealCall$AsyncCall;->executeOn(Ljava/util/concurrent/ExecutorService;)V -HSPLokhttp3/RealCall$AsyncCall;->get()Lokhttp3/RealCall; -HSPLokhttp3/RealCall$AsyncCall;->host()Ljava/lang/String; HSPLokhttp3/RealCall;->(Lokhttp3/OkHttpClient;Lokhttp3/Request;Z)V HSPLokhttp3/RealCall;->captureCallStackTrace()V HSPLokhttp3/RealCall;->enqueue(Lokhttp3/Callback;)V @@ -17932,7 +17856,6 @@ HSPLokhttp3/Response;->isSuccessful()Z HSPLokhttp3/Response;->message()Ljava/lang/String; HSPLokhttp3/Response;->newBuilder()Lokhttp3/Response$Builder; HSPLokhttp3/Response;->request()Lokhttp3/Request; -HSPLokhttp3/Response;->toString()Ljava/lang/String; HSPLokhttp3/ResponseBody$1;->(Lokhttp3/MediaType;JLokio/BufferedSource;)V HSPLokhttp3/ResponseBody$1;->source()Lokio/BufferedSource; HSPLokhttp3/ResponseBody;->()V @@ -18199,10 +18122,11 @@ HSPLokio/Buffer$UnsafeCursor;->()V HSPLokio/Buffer;->()V HSPLokio/Buffer;->clear()V HSPLokio/Buffer;->close()V -HSPLokio/Buffer;->completeSegmentByteCount()J HSPLokio/Buffer;->copyTo(Lokio/Buffer;JJ)Lokio/Buffer; HSPLokio/Buffer;->exhausted()Z +HSPLokio/Buffer;->getByte(J)B HSPLokio/Buffer;->indexOf(BJJ)J +HSPLokio/Buffer;->indexOfElement(Lokio/ByteString;J)J HSPLokio/Buffer;->read(Lokio/Buffer;J)J HSPLokio/Buffer;->read([BII)I HSPLokio/Buffer;->readByte()B @@ -18220,8 +18144,8 @@ HSPLokio/Buffer;->readString(Ljava/nio/charset/Charset;)Ljava/lang/String; HSPLokio/Buffer;->readUtf8(J)Ljava/lang/String; HSPLokio/Buffer;->setSize$okio(J)V HSPLokio/Buffer;->size()J -HSPLokio/Buffer;->skip(J)V HSPLokio/Buffer;->writableSegment$okio(I)Lokio/Segment; +HSPLokio/Buffer;->write(Lokio/Buffer;J)V HSPLokio/Buffer;->write([B)Lokio/Buffer; HSPLokio/Buffer;->write([BII)Lokio/Buffer; HSPLokio/Buffer;->writeAll(Lokio/Source;)J @@ -18316,6 +18240,7 @@ HSPLokio/Options;->of([Lokio/ByteString;)Lokio/Options; HSPLokio/OutputStreamSink;->(Ljava/io/OutputStream;Lokio/Timeout;)V HSPLokio/OutputStreamSink;->close()V HSPLokio/OutputStreamSink;->flush()V +HSPLokio/OutputStreamSink;->write(Lokio/Buffer;J)V HSPLokio/PeekSource;->(Lokio/BufferedSource;)V HSPLokio/PeekSource;->read(Lokio/Buffer;J)J HSPLokio/RealBufferedSink;->(Lokio/Sink;)V @@ -18339,6 +18264,7 @@ HSPLokio/RealBufferedSource;->peek()Lokio/BufferedSource; HSPLokio/RealBufferedSource;->rangeEquals(JLokio/ByteString;)Z HSPLokio/RealBufferedSource;->rangeEquals(JLokio/ByteString;II)Z HSPLokio/RealBufferedSource;->read(Lokio/Buffer;J)J +HSPLokio/RealBufferedSource;->readAll(Lokio/Sink;)J HSPLokio/RealBufferedSource;->readByte()B HSPLokio/RealBufferedSource;->readHexadecimalUnsignedLong()J HSPLokio/RealBufferedSource;->readIntLe()I @@ -18524,6 +18450,7 @@ HSPLorg/conscrypt/ConscryptEngine;->singleDstBuffer(Ljava/nio/ByteBuffer;)[Ljava HSPLorg/conscrypt/ConscryptEngine;->singleSrcBuffer(Ljava/nio/ByteBuffer;)[Ljava/nio/ByteBuffer; HSPLorg/conscrypt/ConscryptEngine;->transitionTo(I)V HSPLorg/conscrypt/ConscryptEngine;->unwrap(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult; +HSPLorg/conscrypt/ConscryptEngine;->unwrap([Ljava/nio/ByteBuffer;II[Ljava/nio/ByteBuffer;II)Ljavax/net/ssl/SSLEngineResult; HSPLorg/conscrypt/ConscryptEngine;->unwrap([Ljava/nio/ByteBuffer;[Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult; HSPLorg/conscrypt/ConscryptEngine;->verifyCertificateChain([[BLjava/lang/String;)V HSPLorg/conscrypt/ConscryptEngine;->wrap(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult; @@ -19060,7 +18987,7 @@ HSPLorg/signal/core/util/CursorExtensionsKt;->requireBlob(Landroid/database/Curs HSPLorg/signal/core/util/CursorExtensionsKt;->requireBoolean(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorExtensionsKt;->requireInt(Landroid/database/Cursor;Ljava/lang/String;)I HSPLorg/signal/core/util/CursorExtensionsKt;->requireLong(Landroid/database/Cursor;Ljava/lang/String;)J -HSPLorg/signal/core/util/CursorExtensionsKt;->requireNonNullString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; +HSPLorg/signal/core/util/CursorExtensionsKt;->requireObject(Landroid/database/Cursor;Ljava/lang/String;Lorg/signal/core/util/IntSerializer;)Ljava/lang/Object; HSPLorg/signal/core/util/CursorExtensionsKt;->requireString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/CursorExtensionsKt;->toInt(Z)I HSPLorg/signal/core/util/CursorUtil;->getBlob(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; @@ -19072,7 +18999,6 @@ HSPLorg/signal/core/util/CursorUtil;->isNull(Landroid/database/Cursor;Ljava/lang HSPLorg/signal/core/util/CursorUtil;->requireBlob(Landroid/database/Cursor;Ljava/lang/String;)[B HSPLorg/signal/core/util/CursorUtil;->requireBoolean(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorUtil;->requireInt(Landroid/database/Cursor;Ljava/lang/String;)I -HSPLorg/signal/core/util/CursorUtil;->requireLong(Landroid/database/Cursor;Ljava/lang/String;)J HSPLorg/signal/core/util/CursorUtil;->requireString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/DeleteBuilderPart1;->(Landroidx/sqlite/db/SupportSQLiteDatabase;Ljava/lang/String;)V HSPLorg/signal/core/util/DeleteBuilderPart1;->where(Ljava/lang/String;[Ljava/lang/Object;)Lorg/signal/core/util/DeleteBuilderPart2; @@ -19172,6 +19098,7 @@ HSPLorg/signal/core/util/SqlUtil;->()V HSPLorg/signal/core/util/SqlUtil;->access$buildSingleCustomCollectionQuery(Lorg/signal/core/util/SqlUtil;Ljava/lang/String;Ljava/util/List;)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/signal/core/util/SqlUtil;->appendArg([Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String; HSPLorg/signal/core/util/SqlUtil;->buildArgs(J)[Ljava/lang/String; +HSPLorg/signal/core/util/SqlUtil;->buildArgs([Ljava/lang/Object;)[Ljava/lang/String; HSPLorg/signal/core/util/SqlUtil;->buildCollectionQuery$default(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;ILorg/signal/core/util/SqlUtil$CollectionOperator;ILjava/lang/Object;)Ljava/util/List; HSPLorg/signal/core/util/SqlUtil;->buildCollectionQuery(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;ILorg/signal/core/util/SqlUtil$CollectionOperator;)Ljava/util/List; HSPLorg/signal/core/util/SqlUtil;->buildCustomCollectionQuery$lambda$11(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Lorg/signal/core/util/SqlUtil$Query; @@ -19399,14 +19326,16 @@ HSPLorg/signal/core/util/logging/Scrubber;->scrubUuids(Ljava/lang/CharSequence;) HSPLorg/signal/core/util/logging/Scrubber;->setIdentifierHmacKeyProvider(Lkotlin/jvm/functions/Function0;)V HSPLorg/signal/core/util/stream/TruncatingInputStream$$ExternalSyntheticBackport0;->m(J)I HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->()V +HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->build()Lorg/signal/core/util/tracing/DebugAnnotation; HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->name(Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation$Builder; HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->string_value(Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation$Builder; HSPLorg/signal/core/util/tracing/DebugAnnotation$Companion$ADAPTER$1;->(Lcom/squareup/wire/FieldEncoding;Lkotlin/reflect/KClass;Lcom/squareup/wire/Syntax;)V HSPLorg/signal/core/util/tracing/DebugAnnotation$Companion;->()V HSPLorg/signal/core/util/tracing/DebugAnnotation$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/signal/core/util/tracing/DebugAnnotation;->()V +HSPLorg/signal/core/util/tracing/DebugAnnotation;->(Ljava/lang/Long;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/Long;Lorg/signal/core/util/tracing/DebugAnnotation$NestedValue;Lokio/ByteString;)V HSPLorg/signal/core/util/tracing/DebugAnnotation;->equals(Ljava/lang/Object;)Z -HSPLorg/signal/core/util/tracing/TracePacket$Builder;->build()Lorg/signal/core/util/tracing/TracePacket; +HSPLorg/signal/core/util/tracing/TracePacket$Builder;->()V HSPLorg/signal/core/util/tracing/TracePacket$Builder;->synchronization_marker(Lokio/ByteString;)Lorg/signal/core/util/tracing/TracePacket$Builder; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->timestamp(Ljava/lang/Long;)Lorg/signal/core/util/tracing/TracePacket$Builder; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->track_descriptor(Lorg/signal/core/util/tracing/TrackDescriptor;)Lorg/signal/core/util/tracing/TracePacket$Builder; @@ -19424,14 +19353,12 @@ HSPLorg/signal/core/util/tracing/Tracer;->()V HSPLorg/signal/core/util/tracing/Tracer;->addPacket(Lorg/signal/core/util/tracing/TracePacket;)V HSPLorg/signal/core/util/tracing/Tracer;->debugAnnotation(Ljava/lang/String;Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation; HSPLorg/signal/core/util/tracing/Tracer;->end(Ljava/lang/String;J)V -HSPLorg/signal/core/util/tracing/Tracer;->forMethodEnd(Ljava/lang/String;JJ)Lorg/signal/core/util/tracing/TracePacket; +HSPLorg/signal/core/util/tracing/Tracer;->forMethodStart(Ljava/lang/String;JJLjava/util/Map;)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forSynchronization(J)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forTrack(JLjava/lang/String;)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forTrackId(J)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->getInstance()Lorg/signal/core/util/tracing/Tracer; HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;)V -HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V -HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;JLjava/util/Map;)V HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;Ljava/util/Map;)V HSPLorg/signal/core/util/tracing/Tracer;->toByteArray(Ljava/util/UUID;)[B @@ -19445,7 +19372,6 @@ HSPLorg/signal/core/util/tracing/TrackDescriptor$Companion;->(Lkotlin/jvm/ HSPLorg/signal/core/util/tracing/TrackDescriptor;->()V HSPLorg/signal/core/util/tracing/TrackDescriptor;->(Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Lorg/signal/core/util/tracing/ThreadDescriptor;Lorg/signal/core/util/tracing/CounterDescriptor;Lokio/ByteString;)V HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->()V -HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->build()Lorg/signal/core/util/tracing/TrackEvent; HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->debug_annotations(Ljava/util/List;)Lorg/signal/core/util/tracing/TrackEvent$Builder; HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->name(Ljava/lang/String;)Lorg/signal/core/util/tracing/TrackEvent$Builder; HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->track_uuid(Ljava/lang/Long;)Lorg/signal/core/util/tracing/TrackEvent$Builder; @@ -19593,14 +19519,14 @@ HSPLorg/signal/libsignal/protocol/state/KyberPreKeyRecord;->lambda$getTimestamp$ HSPLorg/signal/libsignal/protocol/state/KyberPreKeyRecord;->lambda$serialize$5(Lorg/signal/libsignal/internal/NativeHandleGuard;)[B HSPLorg/signal/libsignal/protocol/state/KyberPreKeyRecord;->serialize()[B HSPLorg/signal/libsignal/protocol/state/KyberPreKeyRecord;->unsafeNativeHandleWithoutGuard()J -HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda1;->(Lorg/signal/libsignal/internal/NativeHandleGuard;)V -HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda1;->run()Ljava/lang/Object; HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda2;->(Lorg/signal/libsignal/internal/NativeHandleGuard;)V -HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda2;->run()I +HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda2;->run()Ljava/lang/Object; HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda3;->(Lorg/signal/libsignal/internal/NativeHandleGuard;)V -HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda3;->run()J +HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda3;->run()I HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda4;->(Lorg/signal/libsignal/internal/NativeHandleGuard;)V -HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda4;->run()Ljava/lang/Object; +HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda4;->run()J +HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda5;->(Lorg/signal/libsignal/internal/NativeHandleGuard;)V +HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda5;->run()Ljava/lang/Object; HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord;->$r8$lambda$CDxhLFeixdJJ3Gbnc7rtYlou6XM(Lorg/signal/libsignal/internal/NativeHandleGuard;)I HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord;->$r8$lambda$NNeXiB4zebY13vEwwB1h5auBUd4(Lorg/signal/libsignal/internal/NativeHandleGuard;)[B HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord;->$r8$lambda$jMI4_aTYFD7DBqxBerR9ubPEl0s(Lorg/signal/libsignal/internal/NativeHandleGuard;)Lorg/signal/libsignal/protocol/ecc/ECKeyPair; @@ -19680,6 +19606,7 @@ HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$S1N9oMReIFywjAgkTfX HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$pQWvrV6w7QQq3SnkCgnHNDTtP_I(Lorg/signal/paging/FixedSizePagingController;IIII)V HSPLorg/signal/paging/FixedSizePagingController;->()V HSPLorg/signal/paging/FixedSizePagingController;->(Lorg/signal/paging/PagedDataSource;Lorg/signal/paging/PagingConfig;Lorg/signal/paging/DataStream;I)V +HSPLorg/signal/paging/FixedSizePagingController;->buildDataNeededLog(ILjava/lang/String;)Ljava/lang/String; HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataNeededAroundIndex$0()Z HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataNeededAroundIndex$1(IIII)V HSPLorg/signal/paging/FixedSizePagingController;->onDataInvalidated()V @@ -20042,7 +19969,7 @@ HSPLorg/thoughtcrime/securesms/animation/AnimationCompleteListener;->onAnimation HSPLorg/thoughtcrime/securesms/attachments/Attachment$Companion;->()V HSPLorg/thoughtcrime/securesms/attachments/Attachment$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/attachments/Attachment;->()V -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->(Ljava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;[B[BLjava/lang/String;ZZZIIIZJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;)V +HSPLorg/thoughtcrime/securesms/attachments/Attachment;->(Ljava/lang/String;IJLjava/lang/String;Lorg/thoughtcrime/securesms/attachments/Cdn;Ljava/lang/String;Ljava/lang/String;[B[BLjava/lang/String;ZZZIIIZJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;)V HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isInProgress()Z HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isPermanentlyFailed()Z HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isSticker()Z @@ -20053,22 +19980,36 @@ HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->()V HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->(J)V HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->hashCode()I HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->toString()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/attachments/Cdn$Serializer;->()V +HSPLorg/thoughtcrime/securesms/attachments/Cdn$Serializer;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/attachments/Cdn$Serializer;->deserialize(I)Lorg/thoughtcrime/securesms/attachments/Cdn; +HSPLorg/thoughtcrime/securesms/attachments/Cdn$Serializer;->deserialize(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/attachments/Cdn$Serializer;->fromCdnNumber(I)Lorg/thoughtcrime/securesms/attachments/Cdn; +HSPLorg/thoughtcrime/securesms/attachments/Cdn$Serializer;->serialize(Lorg/thoughtcrime/securesms/attachments/Cdn;)Ljava/lang/Integer; +HSPLorg/thoughtcrime/securesms/attachments/Cdn$WhenMappings;->()V +HSPLorg/thoughtcrime/securesms/attachments/Cdn;->$values()[Lorg/thoughtcrime/securesms/attachments/Cdn; +HSPLorg/thoughtcrime/securesms/attachments/Cdn;->()V +HSPLorg/thoughtcrime/securesms/attachments/Cdn;->(Ljava/lang/String;II)V +HSPLorg/thoughtcrime/securesms/attachments/Cdn;->access$getValue$p(Lorg/thoughtcrime/securesms/attachments/Cdn;)I +HSPLorg/thoughtcrime/securesms/attachments/Cdn;->getCdnNumber()I +HSPLorg/thoughtcrime/securesms/attachments/Cdn;->serialize()I +HSPLorg/thoughtcrime/securesms/attachments/Cdn;->values()[Lorg/thoughtcrime/securesms/attachments/Cdn; HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->()V HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->()V HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->compare(Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)I HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->()V -HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->(Lorg/thoughtcrime/securesms/attachments/AttachmentId;JZZLjava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIZLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;IJLjava/lang/String;)V +HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->(Lorg/thoughtcrime/securesms/attachments/AttachmentId;JZZLjava/lang/String;IJLjava/lang/String;Lorg/thoughtcrime/securesms/attachments/Cdn;Ljava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIZLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->getDisplayOrder()I HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->getUri()Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->hashCode()I HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->()V HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointer$default(Lorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;Lj$/util/Optional;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Ljava/lang/String;ILjava/lang/Object;)Lj$/util/Optional; -HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointer(Lj$/util/Optional;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Ljava/lang/String;)Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointer$default(Lorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;Lj$/util/Optional;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Ljava/lang/String;IILjava/lang/Object;)Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointer(Lj$/util/Optional;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Ljava/lang/String;I)Lj$/util/Optional; HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointers(Lj$/util/Optional;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->()V -HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->(Ljava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;)V +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->(Ljava/lang/String;IJLjava/lang/String;Lorg/thoughtcrime/securesms/attachments/Cdn;Ljava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;)V HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->getUri()Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/avatar/Avatar$Companion;->()V HSPLorg/thoughtcrime/securesms/avatar/Avatar$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -20139,6 +20080,15 @@ HSPLorg/thoughtcrime/securesms/avatar/view/AvatarView;->(Landroid/content/ HSPLorg/thoughtcrime/securesms/avatar/view/AvatarView;->displayChatAvatar(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/recipients/Recipient;Z)V HSPLorg/thoughtcrime/securesms/avatar/view/AvatarView;->hideStoryRing()V HSPLorg/thoughtcrime/securesms/avatar/view/AvatarView;->setStoryRingFromState(Lorg/thoughtcrime/securesms/database/model/StoryViewState;)V +HSPLorg/thoughtcrime/securesms/backup/RestoreState$Companion;->()V +HSPLorg/thoughtcrime/securesms/backup/RestoreState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/backup/RestoreState$Companion;->getSerializer()Lorg/signal/core/util/LongSerializer; +HSPLorg/thoughtcrime/securesms/backup/RestoreState$Serializer;->()V +HSPLorg/thoughtcrime/securesms/backup/RestoreState$Serializer;->()V +HSPLorg/thoughtcrime/securesms/backup/RestoreState;->$values()[Lorg/thoughtcrime/securesms/backup/RestoreState; +HSPLorg/thoughtcrime/securesms/backup/RestoreState;->()V +HSPLorg/thoughtcrime/securesms/backup/RestoreState;->(Ljava/lang/String;IIZ)V +HSPLorg/thoughtcrime/securesms/backup/RestoreState;->access$getSerializer$cp()Lorg/signal/core/util/LongSerializer; HSPLorg/thoughtcrime/securesms/badges/BadgeImageView;->()V HSPLorg/thoughtcrime/securesms/badges/BadgeImageView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/badges/BadgeImageView;->clearDrawable()V @@ -20169,6 +20119,26 @@ HSPLorg/thoughtcrime/securesms/blurhash/BlurHash;->()V HSPLorg/thoughtcrime/securesms/blurhash/BlurHash;->parseOrNull(Ljava/lang/String;)Lorg/thoughtcrime/securesms/blurhash/BlurHash; HSPLorg/thoughtcrime/securesms/blurhash/BlurHashModelLoader$Factory;->()V HSPLorg/thoughtcrime/securesms/blurhash/BlurHashResourceDecoder;->()V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->applyCorners()V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->applyCornersForSizeClass2()V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->getCells()[Lorg/thoughtcrime/securesms/components/ThumbnailView; +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->inflateLayout(I)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setCellBackgroundColor(I)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setRadii(IIII)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setRelativeRadii(Lorg/thoughtcrime/securesms/components/ThumbnailView;IIII)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setSlide(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/mms/Slide;IZ)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setSlides(Lcom/bumptech/glide/RequestManager;Ljava/util/List;Z)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->showSlides(Lcom/bumptech/glide/RequestManager;Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->sizeClass(I)I HSPLorg/thoughtcrime/securesms/components/AlertView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/AlertView;->initialize()V HSPLorg/thoughtcrime/securesms/components/AlertView;->setNone()V @@ -20221,6 +20191,9 @@ HSPLorg/thoughtcrime/securesms/components/AvatarImageView;->setAvatarClickHandle HSPLorg/thoughtcrime/securesms/components/AvatarImageView;->setFallbackPhotoProvider(Lorg/thoughtcrime/securesms/recipients/Recipient$FallbackPhotoProvider;)V HSPLorg/thoughtcrime/securesms/components/AvatarImageView;->setOnClickListener(Landroid/view/View$OnClickListener;)V HSPLorg/thoughtcrime/securesms/components/ComposeText$1;->(Lorg/thoughtcrime/securesms/components/ComposeText;)V +HSPLorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener;->()V +HSPLorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener;->(Lorg/thoughtcrime/securesms/components/InputPanel$MediaListener;)V +HSPLorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener;->(Lorg/thoughtcrime/securesms/components/InputPanel$MediaListener;Lorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener-IA;)V HSPLorg/thoughtcrime/securesms/components/ComposeText$QueryStart;->(IZ)V HSPLorg/thoughtcrime/securesms/components/ComposeText;->()V HSPLorg/thoughtcrime/securesms/components/ComposeText;->(Landroid/content/Context;Landroid/util/AttributeSet;)V @@ -20269,6 +20242,51 @@ HSPLorg/thoughtcrime/securesms/components/ConversationItemFooter;->setOnlyShowSe HSPLorg/thoughtcrime/securesms/components/ConversationItemFooter;->setPlaybackSpeedListener(Lorg/thoughtcrime/securesms/components/PlaybackSpeedToggleTextView$PlaybackSpeedListener;)V HSPLorg/thoughtcrime/securesms/components/ConversationItemFooter;->setRevealDotColor(I)V HSPLorg/thoughtcrime/securesms/components/ConversationItemFooter;->setTextColor(I)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail$Companion;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->dispatchDraw(Landroid/graphics/Canvas;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->getFooter()Lorg/thoughtcrime/securesms/util/views/Stub; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setBorderless(Z)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setClickable(Z)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setConversationColor(I)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setCorners(IIII)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setFocusable(Z)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setImageResource(Lcom/bumptech/glide/RequestManager;Ljava/util/List;ZZ)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setMaximumThumbnailHeight(I)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setMinimumThumbnailWidth(I)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setOnLongClickListener(Landroid/view/View$OnLongClickListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setThumbnailBounds([I)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->showShade(Z)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->showThumbnailView()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState$Creator;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->(ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIII)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->(ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->copy$default(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->copy(ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIII)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$Creator;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState$Creator;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->(FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIII)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->(FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->copy$default(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIIIILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->copy(FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIII)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->()V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;Lorg/thoughtcrime/securesms/util/views/Stub;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->copy$default(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;ILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->copy(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;)Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->getAlbumViewState()Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState; +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->getThumbnailViewState()Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setUnreadCountBackgroundTint(I)V @@ -20297,6 +20315,7 @@ HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcri HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/CharSequence;)V HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/CharSequence;Ljava/lang/CharSequence;)V HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)V +HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZZ)V HSPLorg/thoughtcrime/securesms/components/HidingLinearLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->()V HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;)V @@ -20313,6 +20332,7 @@ HSPLorg/thoughtcrime/securesms/components/InputPanel$RecordTime;->(Landroi HSPLorg/thoughtcrime/securesms/components/InputPanel$SlideToCancel;->(Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/components/InputPanel;->()V HSPLorg/thoughtcrime/securesms/components/InputPanel;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/InputPanel;->getVoiceNoteDraft()Lorg/thoughtcrime/securesms/database/DraftTable$Draft; HSPLorg/thoughtcrime/securesms/components/InputPanel;->onFinishInflate()V HSPLorg/thoughtcrime/securesms/components/InputPanel;->setHideForMessageRequestState(Z)V HSPLorg/thoughtcrime/securesms/components/InputPanel;->setMediaKeyboardToggleMode(Lorg/thoughtcrime/securesms/keyboard/KeyboardPage;)V @@ -20402,9 +20422,17 @@ HSPLorg/thoughtcrime/securesms/components/QuoteView;->setMessageType(Lorg/though HSPLorg/thoughtcrime/securesms/components/QuoteView;->setWallpaperEnabled(Z)V HSPLorg/thoughtcrime/securesms/components/RatingManager;->()V HSPLorg/thoughtcrime/securesms/components/RatingManager;->showRatingDialogIfNecessary(Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->()V +HSPLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->(Landroid/view/ViewGroup;Landroid/animation/LayoutTransition;)V +HSPLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->(Landroid/view/ViewGroup;Landroid/animation/LayoutTransition;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->findRecyclerParent()Landroidx/recyclerview/widget/RecyclerView; +HSPLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->onScrollStateChanged(Landroidx/recyclerview/widget/RecyclerView;I)V +HSPLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->onViewAttachedToWindow(Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$2;->()V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$2;->()V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$3;->(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$3;->test(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$3;->test(Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest;)Z HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$4;->(Lkotlin/jvm/functions/Function1;)V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$5;->(Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;)V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$Companion;->()V @@ -20412,6 +20440,7 @@ HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$Companion;->< HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$DefaultScrollStrategy;->()V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$DefaultScrollStrategy;->()V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest;->(IZLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollStrategy;)V +HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest;->getPosition()I HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$scrollPositionRequests$1;->()V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$scrollPositionRequests$1;->()V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$scrollPositionRequests$1;->apply(JLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest;)Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest; @@ -20423,6 +20452,35 @@ HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->(Land HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->notifyListCommitted()V HSPLorg/thoughtcrime/securesms/components/SendButton;->()V HSPLorg/thoughtcrime/securesms/components/SendButton;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport0;->m(Ljava/lang/Object;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport1;->m(Ljava/lang/Object;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport2;->m([Ljava/lang/Object;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$CancelClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$CancelClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;Lorg/thoughtcrime/securesms/components/ThumbnailView$CancelClickDispatcher-IA;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$DownloadClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$DownloadClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;Lorg/thoughtcrime/securesms/components/ThumbnailView$DownloadClickDispatcher-IA;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$ThumbnailClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView$ThumbnailClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;Lorg/thoughtcrime/securesms/components/ThumbnailView$ThumbnailClickDispatcher-IA;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->()V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->dispatchDraw(Landroid/graphics/Canvas;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->fillTargetDimensions([I[I[I)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->getNonZeroCount([I)I +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->hasSameContents(Lorg/thoughtcrime/securesms/mms/Slide;Lorg/thoughtcrime/securesms/mms/Slide;)Z +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->onMeasure(II)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->onSizeChanged(IIII)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setBounds(IIII)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setClickable(Z)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setFocusable(Z)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setImageResource(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/mms/Slide;ZZ)Lorg/signal/core/util/concurrent/ListenableFuture; +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setImageResource(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/mms/Slide;ZZII)Lorg/signal/core/util/concurrent/ListenableFuture; +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setRadii(IIII)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->showSecondaryText(Z)V HSPLorg/thoughtcrime/securesms/components/TypingIndicatorView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/TypingIndicatorView;->initialize(Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/TypingIndicatorView;->setDotTint(I)V @@ -20481,6 +20539,9 @@ HSPLorg/thoughtcrime/securesms/components/emoji/EmojiProvider;->getEmojiDrawable HSPLorg/thoughtcrime/securesms/components/emoji/EmojiSpan;->(Landroid/graphics/drawable/Drawable;Landroid/widget/TextView;)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiSpan;->draw(Landroid/graphics/Canvas;Ljava/lang/CharSequence;IIFIIILandroid/graphics/Paint;)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiSpan;->getSize(Landroid/graphics/Paint;Ljava/lang/CharSequence;IILandroid/graphics/Paint$FontMetricsInt;)I +HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V +HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda0;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda1;->()V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda2;->run()V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda3;->(Ljava/lang/Runnable;)V @@ -20509,6 +20570,7 @@ HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->onMeasure(II)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->onSizeChanged(IIII)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setMentionBackgroundTint(I)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setOverflowText(Ljava/lang/CharSequence;)V +HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setTextColor(I)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setTextSize(IF)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->unchanged(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)Z @@ -20578,6 +20640,9 @@ HSPLorg/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick;->() HSPLorg/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick;->(Ljava/lang/String;ILjava/lang/String;)V HSPLorg/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick;->fitzpatrickFromUnicode(Ljava/lang/CharSequence;I)Lorg/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick; HSPLorg/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick;->values()[Lorg/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick; +HSPLorg/thoughtcrime/securesms/components/mention/MentionAnnotation$$ExternalSyntheticLambda1;->()V +HSPLorg/thoughtcrime/securesms/components/mention/MentionAnnotation;->getMentionAnnotations(Landroid/text/Spanned;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/components/mention/MentionAnnotation;->getMentionAnnotations(Landroid/text/Spanned;II)Ljava/util/List; HSPLorg/thoughtcrime/securesms/components/mention/MentionDeleter;->()V HSPLorg/thoughtcrime/securesms/components/mention/MentionRenderer$MultiLineMentionRenderer;->(IILandroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;)V HSPLorg/thoughtcrime/securesms/components/mention/MentionRenderer$SingleLineMentionRenderer;->(IILandroid/graphics/drawable/Drawable;)V @@ -20692,6 +20757,82 @@ HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate;->acce HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate;->draw(Landroid/graphics/Canvas;Landroid/text/Spanned;Landroid/text/Layout;)V HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate;->stopAnimating()V HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate;->updateFromTextColor()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->containsPlayableSlides(Ljava/util/List;)Z +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->getTransferState(Ljava/util/List;)I +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->$values()[Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->(JJ)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->toString()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->(Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1;->(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->(Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->(Landroid/content/Context;Landroid/util/AttributeSet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$isUpdateToExistingSet(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;Ljava/util/List;)Z +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$slidesAsListOfTimestamps(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/util/List;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$verboseLog(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/lang/String;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->containsPlayableSlides(Ljava/util/List;)Z +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->deriveMode(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->getTransferState(Ljava/util/List;)I +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->isUpdateToExistingSet(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;Ljava/util/List;)Z +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->onAttachedToWindow()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setCancelClickListener(Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setClickable(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setFocusable(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setShowSecondaryText(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setSlides(Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setTransferClickListener(Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setVisible(Z)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->slidesAsListOfTimestamps(Ljava/util/List;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->updateState(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->verboseLog(Ljava/lang/String;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->(ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZ)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->(ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->copy$default(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZILjava/lang/Object;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->copy(ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZ)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->equals(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->getNetworkProgress()Ljava/util/Map; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->getSlides()Ljava/util/List; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->toString()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$Companion;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State;->$values()[Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->()V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->(Landroid/content/Context;Landroid/util/AttributeSet;II)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->(Landroid/content/Context;Landroid/util/AttributeSet;IIILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->progressPaint(I)Landroid/graphics/Paint; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->stopIconPaint(I)Landroid/graphics/Paint; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->trackPaint(I)Landroid/graphics/Paint; HSPLorg/thoughtcrime/securesms/components/voice/RetryableInitAudioSink$Companion;->()V HSPLorg/thoughtcrime/securesms/components/voice/RetryableInitAudioSink$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/components/voice/RetryableInitAudioSink;->()V @@ -20731,6 +20872,7 @@ HSPLorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvid HSPLorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider;->()V HSPLorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider;->(Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService;)V HSPLorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$KeyClearedReceiver$1;->(Lorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$KeyClearedReceiver;)V HSPLorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$KeyClearedReceiver$1;->onSuccess(Landroidx/media3/session/MediaController;)V HSPLorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$KeyClearedReceiver$1;->onSuccess(Ljava/lang/Object;)V @@ -20964,6 +21106,20 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationData;->getUnreadCount()I HSPLorg/thoughtcrime/securesms/conversation/ConversationData;->shouldJumpToMessage()Z HSPLorg/thoughtcrime/securesms/conversation/ConversationData;->shouldScrollToLastSeen()Z HSPLorg/thoughtcrime/securesms/conversation/ConversationData;->showUniversalExpireTimerMessage()Z +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$$ExternalSyntheticLambda3;->(Ljava/lang/Runnable;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider;->(Lorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider-IA;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideButton()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideDescription()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideSubtitle()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setAbout(Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setAvatar(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setLinkifyDescription(Z)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setTitle(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Runnable;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->showBackgroundBubble(Z)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->updateOutlineVisibility()V HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;JLjava/lang/String;Landroid/net/Uri;Ljava/lang/String;Ljava/util/ArrayList;Lorg/thoughtcrime/securesms/stickers/StickerLocator;ZIIZZLorg/thoughtcrime/securesms/badges/models/Badge;JLorg/thoughtcrime/securesms/conversation/ConversationIntents$ConversationScreenType;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->from(Landroid/os/Bundle;)Lorg/thoughtcrime/securesms/conversation/ConversationIntents$Args; HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->getChatColors()Lorg/thoughtcrime/securesms/conversation/colors/ChatColors; @@ -20998,6 +21154,7 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents;->getIntentData( HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents;->getIntentType(Landroid/os/Bundle;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents;->isBubbleIntentUri(Landroid/net/Uri;)Z HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda4;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$1;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener-IA;)V @@ -21020,6 +21177,8 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$SharedContactEventL HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$SharedContactEventListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$SharedContactEventListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$SlideClickPassthroughListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$SlideClickPassthroughListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/conversation/ConversationItem$SlideClickPassthroughListener-IA;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$ThumbnailClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$ThumbnailClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$ThumbnailClickListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$TouchDelegateChangedListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$TouchDelegateChangedListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$TouchDelegateChangedListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$UrlClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V @@ -21109,6 +21268,16 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationItemBodyBubble;->setOutl HSPLorg/thoughtcrime/securesms/conversation/ConversationItemBodyBubble;->setParentScrolling(Z)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItemBodyBubble;->setQuoteViewProjection(Lorg/thoughtcrime/securesms/util/Projection;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItemBodyBubble;->setVideoPlayerProjection(Lorg/thoughtcrime/securesms/util/Projection;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Detailed;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Detailed;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$EditHistory;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$EditHistory;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Standard;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Standard;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->(Z)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->(ZILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->(ZLkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationMessage$ComputedProperties;->(Lorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationMessage$ConversationMessageFactory;->createWithUnresolvedData(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Ljava/lang/CharSequence;Ljava/util/List;ZLorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/conversation/ConversationMessage; HSPLorg/thoughtcrime/securesms/conversation/ConversationMessage;->()V @@ -21172,6 +21341,9 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationUpdateItem;->()V HSPLorg/thoughtcrime/securesms/conversation/ConversationUpdateItem;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationUpdateItem;->onFinishInflate()V HSPLorg/thoughtcrime/securesms/conversation/ConversationUpdateItem;->setOnClickListener(Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda0;->()V +HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda1;->()V +HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->()V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;Landroid/content/Context;Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->getLatestTimestamp(Lorg/thoughtcrime/securesms/conversation/ConversationAdapterBridge;Landroidx/recyclerview/widget/LinearLayoutManager;)Lj$/util/Optional; @@ -21216,6 +21388,7 @@ HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->()V HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->(Landroid/content/Context;Landroid/util/AttributeSet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->getDraft()Lorg/thoughtcrime/securesms/database/DraftTable$Draft; HSPLorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener;->()V @@ -21257,6 +21430,8 @@ HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$BuiltIn;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet;->()V +HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id;->(J)V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id;->(JLkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -21300,6 +21475,7 @@ HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->acces HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->access$setUseLayer$p(Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;Z)V HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->getLayoutManager()Landroidx/recyclerview/widget/LinearLayoutManager; HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->setChatColors(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V +HSPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$$ExternalSyntheticLambda1;->(Landroidx/recyclerview/widget/RecyclerView;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->()V @@ -21321,6 +21497,11 @@ HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectCollection$Sin HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectCollection;->()V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectCollection;->()V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectCollection;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->$values()[Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference; +HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->()V +HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->values()[Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference; +HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$WhenMappings;->()V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->()V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->(Landroid/content/Context;Lkotlin/jvm/functions/Function0;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->cleanPulseAnimators()V @@ -21331,7 +21512,6 @@ HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->findAdapterBridge(Landroidx/recyclerview/widget/RecyclerView;)Lorg/thoughtcrime/securesms/conversation/ConversationAdapterBridge; HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->getCurrentSelection(Landroidx/recyclerview/widget/RecyclerView;)Ljava/util/Set; HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->getDifferenceForPart(Ljava/util/Set;Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectPart;)Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference; -HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->getItemOffsets(Landroid/graphics/Rect;Landroid/view/View;Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$State;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->hasRunningPulseRequestAnimators()Z HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->invalidateIfEnterExitAnimatorsAreRunning(Landroidx/recyclerview/widget/RecyclerView;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->invalidateIfPulseRequestAnimatorsAreRunning(Landroidx/recyclerview/widget/RecyclerView;)V @@ -21436,7 +21616,25 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSy HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda7;->createViewHolder(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->bindPayloadsIfAvailable()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->canPlayContent()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getBindable()Lorg/thoughtcrime/securesms/BindableConversationItem; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getColorizerProjections(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/ProjectionList; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getDisplayMode()Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getNextMessage()Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getPreviousMessage()Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->showProjectionArea()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->bind(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$OnScrollStateChangedListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->$r8$lambda$0GW66dll143qhTHiVUdlBHolclI(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->$r8$lambda$u2AJxgyeBquqI1nF9ok3s6g0b5Q(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->()V @@ -21494,6 +21692,11 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$Conversation HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$InputPanelMediaListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollDateHeaderHelper;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollDateHeaderHelper;->bind(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$onScrolled$1;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$onScrolled$1;->invoke(J)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$onScrolled$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener;->$r8$lambda$SqiWUifYWEV36mNfvnPsrkHWoAw(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener;->onScrolled$lambda$0(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)V @@ -21564,6 +21767,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConve HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$3;->apply(Lorg/thoughtcrime/securesms/conversation/v2/ConversationThreadState;)Lio/reactivex/rxjava3/core/ObservableSource; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Ljava/util/List;Lkotlin/jvm/internal/Ref$BooleanRef;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$$ExternalSyntheticLambda0;->run()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$invoke$lambda$1$$inlined$doAfterNextLayout$1$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$invoke$lambda$1$$inlined$doAfterNextLayout$1;->(Landroid/view/View;Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$invoke$lambda$1$$inlined$doAfterNextLayout$1;->onLayoutChange(Landroid/view/View;IIIIIIII)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4;->$r8$lambda$oOR6vln5-HH0T67uKvvWOaEanvw(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Ljava/util/List;Lkotlin/jvm/internal/Ref$BooleanRef;)V @@ -21682,6 +21886,11 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentSto HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentWallpaper(Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->registerForResults()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->updateMessageRequestAcceptedState(Z)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations;Landroid/view/View;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationMessageElement;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->getHeight()I +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->getItemView()Landroid/view/View; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->updateForWallpaper()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$UnreadState$None;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$UnreadState$None;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$UnreadState;->()V @@ -21729,10 +21938,10 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;- HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;->(J)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;->getConversationRecipient()Lio/reactivex/rxjava3/core/Observable; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;->getGroupRecord()Lio/reactivex/rxjava3/core/Observable; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda11;->(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda11;->call()Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda7;->(JLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;I)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda7;->call()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda12;->(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda12;->call()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda8;->(JLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;I)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda8;->call()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$MessageCounts;->()V @@ -21865,6 +22074,9 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMember HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$2;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$2;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$setShowScrollButtonsForScrollPosition$1;->(ZZ)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$setShowScrollButtonsForScrollPosition$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$setShowScrollButtonsForScrollPosition$1;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$storyRingState$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$storyRingState$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$storyRingState$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; @@ -22010,8 +22222,12 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewM HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$1;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Lkotlin/Unit;Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState; HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$filteredState$1;->()V @@ -22027,21 +22243,17 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewM HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->access$getStore$p(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;)Lorg/thoughtcrime/securesms/util/rx/RxStore; HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->getHasOngoingGroupCallSnapshot()Z HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->peekGroupCall()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$1;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$1;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$1;->test(Lj$/util/Optional;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$1;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$2;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$2;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$3;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$3;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$4;->(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$1;->(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$Factory;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$Factory;->(JLorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$Factory;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$Factory;->create(Ljava/lang/Class;)Landroidx/lifecycle/ViewModel; HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$Factory;->create(Ljava/lang/Class;Landroidx/lifecycle/viewmodel/CreationExtras;)Landroidx/lifecycle/ViewModel; -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$duplicates$1;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$duplicates$1;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$1;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$1;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$1;->test(Lj$/util/Optional;)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$1;->test(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$2;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$2;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$updateGroupStateIfNeeded$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$updateGroupStateIfNeeded$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$updateGroupStateIfNeeded$1;->test(Ljava/lang/Object;)Z @@ -22049,8 +22261,8 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$updateGroupStateIfNeeded$2;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$updateGroupStateIfNeeded$2;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->(JLorg/thoughtcrime/securesms/groups/v2/GroupManagementRepository;Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->(JLorg/thoughtcrime/securesms/groups/v2/GroupManagementRepository;Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->(Lorg/thoughtcrime/securesms/groups/v2/GroupManagementRepository;Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->(Lorg/thoughtcrime/securesms/groups/v2/GroupManagementRepository;Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository;ILkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->updateGroupStateIfNeeded()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsData;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsData;->(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Landroid/graphics/drawable/Drawable;)V @@ -22066,6 +22278,17 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColo HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsItemDecoration;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsItemDecoration;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsItemDecoration;->onDraw(Landroid/graphics/Canvas;Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$State;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->(Lkotlin/jvm/functions/Function0;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->draw(Landroid/graphics/Canvas;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getChatColors()Lorg/thoughtcrime/securesms/conversation/colors/ChatColors; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getMask()Landroid/graphics/drawable/Drawable; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getOpacity()I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getOutline(Landroid/graphics/Outline;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->isSolidColor()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setCorners(Lorg/thoughtcrime/securesms/util/Projection$Corners;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setCorners([F)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setLocalChatColors(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout;->onMeasure(II)V @@ -22076,6 +22299,127 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;-> HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->onMeasure(II)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->removeOnMeasureListener(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnMeasureListener;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->setOnDispatchTouchEventListener(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnDispatchTouchEventListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->(Ljava/lang/String;IFF)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->getBottomPadding()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->getTopPadding()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->isEndingShape()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->isStartingShape()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->access$getCollapsedSpacing$cp()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->access$getDefaultSpacing$cp()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->getCornersLTR()Lorg/thoughtcrime/securesms/util/Projection$Corners; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isEndOfMessageCluster(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isSingularMessage(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Z)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isStartOfMessageCluster(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Z)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isWithinClusteringTime(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->setBodyBubbleCorners(FFFF)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->setMessageShape(Lorg/thoughtcrime/securesms/database/model/MessageRecord;ZI)Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Landroid/view/ViewGroup;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lcom/google/android/material/imageview/ShapeableImageView;Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView;Lorg/thoughtcrime/securesms/components/DeliveryStatusView;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/ExpirationTimerView;Landroid/view/View;Landroid/widget/Space;Lorg/thoughtcrime/securesms/components/AlertView;Z)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getAlert()Lorg/thoughtcrime/securesms/components/AlertView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getBody()Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getBodyWrapper()Landroid/view/ViewGroup; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getDeliveryStatus()Lorg/thoughtcrime/securesms/components/DeliveryStatusView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterBackground()Landroid/view/View; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterDate()Landroid/widget/TextView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterExpiry()Lorg/thoughtcrime/securesms/components/ExpirationTimerView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterSpace()Landroid/widget/Space; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getReactions()Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getReply()Lcom/google/android/material/imageview/ShapeableImageView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getRoot()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderBadge()Lorg/thoughtcrime/securesms/badges/BadgeImageView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderName()Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderPhoto()Lorg/thoughtcrime/securesms/components/AvatarImageView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->isIncoming()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridgeKt;->bridge(Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;)Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0;->onLayoutChange(Landroid/view/View;IIIIIIII)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/conversation/ConversationAdapter$ItemClickListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$PassthroughClickListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$ReactionMeasureListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1;->invoke()Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsData; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->invoke()Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsData; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$replyDelegate$1;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$senderDrawable$1;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->$r8$lambda$ocilDMoff9b132TfYhzB6ol1qqk(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;Landroid/view/View;IIIIIIII)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->_init_$lambda$0(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;Landroid/view/View;IIIIIIII)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->bind(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->bind(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->canPlayContent()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getColorizerProjections(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/ProjectionList; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getConversationMessage()Lorg/thoughtcrime/securesms/conversation/ConversationMessage; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getShape()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateBodyBubbleDrawable(Landroid/view/ViewGroup;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateChatColorsDrawable(Landroid/view/ViewGroup;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateFooterDrawable(Landroid/view/ViewGroup;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->isContentCondensed()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->isForcedFooter()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->linkifyMessageBody(Landroid/text/Spannable;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentAlert()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentBody()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentDate()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentDeliveryStatus()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterBackground()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterEndPadding()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterExpiry()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentReactions()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSender()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSenderNameBackground()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSenderNameColor()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->setConversationMessage(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->setShape(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->showProjectionArea()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$1;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->invoke(Landroid/content/Context;Z)Ljava/lang/Integer; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getBodyBubbleColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getBodyTextColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getFooterBubbleColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getReplyIconBackgroundColor()I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->linkifyUrlLinks(Landroid/text/Spannable;ZLorg/thoughtcrime/securesms/util/UrlClickHandler;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->getShapeDelegate()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->getThemeDelegate()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Ljava/util/List;Landroid/view/View;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Landroid/widget/Space;Lorg/thoughtcrime/securesms/util/views/Stub;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->displayTuckedIntoBody()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->getFooterWidth()I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->onPostMeasure()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->onPreMeasure()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2Payload;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2Payload; HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2Payload;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2Payload;->(Ljava/lang/String;I)V @@ -22485,7 +22829,6 @@ HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type;->(Ljava/lang/String;I)V HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type;->values()[Lorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type; HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;)V -HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->getThreadRecord()Lorg/thoughtcrime/securesms/database/model/ThreadRecord; HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->getType()Lorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type; HSPLorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter;->$values()[Lorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter; @@ -22641,6 +22984,12 @@ HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore;->storeSigne HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore;->()V HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore;->(Lorg/whispersystems/signalservice/api/push/ServiceId;)V HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore;->archiveAllSessions()V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState$Companion;->()V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState;->$values()[Lorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState;->()V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState;->(Ljava/lang/String;II)V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState;->getValue()I HSPLorg/thoughtcrime/securesms/database/AttachmentTable$Companion;->()V HSPLorg/thoughtcrime/securesms/database/AttachmentTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion;->()V @@ -22649,13 +22998,13 @@ HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Comp HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion;->parse(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties; HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Creator;->()V HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->()V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->(ZZJJIZ)V HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->access$getDEFAULT_MEDIA_QUALITY$cp()I HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertUndownloadedAttachment$attachmentId$1;->(JLorg/thoughtcrime/securesms/attachments/Attachment;Z)V HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertUndownloadedAttachment$attachmentId$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertUndownloadedAttachment$attachmentId$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)Lorg/thoughtcrime/securesms/attachments/AttachmentId; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->()V HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;Lorg/thoughtcrime/securesms/crypto/AttachmentSecret;)V -HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->containsStickerPackId(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->deleteAbandonedPreuploadedAttachments()I HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachment(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachment(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; @@ -22692,6 +23041,10 @@ HSPLorg/thoughtcrime/securesms/database/CallTable$ReadState;->$values()[Lorg/tho HSPLorg/thoughtcrime/securesms/database/CallTable$ReadState;->()V HSPLorg/thoughtcrime/securesms/database/CallTable$ReadState;->(Ljava/lang/String;II)V HSPLorg/thoughtcrime/securesms/database/CallTable$ReadState;->access$getCode$p(Lorg/thoughtcrime/securesms/database/CallTable$ReadState;)I +HSPLorg/thoughtcrime/securesms/database/CallTable$markRingingCallsAsMissed$1;->()V +HSPLorg/thoughtcrime/securesms/database/CallTable$markRingingCallsAsMissed$1;->()V +HSPLorg/thoughtcrime/securesms/database/CallTable$markRingingCallsAsMissed$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/CallTable$markRingingCallsAsMissed$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/CallTable;->()V HSPLorg/thoughtcrime/securesms/database/CallTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/CallTable;->getCalls(Ljava/util/Collection;)Ljava/util/Map; @@ -22721,6 +23074,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambd HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20;->run()V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda22;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda22;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda23;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda23;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda24;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V @@ -22731,10 +23086,6 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambd HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda29;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda40;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Ljava/lang/Runnable;)V @@ -22748,13 +23099,12 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Aq7iz6-OcN HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$FLqOSncPM9UHAHmQfH7ITyYgYis(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$P-H8JPj8WgBa8EorlTkjTC0yG1E(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Q9T3e0x03-9UyovUEacfv32ZkYs(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Rd4ts_QPgl4yKrcyZyl-dxASD8g(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$XcpL0fyOGdTr1sc4d0z4i8eoe14(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$XpAe1b_YlxfSEkV3hD_v20iDkHw(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$dh6RWMfCAixhY74q-duAcBwIwmU(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$mv9tymw4eNQuLtAMo52Pot0i2c4(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$nM9Xevlg3i5jd4hhWqCSJ8V0APs(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$oXFDlhvhHFY1OBIQHYp3Oanmq-k(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$v9-I7k7VKIptUuQHIpRZcaVjlwY(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$wnm9BEANNc03FZmWKcqOLSgrT_U(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$wtkgxGON7fTcqqEso3BleXuYIA8(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$zacSulZCbj18KAJ4fsL5guxghT4(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -22763,9 +23113,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyAttachme HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyConversationListListeners$22()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyConversationListeners$19(J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyRecipientChanged$34(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStickerObservers$26()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStickerPackObservers$27()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStoryObservers$35(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerAttachmentObserver$9(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerConversationListObserver$0(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerConversationObserver$1(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerMessageInsertObserver$11(JLorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V @@ -22780,9 +23129,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyConversationLis HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyMapped(Ljava/util/Map;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyRecipientChanged(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifySet(Ljava/util/Set;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStickerObservers()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStickerPackObservers()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStoryObservers(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerAttachmentObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerConversationListObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerConversationObserver(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerMapped(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V @@ -22799,8 +23147,6 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->getWritableDatabase()Lor HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyAttachmentListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyConversationListListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyConversationListeners(J)V -HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyStickerListeners()V -HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyStickerPackListeners()V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$Companion;->()V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$ListTable;->()V @@ -22889,9 +23235,6 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase$deleteJobs$1;->invoke(Lnet/z HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->(Ljava/util/List;Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->invoke(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->(Ljava/util/List;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->invoke(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->$r8$lambda$ou_p531IVGikC2LNueT6qnVrWyc(Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->()V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->(Landroid/app/Application;Lorg/thoughtcrime/securesms/crypto/DatabaseSecret;)V @@ -22900,7 +23243,6 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertConstraintSpe HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertDependencySpecs(Lorg/thoughtcrime/securesms/database/JobDatabase;Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertJobSpec(Lorg/thoughtcrime/securesms/database/JobDatabase;Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$setInstance$cp(Lorg/thoughtcrime/securesms/database/JobDatabase;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase;->constraintSpecFromCursor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec; HSPLorg/thoughtcrime/securesms/database/JobDatabase;->deleteJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->dropTableIfPresent(Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->getAllConstraintSpecs()Ljava/util/List; @@ -22911,13 +23253,11 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertConstraintSpecs(Lnet HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertDependencySpecs(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertJobSpec(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertJobs(Ljava/util/List;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase;->jobSpecFromCursor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/database/JobDatabase;->markJobAsRunning(Ljava/lang/String;J)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->onOpen$lambda$0(Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->onOpen(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateAllJobsToBePending()V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateJobAfterRetry(Ljava/lang/String;JIJ[B)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0;->run()V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$1;->()V @@ -23115,6 +23455,9 @@ HSPLorg/thoughtcrime/securesms/database/MessageTable$QuoteDescriptor;->hashCode( HSPLorg/thoughtcrime/securesms/database/MessageTable$SyncMessageId;->()V HSPLorg/thoughtcrime/securesms/database/MessageTable$SyncMessageId;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;J)V HSPLorg/thoughtcrime/securesms/database/MessageTable$WhenMappings;->()V +HSPLorg/thoughtcrime/securesms/database/MessageTable$clearIsRingingOnLocalDeviceFlag$1;->(Ljava/util/Collection;)V +HSPLorg/thoughtcrime/securesms/database/MessageTable$clearIsRingingOnLocalDeviceFlag$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/MessageTable$clearIsRingingOnLocalDeviceFlag$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/MessageTable$getConversationSnippet$1;->()V HSPLorg/thoughtcrime/securesms/database/MessageTable$getConversationSnippet$1;->()V HSPLorg/thoughtcrime/securesms/database/MessageTable$getConversationSnippet$1;->invoke(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/MessageRecord; @@ -23130,6 +23473,7 @@ HSPLorg/thoughtcrime/securesms/database/MessageTable;->access$getOutgoingTypeCla HSPLorg/thoughtcrime/securesms/database/MessageTable;->access$getSerializedLinkPreviews(Lorg/thoughtcrime/securesms/database/MessageTable;Ljava/util/Map;Ljava/util/List;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/MessageTable;->access$getSerializedSharedContacts(Lorg/thoughtcrime/securesms/database/MessageTable;Ljava/util/Map;Ljava/util/List;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/MessageTable;->buildMeaningfulMessagesQuery(J)Lorg/signal/core/util/SqlUtil$Query; +HSPLorg/thoughtcrime/securesms/database/MessageTable;->clearIsRingingOnLocalDeviceFlag(Ljava/util/Collection;)V HSPLorg/thoughtcrime/securesms/database/MessageTable;->getAllRateLimitedMessageIds()Ljava/util/Set; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getAllStoriesFor(Lorg/thoughtcrime/securesms/recipients/RecipientId;I)Lorg/thoughtcrime/securesms/database/MessageTable$Reader; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getConversation(JJJ)Landroid/database/Cursor; @@ -23219,6 +23563,10 @@ HSPLorg/thoughtcrime/securesms/database/MessageTypes$-CC;->isStoryReaction(J)Z HSPLorg/thoughtcrime/securesms/database/MessageTypes$-CC;->isThreadMergeType(J)Z HSPLorg/thoughtcrime/securesms/database/MessageTypes$-CC;->isUnsupportedMessageType(J)Z HSPLorg/thoughtcrime/securesms/database/MessageTypes;->()V +HSPLorg/thoughtcrime/securesms/database/NameCollisionTables$Companion;->()V +HSPLorg/thoughtcrime/securesms/database/NameCollisionTables$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/database/NameCollisionTables;->()V +HSPLorg/thoughtcrime/securesms/database/NameCollisionTables;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/NotificationProfileDatabase$Companion;->()V HSPLorg/thoughtcrime/securesms/database/NotificationProfileDatabase$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/NotificationProfileDatabase$NotificationProfileAllowedMembersTable;->()V @@ -23396,6 +23744,7 @@ HSPLorg/thoughtcrime/securesms/database/RecipientTable;->markRegistered(Lorg/tho HSPLorg/thoughtcrime/securesms/database/RecipientTable;->markRegisteredOrThrow(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/whispersystems/signalservice/api/push/ServiceId;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->processPnpTuple(Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;ZZ)Lorg/thoughtcrime/securesms/database/RecipientTable$ProcessPnpTupleResult; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->processPnpTupleToChangeSet(Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;ZZ)Lorg/thoughtcrime/securesms/database/PnpChangeSet; +HSPLorg/thoughtcrime/securesms/database/RecipientTable;->rotateStorageId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setCapabilities(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilities;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setMuted(Lorg/thoughtcrime/securesms/recipients/RecipientId;J)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileAvatar(Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/lang/String;)V @@ -23432,9 +23781,7 @@ HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->()V HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/recipients/Recipient$Extras; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getRecipientExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/databaseprotos/RecipientExtras; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getRecord(Landroid/content/Context;Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord; -HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getRecord(Landroid/content/Context;Landroid/database/Cursor;Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getSyncExtras$lambda$6(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/Boolean; -HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getSyncExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord$SyncExtras; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->parseBadgeList([B)Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->readCapabilities(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities; HSPLorg/thoughtcrime/securesms/database/RemappedRecordTables$Companion;->()V @@ -23450,7 +23797,6 @@ HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLam HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda0;->subscribe(Lio/reactivex/rxjava3/core/FlowableEmitter;)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->(Lio/reactivex/rxjava3/core/Emitter;)V -HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->onChanged()V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->prime()V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$conversation$1;->(J)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$conversation$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -23572,9 +23918,9 @@ HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->replace(Ljava/lang/Stri HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->runPostSuccessfulTransaction(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->runPostSuccessfulTransaction(Ljava/lang/String;Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->setTransactionSuccessful()V +HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->trace(Ljava/lang/String;Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceLockEnd()V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceLockStart()V -HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceSql(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLorg/thoughtcrime/securesms/database/SQLiteDatabase$Returnable;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceSql(Ljava/lang/String;Ljava/lang/String;ZLjava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceSql(Ljava/lang/String;Ljava/lang/String;ZLorg/thoughtcrime/securesms/database/SQLiteDatabase$Returnable;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->update(Ljava/lang/String;ILandroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/Object;)I @@ -23706,17 +24052,9 @@ HSPLorg/thoughtcrime/securesms/database/SqlCipherErrorHandler;->(Ljava/lan HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->()V HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->()V HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->load()V -HSPLorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader;->(Landroid/database/Cursor;)V -HSPLorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader;->getNext()Lorg/thoughtcrime/securesms/database/model/StickerPackRecord; HSPLorg/thoughtcrime/securesms/database/StickerTable;->()V HSPLorg/thoughtcrime/securesms/database/StickerTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;Lorg/thoughtcrime/securesms/crypto/AttachmentSecret;)V -HSPLorg/thoughtcrime/securesms/database/StickerTable;->deleteOrphanedPacks()V -HSPLorg/thoughtcrime/securesms/database/StickerTable;->deleteStickersInPackExceptCover(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/StickerTable;->getAllStickerPacks(Ljava/lang/String;)Landroid/database/Cursor; -HSPLorg/thoughtcrime/securesms/database/StickerTable;->getStickerPack(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/model/StickerPackRecord; -HSPLorg/thoughtcrime/securesms/database/StickerTable;->isPackAvailableAsReference(Ljava/lang/String;)Z -HSPLorg/thoughtcrime/securesms/database/StickerTable;->uninstallPack(Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/database/StickerTable;->updatePackInstalled(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;Ljava/lang/String;ZZ)V HSPLorg/thoughtcrime/securesms/database/StorySendTable$Companion;->()V HSPLorg/thoughtcrime/securesms/database/StorySendTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/StorySendTable;->()V @@ -24025,7 +24363,6 @@ HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails$Companion HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->()V HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->(JJ)V -HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->getDiskCacheKeyBytes()[B HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->hashCode()I HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->toString()Ljava/lang/String; @@ -24160,7 +24497,6 @@ HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setUnreadCo HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setUnreadSelfMentionsCount(I)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)V HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;Lorg/thoughtcrime/securesms/database/model/ThreadRecord-IA;)V -HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getBody()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getBodyRanges()Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getDate()J @@ -24183,6 +24519,8 @@ HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonat HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation$Companion;->()V HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation;->()V +HSPLorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding;->(Landroid/view/View;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Landroid/widget/LinearLayout;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Lcom/google/android/material/button/MaterialButton;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Landroid/view/View;Landroidx/constraintlayout/widget/ConstraintLayout;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V +HSPLorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding; HSPLorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;->(Lorg/thoughtcrime/securesms/components/InputPanel;Landroid/widget/ImageButton;Landroidx/constraintlayout/widget/Barrier;Lorg/thoughtcrime/securesms/components/InputPanel;Lorg/thoughtcrime/securesms/components/AnimatingToggle;Landroid/view/View;Landroidx/constraintlayout/widget/Barrier;Lcom/google/android/material/imageview/ShapeableImageView;Landroidx/appcompat/widget/AppCompatTextView;Lorg/thoughtcrime/securesms/components/ComposeText;Lorg/thoughtcrime/securesms/components/emoji/EmojiToggle;Landroid/widget/ImageButton;Lorg/thoughtcrime/securesms/components/HidingLinearLayout;Landroidx/appcompat/widget/AppCompatImageButton;Landroidx/recyclerview/widget/RecyclerView;Lorg/thoughtcrime/securesms/components/LinkPreviewView;Lorg/thoughtcrime/securesms/components/HidingLinearLayout;Landroid/widget/ImageButton;Lorg/thoughtcrime/securesms/components/QuoteView;Lorg/thoughtcrime/securesms/components/MicrophoneRecorderView;Lorg/thoughtcrime/securesms/components/SendButton;Landroid/widget/ImageButton;Lorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;)V HSPLorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding; HSPLorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;->getRoot()Lorg/thoughtcrime/securesms/components/InputPanel; @@ -24199,8 +24537,14 @@ HSPLorg/thoughtcrime/securesms/databinding/ConversationTitleViewBinding;->getRoo HSPLorg/thoughtcrime/securesms/databinding/OnboardingMegaphoneCardBinding;->(Lcom/google/android/material/card/MaterialCardView;Landroid/widget/ImageView;Landroid/widget/ImageView;Landroid/widget/TextView;)V HSPLorg/thoughtcrime/securesms/databinding/OnboardingMegaphoneCardBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/OnboardingMegaphoneCardBinding; HSPLorg/thoughtcrime/securesms/databinding/OnboardingMegaphoneCardBinding;->getRoot()Lcom/google/android/material/card/MaterialCardView; +HSPLorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding;->(Landroid/view/View;Landroidx/constraintlayout/widget/Guideline;Landroidx/appcompat/widget/AppCompatImageView;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;Landroidx/constraintlayout/widget/Guideline;)V +HSPLorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding; +HSPLorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding;->inflate(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding; HSPLorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;->(Lorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;Landroid/view/ViewStub;Landroid/view/View;Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;Landroid/widget/FrameLayout;Lorg/thoughtcrime/securesms/components/menu/SignalBottomActionBar;Landroidx/constraintlayout/widget/Barrier;Lorg/thoughtcrime/securesms/conversation/v2/DisabledInputView;Lcom/google/android/material/button/MaterialButton;Lorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;Landroidx/constraintlayout/widget/Barrier;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectRecyclerView;Landroid/widget/FrameLayout;Landroid/view/ViewStub;Lorg/thoughtcrime/securesms/databinding/ConversationSearchNavBinding;Lorg/thoughtcrime/securesms/databinding/ConversationTitleViewBinding;Landroid/widget/FrameLayout;Landroid/widget/ImageView;Landroid/view/View;Landroidx/fragment/app/FragmentContainerView;Landroidx/fragment/app/FragmentContainerView;Landroidx/fragment/app/FragmentContainerView;Landroid/widget/FrameLayout;Landroid/view/ViewStub;Landroid/view/ViewStub;Landroid/view/ViewStub;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/ConversationScrollToView;Lorg/thoughtcrime/securesms/components/ConversationScrollToView;Lorg/thoughtcrime/securesms/util/views/DarkOverflowToolbar;Landroid/view/View;Landroid/view/ViewStub;Landroid/view/ViewStub;)V HSPLorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding; +HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout;Lorg/thoughtcrime/securesms/components/ExpirationTimerView;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView;Lcom/google/android/material/imageview/ShapeableImageView;Landroid/widget/Space;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V +HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding; +HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->getRoot()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0;->get()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->$r8$lambda$TTNxYGZvGlMOp1oidmVJeKiRxZs()Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; @@ -24432,8 +24776,6 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->(III)V HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->getPerRow()I HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->getRawHeight()I HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->getRawWidth()I -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->()V -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->(Landroid/net/Uri;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->(Landroid/net/Uri;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->equals(Ljava/lang/Object;)Z @@ -24479,8 +24821,6 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiRemote;->getObject(Lorg/thoughtcrime/s HSPLorg/thoughtcrime/securesms/emoji/EmojiRemote;->getVersion()I HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->()V -HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->invoke(Landroid/net/Uri;)Lorg/thoughtcrime/securesms/emoji/EmojiPage; -HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @@ -24499,6 +24839,7 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion;->refresh()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$canonicalToVariations$2;->(Lorg/thoughtcrime/securesms/emoji/EmojiSource;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$emojiTree$2;->(Lorg/thoughtcrime/securesms/emoji/EmojiSource;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$emojiTree$2;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$emojiTree$2;->invoke()Lorg/thoughtcrime/securesms/components/emoji/parsing/EmojiTree; HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$maxEmojiLength$2;->(Lorg/thoughtcrime/securesms/emoji/EmojiSource;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$maxEmojiLength$2;->invoke()Ljava/lang/Integer; HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$maxEmojiLength$2;->invoke()Ljava/lang/Object; @@ -24632,6 +24973,24 @@ HSPLorg/thoughtcrime/securesms/fonts/Fonts;->resolveFontScriptFromScriptName(Lor HSPLorg/thoughtcrime/securesms/fonts/ScriptUtil;->()V HSPLorg/thoughtcrime/securesms/fonts/ScriptUtil;->getScript(Ljava/util/Locale;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/fonts/ScriptUtil;->getScriptsMap([Ljava/lang/String;)Ljava/util/Map; +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->(Landroid/graphics/Typeface;)V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->update(Landroid/text/TextPaint;)V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->updateDrawState(Landroid/text/TextPaint;)V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->updateMeasureState(Landroid/text/TextPaint;)V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->$values()[Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph; +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->()V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->(Ljava/lang/String;IC)V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->getUnicode()C +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->$values()[Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight; +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->()V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->values()[Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight; +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols$WhenMappings;->()V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols;->()V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols;->()V +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols;->getBoldWeightedFont(Landroid/content/Context;)Landroid/graphics/Typeface; +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols;->getSpannedString(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;)Ljava/lang/CharSequence; +HSPLorg/thoughtcrime/securesms/fonts/SignalSymbols;->getTypeface(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;)Landroid/graphics/Typeface; HSPLorg/thoughtcrime/securesms/fonts/SupportedScript;->$values()[Lorg/thoughtcrime/securesms/fonts/SupportedScript; HSPLorg/thoughtcrime/securesms/fonts/SupportedScript;->()V HSPLorg/thoughtcrime/securesms/fonts/SupportedScript;->(Ljava/lang/String;I)V @@ -24668,6 +25027,7 @@ HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackController;->findLastVis HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackController;->getPlaybackSet(Ljava/util/Set;II)Ljava/util/Set; HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackController;->getPlaybackSetForMaximumDistance(Ljava/util/Set;[I[I)Ljava/util/Set; HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackController;->onLayoutChange(Landroid/view/View;IIIIIIII)V +HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackController;->onScrolled(Landroidx/recyclerview/widget/RecyclerView;II)V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackController;->performPlaybackUpdate(Landroidx/recyclerview/widget/RecyclerView;)V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackPolicy;->maxSimultaneousPlaybackInConversation()I HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->()V @@ -24677,8 +25037,10 @@ HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onCreat HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onResume(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onStart(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler;->(Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler;->getCurrentHolder(I)Lorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder; HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler;->stopAndReleaseAssignedVideos(Ljava/util/Set;)V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler;->update(Landroidx/recyclerview/widget/RecyclerView;Ljava/util/List;Ljava/util/Set;)V +HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler;->updateVideoDisplayPositionAndSize(Landroidx/recyclerview/widget/RecyclerView;Lorg/thoughtcrime/securesms/giph/mp4/GiphyMp4Playable;)V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4VideoPlayer;->()V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4VideoPlayer;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4VideoPlayer;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V @@ -24830,6 +25192,8 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda20;->test(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda22;->(Lorg/thoughtcrime/securesms/jobmanager/JobPredicate;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda22;->test(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda23;->(Lorg/thoughtcrime/securesms/jobmanager/JobController$Callback;)V +HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda23;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/Job;)V @@ -24881,6 +25245,8 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda10; HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda10;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda11;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda11;->run()V +HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda16;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V +HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda16;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda17;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda17;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda19;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Ljava/lang/Runnable;)V @@ -24892,6 +25258,7 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda6;- HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda8;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda8;->shouldRunOnExecutor()Z HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V +HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda9;->onEmpty()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;->enqueue()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;->getJobListChain()Ljava/util/List; @@ -24920,7 +25287,9 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$88wGNi-R9qqqYZ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$B0N_1IwVaEFBHupl4Ii7Rtasq5s(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$LRMTzBAnZzMhc-JGu7V6yfyQyoc(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$EmptyQueueListener;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$XT2SMZJxrQFMSviwHBjZhWrZ8u4(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Configuration;Landroid/app/Application;)V +HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$f8CXlOogPUV2rL47HPh17prqg70(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$n2_WnBTSP7W-v9B5XsgvQbvhaaA(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V +HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$sAhOIfgF3Tsjgqk6ejbaAJGNqJA(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->-$$Nest$menqueueChain(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->(Landroid/app/Application;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Configuration;)V @@ -24932,8 +25301,10 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$addOnEmptyQueueLis HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$beginJobLoop$1()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$enqueueChain$13(Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$new$0(Lorg/thoughtcrime/securesms/jobmanager/JobManager$Configuration;Landroid/app/Application;)V +HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$onEmptyQueue$14()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$runOnExecutor$15(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->onConstraintMet(Ljava/lang/String;)V +HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->onEmptyQueue()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->runOnExecutor(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->startChain(Lorg/thoughtcrime/securesms/jobmanager/Job;)Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain; HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->waitUntilInitialized()V @@ -25163,13 +25534,21 @@ HSPLorg/thoughtcrime/securesms/jobs/AnalyzeDatabaseJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AnalyzeDatabaseJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ApkUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ApkUpdateJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/ArchiveAttachmentJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/ArchiveAttachmentJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentCompressionJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentCopyJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Companion;->()V +HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Companion;->constructQueueString(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->()V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(JLorg/thoughtcrime/securesms/attachments/AttachmentId;Z)V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;JLorg/thoughtcrime/securesms/attachments/AttachmentId;Z)V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->constructQueueString(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(JLorg/thoughtcrime/securesms/attachments/AttachmentId;ZZ)V +HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(JLorg/thoughtcrime/securesms/attachments/AttachmentId;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;JLorg/thoughtcrime/securesms/attachments/AttachmentId;ZZ)V HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->getFactoryKey()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->onAdded()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->serialize()[B @@ -25181,6 +25560,12 @@ HSPLorg/thoughtcrime/securesms/jobs/AttachmentUploadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AutomaticSessionResetJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AvatarGroupsV2DownloadJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/BackupMessagesJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/BackupMessagesJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/BackupRestoreJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/BackupRestoreJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/BackupRestoreMediaJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/BackupRestoreMediaJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->()V HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->getNextRunAttemptBackoff(ILjava/lang/Exception;)J @@ -25320,6 +25705,7 @@ HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$deleteJobs$1;->invoke(Lorg/th HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedBy$1;->()V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedBy$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedByDescending$1;->()V +HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedByDescending$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$lambda$8$$inlined$sortedBy$1;->()V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$lambda$8$$inlined$sortedBy$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->$r8$lambda$Xu-rS-eV7EMHJJ0ct-GtD7_wnkc(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Z @@ -25333,6 +25719,7 @@ HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getConstraintSpecs(Ljava/la HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getDependencySpecsThatDependOnJob(Ljava/lang/String;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobById(Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobCountForFactory(Ljava/lang/String;)I +HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobCountForFactoryAndQueue(Ljava/lang/String;Ljava/lang/String;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobSpec(Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getMigrationJob()Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getPendingJobsWithNoDependenciesInCreatedOrder(J)Ljava/util/List; @@ -25552,6 +25939,8 @@ HSPLorg/thoughtcrime/securesms/jobs/RequestGroupV2InfoWorkerJob$Factory;-> HSPLorg/thoughtcrime/securesms/jobs/ResendMessageJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ResetSvrGuessCountJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ResetSvrGuessCountJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/RestoreAttachmentJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/RestoreAttachmentJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ResumableUploadSpecJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion;->()V @@ -25604,14 +25993,6 @@ HSPLorg/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StickerDownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob; -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Ljava/lang/String;Ljava/lang/String;ZZ)V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Ljava/lang/String;Ljava/lang/String;ZZLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob-IA;)V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onFailure()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onRun()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onShouldRetry(Ljava/lang/Exception;)Z HSPLorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob;->()V HSPLorg/thoughtcrime/securesms/jobs/StorageForcePushJob$Factory;->()V @@ -25644,6 +26025,8 @@ HSPLorg/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/Svr2MirrorJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/Svr2MirrorJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/SyncArchivedMediaJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/SyncArchivedMediaJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$$ExternalSyntheticLambda0;->(J)V @@ -25819,6 +26202,7 @@ HSPLorg/thoughtcrime/securesms/keyvalue/IntValue;->setValue$Signal_Android_playP HSPLorg/thoughtcrime/securesms/keyvalue/IntValue;->setValue$Signal_Android_playProdBenchmark(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->callingDisableLBRed()Z +HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->enterRestoreV2Flow()Z HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->forceBuiltInEmoji()Z HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->isWebsocketModeForced()Z HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->shakeToReport()Z @@ -25845,6 +26229,7 @@ HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->putLong(Ljava/lang/Str HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->putString(Ljava/lang/String;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->readValueAsType(Ljava/lang/String;Ljava/lang/Class;Z)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->removeAll(Ljava/util/Collection;)V +HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueEnumValue;->(Ljava/lang/String;Ljava/lang/Object;Lorg/signal/core/util/LongSerializer;Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueProtoValue;->(Ljava/lang/String;Lcom/squareup/wire/ProtoAdapter;Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueProtoValue;->getValue$Signal_Android_playProdBenchmark(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueStore$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;Lorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;Ljava/util/Collection;)V @@ -25984,6 +26369,7 @@ HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->getTheme()Lorg/thoughtc HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->getUniversalExpireTimer()I HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->getUseCompactNavigationBar()Z HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->isBackupEnabled()Z +HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->isEnterKeySends()Z HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->isMessageNotificationsEnabled()Z HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->isMessageVibrateEnabled()Z HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->isPreferSystemContactPhotos()Z @@ -26018,6 +26404,7 @@ HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate;->getValue(Ljav HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate;->setValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->blobValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;[B)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->booleanValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;Z)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; +HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->enumValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;Ljava/lang/Object;Lorg/signal/core/util/LongSerializer;)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->integerValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;I)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->longValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;J)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->nullableBlobValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;[B)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; @@ -26098,7 +26485,6 @@ HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequests;->blockForRe HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequests;->notifyFlushed()V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$WriteThread;->(Lorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequests;Lorg/thoughtcrime/securesms/database/LogDatabase;)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$WriteThread;->formatBody(Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$WriteThread;->requestToEntries(Lorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$WriteThread;->run()V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->()V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->(Landroid/app/Application;)V @@ -26595,6 +26981,8 @@ HSPLorg/thoughtcrime/securesms/mms/Slide;->hashCode()I HSPLorg/thoughtcrime/securesms/mms/Slide;->isInProgress()Z HSPLorg/thoughtcrime/securesms/mms/Slide;->isPendingDownload()Z HSPLorg/thoughtcrime/securesms/mms/Slide;->isVideoGif()Z +HSPLorg/thoughtcrime/securesms/mms/SlideDeck$$ExternalSyntheticLambda0;->()V +HSPLorg/thoughtcrime/securesms/mms/SlideDeck$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/mms/SlideDeck;->(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/mms/SlideDeck;->getAudioSlide()Lorg/thoughtcrime/securesms/mms/AudioSlide; HSPLorg/thoughtcrime/securesms/mms/SlideDeck;->getBody()Ljava/lang/String; @@ -26768,6 +27156,7 @@ HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->()V HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Ljava/io/InputStream; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarDirectory(Landroid/content/Context;)Ljava/io/File; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarFile(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Ljava/io/File; +HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarFile(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)Ljava/io/File; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarFileDetails(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getOutputStream(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)Ljava/io/OutputStream; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->hasAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Z @@ -26781,7 +27170,6 @@ HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->$r8$lambda$pNHvm3E5R2_hKbt HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->()V HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->(Ljava/lang/String;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->asGiven(Ljava/lang/String;)Lorg/thoughtcrime/securesms/profiles/ProfileName; -HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->fromParts(Ljava/lang/String;Ljava/lang/String;)Lorg/thoughtcrime/securesms/profiles/ProfileName; HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->getFamilyName()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->getGivenName()Ljava/lang/String; @@ -26882,6 +27270,7 @@ HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess$HostConfig;->getC HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess$HostConfig;->getHost()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess$WhenMappings;->()V HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->()V +HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->buildGConfiguration(Ljava/util/List;)Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->getConfiguration()Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->getConfiguration(Ljava/lang/String;)Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->getUncensoredConfiguration()Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; @@ -26896,9 +27285,9 @@ HSPLorg/thoughtcrime/securesms/reactions/ReactionsConversationView;->()V HSPLorg/thoughtcrime/securesms/reactions/ReactionsConversationView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/reactions/ReactionsConversationView;->clear()V HSPLorg/thoughtcrime/securesms/reactions/ReactionsConversationView;->init(Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->()V -HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->contentsMatch(Ljava/lang/Object;Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4;->()V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda5;->()V @@ -26983,6 +27372,7 @@ HSPLorg/thoughtcrime/securesms/recipients/Recipient$messageRingtone$2;->(L HSPLorg/thoughtcrime/securesms/recipients/Recipient$shouldHideStory$1;->()V HSPLorg/thoughtcrime/securesms/recipients/Recipient$shouldHideStory$1;->()V HSPLorg/thoughtcrime/securesms/recipients/Recipient;->()V +HSPLorg/thoughtcrime/securesms/recipients/Recipient;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZLorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/util/List;Lj$/util/Optional;ZZZJLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Landroid/net/Uri;Landroid/net/Uri;ILorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Landroid/net/Uri;Ljava/lang/String;Landroid/net/Uri;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;JLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/recipients/Recipient;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZLorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/util/List;Lj$/util/Optional;ZZZJLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Landroid/net/Uri;Landroid/net/Uri;ILorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Landroid/net/Uri;Ljava/lang/String;Landroid/net/Uri;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;JLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/recipients/Recipient;->access$getTAG$cp()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->equals(Ljava/lang/Object;)Z @@ -27018,7 +27408,6 @@ HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getShouldShowE164()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getShowVerified()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getSmallFallbackContactPhotoDrawable(Landroid/content/Context;ZLorg/thoughtcrime/securesms/recipients/Recipient$FallbackPhotoProvider;I)Landroid/graphics/drawable/Drawable; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getWallpaper()Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper; -HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hasSameContent(Lorg/thoughtcrime/securesms/recipients/Recipient;)Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hashCode()I HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isActiveGroup()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isBlocked()Z @@ -27055,10 +27444,10 @@ HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator$create$2;->() HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator$create$2;->()V HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->()V HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->()V -HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->create(ZLjava/lang/String;Ljava/lang/String;ZLorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;Lorg/thoughtcrime/securesms/database/model/RecipientRecord;Ljava/util/List;ZLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Lj$/util/Optional;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forId$default(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZILjava/lang/Object;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forId(Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)Lorg/thoughtcrime/securesms/recipients/Recipient; +HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forIndividual(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/RecipientRecord;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientId$1;->()V HSPLorg/thoughtcrime/securesms/recipients/RecipientId$Serializer;->()V HSPLorg/thoughtcrime/securesms/recipients/RecipientId$Serializer;->(Lorg/thoughtcrime/securesms/recipients/RecipientId$Serializer-IA;)V @@ -27242,11 +27631,6 @@ HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->()V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->(Landroid/app/Application;)V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->enable()V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->registerActivity(Landroidx/appcompat/app/AppCompatActivity;)V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$1;->()V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack;->(Ljava/lang/String;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack;->getPackId()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks;->()V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks;->contains(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/stickers/StickerRemoteUriLoader$Factory;->()V HSPLorg/thoughtcrime/securesms/stickers/StickerSearchRepository$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/stickers/StickerSearchRepository;Lorg/thoughtcrime/securesms/stickers/StickerSearchRepository$Callback;)V HSPLorg/thoughtcrime/securesms/stickers/StickerSearchRepository$$ExternalSyntheticLambda0;->run()V @@ -27539,6 +27923,7 @@ HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->mapToJson(Ljava/util/Map;)Lja HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->okHttpAutomaticRetry()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->parseStoredConfig(Ljava/lang/String;)Ljava/util/Map; HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->refreshIfNecessary()V +HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->registrationV2()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->retryReceipts()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->retryRespondMaxAge()J HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->triggerFlagChangeListeners(Ljava/util/Map;)V @@ -27625,6 +28010,11 @@ HSPLorg/thoughtcrime/securesms/util/LocalMetrics;->start(Ljava/lang/String;Ljava HSPLorg/thoughtcrime/securesms/util/LocaleUtil;->()V HSPLorg/thoughtcrime/securesms/util/LocaleUtil;->()V HSPLorg/thoughtcrime/securesms/util/LocaleUtil;->getLocaleDefaults()Ljava/util/List; +HSPLorg/thoughtcrime/securesms/util/LongClickMovementMethod$1;->(Lorg/thoughtcrime/securesms/util/LongClickMovementMethod;)V +HSPLorg/thoughtcrime/securesms/util/LongClickMovementMethod;->(Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/util/LongClickMovementMethod;->getInstance(Landroid/content/Context;)Lorg/thoughtcrime/securesms/util/LongClickMovementMethod; +HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper;IIII)V +HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$$ExternalSyntheticLambda0;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$1;->(Landroid/app/Activity;)V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$1;->invoke(I)V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -27705,17 +28095,23 @@ HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda3;->call HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$64DRPwLhDKidiYVBrJ1oGsaeSVY(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$cqO5Ws54dRBOxkD_sPlVLlSYwIg(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$dDuBqdOM1yCYB_18NZWtjJd7BlA(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; +HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$-c-8Rwy6e64peLv2SUk2gjvq7PE(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; +HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$0JB7ay9SNj5m4iSCvT2_N6rKyuM(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; +HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$Cd53nSsB80_pPTYAR1aj3sUFWaU(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->()V HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->handleSelfProfileKeyChange()V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$0(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$1(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$2(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; +HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$3(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; +HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$4(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; +HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$5(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->retrieveProfile(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Z)Lio/reactivex/rxjava3/core/Single; HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->retrieveProfileSync(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Z)Lorg/whispersystems/signalservice/api/profiles/ProfileAndCredential; HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->toSignalServiceAddress(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->()V +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->(F)V +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->(FFFF)V +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->([F)V +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->toRadii()[F +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->toRelativeRadii(Z)[F HSPLorg/thoughtcrime/securesms/util/ProjectionList;->()V HSPLorg/thoughtcrime/securesms/util/ProjectionList;->()V HSPLorg/thoughtcrime/securesms/util/ProjectionList;->(I)V @@ -27741,6 +28137,7 @@ HSPLorg/thoughtcrime/securesms/util/ScreenDensity;->get(Landroid/content/Context HSPLorg/thoughtcrime/securesms/util/ScreenDensity;->getBucket()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/ScreenDensity;->isKnownDensity()Z HSPLorg/thoughtcrime/securesms/util/ScreenDensity;->xhdpiRelativeDensityScaleFactor(Ljava/lang/String;)F +HSPLorg/thoughtcrime/securesms/util/SearchUtil;->getHighlightedSpan(Ljava/util/Locale;Lorg/thoughtcrime/securesms/util/SearchUtil$StyleFactory;Landroid/text/Spannable;Ljava/lang/String;I)Landroid/text/Spannable; HSPLorg/thoughtcrime/securesms/util/ServiceUtil;->getActivityManager(Landroid/content/Context;)Landroid/app/ActivityManager; HSPLorg/thoughtcrime/securesms/util/ServiceUtil;->getAlarmManager(Landroid/content/Context;)Landroid/app/AlarmManager; HSPLorg/thoughtcrime/securesms/util/ServiceUtil;->getConnectivityManager(Landroid/content/Context;)Landroid/net/ConnectivityManager; @@ -27798,6 +28195,8 @@ HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->get(Ljava/lang/Object;)Ljava/l HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->processQueue()V HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->trimStrongReferencesIfNecessary()V +HSPLorg/thoughtcrime/securesms/util/SpanUtil;->()V +HSPLorg/thoughtcrime/securesms/util/SpanUtil;->ofSize(Ljava/lang/CharSequence;I)Ljava/lang/CharSequence; HSPLorg/thoughtcrime/securesms/util/StorageUtil;->getCleanFileName(Ljava/lang/String;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode;->$values()[Lorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode; HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode;->()V @@ -27839,6 +28238,7 @@ HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->getUnidentifiedAcces HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->hasPromptedPushRegistration(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->hasSeenStickerIntroTooltip(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isBackupEnabled(Landroid/content/Context;)Z +HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isEnterSendsEnabled(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isIncognitoKeyboardEnabled(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isNotificationVibrateEnabled(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isNotificationsEnabled(Landroid/content/Context;)Z @@ -27980,6 +28380,9 @@ HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->registerFac HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->registerFactory(Ljava/lang/Class;Lorg/thoughtcrime/securesms/util/adapter/mapping/Factory;)V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->()V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingModelList;->(Ljava/util/Collection;)V +HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->(Landroid/view/View;)V +HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->onAttachedToWindow()V +HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->setPayload(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter$$ExternalSyntheticLambda0;->(II)V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter;->()V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter;->(II)V @@ -27999,6 +28402,8 @@ HSPLorg/thoughtcrime/securesms/util/concurrent/SerialExecutor;->execute(Ljava/la HSPLorg/thoughtcrime/securesms/util/concurrent/SerialExecutor;->lambda$execute$0(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/util/concurrent/SerialExecutor;->scheduleNext()V HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->(Ljava/util/concurrent/Executor;)V +HSPLorg/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat;->()V +HSPLorg/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat;->(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper;->getUsersSelectedLocale(Landroid/content/Context;)Ljava/util/Locale; HSPLorg/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper;->prepareOverrideConfiguration(Landroid/content/Context;Landroid/content/res/Configuration;)V HSPLorg/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper;->updateContext(Landroid/content/Context;)V @@ -28140,10 +28545,9 @@ HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getPreKey HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getSecureValueRecoveryV2(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2; HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getSenderCertificate()[B HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V -HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda0;->()V +HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda2;->()V HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Z)V HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->retrieveProfile(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lorg/signal/core/util/concurrent/ListenableFuture; -HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->retrieveStickerManifest([B[B)Lorg/whispersystems/signalservice/api/messages/SignalServiceStickerManifest; HSPLorg/whispersystems/signalservice/api/SignalWebSocket$$ExternalSyntheticLambda0;->(Lio/reactivex/rxjava3/subjects/BehaviorSubject;)V HSPLorg/whispersystems/signalservice/api/SignalWebSocket$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->()V @@ -28224,9 +28628,15 @@ HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer;->isBorderless()Z HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer;->isGif()Z HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer;->isPointer()Z -HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId;->(Ljava/lang/String;)V -HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId;->from(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId; -HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId;->toString()Ljava/lang/String; +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$Companion;->()V +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$Companion;->()V +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$Companion;->from(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId; +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$S3;->()V +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$S3;->()V +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$S3;->toString()Ljava/lang/String; +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$V4;->(Ljava/lang/String;)V +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$V4;->toString()Ljava/lang/String; +HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId;->()V HSPLorg/whispersystems/signalservice/api/payments/Currency$CryptoCurrency;->(Ljava/lang/String;I)V HSPLorg/whispersystems/signalservice/api/payments/Currency;->()V HSPLorg/whispersystems/signalservice/api/payments/Currency;->(Lorg/whispersystems/signalservice/api/payments/Currency-IA;)V @@ -28274,6 +28684,7 @@ HSPLorg/whispersystems/signalservice/api/push/ServiceId$PNI;->equals(Ljava/lang/ HSPLorg/whispersystems/signalservice/api/push/ServiceId$PNI;->parseOrThrow(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/push/ServiceId$PNI; HSPLorg/whispersystems/signalservice/api/push/ServiceId$PNI;->toString()Ljava/lang/String; HSPLorg/whispersystems/signalservice/api/push/ServiceId;->()V +HSPLorg/whispersystems/signalservice/api/push/ServiceId;->(Lorg/signal/libsignal/protocol/ServiceId;)V HSPLorg/whispersystems/signalservice/api/push/ServiceId;->(Lorg/signal/libsignal/protocol/ServiceId;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/whispersystems/signalservice/api/push/ServiceId;->fromLibSignal(Lorg/signal/libsignal/protocol/ServiceId;)Lorg/whispersystems/signalservice/api/push/ServiceId; HSPLorg/whispersystems/signalservice/api/push/ServiceId;->isUnknown()Z @@ -28393,6 +28804,8 @@ HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyRespon HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler;->handle(ILokhttp3/ResponseBody;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder;->(Lokhttp3/OkHttpClient;Lokhttp3/OkHttpClient;Ljava/lang/String;Lj$/util/Optional;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder;->(Lokhttp3/OkHttpClient;Lokhttp3/OkHttpClient;Ljava/lang/String;Lj$/util/Optional;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder-IA;)V +HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$UnopinionatedResponseCodeHandler;->()V +HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$UnopinionatedResponseCodeHandler;->(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$UnopinionatedResponseCodeHandler-IA;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->-$$Nest$mvalidateServiceResponse(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket;Lokhttp3/Response;)Lokhttp3/Response; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->()V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Z)V @@ -28402,7 +28815,6 @@ HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createCdn HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createConnectionClient(Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)Lokhttp3/OkHttpClient; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createConnectionHolders([Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)[Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createServiceConnectionHolders([Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)[Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->downloadFromCdn(Ljava/io/OutputStream;JILjava/lang/String;JLorg/whispersystems/signalservice/api/messages/SignalServiceAttachment$ProgressListener;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAuthorizationHeader(Lorg/whispersystems/signalservice/api/util/CredentialsProvider;)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAvailablePreKeys(Lorg/whispersystems/signalservice/api/push/ServiceIdType;)Lorg/whispersystems/signalservice/internal/push/OneTimePreKeyCounts; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getDevices()Ljava/util/List; @@ -28413,7 +28825,6 @@ HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->jsonReque HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;Z)Lokhttp3/Response; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->retrieveStickerManifest([B)[B HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->retrieveVersionedProfile(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;Lj$/util/Optional;Ljava/util/Locale;)Lorg/signal/core/util/concurrent/ListenableFuture; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->submitServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lj$/util/Optional;)Lorg/signal/core/util/concurrent/ListenableFuture; @@ -28433,9 +28844,6 @@ HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->ch HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->createFor(Lorg/whispersystems/signalservice/api/push/TrustStore;)[Ljavax/net/ssl/TrustManager; HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->createFor([Ljavax/net/ssl/TrustManager;)[Ljavax/net/ssl/TrustManager; HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->getAcceptedIssuers()[Ljava/security/cert/X509Certificate; -HSPLorg/whispersystems/signalservice/internal/util/Hex;->()V -HSPLorg/whispersystems/signalservice/internal/util/Hex;->appendHexChar(Ljava/lang/StringBuffer;I)V -HSPLorg/whispersystems/signalservice/internal/util/Hex;->toStringCondensed([B)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->()V HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->fromJson(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->toJson(Ljava/lang/Object;)Ljava/lang/String; @@ -28724,6 +29132,7 @@ Landroidx/appcompat/widget/AppCompatDrawableManager; Landroidx/appcompat/widget/AppCompatEditText; Landroidx/appcompat/widget/AppCompatEmojiEditTextHelper; Landroidx/appcompat/widget/AppCompatEmojiTextHelper; +Landroidx/appcompat/widget/AppCompatHintHelper; Landroidx/appcompat/widget/AppCompatImageButton; Landroidx/appcompat/widget/AppCompatImageHelper; Landroidx/appcompat/widget/AppCompatImageView; @@ -28851,8 +29260,10 @@ Landroidx/constraintlayout/core/Cache; Landroidx/constraintlayout/core/LinearSystem$Row; Landroidx/constraintlayout/core/LinearSystem$ValuesRow; Landroidx/constraintlayout/core/LinearSystem; +Landroidx/constraintlayout/core/Metrics; Landroidx/constraintlayout/core/Pools$Pool; Landroidx/constraintlayout/core/Pools$SimplePool; +Landroidx/constraintlayout/core/PriorityGoalRow$1; Landroidx/constraintlayout/core/PriorityGoalRow$GoalVariableAccessor; Landroidx/constraintlayout/core/PriorityGoalRow; Landroidx/constraintlayout/core/SolverVariable$Type; @@ -28901,9 +29312,11 @@ Landroidx/constraintlayout/widget/ConstraintSet$Motion; Landroidx/constraintlayout/widget/ConstraintSet$PropertySet; Landroidx/constraintlayout/widget/ConstraintSet$Transform; Landroidx/constraintlayout/widget/ConstraintSet; +Landroidx/constraintlayout/widget/ConstraintsChangedListener; Landroidx/constraintlayout/widget/Guideline; Landroidx/constraintlayout/widget/R$id; Landroidx/constraintlayout/widget/R$styleable; +Landroidx/constraintlayout/widget/SharedValues; Landroidx/constraintlayout/widget/VirtualLayout; Landroidx/coordinatorlayout/R$attr; Landroidx/coordinatorlayout/R$styleable; @@ -28987,6 +29400,8 @@ Landroidx/core/os/TraceCompat; Landroidx/core/os/UserManagerCompat$Api24Impl$$ExternalSyntheticApiModelOutline0; Landroidx/core/os/UserManagerCompat$Api24Impl; Landroidx/core/os/UserManagerCompat; +Landroidx/core/text/util/LinkifyCompat$$ExternalSyntheticLambda0; +Landroidx/core/text/util/LinkifyCompat; Landroidx/core/util/Consumer; Landroidx/core/util/ObjectsCompat$Api19Impl; Landroidx/core/util/ObjectsCompat; @@ -29127,6 +29542,11 @@ Landroidx/core/view/accessibility/AccessibilityViewCommand$SetTextArguments; Landroidx/core/view/accessibility/AccessibilityViewCommand; Landroidx/core/view/animation/PathInterpolatorCompat$Api21Impl; Landroidx/core/view/animation/PathInterpolatorCompat; +Landroidx/core/view/inputmethod/EditorInfoCompat$$ExternalSyntheticApiModelOutline0; +Landroidx/core/view/inputmethod/EditorInfoCompat; +Landroidx/core/view/inputmethod/InputConnectionCompat$1; +Landroidx/core/view/inputmethod/InputConnectionCompat$OnCommitContentListener; +Landroidx/core/view/inputmethod/InputConnectionCompat; Landroidx/core/widget/ImageViewCompat$Api21Impl; Landroidx/core/widget/ImageViewCompat; Landroidx/core/widget/NestedScrollView$OnScrollChangeListener; @@ -29178,6 +29598,8 @@ Landroidx/emoji2/viewsintegration/EmojiEditTextHelper$HelperInternal19; Landroidx/emoji2/viewsintegration/EmojiEditTextHelper$HelperInternal; Landroidx/emoji2/viewsintegration/EmojiEditTextHelper; Landroidx/emoji2/viewsintegration/EmojiEditableFactory; +Landroidx/emoji2/viewsintegration/EmojiInputConnection$EmojiCompatDeleteHelper; +Landroidx/emoji2/viewsintegration/EmojiInputConnection; Landroidx/emoji2/viewsintegration/EmojiInputFilter; Landroidx/emoji2/viewsintegration/EmojiKeyListener$EmojiCompatHandleKeyDownHelper; Landroidx/emoji2/viewsintegration/EmojiKeyListener; @@ -30262,6 +30684,7 @@ Lcom/airbnb/lottie/value/LottieFrameInfo; Lcom/airbnb/lottie/value/LottieValueCallback; Lcom/airbnb/lottie/value/ScaleXY; Lcom/airbnb/lottie/value/SimpleLottieValueCallback; +Lcom/android/tools/r8/RecordTag; Lcom/annimon/stream/Collector; Lcom/annimon/stream/Collectors$10; Lcom/annimon/stream/Collectors$11; @@ -30286,6 +30709,9 @@ Lcom/annimon/stream/Stream$3; Lcom/annimon/stream/Stream; Lcom/annimon/stream/function/BiConsumer; Lcom/annimon/stream/function/BiFunction; +Lcom/annimon/stream/function/BinaryOperator$Util$2; +Lcom/annimon/stream/function/BinaryOperator$Util; +Lcom/annimon/stream/function/BinaryOperator; Lcom/annimon/stream/function/Consumer; Lcom/annimon/stream/function/Function; Lcom/annimon/stream/function/IndexedFunction; @@ -30303,6 +30729,7 @@ Lcom/annimon/stream/iterator/LsaExtIterator; Lcom/annimon/stream/iterator/LsaIterator; Lcom/annimon/stream/iterator/PrimitiveIterator$OfInt; Lcom/annimon/stream/operator/IntArray; +Lcom/annimon/stream/operator/IntRangeClosed; Lcom/annimon/stream/operator/ObjArray; Lcom/annimon/stream/operator/ObjFilter; Lcom/annimon/stream/operator/ObjLimit; @@ -31412,6 +31839,8 @@ Lcom/google/common/collect/RegularImmutableBiMap; Lcom/google/common/collect/RegularImmutableList; Lcom/google/common/collect/RegularImmutableMap; Lcom/google/common/collect/RegularImmutableSet; +Lcom/google/common/collect/Sets$2; +Lcom/google/common/collect/Sets$SetView; Lcom/google/common/collect/Sets; Lcom/google/common/collect/SingletonImmutableSet; Lcom/google/common/collect/UnmodifiableIterator; @@ -31945,6 +32374,7 @@ Lio/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription; Lio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription; Lio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper; Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList$NonThrowingPredicate; +Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList; Lio/reactivex/rxjava3/internal/util/ArrayListSupplier; Lio/reactivex/rxjava3/internal/util/AtomicThrowable; Lio/reactivex/rxjava3/internal/util/BackpressureHelper; @@ -31997,10 +32427,12 @@ Lj$/time/LocalTime; Lj$/time/OffsetDateTime; Lj$/time/ZoneId; Lj$/time/ZoneOffset; +Lj$/time/ZonedDateTime; Lj$/time/a; Lj$/time/b; Lj$/time/c; Lj$/time/chrono/ChronoLocalDateTime; +Lj$/time/chrono/ChronoZonedDateTime; Lj$/time/chrono/a; Lj$/time/chrono/b; Lj$/time/chrono/e; @@ -32113,6 +32545,7 @@ Lj$/util/function/BinaryOperator; Lj$/util/function/Consumer; Lj$/util/function/Function; Lj$/util/function/IntFunction; +Lj$/util/function/LongFunction; Lj$/util/function/N; Lj$/util/function/Predicate; Lj$/util/function/S; @@ -32120,16 +32553,17 @@ Lj$/util/function/Supplier; Lj$/util/function/ToDoubleFunction; Lj$/util/function/ToIntFunction; Lj$/util/function/ToLongFunction; -Lj$/util/function/Z; -Lj$/util/function/g0; +Lj$/util/function/b; +Lj$/util/function/e0; +Lj$/util/function/h0; Lj$/util/function/h; -Lj$/util/function/j0; -Lj$/util/function/m0; +Lj$/util/function/k0; +Lj$/util/function/n0; Lj$/util/function/o; -Lj$/util/function/p0; Lj$/util/function/t; Lj$/util/g; Lj$/util/m; +Lj$/util/stream/A1; Lj$/util/stream/B0; Lj$/util/stream/B2; Lj$/util/stream/C0; @@ -32139,6 +32573,7 @@ Lj$/util/stream/Collector; Lj$/util/stream/Collectors; Lj$/util/stream/D0; Lj$/util/stream/E0; +Lj$/util/stream/E1; Lj$/util/stream/F0; Lj$/util/stream/F1; Lj$/util/stream/F3; @@ -32173,6 +32608,8 @@ Lj$/util/stream/b; Lj$/util/stream/c2; Lj$/util/stream/c; Lj$/util/stream/g2; +Lj$/util/stream/h2; +Lj$/util/stream/i2; Lj$/util/stream/i; Lj$/util/stream/l; Lj$/util/stream/n; @@ -34002,9 +34439,14 @@ Lorg/signal/libsignal/internal/FilterExceptions$ThrowingNativeVoidOperation; Lorg/signal/libsignal/internal/FilterExceptions; Lorg/signal/libsignal/internal/Native; Lorg/signal/libsignal/internal/NativeHandleGuard$Owner; +Lorg/signal/libsignal/internal/NativeHandleGuard$SimpleOwner; Lorg/signal/libsignal/internal/NativeHandleGuard; Lorg/signal/libsignal/net/CdsiLookupResponse$Entry; Lorg/signal/libsignal/net/CdsiLookupResponse; +Lorg/signal/libsignal/net/ChatService$DebugInfo; +Lorg/signal/libsignal/net/ChatService$Response; +Lorg/signal/libsignal/net/ChatService$ResponseAndDebugInfo; +Lorg/signal/libsignal/net/ChatService; Lorg/signal/libsignal/net/Network$Environment; Lorg/signal/libsignal/protocol/IdentityKey; Lorg/signal/libsignal/protocol/IdentityKeyPair; @@ -34044,10 +34486,10 @@ Lorg/signal/libsignal/protocol/state/KyberPreKeyStore; Lorg/signal/libsignal/protocol/state/PreKeyStore; Lorg/signal/libsignal/protocol/state/SessionStore; Lorg/signal/libsignal/protocol/state/SignalProtocolStore; -Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda1; Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda2; Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda3; Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda4; +Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord$$ExternalSyntheticLambda5; Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord; Lorg/signal/libsignal/protocol/state/SignedPreKeyStore; Lorg/signal/libsignal/protocol/util/ByteUtil; @@ -34188,11 +34630,15 @@ Lorg/thoughtcrime/securesms/PassphraseRequiredActivity; Lorg/thoughtcrime/securesms/R$styleable; Lorg/thoughtcrime/securesms/Unbindable; Lorg/thoughtcrime/securesms/animation/AnimationCompleteListener; +Lorg/thoughtcrime/securesms/attachments/ArchivedAttachment; Lorg/thoughtcrime/securesms/attachments/Attachment$Companion; Lorg/thoughtcrime/securesms/attachments/Attachment; Lorg/thoughtcrime/securesms/attachments/AttachmentCreator; Lorg/thoughtcrime/securesms/attachments/AttachmentId$Creator; Lorg/thoughtcrime/securesms/attachments/AttachmentId; +Lorg/thoughtcrime/securesms/attachments/Cdn$Serializer; +Lorg/thoughtcrime/securesms/attachments/Cdn$WhenMappings; +Lorg/thoughtcrime/securesms/attachments/Cdn; Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator; Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; Lorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion; @@ -34217,6 +34663,9 @@ Lorg/thoughtcrime/securesms/avatar/Avatars; Lorg/thoughtcrime/securesms/avatar/TextAvatarDrawable; Lorg/thoughtcrime/securesms/avatar/view/AvatarView$WhenMappings; Lorg/thoughtcrime/securesms/avatar/view/AvatarView; +Lorg/thoughtcrime/securesms/backup/RestoreState$Companion; +Lorg/thoughtcrime/securesms/backup/RestoreState$Serializer; +Lorg/thoughtcrime/securesms/backup/RestoreState; Lorg/thoughtcrime/securesms/badges/BadgeImageView; Lorg/thoughtcrime/securesms/badges/gifts/GiftMessageView$Callback; Lorg/thoughtcrime/securesms/badges/gifts/OpenableGift; @@ -34233,6 +34682,11 @@ Lorg/thoughtcrime/securesms/blurhash/BlurHashModelLoader$Factory; Lorg/thoughtcrime/securesms/blurhash/BlurHashResourceDecoder; Lorg/thoughtcrime/securesms/calls/log/CallLogFragment$Callback; Lorg/thoughtcrime/securesms/color/MaterialColor$UnknownColorException; +Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda2; +Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda3; +Lorg/thoughtcrime/securesms/components/AlbumThumbnailView; Lorg/thoughtcrime/securesms/components/AlertView; Lorg/thoughtcrime/securesms/components/AnimatingToggle; Lorg/thoughtcrime/securesms/components/AudioView$$ExternalSyntheticLambda0; @@ -34249,6 +34703,7 @@ Lorg/thoughtcrime/securesms/components/AvatarImageView$RecipientContactPhoto; Lorg/thoughtcrime/securesms/components/AvatarImageView$RedownloadRequestListener; Lorg/thoughtcrime/securesms/components/AvatarImageView; Lorg/thoughtcrime/securesms/components/ComposeText$1; +Lorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener; Lorg/thoughtcrime/securesms/components/ComposeText$CursorPositionChangedListener; Lorg/thoughtcrime/securesms/components/ComposeText$QueryStart; Lorg/thoughtcrime/securesms/components/ComposeText$StylingChangedListener; @@ -34258,6 +34713,14 @@ Lorg/thoughtcrime/securesms/components/ConversationItemFooter$$ExternalSynthetic Lorg/thoughtcrime/securesms/components/ConversationItemFooter$$ExternalSyntheticLambda2; Lorg/thoughtcrime/securesms/components/ConversationItemFooter$OnTouchDelegateChangedListener; Lorg/thoughtcrime/securesms/components/ConversationItemFooter; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnail$Companion; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnail; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState$Creator; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$Creator; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState$Creator; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; +Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState; Lorg/thoughtcrime/securesms/components/ConversationScrollToView; Lorg/thoughtcrime/securesms/components/ConversationSearchBottomBar; Lorg/thoughtcrime/securesms/components/CornerMask; @@ -34308,6 +34771,7 @@ Lorg/thoughtcrime/securesms/components/QuoteView$MessageType; Lorg/thoughtcrime/securesms/components/QuoteView; Lorg/thoughtcrime/securesms/components/RatingManager; Lorg/thoughtcrime/securesms/components/RecyclerViewFastScroller$FastScrollAdapter; +Lorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController; Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$2; Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$3; Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$4; @@ -34320,6 +34784,13 @@ Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$scrollPositionRe Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate; Lorg/thoughtcrime/securesms/components/SendButton; Lorg/thoughtcrime/securesms/components/SharedContactView$EventListener; +Lorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport0; +Lorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport1; +Lorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport2; +Lorg/thoughtcrime/securesms/components/ThumbnailView$CancelClickDispatcher; +Lorg/thoughtcrime/securesms/components/ThumbnailView$DownloadClickDispatcher; +Lorg/thoughtcrime/securesms/components/ThumbnailView$ThumbnailClickDispatcher; +Lorg/thoughtcrime/securesms/components/ThumbnailView; Lorg/thoughtcrime/securesms/components/TypingIndicatorView; Lorg/thoughtcrime/securesms/components/TypingStatusRepository; Lorg/thoughtcrime/securesms/components/ViewBinderDelegate$1; @@ -34340,6 +34811,8 @@ Lorg/thoughtcrime/securesms/components/emoji/EmojiProvider$1; Lorg/thoughtcrime/securesms/components/emoji/EmojiProvider$EmojiDrawable; Lorg/thoughtcrime/securesms/components/emoji/EmojiProvider; Lorg/thoughtcrime/securesms/components/emoji/EmojiSpan; +Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda2; Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView; @@ -34359,6 +34832,8 @@ Lorg/thoughtcrime/securesms/components/emoji/parsing/EmojiTree$EmojiTreeNode; Lorg/thoughtcrime/securesms/components/emoji/parsing/EmojiTree$Matches; Lorg/thoughtcrime/securesms/components/emoji/parsing/EmojiTree; Lorg/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick; +Lorg/thoughtcrime/securesms/components/mention/MentionAnnotation$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/components/mention/MentionAnnotation; Lorg/thoughtcrime/securesms/components/mention/MentionDeleter; Lorg/thoughtcrime/securesms/components/mention/MentionRenderer$MultiLineMentionRenderer; Lorg/thoughtcrime/securesms/components/mention/MentionRenderer$SingleLineMentionRenderer; @@ -34403,6 +34878,22 @@ Lorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$$External Lorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1; Lorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1; Lorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$Companion; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView; Lorg/thoughtcrime/securesms/components/voice/RetryableInitAudioSink$Companion; Lorg/thoughtcrime/securesms/components/voice/RetryableInitAudioSink; Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController$1; @@ -34414,6 +34905,7 @@ Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController; Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaControllerOwner; Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider$Companion; Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider; +Lorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$KeyClearedReceiver$1; Lorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$KeyClearedReceiver; Lorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService$MediaSessionServiceListener; @@ -34514,7 +35006,6 @@ Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$controller$1; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$data$1; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$safetyNumberRepository$2; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel; -Lorg/thoughtcrime/securesms/contacts/paged/collections/ContactSearchIterator; Lorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery; Lorg/thoughtcrime/securesms/contactshare/Contact; Lorg/thoughtcrime/securesms/contactshare/ContactUtil; @@ -34525,11 +35016,15 @@ Lorg/thoughtcrime/securesms/conversation/ConversationAdapterBridge; Lorg/thoughtcrime/securesms/conversation/ConversationBottomSheetCallback; Lorg/thoughtcrime/securesms/conversation/ConversationData$MessageRequestData; Lorg/thoughtcrime/securesms/conversation/ConversationData; +Lorg/thoughtcrime/securesms/conversation/ConversationHeaderView$$ExternalSyntheticLambda3; +Lorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider; +Lorg/thoughtcrime/securesms/conversation/ConversationHeaderView; Lorg/thoughtcrime/securesms/conversation/ConversationIntents$Args; Lorg/thoughtcrime/securesms/conversation/ConversationIntents$Builder; Lorg/thoughtcrime/securesms/conversation/ConversationIntents$ConversationScreenType; Lorg/thoughtcrime/securesms/conversation/ConversationIntents; Lorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda4; +Lorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda7; Lorg/thoughtcrime/securesms/conversation/ConversationItem$1; Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$ClickListener; @@ -34542,6 +35037,7 @@ Lorg/thoughtcrime/securesms/conversation/ConversationItem$ScheduledIndicatorClic Lorg/thoughtcrime/securesms/conversation/ConversationItem$SharedContactClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$SharedContactEventListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$SlideClickPassthroughListener; +Lorg/thoughtcrime/securesms/conversation/ConversationItem$ThumbnailClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$TouchDelegateChangedListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$UrlClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$ViewOnceMessageClickListener; @@ -34549,6 +35045,11 @@ Lorg/thoughtcrime/securesms/conversation/ConversationItem; Lorg/thoughtcrime/securesms/conversation/ConversationItemBodyBubble$$ExternalSyntheticBackport0; Lorg/thoughtcrime/securesms/conversation/ConversationItemBodyBubble$$ExternalSyntheticLambda2; Lorg/thoughtcrime/securesms/conversation/ConversationItemBodyBubble; +Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Condensed; +Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Detailed; +Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$EditHistory; +Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Standard; +Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode; Lorg/thoughtcrime/securesms/conversation/ConversationMessage$ComputedProperties; Lorg/thoughtcrime/securesms/conversation/ConversationMessage$ConversationMessageFactory; Lorg/thoughtcrime/securesms/conversation/ConversationMessage; @@ -34573,6 +35074,9 @@ Lorg/thoughtcrime/securesms/conversation/ConversationUpdateItem$PresentOnChange; Lorg/thoughtcrime/securesms/conversation/ConversationUpdateItem$RecipientObserverManager; Lorg/thoughtcrime/securesms/conversation/ConversationUpdateItem$UpdateObserver; Lorg/thoughtcrime/securesms/conversation/ConversationUpdateItem; +Lorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/conversation/MarkReadHelper; Lorg/thoughtcrime/securesms/conversation/MessageSendType$Companion; Lorg/thoughtcrime/securesms/conversation/MessageSendType$SignalMessageSendType$Creator; @@ -34603,6 +35107,7 @@ Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Auto; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$BuiltIn; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion$CREATOR$1; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion; +Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$LinearGradient$Creator; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$LinearGradient; @@ -34611,18 +35116,22 @@ Lorg/thoughtcrime/securesms/conversation/colors/ChatColorsPalette$Bubbles; Lorg/thoughtcrime/securesms/conversation/colors/Colorizable; Lorg/thoughtcrime/securesms/conversation/colors/Colorizer$onGroupMembershipChanged$$inlined$sortedBy$1; Lorg/thoughtcrime/securesms/conversation/colors/Colorizer; +Lorg/thoughtcrime/securesms/conversation/colors/NameColor; Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer$edgeEffectFactory$1; Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer$itemDecoration$1; Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer$scrollListener$1; Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer; Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$ShareOrDraftData; Lorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel; +Lorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$Companion; Lorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator; Lorg/thoughtcrime/securesms/conversation/mutiselect/Multiselect; Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectCollection$Companion; Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectCollection$Single; Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectCollection; +Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference; +Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$WhenMappings; Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration; Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectPart$Attachments; Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectPart$Text; @@ -34680,7 +35189,13 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSynth Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda6; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda7; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$Companion; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$OnScrollStateChangedListener; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reminderStub$2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reviewBannerStub$2; @@ -34702,6 +35217,8 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOpt Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$DataObserver; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$InputPanelMediaListener; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollDateHeaderHelper; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$onScrolled$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ThreadHeaderMarginDecoration; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ToolbarDependentMarginListener; @@ -34733,6 +35250,7 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversa Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$3; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$invoke$lambda$1$$inlined$doAfterNextLayout$1$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$invoke$lambda$1$$inlined$doAfterNextLayout$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$onViewCreated$$inlined$createActivityViewModel$1; @@ -34763,6 +35281,7 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$stickerViewMode Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$viewModel$2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$voiceNotePlayerListener$2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder; Lorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$UnreadState$CompleteUnreadState; Lorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$UnreadState$InitialUnreadState; Lorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$UnreadState$None; @@ -34775,8 +35294,8 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository$grou Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository$groupRecord$2$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository$groupRecord$2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository; -Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda11; -Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda7; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda12; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda8; Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$Companion; Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$MessageCounts; Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$getMessageCounts$1; @@ -34794,6 +35313,7 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationTooltips$special$$inline Lorg/thoughtcrime/securesms/conversation/v2/ConversationTooltips$special$$inlined$viewModels$default$5; Lorg/thoughtcrime/securesms/conversation/v2/ConversationTooltips; Lorg/thoughtcrime/securesms/conversation/v2/ConversationTypingIndicatorAdapter$State; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationTypingIndicatorAdapter$ViewHolder; Lorg/thoughtcrime/securesms/conversation/v2/ConversationTypingIndicatorAdapter; Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$10; Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$11; @@ -34824,6 +35344,7 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$chatColorsData Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$chatColorsDataObservable$2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$2; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$setShowScrollButtonsForScrollPosition$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$storyRingState$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel; Lorg/thoughtcrime/securesms/conversation/v2/DisabledInputView$inflater$2; @@ -34871,11 +35392,9 @@ Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewMode Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$filteredState$2; Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel; Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$1; -Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$2; -Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$3; -Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$4; Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$Factory; -Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$duplicates$1; +Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$1; +Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$_groupRecord$2; Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$updateGroupStateIfNeeded$1; Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel$updateGroupStateIfNeeded$2; Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel; @@ -34884,10 +35403,42 @@ Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsD Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsItemDecoration$onDraw$$inlined$filterIsInstance$1; Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsItemDecoration$onDraw$1; Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsItemDecoration; +Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable; Lorg/thoughtcrime/securesms/conversation/v2/items/InteractiveConversationElement; Lorg/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnDispatchTouchEventListener; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnMeasureListener; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridgeKt; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda2; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda3; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda7; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$PassthroughClickListener; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$ReactionMeasureListener; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$replyDelegate$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$senderDrawable$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener; Lorg/thoughtcrime/securesms/conversation/v2/items/V2Payload; Lorg/thoughtcrime/securesms/conversationlist/ClearFilterViewHolder$OnClearFilterClickListener; Lorg/thoughtcrime/securesms/conversationlist/ConversationFilterBehavior$Callback; @@ -35056,6 +35607,8 @@ Lorg/thoughtcrime/securesms/crypto/storage/SignalServiceAccountDataStoreImpl; Lorg/thoughtcrime/securesms/crypto/storage/SignalServiceDataStoreImpl; Lorg/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore; Lorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore; +Lorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState$Companion; +Lorg/thoughtcrime/securesms/database/AttachmentTable$ArchiveTransferState; Lorg/thoughtcrime/securesms/database/AttachmentTable$Companion; Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion; Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Creator; @@ -35072,6 +35625,7 @@ Lorg/thoughtcrime/securesms/database/CallTable$Event$Serializer; Lorg/thoughtcrime/securesms/database/CallTable$Event; Lorg/thoughtcrime/securesms/database/CallTable$ReadState$Serializer; Lorg/thoughtcrime/securesms/database/CallTable$ReadState; +Lorg/thoughtcrime/securesms/database/CallTable$markRingingCallsAsMissed$1; Lorg/thoughtcrime/securesms/database/CallTable; Lorg/thoughtcrime/securesms/database/CdsTable$Companion; Lorg/thoughtcrime/securesms/database/CdsTable; @@ -35082,13 +35636,12 @@ Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda10 Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda11; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20; +Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda22; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda23; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda24; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda28; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda29; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30; -Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32; -Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda40; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda4; @@ -35116,7 +35669,6 @@ Lorg/thoughtcrime/securesms/database/GroupReceiptTable; Lorg/thoughtcrime/securesms/database/GroupTable$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$MembershipTable$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$MembershipTable; -Lorg/thoughtcrime/securesms/database/GroupTable$Reader; Lorg/thoughtcrime/securesms/database/GroupTable$ShowAsStoryState$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$ShowAsStoryState; Lorg/thoughtcrime/securesms/database/GroupTable; @@ -35129,7 +35681,6 @@ Lorg/thoughtcrime/securesms/database/JobDatabase$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/database/JobDatabase$Companion; Lorg/thoughtcrime/securesms/database/JobDatabase$deleteJobs$1; Lorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2; -Lorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2; Lorg/thoughtcrime/securesms/database/JobDatabase; Lorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/database/KeyValueDatabase$1; @@ -35182,6 +35733,7 @@ Lorg/thoughtcrime/securesms/database/MessageTable$QuoteDescriptor; Lorg/thoughtcrime/securesms/database/MessageTable$Reader; Lorg/thoughtcrime/securesms/database/MessageTable$SyncMessageId; Lorg/thoughtcrime/securesms/database/MessageTable$WhenMappings; +Lorg/thoughtcrime/securesms/database/MessageTable$clearIsRingingOnLocalDeviceFlag$1; Lorg/thoughtcrime/securesms/database/MessageTable$getConversationSnippet$1; Lorg/thoughtcrime/securesms/database/MessageTable$getOldestStorySendTimestamp$1; Lorg/thoughtcrime/securesms/database/MessageTable$insertMediaMessage$3; @@ -35189,6 +35741,8 @@ Lorg/thoughtcrime/securesms/database/MessageTable; Lorg/thoughtcrime/securesms/database/MessageType; Lorg/thoughtcrime/securesms/database/MessageTypes$-CC; Lorg/thoughtcrime/securesms/database/MessageTypes; +Lorg/thoughtcrime/securesms/database/NameCollisionTables$Companion; +Lorg/thoughtcrime/securesms/database/NameCollisionTables; Lorg/thoughtcrime/securesms/database/NoExternalStorageException; Lorg/thoughtcrime/securesms/database/NoSuchMessageException; Lorg/thoughtcrime/securesms/database/NotificationProfileDatabase$Companion; @@ -35302,7 +35856,6 @@ Lorg/thoughtcrime/securesms/database/SqlCipherDeletingErrorHandler; Lorg/thoughtcrime/securesms/database/SqlCipherErrorHandler$Companion; Lorg/thoughtcrime/securesms/database/SqlCipherErrorHandler; Lorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader; -Lorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader; Lorg/thoughtcrime/securesms/database/StickerTable; Lorg/thoughtcrime/securesms/database/StorySendTable$Companion; Lorg/thoughtcrime/securesms/database/StorySendTable; @@ -35406,13 +35959,16 @@ Lorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation Lorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation; Lorg/thoughtcrime/securesms/database/model/databaseprotos/RecipientExtras; Lorg/thoughtcrime/securesms/database/model/databaseprotos/Wallpaper; +Lorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding; Lorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding; Lorg/thoughtcrime/securesms/databinding/ConversationListFilterPullViewBinding; Lorg/thoughtcrime/securesms/databinding/ConversationListTabsBinding; Lorg/thoughtcrime/securesms/databinding/ConversationSearchNavBinding; Lorg/thoughtcrime/securesms/databinding/ConversationTitleViewBinding; Lorg/thoughtcrime/securesms/databinding/OnboardingMegaphoneCardBinding; +Lorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding; Lorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding; +Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$Provider; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies; @@ -35476,7 +36032,6 @@ Lorg/thoughtcrime/securesms/emoji/EmojiSourceKt; Lorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/emoji/JumboEmoji; -Lorg/thoughtcrime/securesms/emoji/ObsoleteEmoji; Lorg/thoughtcrime/securesms/emoji/ParsedEmojiData; Lorg/thoughtcrime/securesms/events/GroupCallPeekEvent; Lorg/thoughtcrime/securesms/events/PartProgressEvent; @@ -35500,6 +36055,11 @@ Lorg/thoughtcrime/securesms/fonts/Fonts$FontResult; Lorg/thoughtcrime/securesms/fonts/Fonts$WhenMappings; Lorg/thoughtcrime/securesms/fonts/Fonts; Lorg/thoughtcrime/securesms/fonts/ScriptUtil; +Lorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan; +Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph; +Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight; +Lorg/thoughtcrime/securesms/fonts/SignalSymbols$WhenMappings; +Lorg/thoughtcrime/securesms/fonts/SignalSymbols; Lorg/thoughtcrime/securesms/fonts/SupportedScript; Lorg/thoughtcrime/securesms/fonts/TextFont$Companion; Lorg/thoughtcrime/securesms/fonts/TextFont; @@ -35575,6 +36135,7 @@ Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda19; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda20; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda22; +Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda23; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda6; @@ -35586,6 +36147,7 @@ Lorg/thoughtcrime/securesms/jobmanager/JobInstantiator; Lorg/thoughtcrime/securesms/jobmanager/JobLogger; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda10; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda11; +Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda16; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda17; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda19; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda1; @@ -35671,8 +36233,11 @@ Lorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Factory; Lorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob; Lorg/thoughtcrime/securesms/jobs/AnalyzeDatabaseJob$Factory; Lorg/thoughtcrime/securesms/jobs/ApkUpdateJob$Factory; +Lorg/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob$Factory; +Lorg/thoughtcrime/securesms/jobs/ArchiveAttachmentJob$Factory; Lorg/thoughtcrime/securesms/jobs/AttachmentCompressionJob$Factory; Lorg/thoughtcrime/securesms/jobs/AttachmentCopyJob$Factory; +Lorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Companion; Lorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Factory; Lorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$InvalidPartException; Lorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob; @@ -35682,6 +36247,9 @@ Lorg/thoughtcrime/securesms/jobs/AttachmentUploadJob$Factory; Lorg/thoughtcrime/securesms/jobs/AutomaticSessionResetJob$Factory; Lorg/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob$Factory; Lorg/thoughtcrime/securesms/jobs/AvatarGroupsV2DownloadJob$Factory; +Lorg/thoughtcrime/securesms/jobs/BackupMessagesJob$Factory; +Lorg/thoughtcrime/securesms/jobs/BackupRestoreJob$Factory; +Lorg/thoughtcrime/securesms/jobs/BackupRestoreMediaJob$Factory; Lorg/thoughtcrime/securesms/jobs/BaseJob; Lorg/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob$Factory; Lorg/thoughtcrime/securesms/jobs/CallLinkPeekJob$Factory; @@ -35821,6 +36389,7 @@ Lorg/thoughtcrime/securesms/jobs/RequestGroupV2InfoJob$Factory; Lorg/thoughtcrime/securesms/jobs/RequestGroupV2InfoWorkerJob$Factory; Lorg/thoughtcrime/securesms/jobs/ResendMessageJob$Factory; Lorg/thoughtcrime/securesms/jobs/ResetSvrGuessCountJob$Factory; +Lorg/thoughtcrime/securesms/jobs/RestoreAttachmentJob$Factory; Lorg/thoughtcrime/securesms/jobs/ResumableUploadSpecJob$Factory; Lorg/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob$Factory; Lorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion; @@ -35843,7 +36412,6 @@ Lorg/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob$Factory; Lorg/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob$Factory; Lorg/thoughtcrime/securesms/jobs/StickerDownloadJob$Factory; Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory; -Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob; Lorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob$Factory; Lorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob; Lorg/thoughtcrime/securesms/jobs/StorageForcePushJob$Factory; @@ -35856,6 +36424,7 @@ Lorg/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob$Factory; Lorg/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob; Lorg/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob$Factory; Lorg/thoughtcrime/securesms/jobs/Svr2MirrorJob$Factory; +Lorg/thoughtcrime/securesms/jobs/SyncArchivedMediaJob$Factory; Lorg/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob$Factory; Lorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$Factory; @@ -35905,6 +36474,7 @@ Lorg/thoughtcrime/securesms/keyvalue/IntValue; Lorg/thoughtcrime/securesms/keyvalue/InternalValues; Lorg/thoughtcrime/securesms/keyvalue/KeepMessagesDuration; Lorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet; +Lorg/thoughtcrime/securesms/keyvalue/KeyValueEnumValue; Lorg/thoughtcrime/securesms/keyvalue/KeyValuePersistentStorage; Lorg/thoughtcrime/securesms/keyvalue/KeyValueProtoValue; Lorg/thoughtcrime/securesms/keyvalue/KeyValueReader; @@ -36137,6 +36707,7 @@ Lorg/thoughtcrime/securesms/mms/SignalGlideModule$Companion; Lorg/thoughtcrime/securesms/mms/SignalGlideModule; Lorg/thoughtcrime/securesms/mms/Slide; Lorg/thoughtcrime/securesms/mms/SlideClickListener; +Lorg/thoughtcrime/securesms/mms/SlideDeck$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/mms/SlideDeck; Lorg/thoughtcrime/securesms/mms/SlideFactory$MediaType; Lorg/thoughtcrime/securesms/mms/SlidesClickedListener; @@ -36231,6 +36802,7 @@ Lorg/thoughtcrime/securesms/ratelimit/RecaptchaRequiredEvent; Lorg/thoughtcrime/securesms/reactions/ReactionsBottomSheetDialogFragment$Callback; Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView; Lorg/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiBottomSheetDialogFragment$Callback; +Lorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda2; Lorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4; @@ -36322,9 +36894,6 @@ Lorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId; Lorg/thoughtcrime/securesms/shakereport/ShakeToReport; Lorg/thoughtcrime/securesms/sms/GroupV2UpdateMessageUtil; Lorg/thoughtcrime/securesms/sms/MessageSender$MessageSentEvent; -Lorg/thoughtcrime/securesms/stickers/BlessedPacks$1; -Lorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack; -Lorg/thoughtcrime/securesms/stickers/BlessedPacks; Lorg/thoughtcrime/securesms/stickers/StickerEventListener; Lorg/thoughtcrime/securesms/stickers/StickerLocator; Lorg/thoughtcrime/securesms/stickers/StickerPackInstallEvent; @@ -36435,6 +37004,9 @@ Lorg/thoughtcrime/securesms/util/LocalMetrics$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/util/LocalMetrics$db$2; Lorg/thoughtcrime/securesms/util/LocalMetrics; Lorg/thoughtcrime/securesms/util/LocaleUtil; +Lorg/thoughtcrime/securesms/util/LongClickMovementMethod$1; +Lorg/thoughtcrime/securesms/util/LongClickMovementMethod; +Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper$1; Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper$2; Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3; @@ -36454,6 +37026,7 @@ Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/util/ProfileUtil; +Lorg/thoughtcrime/securesms/util/Projection$Corners; Lorg/thoughtcrime/securesms/util/Projection; Lorg/thoughtcrime/securesms/util/ProjectionList; Lorg/thoughtcrime/securesms/util/PushCharacterCalculator$1; @@ -36465,6 +37038,7 @@ Lorg/thoughtcrime/securesms/util/SavedStateViewModelFactory; Lorg/thoughtcrime/securesms/util/ScreenDensity$1; Lorg/thoughtcrime/securesms/util/ScreenDensity; Lorg/thoughtcrime/securesms/util/SearchUtil$StyleFactory; +Lorg/thoughtcrime/securesms/util/SearchUtil; Lorg/thoughtcrime/securesms/util/ServiceUtil; Lorg/thoughtcrime/securesms/util/SignalLocalMetrics$ColdStart; Lorg/thoughtcrime/securesms/util/SignalLocalMetrics$ConversationOpen; @@ -36480,6 +37054,7 @@ Lorg/thoughtcrime/securesms/util/SnapToTopDataObserver$ScrollToTop; Lorg/thoughtcrime/securesms/util/SnapToTopDataObserver; Lorg/thoughtcrime/securesms/util/SoftHashMap$SoftValue; Lorg/thoughtcrime/securesms/util/SoftHashMap; +Lorg/thoughtcrime/securesms/util/SpanUtil; Lorg/thoughtcrime/securesms/util/StorageUtil; Lorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode; Lorg/thoughtcrime/securesms/util/TextSecurePreferences; @@ -36511,6 +37086,7 @@ Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter; Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback; Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel; Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModelList; +Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; Lorg/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter$Placeholder; Lorg/thoughtcrime/securesms/util/adapter/mapping/PagingMappingAdapter; @@ -36520,6 +37096,7 @@ Lorg/thoughtcrime/securesms/util/concurrent/FilteredExecutor; Lorg/thoughtcrime/securesms/util/concurrent/SerialExecutor$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/util/concurrent/SerialExecutor; Lorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor; +Lorg/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat; Lorg/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper; Lorg/thoughtcrime/securesms/util/dynamiclanguage/LanguageString; Lorg/thoughtcrime/securesms/util/dynamiclanguage/LocaleParser; @@ -36582,7 +37159,7 @@ Lorg/whispersystems/signalservice/api/SignalServiceAccountDataStore; Lorg/whispersystems/signalservice/api/SignalServiceAccountManager; Lorg/whispersystems/signalservice/api/SignalServiceDataStore; Lorg/whispersystems/signalservice/api/SignalServiceKyberPreKeyStore; -Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda0; +Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda2; Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; Lorg/whispersystems/signalservice/api/SignalServicePreKeyStore; Lorg/whispersystems/signalservice/api/SignalServiceSenderKeyStore; @@ -36602,6 +37179,9 @@ Lorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations; Lorg/whispersystems/signalservice/api/kbs/MasterKey; Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachment; Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer; +Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$Companion; +Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$S3; +Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId$V4; Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentRemoteId; Lorg/whispersystems/signalservice/api/payments/Currency$CryptoCurrency; Lorg/whispersystems/signalservice/api/payments/Currency; @@ -36676,13 +37256,13 @@ Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHold Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder; +Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$UnopinionatedResponseCodeHandler; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket; Lorg/whispersystems/signalservice/internal/push/VerifyAccountResponse; Lorg/whispersystems/signalservice/internal/push/exceptions/GroupPatchNotAcceptedException; Lorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil; Lorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager$1; Lorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager; -Lorg/whispersystems/signalservice/internal/util/Hex; Lorg/whispersystems/signalservice/internal/util/JsonUtil; Lorg/whispersystems/signalservice/internal/util/Util; Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder; @@ -36772,7 +37352,6 @@ PLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->(Landroid/cont PLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->initEmojiKeyListener(Landroidx/appcompat/widget/AppCompatEmojiEditTextHelper;)V PLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->setCompoundDrawables(Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;)V PLandroidx/appcompat/widget/AppCompatDrawableManager$1;->getTintModeForDrawableRes(I)Landroid/graphics/PorterDuff$Mode; -PLandroidx/appcompat/widget/AppCompatHintHelper;->onCreateInputConnection(Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;Landroid/view/View;)Landroid/view/inputmethod/InputConnection; PLandroidx/appcompat/widget/ForwardingListener;->onViewDetachedFromWindow(Landroid/view/View;)V PLandroidx/appcompat/widget/ResourceManagerInternal;->addTintListToCache(Landroid/content/Context;ILandroid/content/res/ColorStateList;)V PLandroidx/appcompat/widget/ResourceManagerInternal;->getTintMode(I)Landroid/graphics/PorterDuff$Mode; @@ -36848,14 +37427,9 @@ PLandroidx/core/os/BundleKt;->bundleOf([Lkotlin/Pair;)Landroid/os/Bundle; PLandroidx/core/os/HandlerCompat$Api28Impl$$ExternalSyntheticApiModelOutline0;->m(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z PLandroidx/core/os/HandlerCompat$Api28Impl;->postDelayed(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z PLandroidx/core/os/HandlerCompat;->postDelayed(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z -PLandroidx/core/text/util/LinkifyCompat$$ExternalSyntheticLambda0;->()V -PLandroidx/core/text/util/LinkifyCompat;->()V -PLandroidx/core/text/util/LinkifyCompat;->addLinks(Landroid/text/Spannable;I)Z -PLandroidx/core/text/util/LinkifyCompat;->shouldAddLinksFallbackToFramework()Z PLandroidx/core/view/NestedScrollingChildHelper;->stopNestedScroll()V PLandroidx/core/view/ViewCompat$Api16Impl;->hasTransientState(Landroid/view/View;)Z PLandroidx/core/view/ViewCompat;->hasTransientState(Landroid/view/View;)Z -PLandroidx/core/view/ViewGroupKt$descendants$1;->(Landroid/view/ViewGroup;Lkotlin/coroutines/Continuation;)V PLandroidx/core/view/ViewGroupKt$descendants$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; PLandroidx/core/view/ViewGroupKt;->getDescendants(Landroid/view/ViewGroup;)Lkotlin/sequences/Sequence; PLandroidx/core/view/ViewKt$allViews$1;->(Landroid/view/View;Lkotlin/coroutines/Continuation;)V @@ -36866,12 +37440,9 @@ PLandroidx/core/view/accessibility/AccessibilityEventCompat$Api19Impl;->getConte PLandroidx/core/view/accessibility/AccessibilityEventCompat$Api19Impl;->setContentChangeTypes(Landroid/view/accessibility/AccessibilityEvent;I)V PLandroidx/core/view/accessibility/AccessibilityEventCompat;->getContentChangeTypes(Landroid/view/accessibility/AccessibilityEvent;)I PLandroidx/core/view/accessibility/AccessibilityEventCompat;->setContentChangeTypes(Landroid/view/accessibility/AccessibilityEvent;I)V -PLandroidx/core/view/inputmethod/EditorInfoCompat$$ExternalSyntheticApiModelOutline0;->m(Landroid/view/inputmethod/EditorInfo;[Ljava/lang/String;)V -PLandroidx/core/view/inputmethod/EditorInfoCompat;->()V -PLandroidx/core/view/inputmethod/EditorInfoCompat;->setContentMimeTypes(Landroid/view/inputmethod/EditorInfo;[Ljava/lang/String;)V -PLandroidx/core/view/inputmethod/InputConnectionCompat$1;->(Landroid/view/inputmethod/InputConnection;ZLandroidx/core/view/inputmethod/InputConnectionCompat$OnCommitContentListener;)V -PLandroidx/core/view/inputmethod/InputConnectionCompat;->createWrapper(Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;Landroidx/core/view/inputmethod/InputConnectionCompat$OnCommitContentListener;)Landroid/view/inputmethod/InputConnection; PLandroidx/customview/poolingcontainer/PoolingContainer;->callPoolingContainerOnReleaseForChildren(Landroid/view/ViewGroup;)V +PLandroidx/customview/poolingcontainer/PoolingContainer;->getPoolingContainerListenerHolder(Landroid/view/View;)Landroidx/customview/poolingcontainer/PoolingContainerListenerHolder; +PLandroidx/customview/poolingcontainer/PoolingContainerListenerHolder;->()V PLandroidx/customview/poolingcontainer/PoolingContainerListenerHolder;->onRelease()V PLandroidx/customview/view/AbsSavedState$1;->()V PLandroidx/customview/view/AbsSavedState$2;->()V @@ -36881,10 +37452,6 @@ PLandroidx/customview/view/AbsSavedState;->(Landroid/os/Parcelable;)V PLandroidx/customview/view/AbsSavedState;->(Landroidx/customview/view/AbsSavedState$1;)V PLandroidx/customview/view/AbsSavedState;->writeToParcel(Landroid/os/Parcel;I)V PLandroidx/emoji2/text/SpannableBuilder;->getSpanEnd(Ljava/lang/Object;)I -PLandroidx/emoji2/viewsintegration/EmojiInputConnection$EmojiCompatDeleteHelper;->()V -PLandroidx/emoji2/viewsintegration/EmojiInputConnection$EmojiCompatDeleteHelper;->updateEditorInfoAttrs(Landroid/view/inputmethod/EditorInfo;)V -PLandroidx/emoji2/viewsintegration/EmojiInputConnection;->(Landroid/widget/TextView;Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;)V -PLandroidx/emoji2/viewsintegration/EmojiInputConnection;->(Landroid/widget/TextView;Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;Landroidx/emoji2/viewsintegration/EmojiInputConnection$EmojiCompatDeleteHelper;)V PLandroidx/fragment/app/Fragment$Api19Impl;->cancelPendingInputEvents(Landroid/view/View;)V PLandroidx/fragment/app/Fragment;->getHost()Ljava/lang/Object; PLandroidx/fragment/app/Fragment;->initState()V @@ -37080,6 +37647,7 @@ PLandroidx/recyclerview/widget/AdapterListUpdateCallback;->onChanged(IILjava/lan PLandroidx/recyclerview/widget/AsyncDifferConfig;->getDiffCallback()Landroidx/recyclerview/widget/DiffUtil$ItemCallback; PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areContentsTheSame(II)Z PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->getChangePayload(II)Ljava/lang/Object; +PLandroidx/recyclerview/widget/BatchingListUpdateCallback;->onChanged(IILjava/lang/Object;)V PLandroidx/recyclerview/widget/ChildHelper;->removeViewAt(I)V PLandroidx/recyclerview/widget/ConcatAdapter;->onDetachedFromRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/ConcatAdapter;->onViewDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V @@ -37095,6 +37663,7 @@ PLandroidx/recyclerview/widget/DiffUtil$Snake;->()V PLandroidx/recyclerview/widget/DiffUtil$Snake;->diagonalSize()I PLandroidx/recyclerview/widget/DiffUtil$Snake;->hasAdditionOrRemoval()Z PLandroidx/recyclerview/widget/DiffUtil$Snake;->toDiagonal()Landroidx/recyclerview/widget/DiffUtil$Diagonal; +PLandroidx/recyclerview/widget/DiffUtil;->backward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; PLandroidx/recyclerview/widget/GapWorker$LayoutPrefetchRegistryImpl;->lastPrefetchIncludedPosition(I)Z PLandroidx/recyclerview/widget/GapWorker;->remove(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/ItemTouchHelper;->endRecoverAnimation(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Z)V @@ -37152,6 +37721,7 @@ PLandroidx/recyclerview/widget/RecyclerView$ViewHolder;->isRecyclable()Z PLandroidx/recyclerview/widget/RecyclerView$ViewHolder;->resetInternal()V PLandroidx/recyclerview/widget/RecyclerView;->access$300(Landroidx/recyclerview/widget/RecyclerView;Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;)V PLandroidx/recyclerview/widget/RecyclerView;->access$400(Landroidx/recyclerview/widget/RecyclerView;Landroid/view/View;)V +PLandroidx/recyclerview/widget/RecyclerView;->animateChange(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;ZZ)V PLandroidx/recyclerview/widget/RecyclerView;->canReuseUpdatedViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Z PLandroidx/recyclerview/widget/RecyclerView;->clearNestedRecyclerViewIfNotNested(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLandroidx/recyclerview/widget/RecyclerView;->dispatchChildDetached(Landroid/view/View;)V @@ -37164,6 +37734,7 @@ PLandroidx/recyclerview/widget/RecyclerView;->stopNestedScroll()V PLandroidx/recyclerview/widget/RecyclerViewAccessibilityDelegate$ItemDelegate;->getAndRemoveOriginalDelegateForItem(Landroid/view/View;)Landroidx/core/view/AccessibilityDelegateCompat; PLandroidx/recyclerview/widget/ViewInfoStore$InfoRecord;->drainCache()V PLandroidx/recyclerview/widget/ViewInfoStore;->addToOldChangeHolders(JLandroidx/recyclerview/widget/RecyclerView$ViewHolder;)V +PLandroidx/recyclerview/widget/ViewInfoStore;->isDisappearing(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Z PLandroidx/recyclerview/widget/ViewInfoStore;->onDetach()V PLandroidx/recyclerview/widget/ViewInfoStore;->popFromPostLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; PLandroidx/recyclerview/widget/ViewInfoStore;->popFromPreLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; @@ -37177,19 +37748,12 @@ PLcom/airbnb/lottie/LottieAnimationView$SavedState;->()V PLcom/airbnb/lottie/LottieAnimationView$SavedState;->(Landroid/os/Parcelable;)V PLcom/airbnb/lottie/LottieAnimationView$SavedState;->writeToParcel(Landroid/os/Parcel;I)V PLcom/airbnb/lottie/LottieAnimationView;->onSaveInstanceState()Landroid/os/Parcelable; -PLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda9;->run(Lcom/airbnb/lottie/LottieComposition;)V -PLcom/airbnb/lottie/LottieDrawable;->$r8$lambda$riFJCWOqfI5iOFlatZRlwc9qv1U(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/KeyPath;Ljava/lang/Object;Lcom/airbnb/lottie/value/LottieValueCallback;Lcom/airbnb/lottie/LottieComposition;)V PLcom/airbnb/lottie/LottieDrawable;->getImageAssetsFolder()Ljava/lang/String; PLcom/airbnb/lottie/LottieDrawable;->getProgress()F PLcom/airbnb/lottie/LottieDrawable;->getRepeatCount()I PLcom/airbnb/lottie/LottieDrawable;->getRepeatMode()I PLcom/airbnb/lottie/LottieDrawable;->isAnimatingOrWillAnimateOnVisible()Z -PLcom/airbnb/lottie/LottieDrawable;->lambda$addValueCallback$14(Lcom/airbnb/lottie/model/KeyPath;Ljava/lang/Object;Lcom/airbnb/lottie/value/LottieValueCallback;Lcom/airbnb/lottie/LottieComposition;)V -PLcom/annimon/stream/function/BinaryOperator$Util$2;->(Ljava/util/Comparator;)V -PLcom/annimon/stream/function/BinaryOperator$Util;->maxBy(Ljava/util/Comparator;)Lcom/annimon/stream/function/BinaryOperator; -PLcom/annimon/stream/operator/IntRangeClosed;->(II)V -PLcom/annimon/stream/operator/IntRangeClosed;->hasNext()Z -PLcom/annimon/stream/operator/IntRangeClosed;->nextInt()I +PLcom/airbnb/lottie/animation/keyframe/ValueCallbackKeyframeAnimation;->setProgress(F)V PLcom/bumptech/glide/Glide;->unregisterRequestManager(Lcom/bumptech/glide/RequestManager;)V PLcom/bumptech/glide/RequestManager;->onDestroy()V PLcom/bumptech/glide/load/Options;->equals(Ljava/lang/Object;)Z @@ -37244,10 +37808,6 @@ PLcom/google/android/material/stateful/ExtendableSavedState$1;->()V PLcom/google/android/material/stateful/ExtendableSavedState;->()V PLcom/google/android/material/stateful/ExtendableSavedState;->(Landroid/os/Parcelable;)V PLcom/google/android/material/stateful/ExtendableSavedState;->writeToParcel(Landroid/os/Parcel;I)V -PLcom/google/common/collect/Sets$2;->(Ljava/util/Set;Ljava/util/Set;)V -PLcom/google/common/collect/Sets$2;->isEmpty()Z -PLcom/google/common/collect/Sets$SetView;->()V -PLcom/google/common/collect/Sets$SetView;->(Lcom/google/common/collect/Sets$1;)V PLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V PLcom/pnikosis/materialishprogress/ProgressWheel$WheelSavedState$1;->()V PLcom/pnikosis/materialishprogress/ProgressWheel$WheelSavedState;->()V @@ -37289,6 +37849,8 @@ PLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSu PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefCountSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount;->cancel(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefConnection;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount;->timeout(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefConnection;)V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->removeFirst()V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->dispose()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscriber;->dispose()V @@ -37317,7 +37879,6 @@ PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten$FlatMapMaybeObserve PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten;->(Lio/reactivex/rxjava3/core/MaybeSource;Lio/reactivex/rxjava3/functions/Function;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten;->subscribeActual(Lio/reactivex/rxjava3/core/MaybeObserver;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable;->(Ljava/util/concurrent/Callable;)V -PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable;->subscribeActual(Lio/reactivex/rxjava3/core/MaybeObserver;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn$ObserveOnMaybeObserver;->(Lio/reactivex/rxjava3/core/MaybeObserver;Lio/reactivex/rxjava3/core/Scheduler;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn$ObserveOnMaybeObserver;->onComplete()V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn$ObserveOnMaybeObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -37394,9 +37955,10 @@ PLio/reactivex/rxjava3/internal/operators/observable/ObservableSkip$SkipObserver PLio/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap$SwitchMapObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap$SwitchMapObserver;->disposeInner()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed$DebounceTimedObserver;->dispose()V +PLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->isEmpty()Z +PLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; PLio/reactivex/rxjava3/internal/queue/SpscArrayQueue;->clear()V PLio/reactivex/rxjava3/internal/queue/SpscArrayQueue;->isEmpty()Z -PLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->cancelFuture(Ljava/util/concurrent/Future;)V PLio/reactivex/rxjava3/internal/schedulers/DisposeOnCancel;->cancel(Z)Z PLio/reactivex/rxjava3/internal/schedulers/ScheduledRunnable;->dispose()V PLio/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber;->cancel()V @@ -37406,6 +37968,7 @@ PLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->dispose()V PLio/reactivex/rxjava3/internal/util/AtomicThrowable;->terminate()Ljava/lang/Throwable; PLio/reactivex/rxjava3/internal/util/AtomicThrowable;->tryTerminateAndReport()V PLio/reactivex/rxjava3/internal/util/ExceptionHelper;->terminate(Ljava/util/concurrent/atomic/AtomicReference;)Ljava/lang/Throwable; +PLio/reactivex/rxjava3/internal/util/NotificationLite;->acceptFull(Ljava/lang/Object;Lio/reactivex/rxjava3/core/Observer;)Z PLio/reactivex/rxjava3/internal/util/NotificationLite;->complete()Ljava/lang/Object; PLio/reactivex/rxjava3/internal/util/OpenHashSet;->rehash()V PLio/reactivex/rxjava3/internal/util/QueueDrainHelper;->checkTerminated(ZZLio/reactivex/rxjava3/core/Observer;ZLio/reactivex/rxjava3/internal/fuseable/SimpleQueue;Lio/reactivex/rxjava3/disposables/Disposable;Lio/reactivex/rxjava3/internal/util/ObservableQueueDrain;)Z @@ -37418,41 +37981,27 @@ PLio/reactivex/rxjava3/observers/SerializedObserver;->onComplete()V PLio/reactivex/rxjava3/processors/BehaviorProcessor$BehaviorSubscription;->cancel()V PLio/reactivex/rxjava3/processors/BehaviorProcessor;->remove(Lio/reactivex/rxjava3/processors/BehaviorProcessor$BehaviorSubscription;)V PLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->cancel()V -PLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->onNext(Ljava/lang/Object;)V PLio/reactivex/rxjava3/processors/PublishProcessor;->remove(Lio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;)V PLio/reactivex/rxjava3/subjects/BehaviorSubject;->onComplete()V PLio/reactivex/rxjava3/subjects/BehaviorSubject;->terminate(Ljava/lang/Object;)[Lio/reactivex/rxjava3/subjects/BehaviorSubject$BehaviorDisposable; PLio/reactivex/rxjava3/subjects/PublishSubject$PublishDisposable;->dispose()V PLio/reactivex/rxjava3/subjects/PublishSubject;->remove(Lio/reactivex/rxjava3/subjects/PublishSubject$PublishDisposable;)V +PLio/reactivex/rxjava3/subjects/SerializedSubject;->test(Ljava/lang/Object;)Z PLj$/util/DesugarCollections;->a()Ljava/lang/reflect/Constructor; +PLj$/util/concurrent/g;->a(Ljava/lang/Object;I)Lj$/util/concurrent/l; PLj$/util/d;->a(Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set; PLj$/util/d;->keySet()Ljava/util/Set; -PLj$/util/function/b;->(Ljava/util/Comparator;I)V -PLj$/util/stream/A1;->(Lj$/util/stream/V2;Ljava/lang/Object;I)V -PLj$/util/stream/A1;->l1()Lj$/util/stream/P1; -PLj$/util/stream/E1;->(Lj$/util/function/BinaryOperator;)V -PLj$/util/stream/E1;->accept(Ljava/lang/Object;)V -PLj$/util/stream/E1;->get()Ljava/lang/Object; -PLj$/util/stream/E1;->m()V -PLj$/util/stream/E1;->n(J)V -PLj$/util/stream/E1;->q()Z -PLj$/util/stream/h2;->(Lj$/util/stream/i2;Lj$/util/stream/g2;)V -PLj$/util/stream/h2;->accept(Ljava/lang/Object;)V -PLj$/util/stream/h2;->n(J)V -PLj$/util/stream/h2;->q()Z -PLj$/util/stream/i2;->(Lj$/util/stream/c;IJJ)V -PLj$/util/stream/i2;->B1(ILj$/util/stream/g2;)Lj$/util/stream/g2; PLkotlin/collections/CollectionsKt;->intersect(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set; PLkotlin/collections/CollectionsKt;->retainAll(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->filterInPlace$CollectionsKt__MutableCollectionsKt(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Z)Z PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->retainAll(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->retainAll(Ljava/util/Collection;Ljava/lang/Iterable;)Z PLkotlin/collections/CollectionsKt___CollectionsKt;->intersect(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set; -PLkotlin/sequences/SequenceBuilderIterator;->yieldAll(Ljava/util/Iterator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; PLkotlin/sequences/SequenceScope;->yieldAll(Lkotlin/sequences/Sequence;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; PLkotlin/sequences/SequencesKt;->sequence(Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; PLkotlin/sequences/SequencesKt__SequenceBuilderKt$sequence$$inlined$Sequence$1;->(Lkotlin/jvm/functions/Function2;)V PLkotlin/sequences/SequencesKt__SequenceBuilderKt$sequence$$inlined$Sequence$1;->iterator()Ljava/util/Iterator; +PLkotlin/sequences/SequencesKt__SequenceBuilderKt;->sequence(Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; PLme/leolin/shortcutbadger/ShortcutBadgeException;->(Ljava/lang/String;)V PLme/leolin/shortcutbadger/ShortcutBadgeException;->(Ljava/lang/String;Ljava/lang/Exception;)V PLme/leolin/shortcutbadger/ShortcutBadger;->()V @@ -37525,6 +38074,7 @@ PLorg/signal/core/util/logging/Log$Logger;->i(Ljava/lang/String;Ljava/lang/Strin PLorg/signal/core/util/logging/Log;->internal()Lorg/signal/core/util/logging/Log$Logger; PLorg/signal/libsignal/protocol/IdentityKey;->equals(Ljava/lang/Object;)Z PLorg/signal/libsignal/protocol/IdentityKey;->getPublicKey()Lorg/signal/libsignal/protocol/ecc/ECPublicKey; +PLorg/signal/libsignal/protocol/ecc/ECPublicKey;->equals(Ljava/lang/Object;)Z PLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V PLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->run()V PLorg/signal/paging/BufferedPagingController;->$r8$lambda$GxlLAxjfERBgyqmyvxteAPWaQkA(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V @@ -37534,7 +38084,6 @@ PLorg/signal/paging/DataStatus;->mark(I)V PLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V PLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->run()V PLorg/signal/paging/FixedSizePagingController;->$r8$lambda$2jZFFAhs3dG0IThMmzJQSvWvcd0(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V -PLorg/signal/paging/FixedSizePagingController;->buildDataNeededLog(ILjava/lang/String;)Ljava/lang/String; PLorg/signal/paging/FixedSizePagingController;->onDataItemChanged(Ljava/lang/Object;)V PLorg/signal/paging/ProxyPagingController;->onDataItemChanged(Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda60;->isInternal()Z @@ -37556,18 +38105,6 @@ PLorg/thoughtcrime/securesms/audio/AudioRecorderFocusManager26;->(Landroid PLorg/thoughtcrime/securesms/audio/AudioRecorderFocusManager;->()V PLorg/thoughtcrime/securesms/audio/AudioRecorderFocusManager;->(Landroid/content/Context;)V PLorg/thoughtcrime/securesms/audio/AudioRecorderFocusManager;->create(Landroid/content/Context;Landroid/media/AudioManager$OnAudioFocusChangeListener;)Lorg/thoughtcrime/securesms/audio/AudioRecorderFocusManager; -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;Ljava/util/List;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;Ljava/util/List;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->inflateLayout(I)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->showSlides(Lcom/bumptech/glide/RequestManager;Ljava/util/List;)V -PLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->sizeClass(I)I PLorg/thoughtcrime/securesms/components/AnimatingToggle;->display(Landroid/view/View;)V PLorg/thoughtcrime/securesms/components/AudioView$$ExternalSyntheticLambda0;->onChanged(Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/components/AudioView;->$r8$lambda$70S_ChWvvHg6uKYhRw5m0jX0OG0(Lorg/thoughtcrime/securesms/components/AudioView;Lorg/thoughtcrime/securesms/components/voice/VoiceNotePlaybackState;)V @@ -37580,30 +38117,11 @@ PLorg/thoughtcrime/securesms/components/AudioView;->onPlaybackState(Lorg/thought PLorg/thoughtcrime/securesms/components/AudioView;->onProgress(Landroid/net/Uri;DJ)V PLorg/thoughtcrime/securesms/components/AudioView;->onSpeedChanged(Landroid/net/Uri;F)V PLorg/thoughtcrime/securesms/components/AudioView;->onStart(Landroid/net/Uri;ZZ)V -PLorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener;->()V -PLorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener;->(Lorg/thoughtcrime/securesms/components/InputPanel$MediaListener;)V -PLorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener;->(Lorg/thoughtcrime/securesms/components/InputPanel$MediaListener;Lorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener-IA;)V PLorg/thoughtcrime/securesms/components/ComposeText;->setCursorPositionChangedListener(Lorg/thoughtcrime/securesms/components/ComposeText$CursorPositionChangedListener;)V PLorg/thoughtcrime/securesms/components/ComposeText;->setInlineQueryChangedListener(Lorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryChangedListener;)V PLorg/thoughtcrime/securesms/components/ComposeText;->setMentionValidator(Lorg/thoughtcrime/securesms/components/mention/MentionValidatorWatcher$MentionValidator;)V PLorg/thoughtcrime/securesms/components/ComposeText;->setStylingChangedListener(Lorg/thoughtcrime/securesms/components/ComposeText$StylingChangedListener;)V PLorg/thoughtcrime/securesms/components/ConversationItemFooter;->onDetachedFromWindow()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnail$Companion;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnail$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->dispatchDraw(Landroid/graphics/Canvas;)V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->getFooter()Lorg/thoughtcrime/securesms/util/views/Stub; -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setBorderless(Z)V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->showShade(Z)V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState$Creator;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;->(ZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$Creator;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState$Creator;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;->(FZZLorg/thoughtcrime/securesms/mms/SlideClickListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;Lorg/thoughtcrime/securesms/mms/SlideClickListener;Landroid/view/View$OnLongClickListener;IIIIIIIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->()V -PLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->(Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState;Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$AlbumViewState;ILkotlin/jvm/internal/DefaultConstructorMarker;)V PLorg/thoughtcrime/securesms/components/ConversationScrollToView;->formatUnreadCount(I)Ljava/lang/CharSequence; PLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setOnClickListener(Landroid/view/View$OnClickListener;)V PLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setShown(Z)V @@ -37637,16 +38155,8 @@ PLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->getDownl PLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->cancelAction(Z)V PLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->isRecordingLocked()Z PLorg/thoughtcrime/securesms/components/QuoteView;->onDetachedFromWindow()V -PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->()V -PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->(Landroid/view/ViewGroup;Landroid/animation/LayoutTransition;)V -PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->(Landroid/view/ViewGroup;Landroid/animation/LayoutTransition;ILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->findRecyclerParent()Landroidx/recyclerview/widget/RecyclerView; -PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->onScrollStateChanged(Landroidx/recyclerview/widget/RecyclerView;I)V -PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->onViewAttachedToWindow(Landroid/view/View;)V PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->onViewDetachedFromWindow(Landroid/view/View;)V -PLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$3;->test(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$3;->test(Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest;)Z -PLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest;->getPosition()I +PLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->isListCommitted()Z PLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;)V PLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V PLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V @@ -37654,23 +38164,6 @@ PLorg/thoughtcrime/securesms/components/SearchView;->appendEmojiFilter(Landroid/ PLorg/thoughtcrime/securesms/components/SearchView;->initEmojiFilter()V PLorg/thoughtcrime/securesms/components/SendButton;->setPopupContainer(Landroid/view/ViewGroup;)V PLorg/thoughtcrime/securesms/components/SendButton;->setScheduledSendListener(Lorg/thoughtcrime/securesms/components/SendButton$ScheduledSendListener;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport0;->m(Ljava/lang/Object;)Ljava/util/List; -PLorg/thoughtcrime/securesms/components/ThumbnailView$$ExternalSyntheticBackport1;->m(Ljava/lang/Object;)Ljava/util/List; -PLorg/thoughtcrime/securesms/components/ThumbnailView$CancelClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView$CancelClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;Lorg/thoughtcrime/securesms/components/ThumbnailView$CancelClickDispatcher-IA;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView$DownloadClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView$DownloadClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;Lorg/thoughtcrime/securesms/components/ThumbnailView$DownloadClickDispatcher-IA;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView$ThumbnailClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView$ThumbnailClickDispatcher;->(Lorg/thoughtcrime/securesms/components/ThumbnailView;Lorg/thoughtcrime/securesms/components/ThumbnailView$ThumbnailClickDispatcher-IA;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView;->()V -PLorg/thoughtcrime/securesms/components/ThumbnailView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView;->dispatchDraw(Landroid/graphics/Canvas;)V -PLorg/thoughtcrime/securesms/components/ThumbnailView;->fillTargetDimensions([I[I[I)V -PLorg/thoughtcrime/securesms/components/ThumbnailView;->getNonZeroCount([I)I -PLorg/thoughtcrime/securesms/components/ThumbnailView;->hasSameContents(Lorg/thoughtcrime/securesms/mms/Slide;Lorg/thoughtcrime/securesms/mms/Slide;)Z -PLorg/thoughtcrime/securesms/components/ThumbnailView;->onSizeChanged(IIII)V -PLorg/thoughtcrime/securesms/components/ThumbnailView;->setImageResource(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/mms/Slide;ZZ)Lorg/signal/core/util/concurrent/ListenableFuture; -PLorg/thoughtcrime/securesms/components/ThumbnailView;->showSecondaryText(Z)V PLorg/thoughtcrime/securesms/components/TypingStatusRepository;->getTypists(J)Landroidx/lifecycle/LiveData; PLorg/thoughtcrime/securesms/components/ViewBinderDelegate$1;->invoke(Landroidx/viewbinding/ViewBinding;)V PLorg/thoughtcrime/securesms/components/ViewBinderDelegate$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -37683,12 +38176,6 @@ PLorg/thoughtcrime/securesms/components/emoji/EmojiProvider$$ExternalSyntheticLa PLorg/thoughtcrime/securesms/components/emoji/EmojiProvider$$ExternalSyntheticLambda0;->run()V PLorg/thoughtcrime/securesms/components/emoji/EmojiProvider;->$r8$lambda$yWqSXcpiMY6E3k4l81kOhV5ANk4(Lorg/thoughtcrime/securesms/components/emoji/EmojiProvider$EmojiDrawable;Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult;)V PLorg/thoughtcrime/securesms/components/emoji/EmojiProvider;->lambda$getEmojiDrawable$0(Lorg/thoughtcrime/securesms/components/emoji/EmojiProvider$EmojiDrawable;Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult;)V -PLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V -PLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda0;->apply(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/components/emoji/EmojiTextView$$ExternalSyntheticLambda1;->()V -PLorg/thoughtcrime/securesms/components/mention/MentionAnnotation$$ExternalSyntheticLambda1;->()V -PLorg/thoughtcrime/securesms/components/mention/MentionAnnotation;->getMentionAnnotations(Landroid/text/Spanned;)Ljava/util/List; -PLorg/thoughtcrime/securesms/components/mention/MentionAnnotation;->getMentionAnnotations(Landroid/text/Spanned;II)Ljava/util/List; PLorg/thoughtcrime/securesms/components/mention/MentionValidatorWatcher;->setMentionValidator(Lorg/thoughtcrime/securesms/components/mention/MentionValidatorWatcher$MentionValidator;)V PLorg/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton$1$1$$ExternalSyntheticLambda0;->run()V PLorg/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton$1$1;->$r8$lambda$7zbeUa9i765Aa2G9x9qw8f4qjOo(Lorg/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton$1$1;J)V @@ -37699,59 +38186,9 @@ PLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/Term PLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1;->onViewDetachedFromWindow(Landroid/view/View;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Companion;->containsPlayableSlides(Ljava/util/List;)Z -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->$values()[Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->(Ljava/lang/String;I)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->(JJ)V PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->equals(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->(Landroid/view/View$OnClickListener;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1;->(Z)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/util/List;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->(Landroid/view/View$OnClickListener;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->(Z)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->(Landroid/content/Context;Landroid/util/AttributeSet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$isUpdateToExistingSet(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;Ljava/util/List;)Z -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$slidesAsListOfTimestamps(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/util/List;)Ljava/lang/String; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$verboseLog(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/lang/String;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->containsPlayableSlides(Ljava/util/List;)Z -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->getTransferState(Ljava/util/List;)I -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->onAttachedToWindow()V PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->onDetachedFromWindow()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->slidesAsListOfTimestamps(Ljava/util/List;)Ljava/lang/String; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->verboseLog(Ljava/lang/String;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->(ZZZLjava/util/List;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;Landroid/view/View$OnClickListener;ZLjava/util/Map;Ljava/util/Map;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->getCompressionProgress()Ljava/util/Map; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->getNetworkProgress()Ljava/util/Map; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;->getSlides()Ljava/util/List; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$Companion;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State;->$values()[Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView$State;->(Ljava/lang/String;I)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->()V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->(Landroid/content/Context;Landroid/util/AttributeSet;IIILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->progressPaint(I)Landroid/graphics/Paint; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->stopIconPaint(I)Landroid/graphics/Paint; -PLorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;->trackPaint(I)Landroid/graphics/Paint; PLorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController;->getVoiceNotePlaybackState()Landroidx/lifecycle/MutableLiveData; PLorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController;->onStop(Landroidx/lifecycle/LifecycleOwner;)V @@ -37769,18 +38206,6 @@ PLorg/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto;->equals(Ljava PLorg/thoughtcrime/securesms/contacts/paged/ContactSearchMediator$sam$androidx_lifecycle_Observer$0;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/contacts/paged/ContactSearchMediator$sam$androidx_lifecycle_Observer$0;->getFunctionDelegate()Lkotlin/Function; PLorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel;->onCleared()V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$$ExternalSyntheticLambda3;->(Ljava/lang/Runnable;)V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider;->(Lorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider-IA;)V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideButton()V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideDescription()V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideSubtitle()V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setAbout(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setAvatar(Lcom/bumptech/glide/RequestManager;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setLinkifyDescription(Z)V -PLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->showBackgroundBubble(Z)V PLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->canInitializeFromDatabase()Z PLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->getDraftContentType()Ljava/lang/String; PLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->getDraftMedia()Landroid/net/Uri; @@ -37790,9 +38215,6 @@ PLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->getMedia()L PLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->getShareDataTimestamp()J PLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->getStickerLocator()Lorg/thoughtcrime/securesms/stickers/StickerLocator; PLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->isBorderless()Z -PLorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -PLorg/thoughtcrime/securesms/conversation/ConversationItem$ThumbnailClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V -PLorg/thoughtcrime/securesms/conversation/ConversationItem$ThumbnailClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$ThumbnailClickListener-IA;)V PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getBadgeImageView()Landroid/view/View; PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getBubbleViews()Ljava/util/List; PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getContactPhotoHolderView()Landroid/view/View; @@ -37802,16 +38224,6 @@ PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getReactionsView()L PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getReplyView()Landroid/view/View; PLorg/thoughtcrime/securesms/conversation/ConversationItem;->onDetachedFromWindow()V PLorg/thoughtcrime/securesms/conversation/ConversationItem;->onRecipientChanged(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Detailed;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Detailed;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$EditHistory;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$EditHistory;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Standard;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode$Standard;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->(Z)V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->(ZILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;->(ZLkotlin/jvm/internal/DefaultConstructorMarker;)V PLorg/thoughtcrime/securesms/conversation/ConversationItemSwipeCallback$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/ConversationItemSwipeCallback;)V PLorg/thoughtcrime/securesms/conversation/ConversationItemSwipeCallback;->()V PLorg/thoughtcrime/securesms/conversation/ConversationItemSwipeCallback;->(Lorg/thoughtcrime/securesms/conversation/ConversationItemSwipeCallback$SwipeAvailabilityProvider;Lorg/thoughtcrime/securesms/conversation/ConversationItemSwipeCallback$OnSwipeListener;)V @@ -37823,9 +38235,6 @@ PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider$onCre PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->applyTitleSpan(Landroid/view/MenuItem;Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->hideMenuItem(Landroid/view/Menu;I)V PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->setAfterFirstRenderMode(Z)V -PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu;->()V -PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu;->access$getTAG$p()Ljava/lang/String; PLorg/thoughtcrime/securesms/conversation/ConversationSearchViewModel;->(Ljava/lang/String;)V PLorg/thoughtcrime/securesms/conversation/ConversationSearchViewModel;->getSearchResults()Landroidx/lifecycle/LiveData; PLorg/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter;->setStickers(Ljava/util/List;)V @@ -37858,11 +38267,8 @@ PLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onPause(Landr PLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onResume(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onStart(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onStop(Landroidx/lifecycle/LifecycleOwner;)V -PLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda0;->()V -PLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda1;->()V PLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V PLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda2;->run()V -PLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V PLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda3;->run()V PLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->$r8$lambda$gcFI10LhFCaBEmJzQp8t_xBcU8U(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V PLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->$r8$lambda$h27hRrs_Rwv2sGlsmjqcW0dGIZI(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V @@ -37894,8 +38300,6 @@ PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onResume(Landr PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onStart(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->release()V -PLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet;->()V -PLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet;->()V PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$$ExternalSyntheticLambda0;->run()V PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$Companion;->()V @@ -37945,17 +38349,11 @@ PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->getVoiceNoteDr PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->loadShareOrDraftData(J)Lio/reactivex/rxjava3/core/Maybe; PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->saveDrafts(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; -PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$$ExternalSyntheticLambda1;->(Landroidx/recyclerview/widget/RecyclerView;)V PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$$ExternalSyntheticLambda1;->run()V PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->$r8$lambda$IJ0Uk3eauzGuBkd_D1PotCUrlIQ(Landroidx/recyclerview/widget/RecyclerView;)V PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->endAnimations()V PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->endSlideAnimations()V PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->onAnimationFinished$lambda$4(Landroidx/recyclerview/widget/RecyclerView;)V -PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->$values()[Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference; -PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->()V -PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->(Ljava/lang/String;I)V -PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference;->values()[Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference; -PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$WhenMappings;->()V PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->onPause(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->onStop(Landroidx/lifecycle/LifecycleOwner;)V @@ -37991,17 +38389,6 @@ PLorg/thoughtcrime/securesms/conversation/v2/BubbleLayoutTransitionListener;->on PLorg/thoughtcrime/securesms/conversation/v2/BubbleLayoutTransitionListener;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationActivity;->getVoiceNoteMediaController()Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController; PLorg/thoughtcrime/securesms/conversation/v2/ConversationActivity;->onDestroy()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->canPlayContent()Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->getDisplayMode()Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;->showProjectionArea()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->bind(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind(Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$onDetachedFromRecyclerView$$inlined$filterIsInstance$1;->()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$onDetachedFromRecyclerView$$inlined$filterIsInstance$1;->()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$onDetachedFromRecyclerView$$inlined$filterIsInstance$1;->invoke(Ljava/lang/Object;)Ljava/lang/Boolean; @@ -38059,11 +38446,6 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$LastScrolledPo PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$LastScrolledPositionUpdater;->onStart(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$LastScrolledPositionUpdater;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$MotionEventRelayDrain;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Landroidx/lifecycle/LifecycleOwner;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$onScrolled$1;->(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$onScrolled$1;->invoke(J)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener$onScrolled$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$SearchEventListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$SendButtonListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$SwipeAvailabilityProvider;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V @@ -38127,7 +38509,6 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$linkPreviewVie PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$linkPreviewViewModel$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$motionEventRelay$2;->invoke()Landroidx/lifecycle/ViewModelStoreOwner; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$motionEventRelay$2;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$invoke$lambda$1$$inlined$doAfterNextLayout$1$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$observeConversationThread$4$invoke$lambda$1$$inlined$doAfterNextLayout$1$1;->run()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentGroupCallJoinButton$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentGroupCallJoinButton$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -38203,24 +38584,20 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentReque PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentScrollButtons(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentTypingIndicator()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->updateToggleButtonState()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations;Landroid/view/View;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationMessageElement;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->getHeight()I -PLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->getItemView()Landroid/view/View; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->updateForWallpaper()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda12;->(JJ)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda12;->run()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda15;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;Lorg/thoughtcrime/securesms/database/model/GroupRecord;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda15;->call()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda20;->(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda20;->call()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda9;->(J)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda9;->run()V +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda10;->(J)V +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda10;->run()V +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda13;->(JJ)V +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda13;->run()V +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda16;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;Lorg/thoughtcrime/securesms/database/model/GroupRecord;)V +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda16;->call()Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda21;->(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda21;->call()Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->$r8$lambda$-gMXe-w-Xm5jHJapWlyQ6eOUlEE(JJ)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->$r8$lambda$ATtSNJzojyDhPrLSHCNff4Xrn4s(J)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->$r8$lambda$KdCOHPNqejWN1AhOnsjSsYWIQ1E(Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;Lorg/thoughtcrime/securesms/database/model/GroupRecord;)Lj$/util/Optional; PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->$r8$lambda$jYghkNuRsI_xLxRgZRxsCeMcFTc(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState; PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getReminder(Lorg/thoughtcrime/securesms/database/model/GroupRecord;)Lio/reactivex/rxjava3/core/Maybe; +PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getRequestReviewState$lambda$15(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState; PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->markLastSeen$lambda$28(J)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->markLastSeen(J)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->setLastVisibleMessageTimestamp$lambda$5(JJ)V @@ -38245,11 +38622,9 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->invoke(Lo PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)Lio/reactivex/rxjava3/core/SingleSource; PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$1;->test(Lj$/util/Optional;)Z PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$1;->test(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$setShowScrollButtonsForScrollPosition$1;->(ZZ)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$setShowScrollButtonsForScrollPosition$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$setShowScrollButtonsForScrollPosition$1;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState; PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;->getGroupMemberServiceIds()Lio/reactivex/rxjava3/core/Observable; PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;->getHasMessageRequestState()Z PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;->getIdentityRecordsObservable()Lio/reactivex/rxjava3/core/Observable; @@ -38273,7 +38648,6 @@ PLorg/thoughtcrime/securesms/conversation/v2/MotionEventRelay;->setDrain(Lorg/th PLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->()V PLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$IndividualReviewState;Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$GroupReviewState;)V PLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$IndividualReviewState;Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$GroupReviewState;ILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->shouldShowReviewBanner()Z PLorg/thoughtcrime/securesms/conversation/v2/ShareDataTimestampViewModel;->()V PLorg/thoughtcrime/securesms/conversation/v2/ShareDataTimestampViewModel;->()V @@ -38304,7 +38678,6 @@ PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->getChangePaylo PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->getChangePayload(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)Z @@ -38313,133 +38686,14 @@ PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areItemsTheSame PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->getChangePayload(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->getHasCapacity()Z -PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Lkotlin/Unit;Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState; -PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)V PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->getState()Lio/reactivex/rxjava3/core/Flowable; PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->onCleared()V -PLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->(Lkotlin/jvm/functions/Function0;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getMask()Landroid/graphics/drawable/Drawable; -PLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getOpacity()I -PLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->isSolidColor()Z -PLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setCorners(Lorg/thoughtcrime/securesms/util/Projection$Corners;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setLocalChatColors(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->(Ljava/lang/String;IFF)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->getBottomPadding()F -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->getTopPadding()F -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->isEndingShape()Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->isStartingShape()Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->access$getCollapsedSpacing$cp()F -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->access$getDefaultSpacing$cp()F -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->getCornersLTR()Lorg/thoughtcrime/securesms/util/Projection$Corners; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isSingularMessage(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Z)Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isStartOfMessageCluster(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Z)Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isWithinClusteringTime(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->setBodyBubbleCorners(FFFF)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Landroid/view/ViewGroup;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lcom/google/android/material/imageview/ShapeableImageView;Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView;Lorg/thoughtcrime/securesms/components/DeliveryStatusView;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/ExpirationTimerView;Landroid/view/View;Landroid/widget/Space;Lorg/thoughtcrime/securesms/components/AlertView;Z)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getAlert()Lorg/thoughtcrime/securesms/components/AlertView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getBody()Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getBodyWrapper()Landroid/view/ViewGroup; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getDeliveryStatus()Lorg/thoughtcrime/securesms/components/DeliveryStatusView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterBackground()Landroid/view/View; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterDate()Landroid/widget/TextView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterExpiry()Lorg/thoughtcrime/securesms/components/ExpirationTimerView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterSpace()Landroid/widget/Space; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getReactions()Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getReply()Lcom/google/android/material/imageview/ShapeableImageView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getRoot()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderBadge()Lorg/thoughtcrime/securesms/badges/BadgeImageView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderName()Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderPhoto()Lorg/thoughtcrime/securesms/components/AvatarImageView; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->isIncoming()Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridgeKt;->bridge(Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;)Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0;->onLayoutChange(Landroid/view/View;IIIIIIII)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/conversation/ConversationAdapter$ItemClickListener;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$PassthroughClickListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$ReactionMeasureListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1;->(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1;->invoke()Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsData; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->invoke()Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsData; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$replyDelegate$1;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$senderDrawable$1;->(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->$r8$lambda$ocilDMoff9b132TfYhzB6ol1qqk(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;Landroid/view/View;IIIIIIII)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;ILkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->_init_$lambda$0(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;Landroid/view/View;IIIIIIII)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->bind(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->canPlayContent()Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getConversationMessage()Lorg/thoughtcrime/securesms/conversation/ConversationMessage; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getShape()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateFooterDrawable(Landroid/view/ViewGroup;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->isContentCondensed()Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->isForcedFooter()Z -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentAlert()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentDate()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentDeliveryStatus()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterBackground()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterEndPadding()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterExpiry()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentReactions()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSenderNameBackground()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSenderNameColor()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->setConversationMessage(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->setShape(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->showProjectionArea()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$1;->(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->invoke(Landroid/content/Context;Z)Ljava/lang/Integer; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getBodyBubbleColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)I -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getFooterBubbleColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getReplyIconBackgroundColor()I -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->linkifyUrlLinks(Landroid/text/Spannable;ZLorg/thoughtcrime/securesms/util/UrlClickHandler;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->getShapeDelegate()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->getThemeDelegate()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState; -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->(Ljava/lang/String;I)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Ljava/util/List;Landroid/view/View;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Landroid/widget/Space;Lorg/thoughtcrime/securesms/util/views/Stub;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->displayTuckedIntoBody()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->onPreMeasure()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;->()V -PLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;)V PLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onDestroyView()V PLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onStop()V PLorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->onSaveInstanceState()Landroid/os/Parcelable; +PLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13;->run()V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -38475,6 +38729,7 @@ PLorg/thoughtcrime/securesms/database/MessageTable;->markExpireStarted(Ljava/uti PLorg/thoughtcrime/securesms/database/MessageTable;->setMessagesReadSince(JJ)Ljava/util/List; PLorg/thoughtcrime/securesms/database/MessageTable;->setReactionsSeen(JJ)V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda1;->cancel()V +PLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->onChanged()V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver;->$r8$lambda$6u1bbd117Cl1h38MfeI7BgZPo1A(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver;->databaseFlowable$lambda$1$lambda$0(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V PLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->(Ljava/util/Map;Lorg/thoughtcrime/securesms/database/ThreadTable;Ljava/util/List;ZLkotlin/jvm/internal/Ref$BooleanRef;)V @@ -38488,40 +38743,16 @@ PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Ljava/util/Map; PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;ZJ)Ljava/util/List; PLorg/thoughtcrime/securesms/database/ThreadTable;->update(JZZ)Z PLorg/thoughtcrime/securesms/database/identity/IdentityRecordList;->equals(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda3;->cancel()V PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->$r8$lambda$_YM1i9V93JIKhbRirbAeb_98VJw(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3$lambda$2(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isForcedUnread()Z -PLorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding;->(Landroid/view/View;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Landroid/widget/LinearLayout;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Lcom/google/android/material/button/MaterialButton;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Landroid/view/View;Landroidx/constraintlayout/widget/ConstraintLayout;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V -PLorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding; PLorg/thoughtcrime/securesms/databinding/ConversationSearchNavBinding;->getRoot()Lorg/thoughtcrime/securesms/components/ConversationSearchBottomBar; -PLorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding;->(Landroid/view/View;Landroidx/constraintlayout/widget/Guideline;Landroidx/appcompat/widget/AppCompatImageView;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/transfercontrols/TransferProgressView;Landroidx/constraintlayout/widget/Guideline;)V -PLorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding; -PLorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding;->inflate(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding; PLorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;->getRoot()Lorg/thoughtcrime/securesms/components/InputAwareConstraintLayout; -PLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout;Lorg/thoughtcrime/securesms/components/ExpirationTimerView;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView;Lcom/google/android/material/imageview/ShapeableImageView;Landroid/widget/Space;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V -PLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding; -PLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->getRoot()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; PLorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult$Immediate;->()V PLorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult$Immediate;->(Landroid/graphics/Bitmap;)V PLorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult$Immediate;->getBitmap()Landroid/graphics/Bitmap; -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->(Landroid/graphics/Typeface;)V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->updateDrawState(Landroid/text/TextPaint;)V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$CustomTypefaceSpan;->updateMeasureState(Landroid/text/TextPaint;)V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->$values()[Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph; -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->()V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->(Ljava/lang/String;IC)V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Glyph;->getUnicode()C -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->$values()[Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight; -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->()V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->(Ljava/lang/String;I)V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;->values()[Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight; -PLorg/thoughtcrime/securesms/fonts/SignalSymbols$WhenMappings;->()V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols;->()V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols;->()V -PLorg/thoughtcrime/securesms/fonts/SignalSymbols;->getBoldWeightedFont(Landroid/content/Context;)Landroid/graphics/Typeface; -PLorg/thoughtcrime/securesms/fonts/SignalSymbols;->getTypeface(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/SignalSymbols$Weight;)Landroid/graphics/Typeface; -PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4PlaybackController;->onScrolled(Landroidx/recyclerview/widget/RecyclerView;II)V PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onPause(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onStop(Landroidx/lifecycle/LifecycleOwner;)V @@ -38534,7 +38765,6 @@ PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->enqueue()V PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->getFactoryKey()Ljava/lang/String; PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->serialize()[B PLorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;->values()[Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme; -PLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->isEnterKeySends()Z PLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->isLinkPreviewsEnabled()Z PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewRepository;->()V PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewRepository;->()V @@ -38569,8 +38799,6 @@ PLorg/thoughtcrime/securesms/main/MainActivityListHostFragment;->onDestroyView() PLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->getState()Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState$State; PLorg/thoughtcrime/securesms/mms/AttachmentManager;->isAttachmentPresent()Z -PLorg/thoughtcrime/securesms/mms/SlideDeck$$ExternalSyntheticLambda0;->()V -PLorg/thoughtcrime/securesms/mms/SlideDeck$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->()V PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->process(Ljava/util/List;)V PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->()V @@ -38616,9 +38844,10 @@ PLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;-> PLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;->constructNotificationState(Ljava/util/Map;Lorg/thoughtcrime/securesms/notifications/profiles/NotificationProfile;)Lorg/thoughtcrime/securesms/notifications/v2/NotificationState; PLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->isDisplayContact()Z -PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda0;->run()V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda2;->onChanged(Ljava/lang/Object;)V +PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->contentsMatch(Ljava/lang/Object;Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda6;->run()V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/Recipient;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda9;->run()V @@ -38630,6 +38859,7 @@ PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$new$0(Lorg/though PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$new$1(Lorg/thoughtcrime/securesms/recipients/Recipient;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$observeForever$6(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$removeForeverObserver$7(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V +PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->removeForeverObserver(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V PLorg/thoughtcrime/securesms/recipients/Recipient;->getContactUri()Landroid/net/Uri; PLorg/thoughtcrime/securesms/recipients/Recipient;->getNotificationChannel()Ljava/lang/String; PLorg/thoughtcrime/securesms/service/ExpiringMessageManager$$ExternalSyntheticLambda0;->()V @@ -38661,21 +38891,12 @@ PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter$$ExternalSyntheticLambda0;- PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->$r8$lambda$1L8FIPWGmHakh7u9Krsm8K4DSjQ(Lorg/thoughtcrime/securesms/util/LeakyBucketLimiter;)V PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->drip()V PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->run(Ljava/lang/Runnable;)V -PLorg/thoughtcrime/securesms/util/LongClickMovementMethod$1;->(Lorg/thoughtcrime/securesms/util/LongClickMovementMethod;)V -PLorg/thoughtcrime/securesms/util/LongClickMovementMethod;->(Landroid/content/Context;)V -PLorg/thoughtcrime/securesms/util/LongClickMovementMethod;->getInstance(Landroid/content/Context;)Lorg/thoughtcrime/securesms/util/LongClickMovementMethod; -PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper;IIII)V -PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$$ExternalSyntheticLambda0;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper;->access$getAnimator$p(Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper;)Landroid/animation/ValueAnimator; PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper;->access$getSetStatusBarColor$p(Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper;)Lkotlin/jvm/functions/Function1; PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper;->getPreviousStatusBarColor()I PLorg/thoughtcrime/securesms/util/NullableSavedStateHandleDelegate;->(Landroidx/lifecycle/SavedStateHandle;Ljava/lang/String;)V -PLorg/thoughtcrime/securesms/util/Projection$Corners;->()V -PLorg/thoughtcrime/securesms/util/Projection$Corners;->(F)V -PLorg/thoughtcrime/securesms/util/Projection$Corners;->([F)V -PLorg/thoughtcrime/securesms/util/Projection$Corners;->toRelativeRadii(Z)[F PLorg/thoughtcrime/securesms/util/SavedStateHandleExtensionsKt$delegate$1;->(Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/util/SavedStateHandleExtensionsKt;->delegate(Landroidx/lifecycle/SavedStateHandle;Ljava/lang/String;)Lkotlin/properties/ReadWriteProperty; PLorg/thoughtcrime/securesms/util/SavedStateHandleExtensionsKt;->delegate(Landroidx/lifecycle/SavedStateHandle;Ljava/lang/String;Ljava/lang/Object;)Lkotlin/properties/ReadWriteProperty; @@ -38684,16 +38905,12 @@ PLorg/thoughtcrime/securesms/util/SavedStateViewModelFactory$Companion$factoryPr PLorg/thoughtcrime/securesms/util/SavedStateViewModelFactory$Companion$factoryProducer$1;->invoke()Lorg/thoughtcrime/securesms/util/SavedStateViewModelFactory; PLorg/thoughtcrime/securesms/util/SavedStateViewModelFactory;->(Lkotlin/jvm/functions/Function1;Landroidx/savedstate/SavedStateRegistryOwner;)V PLorg/thoughtcrime/securesms/util/SavedStateViewModelFactory;->create(Ljava/lang/String;Ljava/lang/Class;Landroidx/lifecycle/SavedStateHandle;)Landroidx/lifecycle/ViewModel; -PLorg/thoughtcrime/securesms/util/SearchUtil;->getHighlightedSpan(Ljava/util/Locale;Lorg/thoughtcrime/securesms/util/SearchUtil$StyleFactory;Landroid/text/Spannable;Ljava/lang/String;I)Landroid/text/Spannable; PLorg/thoughtcrime/securesms/util/ServiceUtil;->getAudioManager(Landroid/content/Context;)Landroid/media/AudioManager; PLorg/thoughtcrime/securesms/util/SignalLocalMetrics$ConversationOpen;->onRenderFinished()V -PLorg/thoughtcrime/securesms/util/SpanUtil;->()V -PLorg/thoughtcrime/securesms/util/SpanUtil;->ofSize(Ljava/lang/CharSequence;I)Ljava/lang/CharSequence; PLorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/Activity;)Landroid/window/SplashScreen; PLorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline1;->m(Landroid/window/SplashScreen;I)V PLorg/thoughtcrime/securesms/util/SplashScreenUtil$1;->()V PLorg/thoughtcrime/securesms/util/SplashScreenUtil;->setSplashScreenThemeIfNecessary(Landroid/app/Activity;Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;)V -PLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isEnterSendsEnabled(Landroid/content/Context;)Z PLorg/thoughtcrime/securesms/util/Util;->clamp(FFF)F PLorg/thoughtcrime/securesms/util/ViewModelFactoryKt$savedStateViewModel$$inlined$viewModels$default$1;->invoke()Landroidx/fragment/app/Fragment; PLorg/thoughtcrime/securesms/util/ViewModelFactoryKt$savedStateViewModel$$inlined$viewModels$default$1;->invoke()Ljava/lang/Object; @@ -38711,14 +38928,13 @@ PLorg/thoughtcrime/securesms/util/ViewUtil;->fadeOut(Landroid/view/View;II)Lorg/ PLorg/thoughtcrime/securesms/util/ViewUtil;->getAlphaAnimation(FFI)Landroid/view/animation/Animation; PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->onViewDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->onViewDetachedFromWindow(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;)V +PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areContentsTheSame(Ljava/lang/Object;Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areContentsTheSame(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Z PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areItemsTheSame(Ljava/lang/Object;Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areItemsTheSame(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Z PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->getChangePayload(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->getChangePayload(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel$-CC;->$default$getChangePayload(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->(Landroid/view/View;)V -PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->onAttachedToWindow()V PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->onDetachedFromWindow()V PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;Ljava/lang/Runnable;)V PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor$$ExternalSyntheticLambda0;->run()V @@ -38727,8 +38943,6 @@ PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->enqueue(Lj PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->execute(Ljava/lang/Runnable;)V PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->lambda$enqueue$0(Ljava/lang/Runnable;)V PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->scheduleNext()V -PLorg/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat;->()V -PLorg/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat;->(Landroid/content/Context;)V PLorg/thoughtcrime/securesms/util/rx/RxStore;->dispose()V PLorg/thoughtcrime/securesms/util/views/Stub;->getVisibility()I PLorg/thoughtcrime/securesms/util/views/Stub;->isVisible()Z From da43ff1e95464d89f34ba7abad957f0cabe6b2f4 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 11:42:24 -0400 Subject: [PATCH 050/113] Bump version to 7.5.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 008d90fc2f..eb6c31e56d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1412 -val canonicalVersionName = "7.5.1" +val canonicalVersionCode = 1413 +val canonicalVersionName = "7.5.2" val postFixSize = 100 val abiPostFix: Map = mapOf( From f34ae8d118e777b316f12bb66ddca7cb907c9b6c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 19 Apr 2024 12:09:11 -0400 Subject: [PATCH 051/113] Add padding to the gzipped backup output. --- .../backup/v2/stream/EncryptedBackupWriter.kt | 11 +--- .../v2/stream/PaddedGzipOutputStream.kt | 55 +++++++++++++++++++ .../stream/EncryptedBackupReaderWriterTest.kt | 29 +++++++++- 3 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PaddedGzipOutputStream.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt index c8d2ea4de9..97828c6f9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt @@ -13,7 +13,6 @@ import org.whispersystems.signalservice.api.backup.BackupKey import org.whispersystems.signalservice.api.push.ServiceId.ACI import java.io.IOException import java.io.OutputStream -import java.util.zip.GZIPOutputStream import javax.crypto.Cipher import javax.crypto.CipherOutputStream import javax.crypto.Mac @@ -33,7 +32,7 @@ class EncryptedBackupWriter( private val append: (ByteArray) -> Unit ) : BackupExportWriter { - private val mainStream: GZIPOutputStream + private val mainStream: PaddedGzipOutputStream private val macStream: MacOutputStream init { @@ -48,13 +47,9 @@ class EncryptedBackupWriter( } macStream = MacOutputStream(outputStream, mac) + val cipherStream = CipherOutputStream(macStream, cipher) - mainStream = GZIPOutputStream( - CipherOutputStream( - macStream, - cipher - ) - ) + mainStream = PaddedGzipOutputStream(cipherStream) } override fun write(header: BackupInfo) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PaddedGzipOutputStream.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PaddedGzipOutputStream.kt new file mode 100644 index 0000000000..791adc036a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PaddedGzipOutputStream.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.whispersystems.signalservice.internal.crypto.PaddingInputStream +import java.io.FilterOutputStream +import java.io.OutputStream +import java.util.zip.GZIPOutputStream + +/** + * GZIPs the content of the provided [outputStream], but also adds padding to the end of the stream using the same algorithm as [PaddingInputStream]. + * We do this to fit files into a smaller number of size buckets to avoid fingerprinting. And it turns out that bolting on zeros to the end of a GZIP stream is + * fine, because GZIP is smart enough to ignore it. This means readers of this data don't have to do anything special. + */ +class PaddedGzipOutputStream private constructor(private val outputStream: SizeObservingOutputStream) : GZIPOutputStream(outputStream) { + + constructor(outputStream: OutputStream) : this(SizeObservingOutputStream(outputStream)) + + override fun finish() { + super.finish() + + val totalLength = outputStream.size + val paddedSize: Long = PaddingInputStream.getPaddedSize(totalLength) + val paddingToAdd: Int = (paddedSize - totalLength).toInt() + + outputStream.write(ByteArray(paddingToAdd)) + } + + /** + * We need to know the size of the *compressed* stream to know how much padding to add at the end. + */ + private class SizeObservingOutputStream(val wrapped: OutputStream) : FilterOutputStream(wrapped) { + + var size: Long = 0L + private set + + override fun write(b: Int) { + wrapped.write(b) + size++ + } + + override fun write(b: ByteArray) { + wrapped.write(b) + size += b.size + } + + override fun write(b: ByteArray, off: Int, len: Int) { + wrapped.write(b, off, len) + size += len + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt b/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt index 12198e7e43..7692ea5e39 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.backup.v2.stream import org.junit.Assert.assertEquals import org.junit.Test +import org.signal.core.util.Hex import org.thoughtcrime.securesms.backup.v2.proto.AccountData import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo import org.thoughtcrime.securesms.backup.v2.proto.Frame @@ -27,13 +28,15 @@ class EncryptedBackupReaderWriterTest { val frameCount = 10_000 EncryptedBackupWriter(key, aci, outputStream, append = { outputStream.write(it) }).use { writer -> - writer.write(BackupInfo(1, 1000L)) + writer.write(BackupInfo(version = 1, backupTimeMs = 1000L)) + for (i in 0 until frameCount) { writer.write(Frame(account = AccountData(username = "username-$i"))) } } val ciphertext: ByteArray = outputStream.toByteArray() + println(ciphertext.size) val frames: List = EncryptedBackupReader(key, aci, ciphertext.size.toLong()) { ciphertext.inputStream() }.use { reader -> assertEquals(reader.backupInfo?.version, 1L) @@ -47,4 +50,28 @@ class EncryptedBackupReaderWriterTest { assertEquals("username-$i", frames[i].account?.username) } } + + @Test + fun `padding limits number of sizes`() { + val key = BackupKey(Util.getSecretBytes(32)) + val aci = ACI.from(UUID.randomUUID()) + + val sizes = (1..10) + .map { frameCount -> + val outputStream = ByteArrayOutputStream() + + EncryptedBackupWriter(key, aci, outputStream, append = { outputStream.write(it) }).use { writer -> + writer.write(BackupInfo(version = 1, backupTimeMs = 1000L)) + + for (i in 0 until frameCount) { + writer.write(Frame(account = AccountData(username = Hex.toStringCondensed(Util.getSecretBytes(32))))) + } + } + + outputStream.toByteArray().size + } + .toSet() + + assertEquals(1, sizes.size) + } } From 690a68f0d0d7e4485ecbb6749ece2380ebde91da Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 19 Apr 2024 12:13:45 -0400 Subject: [PATCH 052/113] Remove libweb submodule entirely. --- .gitmodules | 3 --- libwebp | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 libwebp diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8024d27056..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "libwebp"] - path = libwebp - url = https://github.com/webmproject/libwebp.git diff --git a/libwebp b/libwebp deleted file mode 160000 index ca332209cb..0000000000 --- a/libwebp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ca332209cb5567c9b249c86788cb2dbf8847e760 From 04fb459acd91d7b122ee092d14105fc7fa390790 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 19 Apr 2024 13:41:15 -0400 Subject: [PATCH 053/113] Remove unused backup outputstream class. --- .../v2/stream/BackupEncryptedOutputStream.kt | 75 ------------------- 1 file changed, 75 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupEncryptedOutputStream.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupEncryptedOutputStream.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupEncryptedOutputStream.kt deleted file mode 100644 index 5a7c535d13..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupEncryptedOutputStream.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.backup.v2.stream - -import org.signal.libsignal.protocol.kdf.HKDF -import java.io.FilterOutputStream -import java.io.OutputStream -import javax.crypto.Cipher -import javax.crypto.Mac -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -class BackupEncryptedOutputStream(key: ByteArray, backupId: ByteArray, wrapped: OutputStream) : FilterOutputStream(wrapped) { - - val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - val mac: Mac = Mac.getInstance("HmacSHA256") - - var finalMac: ByteArray? = null - - init { - if (key.size != 32) { - throw IllegalArgumentException("Key must be 32 bytes!") - } - - if (backupId.size != 16) { - throw IllegalArgumentException("BackupId must be 32 bytes!") - } - - val extendedKey = HKDF.deriveSecrets(key, backupId, "20231003_Signal_Backups_EncryptMessageBackup".toByteArray(), 80) - val macKey = extendedKey.copyOfRange(0, 32) - val cipherKey = extendedKey.copyOfRange(32, 64) - val iv = extendedKey.copyOfRange(64, 80) - - cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) - mac.init(SecretKeySpec(macKey, "HmacSHA256")) - } - - override fun write(b: Int) { - throw UnsupportedOperationException() - } - - override fun write(data: ByteArray) { - write(data, 0, data.size) - } - - override fun write(data: ByteArray, off: Int, len: Int) { - cipher.update(data, off, len)?.let { ciphertext -> - mac.update(ciphertext) - super.write(ciphertext) - } - } - - override fun flush() { - cipher.doFinal()?.let { ciphertext -> - mac.update(ciphertext) - super.write(ciphertext) - } - - finalMac = mac.doFinal() - - super.flush() - } - - override fun close() { - flush() - super.close() - } - - fun getMac(): ByteArray { - return finalMac ?: throw IllegalStateException("Mac not yet available! You must call flush() before asking for the mac.") - } -} From b771a21518056cecb15ed03d2d9d0a997b9315ca Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 19 Apr 2024 15:05:03 -0300 Subject: [PATCH 054/113] Add screen for managing backup type. --- .../backups/RemoteBackupsSettingsFragment.kt | 9 +- .../type/BackupsTypeSettingsFragment.kt | 197 ++++++++++++++++++ .../backups/type/BackupsTypeSettingsState.kt | 17 ++ .../type/BackupsTypeSettingsViewModel.kt | 16 ++ app/src/main/res/navigation/app_settings.xml | 15 +- app/src/main/res/values/strings.xml | 15 ++ 6 files changed, 264 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt 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/chats/backups/RemoteBackupsSettingsFragment.kt index ddb7bd3f84..d1c0cd378c 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/chats/backups/RemoteBackupsSettingsFragment.kt @@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType 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 org.thoughtcrime.securesms.util.viewModel import java.math.BigDecimal import java.util.Currency @@ -130,8 +131,8 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { viewModel.turnOffAndDeleteBackups() } - override fun onChangeBackupsTypeClick() { - // TODO - launch flow at appropriate point + override fun onBackupsTypeClick() { + findNavController().safeNavigate(R.id.action_remoteBackupsSettingsFragment_to_backupsTypeSettingsFragment) } } } @@ -142,7 +143,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { private interface ContentCallbacks { fun onNavigationClick() = Unit fun onEnableBackupsClick() = Unit - fun onChangeBackupsTypeClick() = Unit + fun onBackupsTypeClick() = Unit fun onBackUpUsingCellularClick(canUseCellular: Boolean) = Unit fun onViewPaymentHistory() = Unit fun onBackupNowClick() = Unit @@ -184,7 +185,7 @@ private fun RemoteBackupsSettingsContent( BackupTypeRow( messageBackupsType = messageBackupsType, onEnableBackupsClick = contentCallbacks::onEnableBackupsClick, - onChangeBackupsTypeClick = contentCallbacks::onChangeBackupsTypeClick + onChangeBackupsTypeClick = contentCallbacks::onBackupsTypeClick ) } 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/chats/backups/type/BackupsTypeSettingsFragment.kt new file mode 100644 index 0000000000..66b1f33184 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt @@ -0,0 +1,197 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups.type + +import android.content.Intent +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.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.stringResource +import androidx.navigation.fragment.findNavController +import kotlinx.collections.immutable.persistentListOf +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.MessageBackupsFlowActivity +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType +import org.thoughtcrime.securesms.compose.ComposeFragment +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.Currency +import java.util.Locale + +/** + * Allows the user to modify their backup plan + */ +class BackupsTypeSettingsFragment : ComposeFragment() { + + private val viewModel: BackupsTypeSettingsViewModel by viewModel { + BackupsTypeSettingsViewModel() + } + + @Composable + override fun FragmentContent() { + val contentCallbacks = remember { + Callbacks() + } + + val state by viewModel.state + + BackupsTypeSettingsContent( + state = state, + contentCallbacks = contentCallbacks + ) + } + + private inner class Callbacks : ContentCallbacks { + override fun onNavigationClick() { + findNavController().popBackStack() + } + + override fun onPaymentHistoryClick() { + // TODO [message-backups] Navigate to payment history + } + + override fun onChangeOrCancelSubscriptionClick() { + startActivity(Intent(requireContext(), MessageBackupsFlowActivity::class.java)) + } + } +} + +private interface ContentCallbacks { + fun onNavigationClick() = Unit + fun onPaymentHistoryClick() = Unit + fun onChangeOrCancelSubscriptionClick() = Unit +} + +@Composable +private fun BackupsTypeSettingsContent( + state: BackupsTypeSettingsState, + contentCallbacks: ContentCallbacks +) { + if (state.backupsType == 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( + backupsType = state.backupsType, + nextRenewalTimestamp = state.nextRenewalTimestamp + ) + } + + item { + PaymentSourceRow( + paymentSourceType = state.paymentSourceType + ) + } + + item { + Rows.TextRow( + text = "Change or cancel subscription", // TODO [message-backups] final copy + onClick = contentCallbacks::onChangeOrCancelSubscriptionClick + ) + } + + item { + Rows.TextRow( + text = "Payment history", // TODO [message-backups] final copy + onClick = contentCallbacks::onPaymentHistoryClick + ) + } + } + } +} + +@Composable +private fun BackupsTypeRow( + backupsType: MessageBackupsType, + nextRenewalTimestamp: Long +) { + val resources = LocalContext.current.resources + val formattedAmount = remember(backupsType.pricePerMonth) { + FiatMoneyUtil.format(resources, backupsType.pricePerMonth, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) + } + + val renewal = remember(nextRenewalTimestamp) { + DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), nextRenewalTimestamp) + } + + Rows.TextRow(text = { + Column { + Text(text = backupsType.title) + Text( + text = "$formattedAmount/month . Renews $renewal", // TODO [message-backups] final copy + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + }) +} + +@Composable +private fun PaymentSourceRow(paymentSourceType: PaymentSourceType) { + val paymentSourceTextResId = remember(paymentSourceType) { + when (paymentSourceType) { + 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( + backupsType = MessageBackupsType( + pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("USD")), + title = "Text + all media", + features = persistentListOf() + ) + ), + contentCallbacks = object : ContentCallbacks {} + ) + } +} 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/chats/backups/type/BackupsTypeSettingsState.kt new file mode 100644 index 0000000000..5449a81a04 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.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 backupsType: MessageBackupsType? = null, + val paymentSourceType: PaymentSourceType = PaymentSourceType.Unknown, + val nextRenewalTimestamp: Long = 0 +) 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/chats/backups/type/BackupsTypeSettingsViewModel.kt new file mode 100644 index 0000000000..c8ddcf34ea --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups.type + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel + +class BackupsTypeSettingsViewModel : ViewModel() { + private val internalState = mutableStateOf(BackupsTypeSettingsState()) + + val state: State = internalState +} diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 38005862fb..20ea3a6c49 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -930,7 +930,20 @@ + android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.RemoteBackupsSettingsFragment"> + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf629496d3..35987d413f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6751,5 +6751,20 @@ Downloading backup data… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + From 6ad72f00afba3e0a07ebd8a30ebbed4e39743484 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Fri, 19 Apr 2024 17:57:56 -0400 Subject: [PATCH 055/113] Fix phone number formatter in Registration V2. --- .../ui/{shared => }/RegistrationCheckpoint.kt | 2 +- .../v2/ui/RegistrationV2Activity.kt | 1 - .../v2/ui/{shared => }/RegistrationV2State.kt | 2 +- .../{shared => }/RegistrationV2ViewModel.kt | 3 +- .../v2/ui/entercode/EnterCodeV2Fragment.kt | 4 +- .../GrantPermissionsV2Fragment.kt | 6 +-- .../phonenumber/EnterPhoneNumberV2Fragment.kt | 53 +++++++++++++------ .../ui/phonenumber/EnterPhoneNumberV2State.kt | 4 +- .../EnterPhoneNumberV2ViewModel.kt | 17 ++++++ .../v2/ui/welcome/WelcomeV2Fragment.kt | 17 +++--- .../fragment_registration_welcome_v2.xml | 1 + 11 files changed, 73 insertions(+), 37 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/{shared => }/RegistrationCheckpoint.kt (91%) rename app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/{shared => }/RegistrationV2State.kt (91%) rename app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/{shared => }/RegistrationV2ViewModel.kt (98%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationCheckpoint.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationCheckpoint.kt similarity index 91% rename from app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationCheckpoint.kt rename to app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationCheckpoint.kt index 744268df98..1af548b418 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationCheckpoint.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationCheckpoint.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.registration.v2.ui.shared +package org.thoughtcrime.securesms.registration.v2.ui /** * An ordered list of checkpoints of the registration process. diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt index 8592149509..032da33b02 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt @@ -12,7 +12,6 @@ import androidx.activity.viewModels import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.BaseActivity import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel /** * Activity to hold the entire registration process. diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt similarity index 91% rename from app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2State.kt rename to app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt index 45511acf0b..6eba43dcd8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2State.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.registration.v2.ui.shared +package org.thoughtcrime.securesms.registration.v2.ui import com.google.i18n.phonenumbers.Phonenumber diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2ViewModel.kt rename to app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt index e20449fe92..8d75e5cf7f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/shared/RegistrationV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.registration.v2.ui.shared +package org.thoughtcrime.securesms.registration.v2.ui import android.content.Context import androidx.lifecycle.ViewModel @@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.registration.RegistrationData import org.thoughtcrime.securesms.registration.RegistrationUtil import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository -import org.thoughtcrime.securesms.registration.v2.ui.toE164 import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.dualsim.MccMncProducer diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt index 565f0705b7..ee0b9f2e1e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt @@ -24,8 +24,8 @@ import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel /** * The final screen of account registration, where the user enters their verification code. diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt index e61987787c..903bd31a32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt @@ -21,9 +21,9 @@ import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.registration.compose.GrantPermissionsScreen import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2State -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel import org.thoughtcrime.securesms.util.BackupUtil import org.thoughtcrime.securesms.util.navigation.safeNavigate diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt index cca66bb7e7..90702878a0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt @@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.registration.v2.ui.phonenumber import android.content.Context import android.os.Bundle import android.text.SpannableStringBuilder +import android.text.TextWatcher import android.view.KeyEvent import android.view.Menu import android.view.MenuInflater @@ -38,9 +39,9 @@ import org.thoughtcrime.securesms.databinding.FragmentRegistrationEnterPhoneNumb import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView import org.thoughtcrime.securesms.registration.util.CountryPrefix -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2State -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel import org.thoughtcrime.securesms.registration.v2.ui.toE164 import org.thoughtcrime.securesms.util.PlayServicesUtil import org.thoughtcrime.securesms.util.SpanUtil @@ -63,8 +64,11 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio private lateinit var phoneNumberInputLayout: TextInputEditText private lateinit var spinnerView: MaterialAutoCompleteTextView + private var currentPhoneNumberFormatter: TextWatcher? = null + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setDebugLogSubmitMultiTapView(binding.verifyHeader) requireActivity().onBackPressedDispatcher.addCallback( viewLifecycleOwner, object : OnBackPressedCallback(true) { @@ -80,25 +84,14 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio R.layout.registration_country_code_dropdown_item, fragmentViewModel.supportedCountryPrefixes ) - setDebugLogSubmitMultiTapView(binding.verifyHeader) binding.registerButton.setOnClickListener { onRegistrationButtonClicked() } - binding.toolbar.title = null + binding.toolbar.title = "" val activity = requireActivity() as AppCompatActivity activity.setSupportActionBar(binding.toolbar) requireActivity().addMenuProvider(UseProxyMenuProvider(), viewLifecycleOwner) - val existingPhoneNumber = sharedViewModel.uiState.value?.phoneNumber - if (existingPhoneNumber != null) { - fragmentViewModel.restoreState(existingPhoneNumber) - fragmentViewModel.phoneNumber()?.let { - phoneNumberInputLayout.setText(it.nationalNumber.toString()) - } - } else if (spinnerView.editableText.isBlank()) { - spinnerView.setText(fragmentViewModel.countryPrefix().toString()) - } - sharedViewModel.uiState.observe(viewLifecycleOwner) { sharedState -> presentRegisterButton(sharedState) presentProgressBar(sharedState.inProgress, sharedState.isReRegister) @@ -108,6 +101,19 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio } fragmentViewModel.uiState.observe(viewLifecycleOwner) { fragmentState -> + + fragmentState.phoneNumberFormatter?.let { + if (it != currentPhoneNumberFormatter) { + currentPhoneNumberFormatter?.let { oldWatcher -> + Log.d(TAG, "Removing current phone number formatter in fragment") + phoneNumberInputLayout.removeTextChangedListener(oldWatcher) + } + phoneNumberInputLayout.addTextChangedListener(it) + currentPhoneNumberFormatter = it + Log.d(TAG, "Updating phone number formatter in fragment") + } + } + if (fragmentViewModel.isEnteredNumberValid(fragmentState)) { sharedViewModel.setPhoneNumber(fragmentViewModel.parsePhoneNumber(fragmentState)) } else { @@ -121,12 +127,26 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio initializeInputFields() + val existingPhoneNumber = sharedViewModel.uiState.value?.phoneNumber + if (existingPhoneNumber != null) { + fragmentViewModel.restoreState(existingPhoneNumber) + fragmentViewModel.phoneNumber()?.let { + phoneNumberInputLayout.setText(it.nationalNumber.toString()) + } + } else if (spinnerView.editableText.isBlank()) { + spinnerView.setText(fragmentViewModel.countryPrefix().toString()) + } + ViewUtil.focusAndShowKeyboard(phoneNumberInputLayout) } private fun initializeInputFields() { + binding.countryCode.editText?.addTextChangedListener { s -> + val countryCode: Int = s.toString().toInt() + fragmentViewModel.setCountry(countryCode) + } + phoneNumberInputLayout.addTextChangedListener { - // TODO [regv2]: country code as you type formatter fragmentViewModel.setPhoneNumber(it?.toString()) } phoneNumberInputLayout.onFocusChangeListener = View.OnFocusChangeListener { _: View?, hasFocus: Boolean -> @@ -155,7 +175,6 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio } fragmentViewModel.supportedCountryPrefixes.firstOrNull { it.toString() == s.toString() }?.let { - // TODO [regv2]: setCountryFormatter(it.regionCode) fragmentViewModel.setCountry(it.digits) val numberLength: Int = phoneNumberInputLayout.text?.length ?: 0 phoneNumberInputLayout.setSelection(numberLength, numberLength) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt index 6f399443f6..d311bcaa6e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2State.kt @@ -5,10 +5,12 @@ package org.thoughtcrime.securesms.registration.v2.ui.phonenumber +import android.text.TextWatcher + /** * State holder for the phone number entry screen, including phone number and Play Services errors. */ -data class EnterPhoneNumberV2State(val countryPrefixIndex: Int, val phoneNumber: String, val error: Error = Error.NONE) { +data class EnterPhoneNumberV2State(val countryPrefixIndex: Int, val phoneNumber: String, val phoneNumberFormatter: TextWatcher? = null, val error: Error = Error.NONE) { companion object { @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt index 4a7804f67e..7920f69038 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt @@ -5,13 +5,18 @@ package org.thoughtcrime.securesms.registration.v2.ui.phonenumber +import android.telephony.PhoneNumberFormattingTextWatcher import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope import com.google.i18n.phonenumbers.NumberParseException import com.google.i18n.phonenumbers.PhoneNumberUtil import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.registration.util.CountryPrefix @@ -51,6 +56,18 @@ class EnterPhoneNumberV2ViewModel : ViewModel() { store.update { it.copy(countryPrefixIndex = matchingIndex) } + + viewModelScope.launch { + withContext(Dispatchers.Default) { + val regionCode = PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(digits) + val textWatcher = PhoneNumberFormattingTextWatcher(regionCode) + + store.update { + Log.d(TAG, "Updating phone number formatter in state") + it.copy(phoneNumberFormatter = textWatcher) + } + } + } } fun parsePhoneNumber(state: EnterPhoneNumberV2State): PhoneNumber { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt index 1abb0512ba..cf4ca9b2bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt @@ -9,7 +9,6 @@ import android.Manifest import android.content.pm.PackageManager import android.os.Bundle import android.view.View -import android.widget.Toast import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.NavHostFragment @@ -21,9 +20,10 @@ import org.thoughtcrime.securesms.databinding.FragmentRegistrationWelcomeV2Bindi import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel import org.thoughtcrime.securesms.registration.v2.ui.grantpermissions.GrantPermissionsV2Fragment -import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel import org.thoughtcrime.securesms.util.BackupUtil +import org.thoughtcrime.securesms.util.CommunicationActions import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -33,7 +33,6 @@ import kotlin.jvm.optionals.getOrNull * First screen that is displayed on the very first app launch. */ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome_v2) { - private val TAG = Log.tag(WelcomeV2Fragment::class.java) private val sharedViewModel by activityViewModels() private val binding: FragmentRegistrationWelcomeV2Binding by ViewBinderDelegate(FragmentRegistrationWelcomeV2Binding::bind) @@ -44,7 +43,6 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome setDebugLogSubmitMultiTapView(binding.title) binding.welcomeContinueButton.setOnClickListener { onContinueClicked() } binding.welcomeTermsButton.setOnClickListener { onTermsClicked() } - binding.welcomeTransferOrRestore.setOnClickListener { onRestoreFromBackupClicked() } } private fun onContinueClicked() { @@ -65,12 +63,8 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome NavHostFragment.findNavController(this).safeNavigate(WelcomeV2FragmentDirections.actionSkipRestore()) } - private fun onRestoreFromBackupClicked() { - Toast.makeText(requireContext(), "Not yet implemented.", Toast.LENGTH_SHORT).show() - } - private fun onTermsClicked() { - Toast.makeText(requireContext(), "Not yet implemented.", Toast.LENGTH_SHORT).show() + CommunicationActions.openBrowserLink(requireContext(), TERMS_AND_CONDITIONS_URL) } private fun maybePrefillE164() { @@ -87,4 +81,9 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome Log.i(TAG, "No phone permission.") } } + + companion object { + private val TAG = Log.tag(WelcomeV2Fragment::class.java) + private const val TERMS_AND_CONDITIONS_URL = "https://signal.org/legal" + } } diff --git a/app/src/main/res/layout/fragment_registration_welcome_v2.xml b/app/src/main/res/layout/fragment_registration_welcome_v2.xml index 5f5a350be4..e4914c5f42 100644 --- a/app/src/main/res/layout/fragment_registration_welcome_v2.xml +++ b/app/src/main/res/layout/fragment_registration_welcome_v2.xml @@ -55,6 +55,7 @@ app:layout_goneMarginBottom="@dimen/registration_button_bottom_margin" app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal" /> + Date: Mon, 22 Apr 2024 09:59:57 -0400 Subject: [PATCH 056/113] Only display "Processing" text on outgoing media. --- .../components/transfercontrols/TransferControlView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt index 5cf6a69700..b6cd842532 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt @@ -687,7 +687,7 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att } Mode.DOWNLOADING_GALLERY, Mode.DOWNLOADING_SINGLE_ITEM, Mode.DOWNLOADING_VIDEO_PLAYABLE, Mode.UPLOADING_GALLERY, Mode.UPLOADING_SINGLE_ITEM -> { - if (currentState.networkProgress.sumCompleted() == 0L || isCompressing(currentState)) { + if (currentState.isUpload && (currentState.networkProgress.sumCompleted() == 0L || isCompressing(currentState))) { binding.secondaryDetailsText.updateLayoutParams { width = ViewGroup.LayoutParams.WRAP_CONTENT } From 34dbd11db0ac506eb19ca020ab1868b03b4b2a45 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 22 Apr 2024 10:26:00 -0400 Subject: [PATCH 057/113] Update file format for backupV2. --- .../backup/v2/stream/EncryptedBackupReader.kt | 13 +++++---- .../backup/v2/stream/EncryptedBackupWriter.kt | 10 +++++-- .../stream/EncryptedBackupReaderWriterTest.kt | 28 +++++++++++++++++-- .../api/SignalServiceMessageReceiver.java | 2 +- .../signalservice/api/backup/BackupKey.kt | 27 ++++++++++-------- .../crypto/AttachmentCipherInputStream.java | 2 +- .../api/crypto/AttachmentCipherTest.java | 10 +++---- 7 files changed, 65 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReader.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReader.kt index 1c597850b6..7031bca71f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReader.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReader.kt @@ -41,18 +41,21 @@ class EncryptedBackupReader( val stream: InputStream init { - val keyMaterial = key.deriveSecrets(aci) + val keyMaterial = key.deriveBackupSecrets(aci) + + validateMac(keyMaterial.macKey, streamLength, dataStream()) + + val inputStream = dataStream() + val iv = inputStream.readNBytesOrThrow(16) val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding").apply { - init(Cipher.DECRYPT_MODE, SecretKeySpec(keyMaterial.cipherKey, "AES"), IvParameterSpec(keyMaterial.iv)) + init(Cipher.DECRYPT_MODE, SecretKeySpec(keyMaterial.cipherKey, "AES"), IvParameterSpec(iv)) } - validateMac(keyMaterial.macKey, streamLength, dataStream()) - stream = GZIPInputStream( CipherInputStream( TruncatingInputStream( - wrapped = dataStream(), + wrapped = inputStream, maxBytes = streamLength - MAC_SIZE ), cipher diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt index 97828c6f9e..fcbb64d904 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt @@ -9,6 +9,7 @@ import org.signal.core.util.stream.MacOutputStream import org.signal.core.util.writeVarInt32 import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.thoughtcrime.securesms.util.Util import org.whispersystems.signalservice.api.backup.BackupKey import org.whispersystems.signalservice.api.push.ServiceId.ACI import java.io.IOException @@ -36,14 +37,19 @@ class EncryptedBackupWriter( private val macStream: MacOutputStream init { - val keyMaterial = key.deriveSecrets(aci) + val keyMaterial = key.deriveBackupSecrets(aci) + + val iv: ByteArray = Util.getSecretBytes(16) + outputStream.write(iv) + outputStream.flush() val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding").apply { - init(Cipher.ENCRYPT_MODE, SecretKeySpec(keyMaterial.cipherKey, "AES"), IvParameterSpec(keyMaterial.iv)) + init(Cipher.ENCRYPT_MODE, SecretKeySpec(keyMaterial.cipherKey, "AES"), IvParameterSpec(iv)) } val mac = Mac.getInstance("HmacSHA256").apply { init(SecretKeySpec(keyMaterial.macKey, "HmacSHA256")) + update(iv) } macStream = MacOutputStream(outputStream, mac) diff --git a/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt b/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt index 7692ea5e39..f9849b3b61 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReaderWriterTest.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.backup.v2.stream import org.junit.Assert.assertEquals import org.junit.Test +import org.signal.core.util.Base64 import org.signal.core.util.Hex import org.thoughtcrime.securesms.backup.v2.proto.AccountData import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo @@ -56,7 +57,7 @@ class EncryptedBackupReaderWriterTest { val key = BackupKey(Util.getSecretBytes(32)) val aci = ACI.from(UUID.randomUUID()) - val sizes = (1..10) + val uniqueSizes = (1..10) .map { frameCount -> val outputStream = ByteArrayOutputStream() @@ -72,6 +73,29 @@ class EncryptedBackupReaderWriterTest { } .toSet() - assertEquals(1, sizes.size) + assertEquals(1, uniqueSizes.size) + } + + @Test + fun `using a different IV every time`() { + val key = BackupKey(Util.getSecretBytes(32)) + val aci = ACI.from(UUID.randomUUID()) + val count = 10 + + val uniqueOutputs = (0 until count) + .map { + val outputStream = ByteArrayOutputStream() + + EncryptedBackupWriter(key, aci, outputStream, append = { outputStream.write(it) }).use { writer -> + writer.write(BackupInfo(version = 1, backupTimeMs = 1000L)) + writer.write(Frame(account = AccountData(username = "static-data"))) + } + + outputStream.toByteArray() + } + .map { Base64.encodeWithPadding(it) } + .toSet() + + assertEquals(count, uniqueOutputs.size) } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java index 4c37b55cb7..d004801ade 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java @@ -184,7 +184,7 @@ public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, Fi * * @return An InputStream that streams the plaintext attachment contents. */ - public InputStream retrieveArchivedAttachment(@Nonnull BackupKey.KeyMaterial archivedMediaKeyMaterial, + public InputStream retrieveArchivedAttachment(@Nonnull BackupKey.MediaKeyMaterial archivedMediaKeyMaterial, @Nonnull Map readCredentialHeaders, @Nonnull File archiveDestination, @Nonnull SignalServiceAttachmentPointer pointer, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt index aed7e88af9..8c05a55eea 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/backup/BackupKey.kt @@ -22,16 +22,15 @@ class BackupKey(val value: ByteArray) { ) } - fun deriveSecrets(aci: ACI): KeyMaterial { + fun deriveBackupSecrets(aci: ACI): BackupKeyMaterial { val backupId = deriveBackupId(aci) val extendedKey = HKDF.deriveSecrets(this.value, backupId.value, "20231003_Signal_Backups_EncryptMessageBackup".toByteArray(), 80) - return KeyMaterial( + return BackupKeyMaterial( id = backupId, macKey = extendedKey.copyOfRange(0, 32), - cipherKey = extendedKey.copyOfRange(32, 64), - iv = extendedKey.copyOfRange(64, 80) + cipherKey = extendedKey.copyOfRange(32, 64) ) } @@ -39,14 +38,14 @@ class BackupKey(val value: ByteArray) { return MediaId(HKDF.deriveSecrets(value, mediaName.toByteArray(), "Media ID".toByteArray(), 15)) } - fun deriveMediaSecrets(mediaName: MediaName): KeyMaterial { + fun deriveMediaSecrets(mediaName: MediaName): MediaKeyMaterial { return deriveMediaSecrets(deriveMediaId(mediaName)) } - fun deriveMediaSecrets(mediaId: MediaId): KeyMaterial { + private fun deriveMediaSecrets(mediaId: MediaId): MediaKeyMaterial { val extendedKey = HKDF.deriveSecrets(this.value, mediaId.value, "20231003_Signal_Backups_EncryptMedia".toByteArray(), 80) - return KeyMaterial( + return MediaKeyMaterial( id = mediaId, macKey = extendedKey.copyOfRange(0, 32), cipherKey = extendedKey.copyOfRange(32, 64), @@ -54,16 +53,22 @@ class BackupKey(val value: ByteArray) { ) } - class KeyMaterial ( - val id: Id, + class BackupKeyMaterial( + val id: BackupId, + val macKey: ByteArray, + val cipherKey: ByteArray + ) + + class MediaKeyMaterial( + val id: MediaId, val macKey: ByteArray, val cipherKey: ByteArray, val iv: ByteArray ) { companion object { @JvmStatic - fun forMedia(id: ByteArray, keyMac: ByteArray, iv: ByteArray): KeyMaterial { - return KeyMaterial( + fun forMedia(id: ByteArray, keyMac: ByteArray, iv: ByteArray): MediaKeyMaterial { + return MediaKeyMaterial( MediaId(id), keyMac.copyOfRange(32, 64), keyMac.copyOfRange(0, 32), diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java index 66857a07aa..0ad0447092 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java @@ -110,7 +110,7 @@ public static InputStream createForAttachment(File file, long plaintextLength, b /** * Decrypt archived media to it's original attachment encrypted blob. */ - public static InputStream createForArchivedMedia(BackupKey.KeyMaterial archivedMediaKeyMaterial, File file, long originalCipherTextLength) + public static InputStream createForArchivedMedia(BackupKey.MediaKeyMaterial archivedMediaKeyMaterial, File file, long originalCipherTextLength) throws InvalidMessageException, IOException { try { diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java index 9fa91212ee..4ad0ea38e3 100644 --- a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java @@ -93,7 +93,7 @@ public void attachment_decryptFailOnBadKey() throws IOException { @Test public void archive_encryptDecrypt() throws IOException, InvalidMessageException { byte[] key = Util.getSecretBytes(64); - BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); byte[] plaintextInput = "Peter Parker".getBytes(); EncryptResult encryptResult = encryptData(plaintextInput, key, false); File cipherFile = writeToFile(encryptResult.ciphertext); @@ -108,7 +108,7 @@ public void archive_encryptDecrypt() throws IOException, InvalidMessageException @Test public void archive_encryptDecryptEmpty() throws IOException, InvalidMessageException { byte[] key = Util.getSecretBytes(64); - BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); byte[] plaintextInput = "".getBytes(); EncryptResult encryptResult = encryptData(plaintextInput, key, false); File cipherFile = writeToFile(encryptResult.ciphertext); @@ -128,7 +128,7 @@ public void archive_decryptFailOnBadKey() throws IOException { try { byte[] key = Util.getSecretBytes(64); byte[] badKey = Util.getSecretBytes(64); - BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), badKey, Util.getSecretBytes(16)); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), badKey, Util.getSecretBytes(16)); byte[] plaintextInput = "Gwen Stacy".getBytes(); EncryptResult encryptResult = encryptData(plaintextInput, key, false); @@ -270,7 +270,7 @@ public void archive_encryptDecryptPaddedContent() throws IOException, InvalidMes File cipherFile = writeToFile(encryptedData); - BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); InputStream decryptedStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, length); byte[] plaintextOutput = readInputStreamFully(decryptedStream); @@ -348,7 +348,7 @@ public void archive_decryptFailOnBadMac() throws IOException { cipherFile = writeToFile(badMacCiphertext); - BackupKey.KeyMaterial keyMaterial = BackupKey.KeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); fail(); } catch (InvalidMessageException e) { From f3669a5865d4d7c43c05dce1b2430e159e4fb759 Mon Sep 17 00:00:00 2001 From: Clark Date: Mon, 22 Apr 2024 10:31:20 -0400 Subject: [PATCH 058/113] Fix message extra column not being restored properly. --- .../securesms/backup/v2/database/ChatItemImportInserter.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 161fc3e9f0..e735f62060 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 @@ -102,7 +102,8 @@ class ChatItemImportInserter( MessageTable.SHARED_CONTACTS, MessageTable.LINK_PREVIEWS, MessageTable.MESSAGE_RANGES, - MessageTable.VIEW_ONCE + MessageTable.VIEW_ONCE, + MessageTable.MESSAGE_EXTRAS ) private val REACTION_COLUMNS = arrayOf( From a64a02fa0c37af565c4783e3c64905da69c7cd9f Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 22 Apr 2024 11:55:20 -0400 Subject: [PATCH 059/113] Fix issue where structured contact name syncing was delayed. --- .../securesms/jobs/SyncSystemContactLinksJob.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob.kt index 518dd7ccca..dd677e3766 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob.kt @@ -11,10 +11,12 @@ import org.signal.core.util.Stopwatch import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.BuildConfig import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter +import org.thoughtcrime.securesms.storage.StorageSyncHelper import java.lang.Exception /** @@ -71,6 +73,11 @@ class SyncSystemContactLinksJob private constructor(parameters: Parameters) : Ba ) stopwatch.split("add-links") + // Adding links changes how certain structured name records are stored, so we need to re-sync to make sure we get the latest structured name + ContactDiscovery.syncRecipientInfoWithSystemContacts(context) + StorageSyncHelper.scheduleSyncForDataChange() + stopwatch.split("sync-contact-info") + stopwatch.stop(TAG) } catch (e: RemoteException) { Log.w(TAG, "[addSystemContactLinks] Failed to add links to contacts.", e) From 475ca50fab85eed8f4caa7b111ecd3f96b11202b Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 22 Apr 2024 13:17:19 -0400 Subject: [PATCH 060/113] Fix missing local participant state changes in group calls bug. --- .../securesms/events/WebRtcViewModel.kt | 5 ++- .../webrtc/GroupConnectedActionProcessor.java | 34 ++++++++----------- .../service/webrtc/state/CallInfoState.kt | 1 - .../service/webrtc/state/LocalDeviceState.kt | 6 ++-- .../state/WebRtcServiceStateBuilder.java | 10 +++--- 5 files changed, 25 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt index 58e5559b6f..ee133e640c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.events import com.annimon.stream.OptionalLong import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink -import org.thoughtcrime.securesms.events.CallParticipant.Companion.HAND_LOWERED import org.thoughtcrime.securesms.events.CallParticipant.Companion.createLocal import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId @@ -113,11 +112,11 @@ class WebRtcViewModel(state: WebRtcServiceState) { val availableDevices: Set = state.localDeviceState.availableDevices val bluetoothPermissionDenied: Boolean = state.localDeviceState.bluetoothPermissionDenied - val localParticipant: CallParticipant = state.callInfoState.localParticipant ?: createLocal( + val localParticipant: CallParticipant = createLocal( state.localDeviceState.cameraState, (if (state.videoState.localSink != null) state.videoState.localSink else BroadcastVideoSink())!!, state.localDeviceState.isMicrophoneEnabled, - HAND_LOWERED + state.localDeviceState.handRaisedTimestamp ) val isCellularConnection: Boolean = when (state.localDeviceState.networkConnectionType) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java index f91451d7cb..773f4243d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java @@ -260,10 +260,10 @@ private GroupCallReactionEvent createGroupCallReaction(Collection raisedHands) { Log.i(TAG, "handleGroupCallRaisedHand():"); - boolean playSound = !raisedHands.isEmpty(); - long now = System.currentTimeMillis(); - WebRtcServiceStateBuilder.CallInfoStateBuilder builder = currentState.builder().changeCallInfoState(); - Long localDemuxId = currentState.getCallInfoState().requireGroupCall().getLocalDeviceState().getDemuxId(); + boolean playSound = !raisedHands.isEmpty(); + long now = System.currentTimeMillis(); + WebRtcServiceStateBuilder.CallInfoStateBuilder callInfoBuilder = currentState.builder().changeCallInfoState(); + Long localDemuxId = currentState.getCallInfoState().requireGroupCall().getLocalDeviceState().getDemuxId(); List participants = currentState.getCallInfoState().getRemoteCallParticipants(); @@ -276,31 +276,25 @@ private GroupCallReactionEvent createGroupCallReaction(Collection= 0 && !wasHandAlreadyRaised) { - builder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(now + raisedHandIndex)); + callInfoBuilder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(now + raisedHandIndex)); } else if (raisedHandIndex < 0 && wasHandAlreadyRaised) { - builder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(CallParticipant.HAND_LOWERED)); + callInfoBuilder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(CallParticipant.HAND_LOWERED)); } } + currentState = callInfoBuilder.build(); + if (localDemuxId != null) { - if (raisedHands.contains(localDemuxId)) { - builder.setLocalParticipant(CallParticipant.createLocal(currentState.getLocalDeviceState().getCameraState(), - currentState.getVideoState().requireLocalSink(), - currentState.getLocalDeviceState().isMicrophoneEnabled(), - now, - new CallParticipantId(localDemuxId, Recipient.self().getId()))); - } else { - builder.setLocalParticipant(CallParticipant.createLocal(currentState.getLocalDeviceState().getCameraState(), - currentState.getVideoState().requireLocalSink(), - currentState.getLocalDeviceState().isMicrophoneEnabled(), - CallParticipant.HAND_LOWERED, - new CallParticipantId(localDemuxId, Recipient.self().getId()))); - } + currentState = currentState.builder() + .changeLocalDeviceState() + .setHandRaisedTimestamp(raisedHands.contains(localDemuxId) ? now : CallParticipant.HAND_LOWERED) + .build(); } + if (playSound) { webRtcInteractor.playStateChangeUp(); } - return builder.build(); + return currentState; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt index 81a8be220e..511aa45993 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt @@ -22,7 +22,6 @@ data class CallInfoState( var callRecipient: Recipient = Recipient.UNKNOWN, var callConnectedTime: Long = -1, @get:JvmName("getRemoteCallParticipantsMap") var remoteParticipants: MutableMap = mutableMapOf(), - var localParticipant: CallParticipant? = null, var peerMap: MutableMap = mutableMapOf(), var activePeer: RemotePeer? = null, var groupCall: GroupCall? = null, diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt index 05300c71f6..82422bd3e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.service.webrtc.state import org.thoughtcrime.securesms.components.sensors.Orientation +import org.thoughtcrime.securesms.events.CallParticipant import org.thoughtcrime.securesms.ringrtc.CameraState import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager import org.webrtc.PeerConnection @@ -8,7 +9,7 @@ import org.webrtc.PeerConnection /** * Local device specific state. */ -data class LocalDeviceState constructor( +data class LocalDeviceState( var cameraState: CameraState = CameraState.UNKNOWN, var isMicrophoneEnabled: Boolean = true, var orientation: Orientation = Orientation.PORTRAIT_BOTTOM_EDGE, @@ -17,7 +18,8 @@ data class LocalDeviceState constructor( var activeDevice: SignalAudioManager.AudioDevice = SignalAudioManager.AudioDevice.NONE, var availableDevices: Set = emptySet(), var bluetoothPermissionDenied: Boolean = false, - var networkConnectionType: PeerConnection.AdapterType = PeerConnection.AdapterType.UNKNOWN + var networkConnectionType: PeerConnection.AdapterType = PeerConnection.AdapterType.UNKNOWN, + var handRaisedTimestamp: Long = CallParticipant.HAND_LOWERED ) { fun duplicate(): LocalDeviceState { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java index 714e6c05b6..773c21fed6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java @@ -137,6 +137,11 @@ public LocalDeviceStateBuilder() { toBuild.setNetworkConnectionType(type); return this; } + + public @NonNull LocalDeviceStateBuilder setHandRaisedTimestamp(long handRaisedTimestamp) { + toBuild.setHandRaisedTimestamp(handRaisedTimestamp); + return this; + } } public class CallSetupStateBuilder { @@ -364,10 +369,5 @@ public CallInfoStateBuilder() { toBuild.setCallLinkDisconnectReason(callLinkDisconnectReason); return this; } - - public @NonNull CallInfoStateBuilder setLocalParticipant(@NonNull CallParticipant callParticipant) { - toBuild.setLocalParticipant(callParticipant); - return this; - } } } From 0f4618ab11e95b8c0ddc1175a46bf7a66c5c54a1 Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Mon, 22 Apr 2024 14:53:52 -0400 Subject: [PATCH 061/113] Remove link preview images from shared media. --- .../thoughtcrime/securesms/database/MediaTable.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt index 87ac4bd225..f8fea47e96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt @@ -97,7 +97,8 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD """ ${AttachmentTable.DATA_FILE} IS NOT NULL AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/svg%' AND - (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') + (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') AND + ${MessageTable.LINK_PREVIEWS} IS NULL """ ) @@ -106,7 +107,8 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD """ (${AttachmentTable.DATA_FILE} IS NOT NULL OR (${AttachmentTable.CONTENT_TYPE} LIKE 'video/%' AND ${AttachmentTable.REMOTE_INCREMENTAL_DIGEST} IS NOT NULL)) AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/svg%' AND - (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') + (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') AND + ${MessageTable.LINK_PREVIEWS} IS NULL """ ) @@ -118,7 +120,14 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD """ ) - private val ALL_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, "${AttachmentTable.DATA_FILE} IS NOT NULL AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'text/x-signal-plain'") + private val ALL_MEDIA_QUERY = String.format( + BASE_MEDIA_QUERY, + """ + ${AttachmentTable.DATA_FILE} IS NOT NULL AND + ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'text/x-signal-plain' AND + ${MessageTable.LINK_PREVIEWS} IS NULL + """ + ) private val DOCUMENT_MEDIA_QUERY = String.format( BASE_MEDIA_QUERY, From 4bcab49539af80c3c1b6d36470d5fab38cdd8af0 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Tue, 23 Apr 2024 10:26:37 -0400 Subject: [PATCH 062/113] Correct UnopinionatedResponseCodeHandler constant name. --- .../internal/push/PushServiceSocket.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 c3425b118c..83c60f509d 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 @@ -325,9 +325,9 @@ public class PushServiceSocket { private static final String CALL_LINK_CREATION_AUTH = "/v1/call-link/create-auth"; private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp"; - private static final Map NO_HEADERS = Collections.emptyMap(); - private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler(); - private static final ResponseCodeHandler UNOPINIONATED_HANDER = new UnopinionatedResponseCodeHandler(); + private static final Map NO_HEADERS = Collections.emptyMap(); + private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler(); + private static final ResponseCodeHandler UNOPINIONATED_HANDLER = new UnopinionatedResponseCodeHandler(); private static final long CDN2_RESUMABLE_LINK_LIFETIME_MILLIS = TimeUnit.DAYS.toMillis(7); @@ -495,14 +495,14 @@ public ArchiveServiceCredentialsResponse getArchiveCredentials(long currentTime) long secondsRoundedToNearestDay = TimeUnit.DAYS.toSeconds(TimeUnit.MILLISECONDS.toDays(currentTime)); long endTimeInSeconds = secondsRoundedToNearestDay + TimeUnit.DAYS.toSeconds(7); - String response = makeServiceRequest(String.format(Locale.US, ARCHIVE_CREDENTIALS, secondsRoundedToNearestDay, endTimeInSeconds), "GET", null, NO_HEADERS, UNOPINIONATED_HANDER, Optional.empty()); + String response = makeServiceRequest(String.format(Locale.US, ARCHIVE_CREDENTIALS, secondsRoundedToNearestDay, endTimeInSeconds), "GET", null, NO_HEADERS, UNOPINIONATED_HANDLER, Optional.empty()); return JsonUtil.fromJson(response, ArchiveServiceCredentialsResponse.class); } public void setArchiveBackupId(BackupAuthCredentialRequest request) throws IOException { String body = JsonUtil.toJson(new ArchiveSetBackupIdRequest(request)); - makeServiceRequest(ARCHIVE_BACKUP_ID, "PUT", body, NO_HEADERS, UNOPINIONATED_HANDER, Optional.empty()); + makeServiceRequest(ARCHIVE_BACKUP_ID, "PUT", body, NO_HEADERS, UNOPINIONATED_HANDLER, Optional.empty()); } public void setArchivePublicKey(ECPublicKey publicKey, ArchiveCredentialPresentation credentialPresentation) throws IOException { @@ -556,7 +556,7 @@ public ArchiveGetMediaItemsResponse getArchiveMediaItemsPage(ArchiveCredentialPr public ArchiveMediaResponse archiveAttachmentMedia(@Nonnull ArchiveCredentialPresentation credentialPresentation, @Nonnull ArchiveMediaRequest request) throws IOException { Map headers = credentialPresentation.toHeaders(); - String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA, "PUT", JsonUtil.toJson(request), headers, UNOPINIONATED_HANDER); + String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA, "PUT", JsonUtil.toJson(request), headers, UNOPINIONATED_HANDLER); return JsonUtil.fromJson(response, ArchiveMediaResponse.class); } @@ -567,7 +567,7 @@ public ArchiveMediaResponse archiveAttachmentMedia(@Nonnull ArchiveCredentialPre public BatchArchiveMediaResponse archiveAttachmentMedia(@Nonnull ArchiveCredentialPresentation credentialPresentation, @Nonnull BatchArchiveMediaRequest request) throws IOException { Map headers = credentialPresentation.toHeaders(); - String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA_BATCH, "PUT", JsonUtil.toJson(request), headers, UNOPINIONATED_HANDER); + String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA_BATCH, "PUT", JsonUtil.toJson(request), headers, UNOPINIONATED_HANDLER); return JsonUtil.fromJson(response, BatchArchiveMediaResponse.class); } From f82bd64c102f935b1952880384a982e1bf19148e Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 23 Apr 2024 10:28:07 -0400 Subject: [PATCH 063/113] Copy inbound attachments to archive service. --- .../thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt index 090564ffb4..a95ed497fe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt @@ -175,6 +175,13 @@ class AttachmentDownloadJob private constructor( Cdn.S3 -> retrieveAttachmentForReleaseChannel(messageId, attachmentId, attachment) else -> retrieveAttachment(messageId, attachmentId, attachment) } + + if ((attachment.cdn == Cdn.CDN_2 || attachment.cdn == Cdn.CDN_3) && + attachment.archiveMediaId == null && + SignalStore.backup().canReadWriteToArchiveCdn + ) { + ApplicationDependencies.getJobManager().add(ArchiveAttachmentJob(attachmentId)) + } } override fun onFailure() { From 8fe66a14c5195ef26419747442af8dd21f68af0f Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 11:14:58 -0400 Subject: [PATCH 064/113] Fix multi-window camera crash. --- .../v2/images/MediaReviewImagePageFragment.kt | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/images/MediaReviewImagePageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/images/MediaReviewImagePageFragment.kt index f7c56ad59c..145acd3041 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/images/MediaReviewImagePageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/images/MediaReviewImagePageFragment.kt @@ -24,10 +24,10 @@ private val MODE_DELAY = TimeUnit.MILLISECONDS.toMillis(300) */ class MediaReviewImagePageFragment : Fragment(R.layout.fragment_container), ImageEditorFragment.Controller { - private lateinit var imageEditorFragment: ImageEditorFragment - private val sharedViewModel: MediaSelectionViewModel by viewModels(ownerProducer = { requireActivity() }) - private lateinit var hudCommandDisposable: Disposable + + private var imageEditorFragment: ImageEditorFragment? = null + private var hudCommandDisposable: Disposable = Disposable.disposed() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { imageEditorFragment = ensureImageEditorFragment() @@ -49,7 +49,7 @@ class MediaReviewImagePageFragment : Fragment(R.layout.fragment_container), Imag sharedViewModel.setTouchEnabled(false) requireView().postDelayed( { - imageEditorFragment.setMode(ImageEditorHudV2.Mode.DRAW) + imageEditorFragment?.setMode(ImageEditorHudV2.Mode.DRAW) }, MODE_DELAY ) @@ -58,12 +58,12 @@ class MediaReviewImagePageFragment : Fragment(R.layout.fragment_container), Imag sharedViewModel.setTouchEnabled(false) requireView().postDelayed( { - imageEditorFragment.setMode(ImageEditorHudV2.Mode.CROP) + imageEditorFragment?.setMode(ImageEditorHudV2.Mode.CROP) }, MODE_DELAY ) } - HudCommand.SaveMedia -> imageEditorFragment.onSave() + HudCommand.SaveMedia -> imageEditorFragment?.onSave() else -> Unit } } @@ -73,7 +73,9 @@ class MediaReviewImagePageFragment : Fragment(R.layout.fragment_container), Imag override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - sharedViewModel.setEditorState(requireUri(), requireNotNull(imageEditorFragment.saveState())) + imageEditorFragment?.let { + sharedViewModel.setEditorState(requireUri(), requireNotNull(it.saveState())) + } } private fun ensureImageEditorFragment(): ImageEditorFragment { @@ -105,12 +107,12 @@ class MediaReviewImagePageFragment : Fragment(R.layout.fragment_container), Imag if (!needed) { requireView().postDelayed( { - sharedViewModel.setTouchEnabled(!needed) + sharedViewModel.setTouchEnabled(true) }, MODE_DELAY ) } else { - sharedViewModel.setTouchEnabled(!needed) + sharedViewModel.setTouchEnabled(false) } } } @@ -118,10 +120,12 @@ class MediaReviewImagePageFragment : Fragment(R.layout.fragment_container), Imag override fun onRequestFullScreen(fullScreen: Boolean, hideKeyboard: Boolean) = Unit override fun onDoneEditing() { - imageEditorFragment.setMode(ImageEditorHudV2.Mode.NONE) + imageEditorFragment?.setMode(ImageEditorHudV2.Mode.NONE) if (isResumed) { - sharedViewModel.setEditorState(requireUri(), requireNotNull(imageEditorFragment.saveState())) + imageEditorFragment?.let { + sharedViewModel.setEditorState(requireUri(), requireNotNull(it.saveState())) + } } } @@ -141,9 +145,9 @@ class MediaReviewImagePageFragment : Fragment(R.layout.fragment_container), Imag val data = sharedViewModel.getEditorState(requireUri()) as? ImageEditorFragment.Data if (data != null) { - imageEditorFragment.restoreState(data) + imageEditorFragment?.restoreState(data) } else { - imageEditorFragment.onClearAll() + imageEditorFragment?.onClearAll() } } From 8a972d93e9e7aedaf465bdc93540caa7b24d49a1 Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 23 Apr 2024 11:19:31 -0400 Subject: [PATCH 065/113] Actually use backup jitter in local backups. --- .../org/thoughtcrime/securesms/service/LocalBackupListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java index 26b2ffadba..5877dd24f0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java @@ -52,7 +52,7 @@ public static long setNextBackupTimeToIntervalFromNow(@NonNull Context context) int jitter = (new Random().nextInt(BACKUP_JITTER_WINDOW_SECONDS)) - (BACKUP_JITTER_WINDOW_SECONDS / 2); - next.plusSeconds(jitter); + next = next.plusSeconds(jitter); if (now.isAfter(next)) { next = next.plusDays(1); From b043b6e45890f9b353cceff9af9a8ec9d36479e1 Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 23 Apr 2024 11:19:51 -0400 Subject: [PATCH 066/113] Schedule message backups when enabled. --- .../securesms/ApplicationContext.java | 2 + .../v2/data/ConversationDataSource.kt | 4 +- .../securesms/jobs/BackupMessagesJob.kt | 12 +++- .../securesms/keyvalue/BackupValues.kt | 4 ++ .../service/LocalBackupListener.java | 1 - .../service/MessageBackupListener.kt | 58 +++++++++++++++++++ .../securesms/util/FeatureFlags.java | 2 +- 7 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/service/MessageBackupListener.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index a5dcf5df9e..c3bcddebda 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -88,6 +88,7 @@ import org.thoughtcrime.securesms.service.DirectoryRefreshListener; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.LocalBackupListener; +import org.thoughtcrime.securesms.service.MessageBackupListener; import org.thoughtcrime.securesms.service.RotateSenderCertificateListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.webrtc.ActiveCallManager; @@ -420,6 +421,7 @@ private void initializePeriodicTasks() { RotateSignedPreKeyListener.schedule(this); DirectoryRefreshListener.schedule(this); LocalBackupListener.schedule(this); + MessageBackupListener.schedule(this); RotateSenderCertificateListener.schedule(this); RoutineMessageFetchReceiver.startOrUpdateAlarm(this); AnalyzeDatabaseAlarmListener.schedule(this); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt index 5b7e2d3dbc..c2411d314a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource.kt @@ -10,7 +10,6 @@ import org.signal.core.util.Stopwatch import org.signal.core.util.logging.Log import org.signal.core.util.toInt import org.signal.paging.PagedDataSource -import org.thoughtcrime.securesms.BuildConfig import org.thoughtcrime.securesms.backup.v2.BackupRestoreManager import org.thoughtcrime.securesms.conversation.ConversationData import org.thoughtcrime.securesms.conversation.ConversationMessage @@ -25,6 +24,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel private typealias ConversationElement = MappingModel<*> @@ -125,7 +125,7 @@ class ConversationDataSource( records = MessageDataFetcher.updateModelsWithData(records, extraData).toMutableList() stopwatch.split("models") - if (BuildConfig.MESSAGE_BACKUP_RESTORE_ENABLED && SignalStore.backup().restoreState.inProgress) { + if (FeatureFlags.messageBackups() && SignalStore.backup().restoreState.inProgress) { BackupRestoreManager.prioritizeAttachmentsIfNeeded(records) stopwatch.split("restore") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt index c3bd4080c1..7ffbfe67a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt @@ -30,13 +30,21 @@ class BackupMessagesJob private constructor(parameters: Parameters) : BaseJob(pa private val TAG = Log.tag(BackupMessagesJob::class.java) const val KEY = "BackupMessagesJob" + + const val QUEUE = "BackupMessagesQueue" + + fun enqueue() { + val jobManager = ApplicationDependencies.getJobManager() + jobManager.add(BackupMessagesJob()) + } } constructor() : this( Parameters.Builder() .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(Parameters.UNLIMITED) - .setMaxInstancesForFactory(2) + .setMaxAttempts(3) + .setMaxInstancesForFactory(1) + .setQueue(QUEUE) .build() ) 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 57d0e91bc4..f32f06a27d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -20,6 +20,8 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { private const val KEY_CDN_READ_CREDENTIALS_TIMESTAMP = "backup.cdn.readCredentials.timestamp" private const val KEY_RESTORE_STATE = "backup.restoreState" + private const val KEY_NEXT_BACKUP_TIME = "backup.nextBackupTime" + private const val KEY_CDN_BACKUP_DIRECTORY = "backup.cdn.directory" private const val KEY_CDN_BACKUP_MEDIA_DIRECTORY = "backup.cdn.mediaDirectory" @@ -45,6 +47,8 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { var restoreState: RestoreState by enumValue(KEY_RESTORE_STATE, RestoreState.NONE, RestoreState.serializer) var optimizeStorage: Boolean by booleanValue(KEY_OPTIMIZE_STORAGE, false) + var nextBackupTime: Long by longValue(KEY_NEXT_BACKUP_TIME, -1) + var areBackupsEnabled: Boolean by booleanValue(KEY_BACKUPS_ENABLED, false) /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java index 5877dd24f0..d6fa92b6a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java @@ -6,7 +6,6 @@ import androidx.annotation.NonNull; import org.thoughtcrime.securesms.jobs.LocalBackupJob; -import org.thoughtcrime.securesms.keyvalue.SettingsValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.util.JavaTimeExtensionsKt; import org.thoughtcrime.securesms.util.TextSecurePreferences; diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/MessageBackupListener.kt b/app/src/main/java/org/thoughtcrime/securesms/service/MessageBackupListener.kt new file mode 100644 index 0000000000..ddd95042e8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/MessageBackupListener.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.service + +import android.content.Context +import org.thoughtcrime.securesms.jobs.BackupMessagesJob +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.toMillis +import java.time.LocalDateTime +import java.util.Random +import java.util.concurrent.TimeUnit + +class MessageBackupListener : PersistentAlarmManagerListener() { + override fun shouldScheduleExact(): Boolean { + return true + } + + override fun getNextScheduledExecutionTime(context: Context): Long { + return SignalStore.backup().nextBackupTime + } + + override fun onAlarm(context: Context, scheduledTime: Long): Long { + if (SignalStore.backup().areBackupsEnabled) { + BackupMessagesJob.enqueue() + } + return setNextBackupTimeToIntervalFromNow() + } + + companion object { + private val BACKUP_JITTER_WINDOW_SECONDS = Math.toIntExact(TimeUnit.MINUTES.toSeconds(10)) + + @JvmStatic + fun schedule(context: Context?) { + if (FeatureFlags.messageBackups() && SignalStore.backup().areBackupsEnabled) { + MessageBackupListener().onReceive(context, getScheduleIntent()) + } + } + + fun setNextBackupTimeToIntervalFromNow(): Long { + val now = LocalDateTime.now() + val hour = SignalStore.settings().backupHour + val minute = SignalStore.settings().backupMinute + var next = now.withHour(hour).withMinute(minute).withSecond(0) + val jitter = Random().nextInt(BACKUP_JITTER_WINDOW_SECONDS) - BACKUP_JITTER_WINDOW_SECONDS / 2 + next.plusSeconds(jitter.toLong()) + if (now.isAfter(next)) { + next = next.plusDays(1) + } + val nextTime = next.toMillis() + SignalStore.backup().nextBackupTime = nextTime + return nextTime + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index e99380aa9c..e83ee017fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -741,7 +741,7 @@ public static long linkedDeviceLifespan() { * Note: This feature is in active development and is not intended to currently function. */ public static boolean messageBackups() { - return getBoolean(MESSAGE_BACKUPS, false); + return BuildConfig.MESSAGE_BACKUP_RESTORE_ENABLED || getBoolean(MESSAGE_BACKUPS, false); } /** Whether or not to use the custom CameraX controller class */ From 25b1a814feca3e31023b0b7551f3d8770fe0beca Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 12:03:24 -0400 Subject: [PATCH 067/113] Remove legacy keyword search flag from emoji search infra. --- .../securesms/components/ComposeText.java | 34 +++++++------------ .../ui/inlinequery/InlineQuery.kt | 2 +- .../ui/inlinequery/InlineQueryEmojiResult.kt | 2 +- .../ui/inlinequery/InlineQueryReplacement.kt | 8 ++--- .../ui/inlinequery/InlineQueryViewModel.kt | 9 +++-- .../ui/inlinequery/InlineQueryViewModelV2.kt | 11 +++--- 6 files changed, 27 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java index 8d73f8eded..f54d6b0b50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java @@ -370,16 +370,16 @@ private boolean changeSelectionForPartialMentions(@NonNull Spanned spanned, int } private void doAfterCursorChange(@NonNull Editable text) { - if (enoughToFilter(text, false)) { - performFiltering(text, false); + if (enoughToFilter(text)) { + performFiltering(text); } else { clearInlineQuery(); } } - private void performFiltering(@NonNull Editable text, boolean keywordEmojiSearch) { + private void performFiltering(@NonNull Editable text) { int end = getSelectionEnd(); - QueryStart queryStart = findQueryStart(text, end, keywordEmojiSearch); + QueryStart queryStart = findQueryStart(text, end); int start = queryStart.index; String query = text.subSequence(start, end).toString(); @@ -387,7 +387,7 @@ private void performFiltering(@NonNull Editable text, boolean keywordEmojiSearch if (queryStart.isMentionQuery) { inlineQueryChangedListener.onQueryChanged(new InlineQuery.Mention(query)); } else { - inlineQueryChangedListener.onQueryChanged(new InlineQuery.Emoji(query, keywordEmojiSearch)); + inlineQueryChangedListener.onQueryChanged(new InlineQuery.Emoji(query)); } } } @@ -398,23 +398,23 @@ private void clearInlineQuery() { } } - private boolean enoughToFilter(@NonNull Editable text, boolean keywordEmojiSearch) { + private boolean enoughToFilter(@NonNull Editable text) { int end = getSelectionEnd(); if (end < 0) { return false; } - return findQueryStart(text, end, keywordEmojiSearch).index != -1; + return findQueryStart(text, end).index != -1; } public void replaceTextWithMention(@NonNull String displayName, @NonNull RecipientId recipientId) { - replaceText(createReplacementToken(displayName, recipientId), false); + replaceText(createReplacementToken(displayName, recipientId)); } public void replaceText(@NonNull InlineQueryReplacement replacement) { - replaceText(replacement.toCharSequence(getContext()), replacement.isKeywordSearch()); + replaceText(replacement.toCharSequence(getContext())); } - private void replaceText(@NonNull CharSequence replacement, boolean keywordReplacement) { + private void replaceText(@NonNull CharSequence replacement) { Editable text = getText(); if (text == null) { return; @@ -423,7 +423,7 @@ private void replaceText(@NonNull CharSequence replacement, boolean keywordRepla clearComposingText(); int end = getSelectionEnd(); - int start = findQueryStart(text, end, keywordReplacement).index - (keywordReplacement ? 0 : 1); + int start = findQueryStart(text, end).index - 1; text.replace(start, end, ""); text.insert(start, replacement); @@ -444,17 +444,7 @@ private void replaceText(@NonNull CharSequence replacement, boolean keywordRepla return builder; } - private QueryStart findQueryStart(@NonNull CharSequence text, int inputCursorPosition, boolean keywordEmojiSearch) { - if (keywordEmojiSearch) { - int start = findQueryStart(text, inputCursorPosition, ' '); - if (start == -1 && inputCursorPosition != 0) { - start = 0; - } else if (start == inputCursorPosition) { - start = -1; - } - return new QueryStart(start, false); - } - + private QueryStart findQueryStart(@NonNull CharSequence text, int inputCursorPosition) { QueryStart queryStart = new QueryStart(findQueryStart(text, inputCursorPosition, MENTION_STARTER), true); if (queryStart.index < 0) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQuery.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQuery.kt index b14631d181..09e2b3d1d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQuery.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQuery.kt @@ -5,6 +5,6 @@ package org.thoughtcrime.securesms.conversation.ui.inlinequery */ sealed class InlineQuery(val query: String) { object NoQuery : InlineQuery("") - class Emoji(query: String, val keywordSearch: Boolean) : InlineQuery(query.replace('_', ' ')) + class Emoji(query: String) : InlineQuery(query.replace('_', ' ')) class Mention(query: String) : InlineQuery(query) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryEmojiResult.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryEmojiResult.kt index 4d2bc37b8c..866cb6ba2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryEmojiResult.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryEmojiResult.kt @@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder */ object InlineQueryEmojiResult { - class Model(val canonicalEmoji: String, val preferredEmoji: String, val keywordSearch: Boolean) : MappingModel { + class Model(val canonicalEmoji: String, val preferredEmoji: String) : MappingModel { override fun areItemsTheSame(newItem: Model): Boolean { return canonicalEmoji == newItem.canonicalEmoji } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryReplacement.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryReplacement.kt index 83f2d72c17..09408061f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryReplacement.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryReplacement.kt @@ -10,16 +10,16 @@ import org.thoughtcrime.securesms.recipients.Recipient /** * Encapsulate how to replace a query with a user selected result. */ -sealed class InlineQueryReplacement(@get:JvmName("isKeywordSearch") val keywordSearch: Boolean = false) { - abstract fun toCharSequence(context: Context): CharSequence +sealed interface InlineQueryReplacement { + fun toCharSequence(context: Context): CharSequence - class Emoji(private val emoji: String, keywordSearch: Boolean) : InlineQueryReplacement(keywordSearch) { + class Emoji(private val emoji: String) : InlineQueryReplacement { override fun toCharSequence(context: Context): CharSequence { return emoji } } - class Mention(private val recipient: Recipient, keywordSearch: Boolean) : InlineQueryReplacement(keywordSearch) { + class Mention(private val recipient: Recipient) : InlineQueryReplacement { override fun toCharSequence(context: Context): CharSequence { val builder = SpannableStringBuilder().apply { append(MentionUtil.MENTION_STARTER) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModel.kt index 149725ca22..dd4343d2bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModel.kt @@ -44,7 +44,7 @@ class InlineQueryViewModel( private fun queryEmoji(query: InlineQuery.Emoji): Observable> { return emojiSearchRepository .submitQuery(query.query) - .map { r -> toMappingModels(r, query.keywordSearch) } + .map { r -> toMappingModels(r) } .toObservable() } @@ -52,21 +52,20 @@ class InlineQueryViewModel( when (model) { is InlineQueryEmojiResult.Model -> { recentEmojis.onCodePointSelected(model.preferredEmoji) - selectionSubject.onNext(InlineQueryReplacement.Emoji(model.preferredEmoji, model.keywordSearch)) + selectionSubject.onNext(InlineQueryReplacement.Emoji(model.preferredEmoji)) } } } companion object { - fun toMappingModels(emojiWithLabels: List, keywordSearch: Boolean): List { + fun toMappingModels(emojiWithLabels: List): List { val emojiValues = SignalStore.emojiValues() return emojiWithLabels .distinct() .map { emoji -> InlineQueryEmojiResult.Model( canonicalEmoji = emoji, - preferredEmoji = emojiValues.getPreferredVariation(emoji), - keywordSearch = keywordSearch + preferredEmoji = emojiValues.getPreferredVariation(emoji) ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModelV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModelV2.kt index 6939d52bbc..48a247bdb8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModelV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryViewModelV2.kt @@ -54,7 +54,7 @@ class InlineQueryViewModelV2( private fun queryEmoji(query: InlineQuery.Emoji): Observable { return emojiSearchRepository .submitQuery(query.query) - .map { r -> if (r.isEmpty()) None else EmojiResults(toMappingModels(r, query.keywordSearch)) } + .map { r -> if (r.isEmpty()) None else EmojiResults(toMappingModels(r)) } .toObservable() } @@ -77,10 +77,10 @@ class InlineQueryViewModelV2( when (model) { is InlineQueryEmojiResult.Model -> { recentEmojis.onCodePointSelected(model.preferredEmoji) - selectionSubject.onNext(InlineQueryReplacement.Emoji(model.preferredEmoji, model.keywordSearch)) + selectionSubject.onNext(InlineQueryReplacement.Emoji(model.preferredEmoji)) } is MentionViewState -> { - selectionSubject.onNext(InlineQueryReplacement.Mention(model.recipient, false)) + selectionSubject.onNext(InlineQueryReplacement.Mention(model.recipient)) } } } @@ -90,15 +90,14 @@ class InlineQueryViewModelV2( } companion object { - fun toMappingModels(emojiWithLabels: List, keywordSearch: Boolean): List { + fun toMappingModels(emojiWithLabels: List): List { val emojiValues = SignalStore.emojiValues() return emojiWithLabels .distinct() .map { emoji -> InlineQueryEmojiResult.Model( canonicalEmoji = emoji, - preferredEmoji = emojiValues.getPreferredVariation(emoji), - keywordSearch = keywordSearch + preferredEmoji = emojiValues.getPreferredVariation(emoji) ) } } From 9b5387e221fc623c4453b163a6ef34cd70eb25d6 Mon Sep 17 00:00:00 2001 From: moiseev-signal <122060238+moiseev-signal@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:20:27 -0700 Subject: [PATCH 068/113] Upgrade to libsignal 0.45.0 --- dependencies.gradle.kts | 2 +- gradle/verification-metadata.xml | 20 +++++++++---------- .../signalservice/api/archive/ArchiveApi.kt | 5 +++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dependencies.gradle.kts b/dependencies.gradle.kts index 23258f7a48..0207db190e 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.8.10") - version("libsignal-client", "0.44.0") + version("libsignal-client", "0.45.0") version("mp4parser", "1.9.39") version("android-gradle-plugin", "8.0.2") version("accompanist", "0.28.0") diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 4b88b89e2f..1d268cb668 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5703,20 +5703,20 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + - - - + + + - - + + diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt index e66596915b..54aba96445 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt @@ -18,6 +18,7 @@ import org.whispersystems.signalservice.api.backup.BackupKey import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.internal.push.PushServiceSocket import java.io.InputStream +import java.time.Instant /** * Class to interact with various archive-related endpoints. @@ -238,8 +239,8 @@ class ArchiveApi( return backupRequestContext.receiveResponse( backupAuthResponse, - backupServerPublicParams, - 201 + Instant.ofEpochMilli(serviceCredential.redemptionTime), + backupServerPublicParams ) } From d561a1385ca4079bd0793488517f36b46256d81b Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 23 Apr 2024 12:28:18 -0400 Subject: [PATCH 069/113] Fix extremely long emoji search crash. --- .../securesms/components/ComposeText.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java index f54d6b0b50..80a3ad3e8d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java @@ -57,9 +57,9 @@ public class ComposeText extends EmojiEditText { - private static final char EMOJI_STARTER = ':'; - - private static final Pattern TIME_PATTERN = Pattern.compile("^[0-9]{1,2}:[0-9]{1,2}$"); + private static final char EMOJI_STARTER = ':'; + private static final int MAX_QUERY_LENGTH = 64; + private static final Pattern TIME_PATTERN = Pattern.compile("^[0-9]{1,2}:[0-9]{1,2}$"); private CharSequence hint; private MentionRendererDelegate mentionRendererDelegate; @@ -370,7 +370,7 @@ private boolean changeSelectionForPartialMentions(@NonNull Spanned spanned, int } private void doAfterCursorChange(@NonNull Editable text) { - if (enoughToFilter(text)) { + if (canFilter(text)) { performFiltering(text); } else { clearInlineQuery(); @@ -398,12 +398,14 @@ private void clearInlineQuery() { } } - private boolean enoughToFilter(@NonNull Editable text) { + private boolean canFilter(@NonNull Editable text) { int end = getSelectionEnd(); if (end < 0) { return false; } - return findQueryStart(text, end).index != -1; + + QueryStart start = findQueryStart(text, end); + return start.index != -1 && ((end - start.index) <= MAX_QUERY_LENGTH); } public void replaceTextWithMention(@NonNull String displayName, @NonNull RecipientId recipientId) { From 9117a1573600269ecbc6542cafd1930f5ec7f925 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Mon, 22 Apr 2024 18:58:05 +0200 Subject: [PATCH 070/113] Implement gradle task for Mollyify translations --- app/build.gradle.kts | 2 +- app/src/main/res/values/strings.xml | 242 +++++++++--------- .../plugins/src/main/java/molly.gradle.kts | 78 ++++++ .../plugins/src/main/java/translations.gradle | 128 --------- 4 files changed, 200 insertions(+), 250 deletions(-) create mode 100644 build-logic/plugins/src/main/java/molly.gradle.kts delete mode 100644 build-logic/plugins/src/main/java/translations.gradle diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 007302c532..b12aeb6c65 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,7 +9,7 @@ plugins { id("app.cash.exhaustive") id("kotlin-parcelize") id("com.squareup.wire") - id("translations") + id("molly") id("licenses") } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8c18177393..5bfea0abca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,12 +38,12 @@ \+%d - Molly is updating… + Molly is updating… You haven\'t set a passphrase yet! Disable passphrase? - This will permanently unlock Molly and message notifications. + This will permanently unlock Molly and message notifications. Disable Error connecting to server! PINs are required for registration lock. To disable PINs, please first disable registration lock. @@ -80,15 +80,15 @@ File Contact Location - Molly needs permission to show your photos and videos. + Molly needs permission to show your photos and videos. Give Access Payment Can\'t find an app to select media. - Molly requires the Storage permission in order to attach photos, videos, or audio, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Storage\". - Molly requires Contacts permission in order to attach contact information, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Contacts\". - Molly requires Location permission in order to attach a location, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Location\". + Molly requires the Storage permission in order to attach photos, videos, or audio, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Storage\". + Molly requires Contacts permission in order to attach contact information, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Contacts\". + Molly requires Location permission in order to attach a location, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Location\". %1$s hasn\'t activated Payments @@ -222,7 +222,7 @@ No Signal contacts You can only use the camera button to send photos to Signal contacts. Can\'t find who you\'re looking for? - Invite a contact to join Molly + Invite a contact to join Molly Search @@ -236,12 +236,12 @@ No thanks - Update Molly + Update Molly This version of the app is no longer supported. To continue sending and receiving messages, update to the latest version. Update Don\'t Update Warning - Your version of Signal has expired. You can view your message history but you won\'t be able to send or receive messages until you update. + Your version of Molly has expired. You can view your message history but you won\'t be able to send or receive messages until you update. No web browser found. @@ -372,7 +372,7 @@ Signal message Send message - Let\'s switch to Molly %1$s + Let\'s switch to Molly %1$s Please choose a contact Attachment exceeds size limits for the type of message you\'re sending. Unable to record audio! @@ -386,15 +386,15 @@ Your request to join has been sent to the group admin. You\'ll be notified when they take action. Cancel Request - To send audio messages, allow Molly access to your microphone. - Molly requires the Microphone permission in order to send audio messages, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\". - Molly needs the Microphone and Camera permissions in order to call %s, but they have been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". - To capture photos and video, allow Molly access to the camera. - Molly needs the Camera permission to take photos or video, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Camera\". - Molly needs Camera permissions to take photos or video + To send audio messages, allow Molly access to your microphone. + Molly requires the Microphone permission in order to send audio messages, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\". + Molly needs the Microphone and Camera permissions in order to call %s, but they have been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". + To capture photos and video, allow Molly access to the camera. + Molly needs the Camera permission to take photos or video, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Camera\". + Molly needs Camera permissions to take photos or video Enable the microphone permission to capture videos with sound. - Molly needs microphone permissions to record videos, but they have been denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". - Molly needs microphone permissions to record videos. + Molly needs microphone permissions to record videos, but they have been denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". + Molly needs microphone permissions to record videos. %1$s %2$s No @@ -411,7 +411,7 @@ You will leave this group, and it will be deleted from all your devices. Delete Delete and leave - To call %1$s, Molly needs access to your microphone + To call %1$s, Molly needs access to your microphone Join @@ -424,9 +424,9 @@ SMS messaging is no longer supported in Signal. Invite %1$s to Signal to keep the conversation here. - This person is no longer using Molly. Invite them to Molly to keep the conversation here. + This person is no longer using Molly. Invite them to Molly to keep the conversation here. - Invite to Molly + Invite to Molly You will be reminded again soon. @@ -511,13 +511,13 @@ Blocked - Update Molly + Update Molly - Re-register Molly + Re-register Molly Navigate back. - Open Molly + Open Molly No longer verified @@ -706,7 +706,7 @@ Test your backup passphrase and verify that it matches Turn on Turn off - To restore a backup, install a new copy of Molly. Open the app and tap "Restore backup", then locate a backup file. %1$s + To restore a backup, install a new copy of Molly. Open the app and tap "Restore backup", then locate a backup file. %1$s Learn more In progress… @@ -714,7 +714,7 @@ %1$d so far… %1$s%% so far… - Molly requires external storage permission in order to create backups, but it has been permanently denied. Please continue to app settings, select \"Permissions\" and enable \"Storage\". + Molly requires external storage permission in order to create backups, but it has been permanently denied. Please continue to app settings, select \"Permissions\" and enable \"Storage\". Set backup time @@ -803,7 +803,7 @@ Optimize for missing Play Services - This device does not support Play Services. Tap to disable system battery optimizations that prevent Molly from retrieving messages while inactive. + This device does not support Play Services. Tap to disable system battery optimizations that prevent Molly from retrieving messages while inactive. This version of Signal has expired. Update now to send and receive messages. @@ -817,8 +817,8 @@ View - Permanent Molly communication failure! - Molly was unable to register with Google Play Services. Molly messages and calls have been disabled, please try re-registering in Settings > Advanced. + Permanent Molly communication failure! + Molly was unable to register with Google Play Services. Molly messages and calls have been disabled, please try re-registering in Settings > Advanced. @@ -956,12 +956,12 @@ Debug logs helps us diagnose and fix the issue, and do not contain identifying information. - Molly encountered a problem. Submit debug log? + Molly encountered a problem. Submit debug log? Notifications may be delayed due to battery optimizations - You can disable battery optimizations for Molly to ensure that message notifications will not be delayed. + You can disable battery optimizations for Molly to ensure that message notifications will not be delayed. Continue @@ -1214,13 +1214,13 @@ Cancel Sending… Invitations sent! - Invite to Molly + Invite to Molly Send SMS (%d) Send %d SMS invite? Send %d SMS invites? - Let\'s switch to Molly: %1$s + Let\'s switch to Molly: %1$s It looks like you don\'t have any apps to share to. @@ -1234,7 +1234,7 @@ Your message - Molly + Molly Background connection enabled @@ -1304,8 +1304,8 @@ Establishing Signal call - Starting Molly call service - Stopping Molly call service + Starting Molly call service + Stopping Molly call service Cancel call @@ -1596,7 +1596,7 @@ You will no longer be able to send SMS messages from Signal soon. Invite %1$s to Signal to keep the conversation here. - You can no longer send SMS messages in Molly. Invite %1$s to Molly to keep the conversation here. + You can no longer send SMS messages in Molly. Invite %1$s to Molly to keep the conversation here. Payment: %1$s @@ -1677,7 +1677,7 @@ Link a Signal device? It looks like you\'re trying to link a Signal device using a 3rd party scanner. For your protection, please scan the code again from within Signal. - Molly needs the Camera permission in order to scan a QR code, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Camera\". + Molly needs the Camera permission in order to scan a QR code, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Camera\". Unable to scan a QR code without the Camera permission @@ -1690,11 +1690,11 @@ Enter passphrase - Molly icon + Molly icon Submit passphrase Invalid passphrase! - Unlock Molly - Molly Android - Lock Screen + Unlock Molly + Molly Android - Lock Screen Map @@ -1761,7 +1761,7 @@ Verify to continue messaging - To help prevent spam on Molly, please complete verification. + To help prevent spam on Molly, please complete verification. After verifying, you can continue messaging. Any paused messages will automatically be sent. @@ -1769,7 +1769,7 @@ My Story - Molly call + Molly call Block @@ -1794,8 +1794,8 @@ Tap here to turn on your video - To call %1$s, Molly needs access to your camera - Molly %1$s + To call %1$s, Molly needs access to your camera + Molly %1$s Calling… Reconnecting… @@ -2050,13 +2050,13 @@ You\'ll receive a call to verify this number. Edit number Missing Google Play Services - This device is missing Google Play Services. You can still use Molly, but this configuration may result in reduced reliability or performance.\n\nIf you are not an advanced user, are not running an aftermarket Android ROM, or believe that you are seeing this in error, please contact support@molly.im for help troubleshooting. + This device is missing Google Play Services. You can still use Molly, but this configuration may result in reduced reliability or performance.\n\nIf you are not an advanced user, are not running an aftermarket Android ROM, or believe that you are seeing this in error, please contact support@molly.im for help troubleshooting. I understand Play Services Error Google Play Services is updating or temporarily unavailable. Please try again. Terms & Privacy Policy - Signal needs the contacts and media permissions to help you connect with friends and send messages. Your contacts are uploaded using Signal\'s private contact discovery, which means they are end-to-end encrypted and never visible to the Signal service. - Signal needs the contacts permission to help you connect with friends. Your contacts are uploaded using Signal\'s private contact discovery, which means they are end-to-end encrypted and never visible to the Signal service. + Molly needs the contacts and media permissions to help you connect with friends and send messages. Your contacts are uploaded using Signal\'s private contact discovery, which means they are end-to-end encrypted and never visible to the Signal service. + Molly needs the contacts permission to help you connect with friends. Your contacts are uploaded using Signal\'s private contact discovery, which means they are end-to-end encrypted and never visible to the Signal service. You\'ve made too many attempts to register this number. Please try again later. You\'ve made too many attempts to register this number. Please try again in %s. @@ -2067,7 +2067,7 @@ Unable to request a verification code. Please check network connection and try again. Non-standard number format The number you entered (%1$s) appears to be a non-standard format.\n\nDid you mean %2$s? - Molly Android - Phone Number Format + Molly Android - Phone Number Format Call requested @@ -2140,13 +2140,13 @@ Add to Contacts - Invite to Molly + Invite to Molly Signal Message Signal Call Add to Contacts - Invite to Molly + Invite to Molly Signal Message @@ -2210,12 +2210,12 @@ This log will be posted publicly online for contributors to view. You may examine it before uploading. - support@molly.im + support@molly.im Filter: Device info: Android version: - Molly version: - Molly package: + Molly version: + Molly package: Registration lock: Locale: @@ -2277,11 +2277,11 @@ %1$s belongs to %2$s - Molly update - A new version of Molly is available. Tap to update. - Molly failed to update + Molly update + A new version of Molly is available. Tap to update. + Molly failed to update We will try again later. - Molly successfully updated + Molly successfully updated You were automatically updated to version %1$s. @@ -2352,7 +2352,7 @@ Our Signal safety number: It looks like you don\'t have any apps to share to. No safety number to compare was found in the clipboard - Molly needs the Camera permission in order to scan a QR code, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Camera\". + Molly needs the Camera permission in order to scan a QR code, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Camera\". Unable to scan QR code without Camera permission You must first exchange messages in order to view %1$s\'s safety number. @@ -2382,14 +2382,14 @@ Touch to open. - Molly is unlocked - Lock Molly + Molly is unlocked + Lock Molly You Unsupported media type Draft - Molly needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\". + Molly needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\". Unable to save to external storage without permissions Delete message? This will permanently delete this message. @@ -2433,7 +2433,7 @@ Failed to deliver message. Error delivering message. Message delivery paused. - Verify to continue messaging on Molly. + Verify to continue messaging on Molly. Mark all as read Mark read Turn off these notifications @@ -2494,7 +2494,7 @@ - Quick response unavailable when Molly is locked! + Quick response unavailable when Molly is locked! Problem sending message! @@ -2514,7 +2514,7 @@ Invalid shortcut - Molly + Molly New message Message request You @@ -2560,7 +2560,7 @@ - This device is no longer registered. This is likely because you registered your phone number with Molly on a different device. + This device is no longer registered. This is likely because you registered your phone number with Molly on a different device. Re-register device @@ -2573,10 +2573,10 @@ - To answer the call, give Molly access to your microphone. + To answer the call, give Molly access to your microphone. - To answer the video call, give Molly access to your microphone and camera. - Molly requires Microphone and Camera permissions in order to make or receive calls, but they have been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". + To answer the video call, give Molly access to your microphone and camera. + Molly requires Microphone and Camera permissions in order to make or receive calls, but they have been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". Answered on a linked device. Declined on a linked device. Busy on a linked device. @@ -2621,7 +2621,7 @@ Repeat new passphrase - Invite to Molly + Invite to Molly New group Refresh contacts @@ -2643,7 +2643,7 @@ Contact Photo - Molly requires the Contacts permission in order to display your contacts, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Contacts\". + Molly requires the Contacts permission in order to display your contacts, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Contacts\". Error retrieving contacts, check your network connection Username not found "%1$s" is not a Signal user. Please check the username and try again. @@ -2662,7 +2662,7 @@ Find by username - Molly needs access to your contacts in order to display them. + Molly needs access to your contacts in order to display them. Show Contacts @@ -2761,7 +2761,7 @@ - Bubbles are an Android feature that you can turn off for Molly chats. + Bubbles are an Android feature that you can turn off for Molly chats. Not now @@ -2928,9 +2928,9 @@ Group description https://support.signal.org/hc/articles/360007459591 - Update Molly + Update Molly - This version of Molly has expired. Update now to continue using Molly. + This version of Molly has expired. Update now to continue using Molly. Update @@ -3144,7 +3144,7 @@ Slow Help Advanced - Donate to Molly + Donate to Molly Privacy @@ -3181,13 +3181,13 @@ Change app icon and name to \"%1$s\" - Molly will need to close to change the app icon and name. Notifications will always display the default Molly icon and name. + Molly will need to close to change the app icon and name. Notifications will always display the default Molly icon and name. - Select an app icon and name, which will be visible on your phone\'s home screen and app drawer. Notifications will always display the default Molly icon and name. Learn\u00A0more + Select an app icon and name, which will be visible on your phone\'s home screen and app drawer. Notifications will always display the default Molly icon and name. Learn\u00A0more App icons and names are visible on the home screen and app drawer. - Notifications will always display the default Molly icon and name. + Notifications will always display the default Molly icon and name. Learn\u00A0more @@ -3271,7 +3271,7 @@ Censorship circumvention Censorship circumvention - If enabled, Molly will attempt to circumvent censorship. Do not enable this feature unless you are in a location where Molly is censored. + If enabled, Molly will attempt to circumvent censorship. Do not enable this feature unless you are in a location where Molly is censored. Censorship circumvention has been activated based on your account\'s phone number. @@ -3323,7 +3323,7 @@ Introducing payments - Use Molly to send and receive MobileCoin, a new privacy focused digital currency. Activate to get started. + Use Molly to send and receive MobileCoin, a new privacy focused digital currency. Activate to get started. Activate Payments Activating payments… Restore payments account @@ -3349,7 +3349,7 @@ Payments is not available in your region. Could not enable payments. Try again later. Deactivate Payments? - You will not be able to send or receive MobileCoin in Molly if you deactivate payments. + You will not be able to send or receive MobileCoin in Molly if you deactivate payments. Deactivate Continue Balance is not currently available. @@ -3358,10 +3358,10 @@ Details https://support.signal.org/hc/articles/360057625692#payments_activate - You can use Signal to send and receive MobileCoin. All payments are subject to the Terms of Use for MobileCoins and the MobileCoin Wallet. You may encounter some issues and payments or balances you may lose can\'t be recovered. + You can use Molly to send and receive MobileCoin. All payments are subject to the Terms of Use for MobileCoins and the MobileCoin Wallet. You may encounter some issues and payments or balances you may lose can\'t be recovered. Activate View MobileCoin terms - Payments in Molly is no longer available. You can still transfer funds to an exchange but you can no longer send and receive payments or add funds. + Payments in Molly is no longer available. You can still transfer funds to an exchange but you can no longer send and receive payments or add funds. https://www.mobilecoin.com/terms-of-use.html @@ -3389,7 +3389,7 @@ Allow permissions - To help you message people you know, Molly will request these permissions. + To help you message people you know, Molly will request these permissions. Notifications @@ -3468,10 +3468,10 @@ Next Invalid address Check the wallet address you\'re attempting to transfer to and try again. - You can\'t transfer to your own Molly wallet address. Enter the wallet address from your account at a supported exchange. - To scan a QR code, Molly needs access to the camera. - Molly needs the Camera permission to capture a QR code. Go to settings, select \"Permissions\", and enable \"Camera\". - To scan a QR code, Molly needs access to the camera. + You can\'t transfer to your own Molly wallet address. Enter the wallet address from your account at a supported exchange. + To scan a QR code, Molly needs access to the camera. + Molly needs the Camera permission to capture a QR code. Go to settings, select \"Permissions\", and enable \"Camera\". + To scan a QR code, Molly needs access to the camera. Settings @@ -3662,7 +3662,7 @@ New group Settings - Lock + Lock Mark all read Invite friends @@ -3810,17 +3810,17 @@ - Incoming Molly voice call + Incoming Molly voice call - Incoming Molly video call + Incoming Molly video call - Incoming Molly group call + Incoming Molly group call - Ongoing Molly voice call + Ongoing Molly voice call - Ongoing Molly video call + Ongoing Molly video call - Ongoing Molly group call + Ongoing Molly group call Loading… @@ -3867,9 +3867,9 @@ Verify You successfully entered your backup passphrase Passphrase was not correct - Creating Molly backup… + Creating Molly backup… - Verifying Molly backup… + Verifying Molly backup… Backup failed Your backup directory has been deleted or moved. Your backup file is too large to store on this volume. @@ -3884,15 +3884,15 @@ Call me (%1$02d:%2$02d) Resend Code (%1$02d:%2$02d) - Contact Molly Support - Molly Registration - Verification Code for Android + Contact Molly Support + Molly Registration - Verification Code for Android Incorrect code Never Unknown Phone number - Choose who can see your phone number and who can contact you on Molly with it. + Choose who can see your phone number and who can contact you on Molly with it. Who can see my number @@ -3940,7 +3940,7 @@ You\'ve made too many attempts. Please try again later. Error connecting to service Backups - Molly is locked + Molly is locked TAP TO UNLOCK Unknown @@ -3988,19 +3988,19 @@ Preparing to connect to old Android device… Taking a moment, should be ready soon Waiting for old Android device to connect… - Molly needs the location permission to discover and connect to your old Android device. - Molly needs location services enabled to discover and connect with your old Android device. - Molly needs Wi-Fi on to discover and connect with your old Android device. Wi-Fi needs to be on but it does not have to be connected to a Wi-Fi network. - Sorry, it appears this device does not support Wi-Fi Direct. Molly uses Wi-Fi Direct to discover and connect with your old Android device. You can still restore a backup to restore your account from your old Android device. + Molly needs the location permission to discover and connect to your old Android device. + Molly needs location services enabled to discover and connect with your old Android device. + Molly needs Wi-Fi on to discover and connect with your old Android device. Wi-Fi needs to be on but it does not have to be connected to a Wi-Fi network. + Sorry, it appears this device does not support Wi-Fi Direct. Molly uses Wi-Fi Direct to discover and connect with your old Android device. You can still restore a backup to restore your account from your old Android device. Restore a backup An unexpected error occurred while attempting to connect to your old Android device. Searching for new Android device… - Molly needs the location permission to discover and connect to your new Android device. - Molly needs location services enabled to discover and connect with your new Android device. - Molly needs Wi-Fi on to discover and connect with your new Android device. Wi-Fi needs to be on but it does not have to be connected to a Wi-Fi network. - Sorry, it appears this device does not support Wi-Fi Direct. Molly uses Wi-Fi Direct to discover and connect with your new Android device. You can still create a backup to restore your account on your new Android device. + Molly needs the location permission to discover and connect to your new Android device. + Molly needs location services enabled to discover and connect with your new Android device. + Molly needs Wi-Fi on to discover and connect with your new Android device. Wi-Fi needs to be on but it does not have to be connected to a Wi-Fi network. + Sorry, it appears this device does not support Wi-Fi Direct. Molly uses Wi-Fi Direct to discover and connect with your new Android device. You can still create a backup to restore your account on your new Android device. Create a backup An unexpected error occurred while attempting to connect to your new Android device. @@ -4041,7 +4041,7 @@ Transferring data - Keep both devices near each other. Do not turn off the devices and keep Molly open. Transfers are end-to-end encrypted. + Keep both devices near each other. Do not turn off the devices and keep Molly open. Transfers are end-to-end encrypted. %1$d messages so far… %1$s%% of messages so far… @@ -4055,7 +4055,7 @@ Transfer Account 1. - Download Molly on your new Android device + Download Molly on your new Android device 2. Tap on "Transfer or restore account" 3. @@ -4128,7 +4128,7 @@ Anyone with this link can view the group\'s name and photo and request to join. Share it with people you trust. Anyone with this link can view the group\'s name and photo and join the group. Share it with people you trust. - Share via Molly + Share via Molly Copy QR Code Share @@ -4296,7 +4296,7 @@ About MobileCoin MobileCoin is a new privacy focused digital currency. Adding funds - You can add funds for use in Molly by sending MobileCoin to your wallet address. + You can add funds for use in Molly by sending MobileCoin to your wallet address. Cashing out You can cash out MobileCoin anytime on an exchange that supports MobileCoin. Just make a transfer to your account at that exchange. Hide this card? @@ -4317,12 +4317,12 @@ Deactivate Wallet Your balance - It\'s recommended that you transfer your funds to another wallet address before deactivating payments. If you choose not to transfer your funds now, they will remain in your wallet linked to Molly if you reactivate payments. + It\'s recommended that you transfer your funds to another wallet address before deactivating payments. If you choose not to transfer your funds now, they will remain in your wallet linked to Molly if you reactivate payments. Transfer remaining balance Deactivate without transferring Deactivate Deactivate without transferring? - Your balance will remain in your wallet linked to Molly if you choose to reactivate payments. + Your balance will remain in your wallet linked to Molly if you choose to reactivate payments. Error deactivating wallet. https://support.signal.org/hc/articles/360057625692#payments_deactivate @@ -4612,7 +4612,7 @@ Licenses Terms & Privacy Policy - Copyright Molly Messenger + Copyright Molly Messenger Licensed under the GNU AGPLv3 @@ -5062,9 +5062,9 @@ Learn more - Update Molly + Update Molly - This version of Molly has expired. Update now to continue using Molly. + This version of Molly has expired. Update now to continue using Molly. Update @@ -5073,7 +5073,7 @@ Device not registered - This device is no longer registered. Re-register to continue using Molly on this device. + This device is no longer registered. Re-register to continue using Molly on this device. Re-register @@ -5367,7 +5367,7 @@ Failed to open picker. - To enable notifications, Molly needs permission to display them. + To enable notifications, Molly needs permission to display them. Turn on diff --git a/build-logic/plugins/src/main/java/molly.gradle.kts b/build-logic/plugins/src/main/java/molly.gradle.kts new file mode 100644 index 0000000000..0a048f1f77 --- /dev/null +++ b/build-logic/plugins/src/main/java/molly.gradle.kts @@ -0,0 +1,78 @@ +import org.w3c.dom.Document +import org.w3c.dom.Element +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.OutputKeys +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +/** + * Utility object for parsing and manipulating strings.xml files. + */ +object StringsXmlParser { + fun parse(stringsFile: File): Pair> { + val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + val doc = docBuilder.parse(stringsFile).apply { + xmlStandalone = true + } + return doc to doc.getStringElements() + } + + fun writeToFile(doc: Document, file: File) { + val transformer = TransformerFactory.newInstance().newTransformer() + transformer.transform(DOMSource(doc), StreamResult(file)) + } + + private fun Document.getStringElements() = + getElementsByTagName("string").let { nodeList -> + (0 until nodeList.length).map { nodeList.item(it) as Element } + } +} + +val updateTranslationsForMolly by tasks.registering { + group = "Molly" + description = "Updates references to \"Signal\" with \"Molly\" in all translation files." + + doLast { + val englishFile = file("src/main/res/values/strings.xml") + val (_, englishStrings) = StringsXmlParser.parse(englishFile) + + // Gather all string names containing "mollyify" attribute + val mollyStringNames = englishStrings + .filter { it.getAttribute("mollyify") == "true" } + .map { it.getAttribute("name") } + .toSet() + + // Iterate through each translation file and perform the replacements + project.fileTree("src/main/res").matching { + include("**/values-*/strings.xml") + }.forEach { translationFile -> + try { + val (translationDoc, translatedStrings) = StringsXmlParser.parse(translationFile) + var modified = false + + translatedStrings.forEach { translatedString -> + with(translatedString) { + val stringName = getAttribute("name") + if (stringName in mollyStringNames) { + val oldContent = textContent + textContent = textContent + .replace("Signal", "Molly") + .replace("signal.org", "molly.im") + if (oldContent != textContent) { + modified = true + } + } + } + } + if (modified) { + // Write back the modified translation file only if replacements were made + StringsXmlParser.writeToFile(translationDoc, translationFile) + logger.lifecycle("Updated translations in: ${translationFile.path}") + } + } catch (e: Exception) { + logger.error("Error processing file: ${translationFile.path}, ${e.message}") + } + } + } +} diff --git a/build-logic/plugins/src/main/java/translations.gradle b/build-logic/plugins/src/main/java/translations.gradle deleted file mode 100644 index 06a3658ebb..0000000000 --- a/build-logic/plugins/src/main/java/translations.gradle +++ /dev/null @@ -1,128 +0,0 @@ -import groovy.io.FileType -import groovy.transform.stc.ClosureParams -import groovy.transform.stc.SimpleType -import org.signal.buildtools.StaticIpResolver - -def allStringsResourceFiles(@ClosureParams(value = SimpleType.class, options = ['java.io.File']) Closure c) { - file('src/main/res').eachFileRecurse(FileType.FILES) { f -> - if (f.name == 'strings.xml') { - c(f) - } - } -} - -task replaceEllipsis { - group 'Static Files' - description 'Process strings for ellipsis characters.' - doLast { - allStringsResourceFiles { f -> - def before = f.text - def after = f.text.replace('...', '…') - if (before != after) { - f.text = after - logger.info("$f.parentFile.name/$f.name...updated") - } - } - } -} - -task cleanApostropheErrors { - group 'Static Files' - description 'Fix transifex apostrophe string errors.' - doLast { - allStringsResourceFiles { f -> - def before = f.text - def after = before.replaceAll(/([^\\=08])(')/, '$1\\\\\'') - if (before != after) { - f.text = after - logger.info("$f.parentFile.name/$f.name...updated") - } - } - } -} - -task excludeNonTranslatables { - group 'Static Files' - description 'Remove strings that are marked "translatable"="false" or are ExtraTranslations.' - doLast { - def englishFile = file('src/main/res/values/strings.xml') - - def english = new XmlParser().parse(englishFile) - def nonTranslatable = english - .findAll { it['@translatable'] == 'false' } - .collect { it['@name'] } - .toSet() - def all = english.collect { it['@name'] }.toSet() - def translatable = all - nonTranslatable - def inMultiline = false - def endBlockName = "" - - allStringsResourceFiles { f -> - if (f != englishFile) { - def newLines = f.readLines() - .collect { line -> - if (!inMultiline) { - def singleLineMatcher = line =~ /name="([^"]*)".*(<\/|\/>)/ - if (singleLineMatcher.find()) { - def name = singleLineMatcher.group(1) - if (!line.contains('excludeNonTranslatables') && !translatable.contains(name)) { - return " " - } - } else { - def multilineStartMatcher = line =~ /<(.*) .?name="([^"]*)".*/ - if (multilineStartMatcher.find()) { - endBlockName = multilineStartMatcher.group(1) - def name = multilineStartMatcher.group(2) - if (!line.contains('excludeNonTranslatables') && !translatable.contains(name)) { - inMultiline = true; - return " " - } - } - - return line - } - - f.write(newLines.join("\n") + "\n") - } - } - } -} - -task postTranslateQa { - group 'Static Files' - description 'Runs QA to check validity of updated strings, and ensure presence of any new languages in internal lists.' - dependsOn ':qa' -} - -task resolveStaticIps { - group 'Static Files' - description 'Fetches static IPs for core hosts and writes them to static-ips.gradle' - doLast { - def staticIpResolver = new StaticIpResolver() - new File(projectDir, "static-ips.gradle.kts").text = """ - rootProject.extra["service_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("chat.signal.org")}\"\"\" - rootProject.extra["storage_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("storage.signal.org")}\"\"\" - rootProject.extra["cdn_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdn.signal.org")}\"\"\" - rootProject.extra["cdn2_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdn2.signal.org")}\"\"\" - rootProject.extra["cdn3_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdn3.signal.org")}\"\"\" - rootProject.extra["sfu_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("sfu.voip.signal.org")}\"\"\" - rootProject.extra["content_proxy_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("contentproxy.signal.org")}\"\"\" - rootProject.extra["svr2_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("svr2.signal.org")}\"\"\" - rootProject.extra["cdsi_ips"] = \"\"\"${staticIpResolver.resolveToBuildConfig("cdsi.signal.org")}\"\"\" - """.stripIndent().trim() + "\n" - } -} - -task updateStaticFilesAndQa { - group 'Static Files' - description 'Runs tasks to update static files. This includes translations, static IPs, and licenses. Runs QA afterwards to verify all went well. Intended to be run before cutting a release.' - dependsOn replaceEllipsis, cleanApostropheErrors, excludeNonTranslatables, resolveStaticIps, postTranslateQa -} From 9cc1d39235701d06987e8f1314069bc429b852de Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Tue, 23 Apr 2024 10:32:43 +0200 Subject: [PATCH 071/113] Update .gitattributes for merge conflict resolution --- .gitattributes | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 47f6d98b40..03e65f1908 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,8 @@ -*.ai binary +# MOLLY: Reduce the number of merge conflicts. +# +# Note: also need: +# git config merge.ours.driver true +# git config merge.theirs.driver 'cp -- %B %A' + +/app/src/main/res/values-*/strings.xml merge=theirs +/gradle/verification-metadata.xml merge=union From 636b2e82cab11e9327b6f2ef112beb04134ca06a Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Tue, 23 Apr 2024 19:49:07 +0200 Subject: [PATCH 072/113] Allow backups regardless of registration state Closes #304 --- .../securesms/components/settings/app/AppSettingsFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 27787fdf3e..b5f69d4973 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 @@ -177,7 +177,7 @@ class AppSettingsFragment : DSLSettingsFragment( onClick = { findNavController().safeNavigate(R.id.action_appSettingsFragment_to_chatsSettingsFragment) }, - isEnabled = state.isRegisteredAndUpToDate() + isEnabled = true ) clickPref( From f78a019c70dda52cfcedd88671c23086bea1120d Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 23 Apr 2024 15:56:38 -0400 Subject: [PATCH 073/113] Use seconds instead of millis for redemption time. --- .../org/whispersystems/signalservice/api/archive/ArchiveApi.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt index 54aba96445..aa93df8efa 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt @@ -239,7 +239,7 @@ class ArchiveApi( return backupRequestContext.receiveResponse( backupAuthResponse, - Instant.ofEpochMilli(serviceCredential.redemptionTime), + Instant.ofEpochSecond(serviceCredential.redemptionTime), backupServerPublicParams ) } From 4134df3f353efec76b9c1daf6c76db1119d51d5a Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 23 Apr 2024 16:29:03 -0400 Subject: [PATCH 074/113] Use archive-specific endpoint for attachment backfill. --- .../securesms/backup/v2/BackupRepository.kt | 19 +++++++ .../jobs/ArchiveAttachmentBackfillJob.kt | 44 +++++++++++++++- .../api/SignalServiceMessageSender.java | 4 +- .../signalservice/api/archive/ArchiveApi.kt | 32 +++++++++--- .../ArchiveMediaUploadFormStatusCodes.kt | 25 +++++++++ .../ArchiveMessageBackupUploadFormResponse.kt | 22 -------- .../api/services/AttachmentService.kt | 6 +-- .../internal/push/AttachmentUploadForm.kt | 24 +++++++++ .../push/AttachmentV4UploadAttributes.java | 38 -------------- .../internal/push/PushServiceSocket.java | 51 ++++++++++--------- 10 files changed, 170 insertions(+), 95 deletions(-) create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaUploadFormStatusCodes.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMessageBackupUploadFormResponse.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentUploadForm.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentV4UploadAttributes.java 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 8a1377abc8..fd83f9ede0 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 @@ -49,6 +49,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.Pro import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.internal.crypto.PaddingInputStream +import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec import java.io.ByteArrayOutputStream import java.io.File import java.io.InputStream @@ -339,6 +340,24 @@ object BackupRepository { } } + /** + * Retrieves an upload spec that can be used to upload attachment media. + */ + fun getMediaUploadSpec(): NetworkResult { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return api + .triggerBackupIdReservation(backupKey) + .then { getAuthCredential() } + .then { credential -> + api.getMediaUploadForm(backupKey, credential) + } + .then { form -> + api.getResumableUploadSpec(form) + } + } + fun archiveMedia(attachment: DatabaseAttachment): NetworkResult { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt index 71436b32f6..c6cb48706f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ArchiveAttachmentBackfillJob.kt @@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.jobs.protos.ArchiveAttachmentBackfillJobData import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.archive.ArchiveMediaResponse +import org.whispersystems.signalservice.api.archive.ArchiveMediaUploadFormStatusCodes import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer import java.io.IOException import java.util.Optional @@ -123,7 +124,12 @@ class ArchiveAttachmentBackfillJob private constructor( if (transferState == AttachmentTable.ArchiveTransferState.BACKFILL_UPLOAD_IN_PROGRESS) { if (uploadSpec == null || System.currentTimeMillis() > uploadSpec!!.timeout) { Log.d(TAG, "Need an upload spec. Fetching...") - uploadSpec = ApplicationDependencies.getSignalServiceMessageSender().getResumableUploadSpec().toProto() + + val (spec, result) = fetchResumableUploadSpec() + if (result != null) { + return result + } + uploadSpec = spec } else { Log.d(TAG, "Already have an upload spec. Continuing...") } @@ -212,6 +218,42 @@ class ArchiveAttachmentBackfillJob private constructor( } } + private fun fetchResumableUploadSpec(): Pair { + return when (val spec = BackupRepository.getMediaUploadSpec()) { + is NetworkResult.Success -> { + Log.d(TAG, "Got an upload spec!") + spec.result.toProto() to null + } + + is NetworkResult.ApplicationError -> { + Log.w(TAG, "Failed to get an upload spec due to an application error. Retrying.", spec.throwable) + return null to Result.retry(defaultBackoff()) + } + + is NetworkResult.NetworkError -> { + Log.w(TAG, "Encountered a transient network error. Retrying.") + return null to Result.retry(defaultBackoff()) + } + + is NetworkResult.StatusCodeError -> { + Log.w(TAG, "Failed request with status code ${spec.code}") + + when (ArchiveMediaUploadFormStatusCodes.from(spec.code)) { + ArchiveMediaUploadFormStatusCodes.BadArguments, + ArchiveMediaUploadFormStatusCodes.InvalidPresentationOrSignature, + ArchiveMediaUploadFormStatusCodes.InsufficientPermissions, + ArchiveMediaUploadFormStatusCodes.RateLimited -> { + return null to Result.retry(defaultBackoff()) + } + + ArchiveMediaUploadFormStatusCodes.Unknown -> { + return null to Result.retry(defaultBackoff()) + } + } + } + } + } + class Factory : Job.Factory { override fun create(parameters: Parameters, serializedData: ByteArray?): ArchiveAttachmentBackfillJob { val data = serializedData?.let { ArchiveAttachmentBackfillJobData.ADAPTER.decode(it) } 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 9de3591be5..5dda9c6596 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 @@ -93,7 +93,7 @@ import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.internal.push.AttachmentPointer; import org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes; -import org.whispersystems.signalservice.internal.push.AttachmentV4UploadAttributes; +import org.whispersystems.signalservice.internal.push.AttachmentUploadForm; import org.whispersystems.signalservice.internal.push.BodyRange; import org.whispersystems.signalservice.internal.push.CallMessage; import org.whispersystems.signalservice.internal.push.Content; @@ -860,7 +860,7 @@ private SignalServiceAttachmentPointer uploadAttachmentV2(SignalServiceAttachmen } public ResumableUploadSpec getResumableUploadSpec() throws IOException { - AttachmentV4UploadAttributes v4UploadAttributes = null; + AttachmentUploadForm v4UploadAttributes = null; Log.d(TAG, "Using pipe to retrieve attachment upload attributes..."); try { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt index aa93df8efa..670f169981 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveApi.kt @@ -16,7 +16,9 @@ import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse.StoredMediaObject import org.whispersystems.signalservice.api.backup.BackupKey import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.internal.push.AttachmentUploadForm import org.whispersystems.signalservice.internal.push.PushServiceSocket +import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec import java.io.InputStream import java.time.Instant @@ -92,7 +94,7 @@ class ArchiveApi( /** * Fetches an upload form you can use to upload your main message backup file to cloud storage. */ - fun getMessageBackupUploadForm(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential): NetworkResult { + fun getMessageBackupUploadForm(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential): NetworkResult { return NetworkResult.fromFetch { val zkCredential = getZkCredential(backupKey, serviceCredential) val presentationData = CredentialPresentationData.from(backupKey, zkCredential, backupServerPublicParams) @@ -125,20 +127,38 @@ class ArchiveApi( } /** - * Retrieves a resumable upload URL you can use to upload your main message backup file to cloud storage. + * Retrieves a resumable upload URL you can use to upload your main message backup file or an arbitrary media file to cloud storage. */ - fun getBackupResumableUploadUrl(archiveFormResponse: ArchiveMessageBackupUploadFormResponse): NetworkResult { + fun getBackupResumableUploadUrl(uploadForm: AttachmentUploadForm): NetworkResult { return NetworkResult.fromFetch { - pushServiceSocket.getResumableUploadUrl(archiveFormResponse) + pushServiceSocket.getResumableUploadUrl(uploadForm) } } /** * Uploads your main backup file to cloud storage. */ - fun uploadBackupFile(archiveFormResponse: ArchiveMessageBackupUploadFormResponse, resumableUploadUrl: String, data: InputStream, dataLength: Long): NetworkResult { + fun uploadBackupFile(uploadForm: AttachmentUploadForm, resumableUploadUrl: String, data: InputStream, dataLength: Long): NetworkResult { return NetworkResult.fromFetch { - pushServiceSocket.uploadBackupFile(archiveFormResponse, resumableUploadUrl, data, dataLength) + pushServiceSocket.uploadBackupFile(uploadForm, resumableUploadUrl, data, dataLength) + } + } + + /** + * Retrieves an [AttachmentUploadForm] that can be used to upload pre-existing media to the archive. + * After uploading, the media still needs to be copied via [archiveAttachmentMedia]. + */ + fun getMediaUploadForm(backupKey: BackupKey, serviceCredential: ArchiveServiceCredential): NetworkResult { + return NetworkResult.fromFetch { + val zkCredential = getZkCredential(backupKey, serviceCredential) + val presentationData = CredentialPresentationData.from(backupKey, zkCredential, backupServerPublicParams) + pushServiceSocket.getArchiveMediaUploadForm(presentationData.toArchiveCredentialPresentation()) + } + } + + fun getResumableUploadSpec(uploadForm: AttachmentUploadForm): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.getResumableUploadSpec(uploadForm) } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaUploadFormStatusCodes.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaUploadFormStatusCodes.kt new file mode 100644 index 0000000000..f756bbf675 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMediaUploadFormStatusCodes.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.archive + +/** + * Status codes for the ArchiveMediaUploadForm endpoint. + * + * Kept in a separate class because [AttachmentUploadForm] (the model the request returns) is used for multiple endpoints with different status codes. + */ +enum class ArchiveMediaUploadFormStatusCodes(val code: Int) { + BadArguments(400), + InvalidPresentationOrSignature(401), + InsufficientPermissions(403), + RateLimited(429), + Unknown(-1); + + companion object { + fun from(code: Int): ArchiveMediaUploadFormStatusCodes { + return values().firstOrNull { it.code == code } ?: Unknown + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMessageBackupUploadFormResponse.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMessageBackupUploadFormResponse.kt deleted file mode 100644 index 7b0d102bb3..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/archive/ArchiveMessageBackupUploadFormResponse.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.archive - -import com.fasterxml.jackson.annotation.JsonProperty - -/** - * Represents the response body when we ask for a message backup upload form. - */ -data class ArchiveMessageBackupUploadFormResponse( - @JsonProperty - val cdn: Int, - @JsonProperty - val key: String, - @JsonProperty - val headers: Map, - @JsonProperty - val signedUploadLocation: String -) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt index 694b881697..b9299ed42a 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt @@ -4,8 +4,8 @@ import io.reactivex.rxjava3.core.Single import org.whispersystems.signalservice.api.SignalWebSocket import org.whispersystems.signalservice.internal.ServiceResponse import org.whispersystems.signalservice.internal.ServiceResponseProcessor +import org.whispersystems.signalservice.internal.push.AttachmentUploadForm import org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes -import org.whispersystems.signalservice.internal.push.AttachmentV4UploadAttributes import org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage import org.whispersystems.signalservice.internal.websocket.WebsocketResponse @@ -29,7 +29,7 @@ class AttachmentService(private val signalWebSocket: SignalWebSocket) { .onErrorReturn { throwable: Throwable? -> ServiceResponse.forUnknownError(throwable) } } - fun getAttachmentV4UploadAttributes(): Single> { + fun getAttachmentV4UploadAttributes(): Single> { val requestMessage = WebSocketRequestMessage( id = SecureRandom().nextLong(), verb = "GET", @@ -37,7 +37,7 @@ class AttachmentService(private val signalWebSocket: SignalWebSocket) { ) return signalWebSocket.request(requestMessage) - .map { response: WebsocketResponse? -> DefaultResponseMapper.getDefault(AttachmentV4UploadAttributes::class.java).map(response) } + .map { response: WebsocketResponse? -> DefaultResponseMapper.getDefault(AttachmentUploadForm::class.java).map(response) } .onErrorReturn { throwable: Throwable? -> ServiceResponse.forUnknownError(throwable) } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentUploadForm.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentUploadForm.kt new file mode 100644 index 0000000000..b4efe344bd --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentUploadForm.kt @@ -0,0 +1,24 @@ +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty + +/** + * Represents an attachment upload form that can be returned by various service endpoints. + */ +data class AttachmentUploadForm( + @JvmField + @JsonProperty + val cdn: Int, + + @JvmField + @JsonProperty + val key: String, + + @JvmField + @JsonProperty + val headers: Map, + + @JvmField + @JsonProperty + val signedUploadLocation: String +) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentV4UploadAttributes.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentV4UploadAttributes.java deleted file mode 100644 index 86cc4d2fb9..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentV4UploadAttributes.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Map; - -public final class AttachmentV4UploadAttributes { - @JsonProperty - private int cdn; - - @JsonProperty - private String key; - - @JsonProperty - private Map headers; - - @JsonProperty - private String signedUploadLocation; - - public AttachmentV4UploadAttributes() { - } - - public int getCdn() { - return cdn; - } - - public String getKey() { - return key; - } - - public Map getHeaders() { - return headers; - } - - public String getSignedUploadLocation() { - return signedUploadLocation; - } -} 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 83c60f509d..b32df31fac 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 @@ -10,6 +10,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.squareup.wire.Message; +import org.jetbrains.annotations.NotNull; import org.signal.core.util.Base64; import org.signal.core.util.concurrent.FutureTransformers; import org.signal.core.util.concurrent.ListenableFuture; @@ -51,7 +52,6 @@ import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse; import org.whispersystems.signalservice.api.archive.ArchiveMediaRequest; import org.whispersystems.signalservice.api.archive.ArchiveMediaResponse; -import org.whispersystems.signalservice.api.archive.ArchiveMessageBackupUploadFormResponse; import org.whispersystems.signalservice.api.archive.ArchiveServiceCredentialsResponse; import org.whispersystems.signalservice.api.archive.ArchiveSetBackupIdRequest; import org.whispersystems.signalservice.api.archive.ArchiveSetPublicKeyRequest; @@ -316,6 +316,7 @@ public class PushServiceSocket { private static final String ARCHIVE_PUBLIC_KEY = "/v1/archives/keys"; private static final String ARCHIVE_INFO = "/v1/archives"; private static final String ARCHIVE_MESSAGE_UPLOAD_FORM = "/v1/archives/upload/form"; + private static final String ARCHIVE_MEDIA_UPLOAD_FORM = "/v1/archives/media/upload/form"; private static final String ARCHIVE_MEDIA = "/v1/archives/media"; private static final String ARCHIVE_MEDIA_LIST = "/v1/archives/media?limit=%d"; private static final String ARCHIVE_MEDIA_BATCH = "/v1/archives/media/batch"; @@ -581,11 +582,18 @@ public void deleteArchivedMedia(@Nonnull ArchiveCredentialPresentation credentia makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA_DELETE, "POST", JsonUtil.toJson(request), headers, NO_HANDLER); } - public ArchiveMessageBackupUploadFormResponse getArchiveMessageBackupUploadForm(ArchiveCredentialPresentation credentialPresentation) throws IOException { + public AttachmentUploadForm getArchiveMessageBackupUploadForm(ArchiveCredentialPresentation credentialPresentation) throws IOException { Map headers = credentialPresentation.toHeaders(); String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MESSAGE_UPLOAD_FORM, "GET", null, headers, NO_HANDLER); - return JsonUtil.fromJson(response, ArchiveMessageBackupUploadFormResponse.class); + return JsonUtil.fromJson(response, AttachmentUploadForm.class); + } + + public AttachmentUploadForm getArchiveMediaUploadForm(@NotNull ArchiveCredentialPresentation credentialPresentation) throws IOException { + Map headers = credentialPresentation.toHeaders(); + + String response = makeServiceRequestWithoutAuthentication(ARCHIVE_MEDIA_UPLOAD_FORM, "GET", null, headers, UNOPINIONATED_HANDLER); + return JsonUtil.fromJson(response, AttachmentUploadForm.class); } /** @@ -1523,12 +1531,12 @@ public AttachmentV2UploadAttributes getAttachmentV2UploadAttributes() } } - public AttachmentV4UploadAttributes getAttachmentV4UploadAttributes() + public AttachmentUploadForm getAttachmentV4UploadAttributes() throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException { String response = makeServiceRequest(ATTACHMENT_V4_PATH, "GET", null); try { - return JsonUtil.fromJson(response, AttachmentV4UploadAttributes.class); + return JsonUtil.fromJson(response, AttachmentUploadForm.class); } catch (IOException e) { Log.w(TAG, e); throw new MalformedResponseException("Unable to parse entity", e); @@ -1563,14 +1571,14 @@ public Pair uploadAttachment(PushAttachmentData attachme return new Pair<>(id, digest); } - public ResumableUploadSpec getResumableUploadSpec(AttachmentV4UploadAttributes uploadAttributes) throws IOException { + public ResumableUploadSpec getResumableUploadSpec(AttachmentUploadForm uploadForm) throws IOException { return new ResumableUploadSpec(Util.getSecretBytes(64), Util.getSecretBytes(16), - uploadAttributes.getKey(), - uploadAttributes.getCdn(), - getResumableUploadUrl(uploadAttributes.getCdn(), uploadAttributes.getSignedUploadLocation(), uploadAttributes.getHeaders()), + uploadForm.key, + uploadForm.cdn, + getResumableUploadUrl(uploadForm), System.currentTimeMillis() + CDN2_RESUMABLE_LINK_LIFETIME_MILLIS, - uploadAttributes.getHeaders()); + uploadForm.headers); } public AttachmentDigest uploadAttachment(PushAttachmentData attachment) throws IOException { @@ -1741,22 +1749,18 @@ private AttachmentDigest uploadToCdn0(String path, String acl, String key, Strin } } - public String getResumableUploadUrl(ArchiveMessageBackupUploadFormResponse uploadFormResponse) throws IOException { - return getResumableUploadUrl(uploadFormResponse.getCdn(), uploadFormResponse.getSignedUploadLocation(), uploadFormResponse.getHeaders()); - } - - private String getResumableUploadUrl(int cdn, String signedUrl, Map headers) throws IOException { - ConnectionHolder connectionHolder = getRandom(cdnClientsMap.get(cdn), random); + public String getResumableUploadUrl(AttachmentUploadForm uploadForm) throws IOException { + ConnectionHolder connectionHolder = getRandom(cdnClientsMap.get(uploadForm.cdn), random); OkHttpClient okHttpClient = connectionHolder.getClient() .newBuilder() .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) .build(); - Request.Builder request = new Request.Builder().url(buildConfiguredUrl(connectionHolder, signedUrl)) + Request.Builder request = new Request.Builder().url(buildConfiguredUrl(connectionHolder, uploadForm.signedUploadLocation)) .post(RequestBody.create(null, "")); - for (Map.Entry header : headers.entrySet()) { + for (Map.Entry header : uploadForm.headers.entrySet()) { if (!header.getKey().equalsIgnoreCase("host")) { request.header(header.getKey(), header.getValue()); } @@ -1768,13 +1772,13 @@ private String getResumableUploadUrl(int cdn, String signedUrl, Map T readResponseJson(Response response, Class clazz) return readBodyJson(response.body(), clazz); } + public enum VerificationCodeTransport { SMS, VOICE } private static class RegistrationLock { From 293634c758d5841ac7fbd675379e5b2937ac0b21 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 24 Apr 2024 10:48:00 -0300 Subject: [PATCH 075/113] Send call link update sync message upon call link creation. --- .../calls/links/create/CreateCallLinkRepository.kt | 9 +++++++++ .../thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt | 1 + 2 files changed, 10 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt index 1738c944cf..af39062092 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkRepository.kt @@ -10,11 +10,13 @@ import io.reactivex.rxjava3.schedulers.Schedulers import org.thoughtcrime.securesms.database.CallLinkTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.CallLinkUpdateSendJob import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials import org.thoughtcrime.securesms.service.webrtc.links.CreateCallLinkResult import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkManager +import org.whispersystems.signalservice.internal.push.SyncMessage /** * Repository for creating new call links. This will delegate to the [SignalCallLinkManager] @@ -44,6 +46,13 @@ class CreateCallLinkRepository( ) ) + ApplicationDependencies.getJobManager().add( + CallLinkUpdateSendJob( + credentials.roomId, + SyncMessage.CallLinkUpdate.Type.UPDATE + ) + ) + EnsureCallLinkCreatedResult.Success( Recipient.resolved( SignalDatabase.recipients.getByCallLinkRoomId(credentials.roomId).get() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt index 3a37011999..dee97ce24d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt @@ -78,6 +78,7 @@ class CallLinkUpdateSendJob private constructor( val callLinkUpdate = CallLinkUpdate( rootKey = callLink.credentials.linkKeyBytes.toByteString(), + adminPassKey = callLink.credentials.adminPassBytes?.toByteString(), type = callLinkUpdateType ) From 881d231a93591a518f1b0c6e2449393f5bd38b08 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Wed, 24 Apr 2024 10:13:56 -0400 Subject: [PATCH 076/113] Improve group call reactions UI when presented without raise hand. This also dismisses the custom reaction picker when switching to PiP mode. --- .../thoughtcrime/securesms/WebRtcCallActivity.java | 13 +++++++++++-- .../components/webrtc/CallOverflowPopupWindow.kt | 1 - .../components/webrtc/CallReactionScrubber.kt | 12 +++++++++++- app/src/main/res/layout/call_overflow_holder.xml | 3 ++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index 8ede110fbb..35e2f3c1b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -35,6 +35,7 @@ import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.app.PictureInPictureModeChangedInfo; import androidx.core.content.ContextCompat; import androidx.core.util.Consumer; import androidx.lifecycle.LiveDataReactiveStreams; @@ -61,6 +62,7 @@ import org.thoughtcrime.securesms.components.webrtc.CallOverflowPopupWindow; import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePopupWindow; import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState; +import org.thoughtcrime.securesms.components.webrtc.CallReactionScrubber; import org.thoughtcrime.securesms.components.webrtc.CallStateUpdatePopupWindow; import org.thoughtcrime.securesms.components.webrtc.CallToastPopupWindow; import org.thoughtcrime.securesms.components.webrtc.GroupCallSafetyNumberChangeNotificationUtil; @@ -223,6 +225,8 @@ public void onCreate(Bundle savedInstanceState) { processIntent(getIntent()); + registerSystemPipChangeListeners(); + windowLayoutInfoConsumer = new WindowLayoutInfoConsumer(); windowInfoTrackerCallbackAdapter = new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this)); @@ -235,6 +239,13 @@ public void onCreate(Bundle savedInstanceState) { WindowUtil.setNavigationBarColor(this, ContextCompat.getColor(this, R.color.signal_dark_colorSurface)); } + private void registerSystemPipChangeListeners() { + addOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfo -> { + CallParticipantsListDialog.dismiss(getSupportFragmentManager()); + CallReactionScrubber.dismissCustomEmojiBottomSheet(getSupportFragmentManager()); + }); + } + @Override protected void onStart() { super.onStart(); @@ -357,8 +368,6 @@ private boolean enterPipModeIfPossible() { return false; } - CallParticipantsListDialog.dismiss(getSupportFragmentManager()); - return true; } if (Build.VERSION.SDK_INT >= 31) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt index 801438d234..de2b5be2f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt @@ -30,7 +30,6 @@ class CallOverflowPopupWindow(private val activity: FragmentActivity, parentView LayoutInflater.from(activity).inflate(R.layout.call_overflow_holder, parentViewGroup, false), activity.resources.getDimension(R.dimen.calling_reaction_popup_menu_width).toInt(), activity.resources.getDimension(R.dimen.calling_reaction_popup_menu_height).toInt() - ) { private val raiseHandLabel: TextView = (contentView as LinearLayout).findViewById(R.id.raise_hand_label) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallReactionScrubber.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallReactionScrubber.kt index fb2809a8a1..d071003e0a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallReactionScrubber.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallReactionScrubber.kt @@ -20,6 +20,16 @@ class CallReactionScrubber @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { + companion object { + const val CUSTOM_REACTION_BOTTOM_SHEET_TAG = "CallReaction" + + @JvmStatic + fun dismissCustomEmojiBottomSheet(fm: FragmentManager) { + val bottomSheet = fm.findFragmentByTag(CUSTOM_REACTION_BOTTOM_SHEET_TAG) as? ReactWithAnyEmojiBottomSheetDialogFragment + + bottomSheet?.dismissNow() + } + } private val emojiViews: Array private var customEmojiIndex = 0 @@ -48,7 +58,7 @@ class CallReactionScrubber @JvmOverloads constructor( view.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_any_emoji_32)) view.setOnClickListener { val bottomSheet = ReactWithAnyEmojiBottomSheetDialogFragment.createForCallingReactions() - bottomSheet.show(fragmentManager, "CallReaction") + bottomSheet.show(fragmentManager, CUSTOM_REACTION_BOTTOM_SHEET_TAG) } } else { val preferredVariation = SignalStore.emojiValues().getPreferredVariation(emojis[i]) diff --git a/app/src/main/res/layout/call_overflow_holder.xml b/app/src/main/res/layout/call_overflow_holder.xml index e916da1e85..0f07b86d9a 100644 --- a/app/src/main/res/layout/call_overflow_holder.xml +++ b/app/src/main/res/layout/call_overflow_holder.xml @@ -7,7 +7,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + android:gravity="bottom"> Date: Wed, 24 Apr 2024 11:41:31 -0400 Subject: [PATCH 077/113] Change profile fetch REST fallback based on authentication error. --- .../api/services/ProfileService.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/ProfileService.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/ProfileService.java index 1b3ddad5ba..55cdcaced2 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/ProfileService.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/ProfileService.java @@ -18,6 +18,7 @@ import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException; import org.whispersystems.signalservice.internal.ServiceResponse; import org.whispersystems.signalservice.internal.ServiceResponseProcessor; @@ -37,6 +38,7 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -150,7 +152,20 @@ private Single> getProfileRestFallback(@No @Nonnull Locale locale) { return Single.fromFuture(receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType, locale), 10, TimeUnit.SECONDS) - .onErrorResumeNext(t -> Single.fromFuture(receiver.retrieveProfile(address, profileKey, Optional.empty(), requestType, locale), 10, TimeUnit.SECONDS)) + .onErrorResumeNext(t -> { + Throwable error; + if (t instanceof ExecutionException && t.getCause() != null) { + error = t.getCause(); + } else { + error = t; + } + + if (error instanceof AuthorizationFailedException) { + return Single.fromFuture(receiver.retrieveProfile(address, profileKey, Optional.empty(), requestType, locale), 10, TimeUnit.SECONDS); + } else { + return Single.error(t); + } + }) .map(p -> ServiceResponse.forResult(p, 0, null)); } @@ -158,7 +173,20 @@ private Single> getProfileRestFallback(@No @Nonnull Optional unidentifiedAccess, @Nonnull ResponseMapper responseMapper) { return receiver.performIdentityCheck(request, unidentifiedAccess, responseMapper) - .onErrorResumeNext(t -> receiver.performIdentityCheck(request, Optional.empty(), responseMapper)); + .onErrorResumeNext(t -> { + Throwable error; + if (t instanceof ExecutionException && t.getCause() != null) { + error = t.getCause(); + } else { + error = t; + } + + if (error instanceof AuthorizationFailedException) { + return receiver.performIdentityCheck(request, Optional.empty(), responseMapper); + } else { + return Single.error(t); + } + }); } /** From 7dcb8a425a6fc9f09f2d5826ed7f42d4ed8ff40f Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 24 Apr 2024 13:31:35 -0300 Subject: [PATCH 078/113] Handle joined sync message for call links. --- .../service/webrtc/SignalCallManager.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java index 1d35497222..8b71255ff6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java @@ -1049,7 +1049,27 @@ public void sendGroupCallUpdateMessage(@NonNull Recipient recipient, @Nullable S Log.i(TAG, "sendGroupCallUpdateMessage id: " + recipient.getId() + " era: " + groupCallEraId + " isIncoming: " + isIncoming + " isJoinEvent: " + isJoinEvent); if (recipient.isCallLink()) { - Log.i(TAG, "sendGroupCallUpdateMessage -- ignoring for call link"); + if (isJoinEvent) { + SignalExecutors.BOUNDED.execute(() -> { + CallId callIdLocal = callId; + + if (callIdLocal == null && groupCallEraId != null) { + callIdLocal = CallId.fromEra(groupCallEraId); + } + + if (callIdLocal != null) { + ApplicationDependencies.getJobManager().add( + CallSyncEventJob.createForJoin( + recipient.getId(), + callIdLocal.longValue(), + isIncoming + ) + ); + } + }); + } else { + Log.i(TAG, "sendGroupCallUpdateMessage -- ignoring non-join event for call link"); + } return; } From 63e6f955ed86ae189f02bbf6a7d0dfbc2f568eab Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 24 Apr 2024 14:17:58 -0300 Subject: [PATCH 079/113] Prevent getCallLinks from returning links without root keys. --- .../java/org/thoughtcrime/securesms/database/CallLinkTable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt index d06e4ea1f6..202d350c49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt @@ -377,7 +377,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database val statement = """ SELECT $projection FROM $TABLE_NAME - WHERE $noCallEvent AND NOT $REVOKED ${searchFilter?.where ?: ""} + WHERE $noCallEvent AND NOT $REVOKED ${searchFilter?.where ?: ""} AND $ROOT_KEY IS NOT NULL ORDER BY $ID DESC $limitOffset """.trimIndent() From fa32b7a883a83f59f0f1394c617ca76a2afa19f4 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 24 Apr 2024 15:10:12 -0300 Subject: [PATCH 080/113] Fix coloring on outgoing calls. --- .../main/java/org/thoughtcrime/securesms/database/CallTable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt index ec3a21e9e8..3e06fed7a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt @@ -1419,7 +1419,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl companion object Deserializer : Serializer { private fun isDisplayedAsMissedCallInUi(call: Call): Boolean { - return call.event in Event.DISPLAY_AS_MISSED_CALL || (call.event == Event.GENERIC_GROUP_CALL && !call.didLocalUserJoin && !call.isGroupCallActive) + return call.direction == Direction.INCOMING && (call.event in Event.DISPLAY_AS_MISSED_CALL || (call.event == Event.GENERIC_GROUP_CALL && !call.didLocalUserJoin && !call.isGroupCallActive)) } fun getMessageType(type: Type, direction: Direction, event: Event): Long { From 00a91e32fc3e6f86230a11f8f0893bfbd4fd2156 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Wed, 24 Apr 2024 14:41:01 -0400 Subject: [PATCH 081/113] Multiple skin tones for reaction bursts. --- .../stories/viewer/page/StoryViewerPageFragment.kt | 2 +- .../group/StoryGroupReplyBottomSheetDialogFragment.kt | 2 +- .../viewer/reply/reaction/MultiReactionBurstLayout.kt | 8 ++++---- .../stories/viewer/reply/reaction/OnReactionSentView.kt | 7 ++++--- .../reply/tabs/StoryViewsAndRepliesDialogFragment.kt | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) 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 aa92ca3dc7..b74bedfe31 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 @@ -498,7 +498,7 @@ class StoryViewerPageFragment : childFragmentManager.setFragmentResultListener(StoryDirectReplyDialogFragment.REQUEST_EMOJI, viewLifecycleOwner) { _, bundle -> val emoji = bundle.getString(StoryDirectReplyDialogFragment.REQUEST_EMOJI) if (emoji != null) { - reactionAnimationView.playForEmoji(emoji) + reactionAnimationView.playForEmoji(listOf(emoji)) viewModel.setIsDisplayingReactionAnimation(true) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyBottomSheetDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyBottomSheetDialogFragment.kt index e5734e3cd3..c00dc10bed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyBottomSheetDialogFragment.kt @@ -122,7 +122,7 @@ class StoryGroupReplyBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDi } override fun onReactionEmojiSelected(emoji: String) { - reactionView.playForEmoji(emoji) + reactionView.playForEmoji(listOf(emoji)) } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/MultiReactionBurstLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/MultiReactionBurstLayout.kt index 2b366eafc9..5318d312b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/MultiReactionBurstLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/MultiReactionBurstLayout.kt @@ -47,13 +47,13 @@ class MultiReactionBurstLayout @JvmOverloads constructor( .filter { it.value.groupBy { event -> event.sender }.size >= REACTION_COUNT_THRESHOLD } .values .map { it.sortedBy { event -> event.timestamp } } - .map { it[REACTION_COUNT_THRESHOLD - 1] } - .sortedBy { it.timestamp } + .sortedBy { it.last().timestamp } .take(MAX_SIMULTANEOUS_REACTIONS - cooldownTimes.filter { it.value > System.currentTimeMillis() }.size) .forEach { val reactionView = getNextReactionView() - reactionView.playForEmoji(it.reaction) - cooldownTimes[EmojiUtil.getCanonicalRepresentation(it.reaction)] = it.timestamp + cooldownDuration.inWholeMilliseconds + reactionView.playForEmoji(it.map { event -> event.reaction }) + val lastEvent = it.last() + cooldownTimes[EmojiUtil.getCanonicalRepresentation(lastEvent.reaction)] = lastEvent.timestamp + cooldownDuration.inWholeMilliseconds } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt index 074b8362c4..a79f29f05b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt @@ -32,7 +32,7 @@ class OnReactionSentView @JvmOverloads constructor( }) } - fun playForEmoji(emoji: CharSequence) { + fun playForEmoji(emojis: List) { motionLayout.progress = 0f listOf( @@ -47,8 +47,9 @@ class OnReactionSentView @JvmOverloads constructor( R.id.emoji_9, R.id.emoji_10, R.id.emoji_11 - ).forEach { - findViewById(it).setImageEmoji(emoji) + ).forEachIndexed { index, it -> + val emojiIndex = index % emojis.size + findViewById(it).setImageEmoji(emojis[emojiIndex]) } motionLayout.requestLayout() diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/tabs/StoryViewsAndRepliesDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/tabs/StoryViewsAndRepliesDialogFragment.kt index e536683c5e..88d520c1d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/tabs/StoryViewsAndRepliesDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/tabs/StoryViewsAndRepliesDialogFragment.kt @@ -159,7 +159,7 @@ class StoryViewsAndRepliesDialogFragment : FixedRoundedCornerBottomSheetDialogFr } override fun onReactionEmojiSelected(emoji: String) { - reactionView.playForEmoji(emoji) + reactionView.playForEmoji(listOf(emoji)) } private inner class PageChangeCallback : ViewPager2.OnPageChangeCallback() { From 95fbd7a31c50d2618dac917004dcc812df7786a0 Mon Sep 17 00:00:00 2001 From: moiseev-signal <122060238+moiseev-signal@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:19:40 -0700 Subject: [PATCH 082/113] Implement unauthenticated chat web socket connection via libsignal-net. --- .../dependencies/ApplicationDependencies.java | 4 +- .../ApplicationDependencyProvider.java | 42 +-- .../jobs/CheckServiceReachabilityJob.kt | 4 +- .../net/SignalWebSocketHealthMonitor.java | 4 +- .../securesms/util/FeatureFlags.java | 7 +- .../MockApplicationDependencyProvider.java | 2 +- libsignal-service/build.gradle.kts | 1 + .../websocket/LibSignalChatConnection.kt | 217 +++++++++++++++ .../internal/websocket/LibSignalNetwork.kt | 23 ++ ...on.java => OkHttpWebSocketConnection.java} | 47 ++-- .../internal/websocket/WebSocketConnection.kt | 39 +++ .../internal/websocket/WebsocketResponse.java | 8 +- .../websocket/LibSignalChatConnectionTest.kt | 251 ++++++++++++++++++ 13 files changed, 604 insertions(+), 45 deletions(-) create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt rename libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/{WebSocketConnection.java => OkHttpWebSocketConnection.java} (92%) create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.kt create mode 100644 libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java index 362e910e0e..d067d7f088 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java @@ -573,7 +573,7 @@ public static TypingStatusSender getTypingStatusSender() { if (signalWebSocket == null) { synchronized (LOCK) { if (signalWebSocket == null) { - signalWebSocket = provider.provideSignalWebSocket(() -> getSignalServiceNetworkAccess().getConfiguration()); + signalWebSocket = provider.provideSignalWebSocket(() -> getSignalServiceNetworkAccess().getConfiguration(), ApplicationDependencies::getLibsignalNetwork); } } } @@ -726,7 +726,7 @@ public interface Provider { @NonNull SignalCallManager provideSignalCallManager(); @NonNull PendingRetryReceiptManager providePendingRetryReceiptManager(); @NonNull PendingRetryReceiptCache providePendingRetryReceiptCache(); - @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier); + @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier); @NonNull SignalServiceDataStoreImpl provideProtocolStore(); @NonNull GiphyMp4Cache provideGiphyMp4Cache(); @NonNull SimpleExoPlayerPool provideExoPlayerPool(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index adfbf10e76..2bb2f43273 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -90,7 +90,10 @@ import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.websocket.WebSocketFactory; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork; import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; +import org.whispersystems.signalservice.internal.websocket.LibSignalChatConnection; +import org.whispersystems.signalservice.internal.websocket.OkHttpWebSocketConnection; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -287,10 +290,10 @@ public ApplicationDependencyProvider(@NonNull Application context) { } @Override - public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier) { + public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier) { SleepTimer sleepTimer = !SignalStore.account().isFcmEnabled() || SignalStore.internalValues().isWebsocketModeForced() ? new AlarmSleepTimer(context) : new UptimeSleepTimer() ; SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(context, sleepTimer); - SignalWebSocket signalWebSocket = new SignalWebSocket(provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor)); + SignalWebSocket signalWebSocket = new SignalWebSocket(provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor, libSignalNetworkSupplier)); healthMonitor.monitor(signalWebSocket); @@ -397,26 +400,35 @@ public ApplicationDependencyProvider(@NonNull Application context) { return provideClientZkOperations(signalServiceConfiguration).getReceiptOperations(); } - @NonNull WebSocketFactory provideWebSocketFactory(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull SignalWebSocketHealthMonitor healthMonitor) { + @NonNull WebSocketFactory provideWebSocketFactory(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull SignalWebSocketHealthMonitor healthMonitor, @NonNull Supplier libSignalNetworkSupplier) { return new WebSocketFactory() { @Override public WebSocketConnection createWebSocket() { - return new WebSocketConnection("normal", - signalServiceConfigurationSupplier.get(), - Optional.of(new DynamicCredentialsProvider()), - BuildConfig.SIGNAL_AGENT, - healthMonitor, - Stories.isFeatureEnabled()); + return new OkHttpWebSocketConnection("normal", + signalServiceConfigurationSupplier.get(), + Optional.of(new DynamicCredentialsProvider()), + BuildConfig.SIGNAL_AGENT, + healthMonitor, + Stories.isFeatureEnabled()); } @Override public WebSocketConnection createUnidentifiedWebSocket() { - return new WebSocketConnection("unidentified", - signalServiceConfigurationSupplier.get(), - Optional.empty(), - BuildConfig.SIGNAL_AGENT, - healthMonitor, - Stories.isFeatureEnabled()); + if (FeatureFlags.libSignalWebSocketEnabled()) { + var network = new LibSignalNetwork(libSignalNetworkSupplier.get()); + return new LibSignalChatConnection( + "libsignal-unauth", + network.createChatService(null), + healthMonitor, + false); + } else { + return new OkHttpWebSocketConnection("unidentified", + signalServiceConfigurationSupplier.get(), + Optional.empty(), + BuildConfig.SIGNAL_AGENT, + healthMonitor, + Stories.isFeatureEnabled()); + } } }; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CheckServiceReachabilityJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CheckServiceReachabilityJob.kt index 81a1701d94..8a98a9c46f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/CheckServiceReachabilityJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CheckServiceReachabilityJob.kt @@ -9,7 +9,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.stories.Stories import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider -import org.whispersystems.signalservice.internal.websocket.WebSocketConnection +import org.whispersystems.signalservice.internal.websocket.OkHttpWebSocketConnection import java.util.Optional import java.util.concurrent.TimeUnit @@ -64,7 +64,7 @@ class CheckServiceReachabilityJob private constructor(params: Parameters) : Base SignalStore.misc().lastCensorshipServiceReachabilityCheckTime = System.currentTimeMillis() - val uncensoredWebsocket = WebSocketConnection( + val uncensoredWebsocket = OkHttpWebSocketConnection( "uncensored-test", ApplicationDependencies.getSignalServiceNetworkAccess().uncensoredConfiguration, Optional.of( diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java b/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java index 9c7ed91dc1..8686b256c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java @@ -11,7 +11,7 @@ import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.websocket.HealthMonitor; import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState; -import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; +import org.whispersystems.signalservice.internal.websocket.OkHttpWebSocketConnection; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -33,7 +33,7 @@ public final class SignalWebSocketHealthMonitor implements HealthMonitor { /** * This is the amount of time in between sent keep alives. Must be greater than {@link SignalWebSocketHealthMonitor#KEEP_ALIVE_TIMEOUT} */ - private static final long KEEP_ALIVE_SEND_CADENCE = TimeUnit.SECONDS.toMillis(WebSocketConnection.KEEPALIVE_FREQUENCY_SECONDS); + private static final long KEEP_ALIVE_SEND_CADENCE = TimeUnit.SECONDS.toMillis(OkHttpWebSocketConnection.KEEPALIVE_FREQUENCY_SECONDS); /** * This is the amount of time we will wait for a response to the keep alive before we consider the websockets dead. diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index e83ee017fa..b555f7a8e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -129,6 +129,7 @@ public final class FeatureFlags { private static final String MESSAGE_BACKUPS = "android.messageBackups"; private static final String CAMERAX_CUSTOM_CONTROLLER = "android.cameraXCustomController"; private static final String REGISTRATION_V2 = "android.registration.v2"; + private static final String LIBSIGNAL_WEB_SOCKET_ENABLED = "android.libsignalWebSocketEnabled"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -208,7 +209,8 @@ public final class FeatureFlags { CDSI_LIBSIGNAL_NET, RX_MESSAGE_SEND, LINKED_DEVICE_LIFESPAN_SECONDS, - CAMERAX_CUSTOM_CONTROLLER + CAMERAX_CUSTOM_CONTROLLER, + LIBSIGNAL_WEB_SOCKET_ENABLED ); @VisibleForTesting @@ -754,6 +756,9 @@ public static boolean registrationV2() { return getBoolean(REGISTRATION_V2, false); } + /** Whether unauthenticated chat web socket is backed by libsignal-net */ + public static boolean libSignalWebSocketEnabled() { return getBoolean(LIBSIGNAL_WEB_SOCKET_ENABLED, false); } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java index b600822225..88df57b100 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java @@ -181,7 +181,7 @@ public class MockApplicationDependencyProvider implements ApplicationDependencie } @Override - public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier) { + public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier) { return null; } diff --git a/libsignal-service/build.gradle.kts b/libsignal-service/build.gradle.kts index 14b411fe43..8ef56c93a8 100644 --- a/libsignal-service/build.gradle.kts +++ b/libsignal-service/build.gradle.kts @@ -95,6 +95,7 @@ dependencies { testImplementation(testLibs.assertj.core) testImplementation(testLibs.conscrypt.openjdk.uber) testImplementation(testLibs.mockito.core) + testImplementation(testLibs.mockk) testFixturesImplementation(libs.libsignal.client) testFixturesImplementation(testLibs.junit.junit) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt new file mode 100644 index 0000000000..651294d522 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt @@ -0,0 +1,217 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.websocket + +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.schedulers.Schedulers +import io.reactivex.rxjava3.subjects.BehaviorSubject +import io.reactivex.rxjava3.subjects.SingleSubject +import org.signal.core.util.logging.Log +import org.signal.libsignal.internal.CompletableFuture +import org.signal.libsignal.net.ChatService +import org.whispersystems.signalservice.api.websocket.HealthMonitor +import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState +import java.time.Instant +import java.util.Optional +import kotlin.time.Duration.Companion.seconds +import org.signal.libsignal.net.ChatService.Request as LibSignalRequest +import org.signal.libsignal.net.ChatService.Response as LibSignalResponse + +/** + * Implements the WebSocketConnection interface via libsignal-net + * + * Notable implementation choices: + * - [chatService] contains both the authenticated and unauthenticated connections, + * which one to use for [sendRequest]/[sendResponse] is based on [isAuthenticated]. + * - keep-alive requests always use the [org.signal.libsignal.net.ChatService.unauthenticatedSendAndDebug] + * API, and log the debug info on success. + * - regular sends use [org.signal.libsignal.net.ChatService.unauthenticatedSend] and don't create any overhead. + * - [org.whispersystems.signalservice.api.websocket.WebSocketConnectionState] reporting is implemented + * as close as possible to the original implementation in + * [org.whispersystems.signalservice.internal.websocket.OkHttpWebSocketConnection]. + */ +class LibSignalChatConnection( + name: String, + private val chatService: ChatService, + private val healthMonitor: HealthMonitor, + val isAuthenticated: Boolean +) : WebSocketConnection { + + companion object { + private val TAG = Log.tag(LibSignalChatConnection::class.java) + private val SEND_TIMEOUT: Long = 10.seconds.inWholeMilliseconds + + private val KEEP_ALIVE_REQUEST = LibSignalRequest( + "GET", + "/v1/keepalive", + emptyMap(), + ByteArray(0), + SEND_TIMEOUT.toInt() + ) + } + + override val name = "[$name:${System.identityHashCode(this)}]" + + val state = BehaviorSubject.createDefault(WebSocketConnectionState.DISCONNECTED) + + override fun connect(): Observable { + Log.i(TAG, "$name Connecting...") + state.onNext(WebSocketConnectionState.CONNECTING) + val connect = if (isAuthenticated) { + chatService::connectAuthenticated + } else { + chatService::connectUnauthenticated + } + connect() + .whenComplete( + onSuccess = { debugInfo -> + Log.i(TAG, "$name Connected") + Log.d(TAG, "$name $debugInfo") + state.onNext(WebSocketConnectionState.CONNECTED) + }, + onFailure = { throwable -> + // TODO: [libsignal-net] Report WebSocketConnectionState.AUTHENTICATION_FAILED for 401 and 403 errors + Log.d(TAG, "$name Connect failed", throwable) + state.onNext(WebSocketConnectionState.FAILED) + } + ) + return state + } + + override fun isDead(): Boolean = false + + override fun disconnect() { + Log.i(TAG, "$name Disconnecting...") + state.onNext(WebSocketConnectionState.DISCONNECTING) + chatService.disconnect() + .whenComplete( + onSuccess = { + Log.i(TAG, "$name Disconnected") + state.onNext(WebSocketConnectionState.DISCONNECTED) + }, + onFailure = { throwable -> + Log.d(TAG, "$name Disconnect failed", throwable) + state.onNext(WebSocketConnectionState.DISCONNECTED) + } + ) + } + + override fun sendRequest(request: WebSocketRequestMessage): Single { + val single = SingleSubject.create() + val internalRequest = request.toLibSignalRequest() + val send = if (isAuthenticated) { + throw NotImplementedError("Authenticated socket is not yet supported") + } else { + chatService::unauthenticatedSend + } + send(internalRequest) + .whenComplete( + onSuccess = { response -> + when (response!!.status) { + in 400..599 -> { + healthMonitor.onMessageError(response.status, false) + } + } + // Here success means "we received the response" even if it is reporting an error. + // This is consistent with the behavior of the OkHttpWebSocketConnection. + single.onSuccess(response.toWebsocketResponse(isUnidentified = !isAuthenticated)) + }, + onFailure = { throwable -> + Log.i(TAG, "$name sendRequest failed", throwable) + single.onError(throwable) + } + ) + return single.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()) + } + + override fun sendKeepAlive() { + Log.i(TAG, "$name Sending keep alive...") + val send = if (isAuthenticated) { + throw NotImplementedError("Authenticated socket is not yet supported") + } else { + chatService::unauthenticatedSendAndDebug + } + send(KEEP_ALIVE_REQUEST) + .whenComplete( + onSuccess = { debugResponse -> + Log.i(TAG, "$name Keep alive - success") + Log.d(TAG, "$name $debugResponse") + when (debugResponse!!.response.status) { + in 200..299 -> { + healthMonitor.onKeepAliveResponse( + Instant.now().toEpochMilli(), // ignored. can be any value + false + ) + } + + in 400..599 -> { + healthMonitor.onMessageError(debugResponse.response.status, isAuthenticated) + } + + else -> { + Log.w(TAG, "$name Unsupported keep alive response status: ${debugResponse.response.status}") + } + } + }, + onFailure = { throwable -> + Log.i(TAG, "$name Keep alive - failed") + Log.d(TAG, "$name $throwable") + state.onNext(WebSocketConnectionState.DISCONNECTED) + } + ) + } + + override fun readRequestIfAvailable(): Optional { + throw NotImplementedError() + } + + override fun readRequest(timeoutMillis: Long): WebSocketRequestMessage { + throw NotImplementedError() + } + + override fun sendResponse(response: WebSocketResponseMessage?) { + throw NotImplementedError() + } + + private fun WebSocketRequestMessage.toLibSignalRequest(timeout: Long = SEND_TIMEOUT): LibSignalRequest { + return LibSignalRequest( + this.verb?.uppercase() ?: "GET", + this.path ?: "", + this.headers.associate { + val parts = it.split(':', limit = 2) + if (parts.size != 2) { + throw IllegalArgumentException("Headers must contain at least one colon") + } + parts[0] to parts[1] + }, + this.body?.toByteArray() ?: byteArrayOf(), + timeout.toInt() + ) + } + + private fun LibSignalResponse.toWebsocketResponse(isUnidentified: Boolean): WebsocketResponse { + return WebsocketResponse( + this.status, + this.body.decodeToString(), + this.headers, + isUnidentified + ) + } + + private fun CompletableFuture.whenComplete( + onSuccess: ((T?) -> Unit), + onFailure: ((Throwable) -> Unit) + ): CompletableFuture { + return this.whenComplete { value, throwable -> + if (throwable != null) { + onFailure(throwable) + } else { + onSuccess(value) + } + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt new file mode 100644 index 0000000000..625ad2f44c --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.websocket + +import org.signal.libsignal.net.ChatService +import org.signal.libsignal.net.Network +import org.whispersystems.signalservice.api.util.CredentialsProvider + +/** + * Makes Network API more ergonomic to use with Android client types + */ +class LibSignalNetwork(private val inner: Network) { + fun createChatService( + credentialsProvider: CredentialsProvider? = null + ): ChatService { + val username = credentialsProvider?.username ?: "" + val password = credentialsProvider?.password ?: "" + return inner.createChatService(username, password) + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java similarity index 92% rename from libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java rename to libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java index e39142812e..305e69624d 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java @@ -2,7 +2,6 @@ import org.signal.libsignal.protocol.logging.Log; import org.signal.libsignal.protocol.util.Pair; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.Tls12SocketFactory; @@ -25,7 +24,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -51,10 +49,10 @@ import okhttp3.WebSocketListener; import okio.ByteString; -public class WebSocketConnection extends WebSocketListener { +public class OkHttpWebSocketConnection extends WebSocketListener implements WebSocketConnection { - private static final String TAG = WebSocketConnection.class.getSimpleName(); - public static final int KEEPALIVE_FREQUENCY_SECONDS = 30; + private static final String TAG = OkHttpWebSocketConnection.class.getSimpleName(); + public static final int KEEPALIVE_FREQUENCY_SECONDS = 30; private final LinkedList incomingRequests = new LinkedList<>(); private final Map outgoingRequests = new HashMap<>(); @@ -76,22 +74,22 @@ public class WebSocketConnection extends WebSocketListener { private WebSocket client; - public WebSocketConnection(String name, - SignalServiceConfiguration serviceConfiguration, - Optional credentialsProvider, - String signalAgent, - HealthMonitor healthMonitor, - boolean allowStories) { + public OkHttpWebSocketConnection(String name, + SignalServiceConfiguration serviceConfiguration, + Optional credentialsProvider, + String signalAgent, + HealthMonitor healthMonitor, + boolean allowStories) { this(name, serviceConfiguration, credentialsProvider, signalAgent, healthMonitor, "", allowStories); } - public WebSocketConnection(String name, - SignalServiceConfiguration serviceConfiguration, - Optional credentialsProvider, - String signalAgent, - HealthMonitor healthMonitor, - String extraPathUri, - boolean allowStories) + public OkHttpWebSocketConnection(String name, + SignalServiceConfiguration serviceConfiguration, + Optional credentialsProvider, + String signalAgent, + HealthMonitor healthMonitor, + String extraPathUri, + boolean allowStories) { this.name = "[" + name + ":" + System.identityHashCode(this) + "]"; this.trustStore = serviceConfiguration.getSignalServiceUrls()[0].getTrustStore(); @@ -108,6 +106,7 @@ public WebSocketConnection(String name, this.random = new SecureRandom(); } + @Override public String getName() { return name; } @@ -123,6 +122,7 @@ private Pair getConnectionInfo() { } } + @Override public synchronized Observable connect() { log("connect()"); @@ -130,7 +130,7 @@ public synchronized Observable connect() { Pair connectionInfo = getConnectionInfo(); SignalServiceUrl serviceUrl = connectionInfo.first(); String wsUri = connectionInfo.second(); - String filledUri; + String filledUri; if (credentialsProvider.isPresent()) { filledUri = String.format(wsUri, credentialsProvider.get().getUsername(), credentialsProvider.get().getPassword()); @@ -177,10 +177,12 @@ public synchronized Observable connect() { return webSocketState; } + @Override public synchronized boolean isDead() { return client == null; } + @Override public synchronized void disconnect() { log("disconnect()"); @@ -193,6 +195,7 @@ public synchronized void disconnect() { notifyAll(); } + @Override public synchronized Optional readRequestIfAvailable() { if (incomingRequests.size() > 0) { return Optional.of(incomingRequests.removeFirst()); @@ -201,6 +204,7 @@ public synchronized Optional readRequestIfAvailable() { } } + @Override public synchronized WebSocketRequestMessage readRequest(long timeoutMillis) throws TimeoutException, IOException { @@ -223,6 +227,7 @@ public synchronized WebSocketRequestMessage readRequest(long timeoutMillis) } } + @Override public synchronized Single sendRequest(WebSocketRequestMessage request) throws IOException { if (client == null) { throw new IOException("No connection!"); @@ -246,6 +251,7 @@ public synchronized Single sendRequest(WebSocketRequestMessag .timeout(10, TimeUnit.SECONDS, Schedulers.io()); } + @Override public synchronized void sendResponse(WebSocketResponseMessage response) throws IOException { if (client == null) { throw new IOException("Connection closed!"); @@ -261,9 +267,10 @@ public synchronized void sendResponse(WebSocketResponseMessage response) throws } } + @Override public synchronized void sendKeepAlive() throws IOException { if (client != null) { - log( "Sending keep alive..."); + log("Sending keep alive..."); long id = System.currentTimeMillis(); byte[] message = new WebSocketMessage.Builder() .type(WebSocketMessage.Type.REQUEST) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.kt new file mode 100644 index 0000000000..d6e0e1197f --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.kt @@ -0,0 +1,39 @@ +package org.whispersystems.signalservice.internal.websocket + +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single +import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState +import java.io.IOException +import java.util.Optional +import java.util.concurrent.TimeoutException + +/** + * Common interface for the web socket connection API + * + * At the time of this writing there are two implementations available: + * - OkHttpWebSocketConnection - the original Android client implementation in Java using OkHttp library + * - LibSignalChatConnection - the wrapper around libsignal's [org.signal.libsignal.net.ChatService] + */ +interface WebSocketConnection { + val name: String + + fun connect(): Observable + + fun isDead(): Boolean + + fun disconnect() + + @Throws(IOException::class) + fun sendRequest(request: WebSocketRequestMessage): Single + + @Throws(IOException::class) + fun sendKeepAlive() + + fun readRequestIfAvailable(): Optional + + @Throws(TimeoutException::class, IOException::class) + fun readRequest(timeoutMillis: Long): WebSocketRequestMessage + + @Throws(IOException::class) + fun sendResponse(response: WebSocketResponseMessage?) +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebsocketResponse.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebsocketResponse.java index 79ae17c9b2..940aa0aba8 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebsocketResponse.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebsocketResponse.java @@ -15,9 +15,13 @@ public class WebsocketResponse { private final boolean unidentified; WebsocketResponse(int status, String body, List headers, boolean unidentified) { + this(status, body, parseHeaders(headers), unidentified); + } + + WebsocketResponse(int status, String body, Map headerMap, boolean unidentified) { this.status = status; this.body = body; - this.headers = parseHeaders(headers); + this.headers = headerMap; this.unidentified = unidentified; } @@ -41,7 +45,7 @@ private static Map parseHeaders(List rawHeaders) { Map headers = new HashMap<>(rawHeaders.size()); for (String raw : rawHeaders) { - if (raw != null && raw.length() > 0) { + if (raw != null && !raw.isEmpty()) { int colonIndex = raw.indexOf(":"); if (colonIndex > 0 && colonIndex < raw.length() - 1) { diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt b/libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt new file mode 100644 index 0000000000..63350eacc7 --- /dev/null +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt @@ -0,0 +1,251 @@ +package org.whispersystems.signalservice.internal.websocket + +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import io.reactivex.rxjava3.observers.TestObserver +import org.junit.Before +import org.junit.Test +import org.signal.libsignal.internal.CompletableFuture +import org.signal.libsignal.net.ChatService +import org.signal.libsignal.net.ChatService.DebugInfo +import org.signal.libsignal.net.IpType +import org.whispersystems.signalservice.api.websocket.HealthMonitor +import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState +import java.util.concurrent.CountDownLatch +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import org.signal.libsignal.net.ChatService.Response as LibSignalResponse +import org.signal.libsignal.net.ChatService.ResponseAndDebugInfo as LibSignalDebugResponse + +class LibSignalChatConnectionTest { + + private val executor: ExecutorService = Executors.newSingleThreadExecutor() + private val healthMonitor = mockk() + private val chatService = mockk() + private val connection = LibSignalChatConnection("test", chatService, healthMonitor, isAuthenticated = false) + + @Before + fun before() { + clearAllMocks() + every { healthMonitor.onMessageError(any(), any()) } + every { healthMonitor.onKeepAliveResponse(any(), any()) } + } + + @Test + fun orderOfStatesOnSuccessfulConnect() { + val latch = CountDownLatch(1) + + every { chatService.connectUnauthenticated() } answers { + delay { + it.complete(DEBUG_INFO) + latch.countDown() + } + } + + val observer = TestObserver() + connection.state.subscribe(observer) + + connection.connect() + + latch.await(100, TimeUnit.MILLISECONDS) + + observer.assertNotComplete() + observer.assertValues( + WebSocketConnectionState.DISCONNECTED, + WebSocketConnectionState.CONNECTING, + WebSocketConnectionState.CONNECTED + ) + } + + @Test + fun orderOfStatesOnConnectionFailure() { + val connectionException = RuntimeException("connect failed") + val latch = CountDownLatch(1) + + every { chatService.connectUnauthenticated() } answers { + delay { + it.completeExceptionally(connectionException) + } + } + + val observer = TestObserver() + connection.state.subscribe(observer) + + connection.connect() + + latch.await(100, TimeUnit.MILLISECONDS) + + observer.assertNotComplete() + observer.assertValues( + WebSocketConnectionState.DISCONNECTED, + WebSocketConnectionState.CONNECTING, + WebSocketConnectionState.FAILED + ) + } + + @Test + fun orderOfStatesOnConnectAndDisconnect() { + val connectLatch = CountDownLatch(1) + val disconnectLatch = CountDownLatch(1) + + every { chatService.connectUnauthenticated() } answers { + delay { + it.complete(DEBUG_INFO) + connectLatch.countDown() + } + } + every { chatService.disconnect() } answers { + delay { + it.complete(null) + disconnectLatch.countDown() + } + } + + val observer = TestObserver() + + connection.state.subscribe(observer) + + connection.connect() + connectLatch.await(100, TimeUnit.MILLISECONDS) + connection.disconnect() + disconnectLatch.await(100, TimeUnit.MILLISECONDS) + + observer.assertNotComplete() + observer.assertValues( + WebSocketConnectionState.DISCONNECTED, + WebSocketConnectionState.CONNECTING, + WebSocketConnectionState.CONNECTED, + WebSocketConnectionState.DISCONNECTING, + WebSocketConnectionState.DISCONNECTED + ) + } + + @Test + fun orderOfStatesOnDisconnectFailure() { + val disconnectException = RuntimeException("disconnect failed") + + val latch = CountDownLatch(1) + + every { chatService.disconnect() } answers { + delay { + it.completeExceptionally(disconnectException) + } + } + + val observer = TestObserver() + + connection.state.subscribe(observer) + + connection.disconnect() + + latch.await(100, TimeUnit.MILLISECONDS) + + observer.assertNotComplete() + observer.assertValues( + WebSocketConnectionState.DISCONNECTED, + WebSocketConnectionState.DISCONNECTING, + WebSocketConnectionState.DISCONNECTED + ) + } + + @Test + fun keepAliveSuccess() { + val latch = CountDownLatch(1) + + every { chatService.unauthenticatedSendAndDebug(any()) } answers { + delay { + it.complete(make_debug_response(RESPONSE_SUCCESS)) + latch.countDown() + } + } + + connection.sendKeepAlive() + + latch.await(100, TimeUnit.MILLISECONDS) + + verify(exactly = 1) { + healthMonitor.onKeepAliveResponse(any(), false) + } + verify(exactly = 0) { + healthMonitor.onMessageError(any(), any()) + } + } + + @Test + fun keepAliveFailure() { + for (response in listOf(RESPONSE_ERROR, RESPONSE_SERVER_ERROR)) { + val latch = CountDownLatch(1) + + every { chatService.unauthenticatedSendAndDebug(any()) } answers { + delay { + it.complete(make_debug_response(response)) + } + } + + connection.sendKeepAlive() + latch.await(100, TimeUnit.MILLISECONDS) + + verify(exactly = 1) { + healthMonitor.onMessageError(response.status, false) + } + verify(exactly = 0) { + healthMonitor.onKeepAliveResponse(any(), any()) + } + } + } + + @Test + fun keepAliveConnectionFailure() { + val connectionFailure = RuntimeException("Sending keep-alive failed") + val latch = CountDownLatch(1) + + every { + chatService.unauthenticatedSendAndDebug(any()) + } answers { + delay { + it.completeExceptionally(connectionFailure) + } + } + + val observer = TestObserver() + connection.state.subscribe(observer) + + connection.sendKeepAlive() + + latch.await(100, TimeUnit.MILLISECONDS) + + observer.assertNotComplete() + observer.assertValues( + // This is the starting state + WebSocketConnectionState.DISCONNECTED, + // This one is the result of a keep-alive failure + WebSocketConnectionState.DISCONNECTED + ) + verify(exactly = 0) { + healthMonitor.onKeepAliveResponse(any(), any()) + healthMonitor.onMessageError(any(), any()) + } + } + + private fun delay(action: ((CompletableFuture) -> Unit)): CompletableFuture { + val future = CompletableFuture() + executor.submit { + action(future) + } + return future + } + + companion object { + private val DEBUG_INFO: DebugInfo = DebugInfo(0, IpType.UNKNOWN, 100, "") + private val RESPONSE_SUCCESS = LibSignalResponse(200, "", emptyMap(), byteArrayOf()) + private val RESPONSE_ERROR = LibSignalResponse(400, "", emptyMap(), byteArrayOf()) + private val RESPONSE_SERVER_ERROR = LibSignalResponse(500, "", emptyMap(), byteArrayOf()) + + private fun make_debug_response(response: LibSignalResponse): LibSignalDebugResponse { + return LibSignalDebugResponse(response, DEBUG_INFO) + } + } +} From e60b32202efeb17b178f7307e3f62604dfcaccee Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 25 Apr 2024 10:18:01 -0300 Subject: [PATCH 083/113] Improved missed call state handling. --- .../conversation/MarkReadHelper.java | 2 ++ .../securesms/database/CallTable.kt | 24 +++++++++++++++++ .../securesms/database/MessageTable.kt | 20 +++++++++++++- .../helpers/SignalDatabaseMigrations.kt | 6 +++-- .../V229_MarkMissedCallEventsNotified.kt | 26 +++++++++++++++++++ .../securesms/jobs/CallLogEventSendJob.kt | 19 ++++++++++++++ .../messages/SyncMessageProcessor.kt | 15 ++++++++--- .../notifications/MarkReadReceiver.java | 18 ++++++++++++- .../v2/NotificationStateProvider.kt | 3 ++- .../src/main/protowire/SignalService.proto | 5 ++-- 10 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V229_MarkMissedCallEventsNotified.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java index a9a85ba131..b2d747cca3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java @@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.util.Debouncer; import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; @@ -67,6 +68,7 @@ public void onViewsRevealed(long timestamp) { ApplicationDependencies.getMessageNotifier().updateNotification(context); MarkReadReceiver.process(infos); + MarkReadReceiver.processCallEvents(Collections.singletonList(conversationId), timestamp); }); }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt index 3e06fed7a1..3438ab5312 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt @@ -118,6 +118,30 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl notifyConversationListListeners() } + fun markAllCallEventsWithPeerBeforeTimestampRead(peer: RecipientId, timestamp: Long): Call? { + val latestCallAsOfTimestamp = writableDatabase.withinTransaction { db -> + val updated = db.update(TABLE_NAME) + .values(READ to ReadState.serialize(ReadState.READ)) + .where("$PEER = ? AND $TIMESTAMP <= ?", peer.toLong(), timestamp) + .run() + + if (updated == 0) { + null + } else { + db.select() + .from(TABLE_NAME) + .where("$PEER = ? AND $TIMESTAMP <= ?", peer.toLong(), timestamp) + .orderBy("$TIMESTAMP DESC") + .limit(1) + .run() + .readToSingleObject(Call.Deserializer) + } + } + + notifyConversationListListeners() + return latestCallAsOfTimestamp + } + fun getUnreadMissedCallCount(): Long { return readableDatabase .count() 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 e856df7c80..97f088fbe8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -433,6 +433,12 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat ($TYPE = ${MessageTypes.GROUP_CALL_TYPE}) )""" + private const val IS_MISSED_CALL_TYPE_CLAUSE = """( + ($TYPE = ${MessageTypes.MISSED_AUDIO_CALL_TYPE}) + OR + ($TYPE = ${MessageTypes.MISSED_VIDEO_CALL_TYPE}) + )""" + private val outgoingTypeClause: String by lazy { MessageTypes.OUTGOING_MESSAGE_TYPES .map { "($TABLE_NAME.$TYPE & ${MessageTypes.BASE_TYPE_MASK} = $it)" } @@ -4647,7 +4653,19 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat return readableDatabase .select(*MMS_PROJECTION) .from(TABLE_NAME) - .where("$NOTIFIED = 0 AND $STORY_TYPE = 0 AND $LATEST_REVISION_ID IS NULL AND ($READ = 0 OR $REACTIONS_UNREAD = 1 ${if (stickyQuery.isNotEmpty()) "OR ($stickyQuery)" else ""})") + .where( + """ + $NOTIFIED = 0 + AND $STORY_TYPE = 0 + AND $LATEST_REVISION_ID IS NULL + AND ( + $READ = 0 + OR $REACTIONS_UNREAD = 1 + ${if (stickyQuery.isNotEmpty()) "OR ($stickyQuery)" else ""} + OR ($IS_MISSED_CALL_TYPE_CLAUSE AND EXISTS (SELECT 1 FROM ${CallTable.TABLE_NAME} WHERE ${CallTable.MESSAGE_ID} = $TABLE_NAME.$ID AND ${CallTable.EVENT} = ${CallTable.Event.serialize(CallTable.Event.MISSED)} AND ${CallTable.READ} = 0)) + ) + """.trimIndent() + ) .orderBy("$DATE_RECEIVED ASC") .run() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 4f8fbc0f93..27ba81ff5d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -86,6 +86,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V225_AddLocalUserJo import org.thoughtcrime.securesms.database.helpers.migration.V226_AddAttachmentMediaIdIndex import org.thoughtcrime.securesms.database.helpers.migration.V227_AddAttachmentArchiveTransferState import org.thoughtcrime.securesms.database.helpers.migration.V228_AddNameCollisionTables +import org.thoughtcrime.securesms.database.helpers.migration.V229_MarkMissedCallEventsNotified /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -174,10 +175,11 @@ object SignalDatabaseMigrations { 225 to V225_AddLocalUserJoinedStateAndGroupCallActiveState, 226 to V226_AddAttachmentMediaIdIndex, 227 to V227_AddAttachmentArchiveTransferState, - 228 to V228_AddNameCollisionTables + 228 to V228_AddNameCollisionTables, + 229 to V229_MarkMissedCallEventsNotified ) - const val DATABASE_VERSION = 228 + const val DATABASE_VERSION = 229 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V229_MarkMissedCallEventsNotified.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V229_MarkMissedCallEventsNotified.kt new file mode 100644 index 0000000000..16469407cb --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V229_MarkMissedCallEventsNotified.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * In order to both correct how we display missed calls and not spam users, + * we want to mark every missed call event in the database as notified. + */ +@Suppress("ClassName") +object V229_MarkMissedCallEventsNotified : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL( + """ + UPDATE message + SET notified = 1 + WHERE (type = 3) OR (type = 8) + """.trimIndent() + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLogEventSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLogEventSendJob.kt index b087aa260a..81de40c107 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLogEventSendJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLogEventSendJob.kt @@ -66,6 +66,25 @@ class CallLogEventSendJob private constructor( type = SyncMessage.CallLogEvent.Type.MARKED_AS_READ ) ) + + @JvmStatic + @WorkerThread + fun forMarkedAsReadInConversation( + call: CallTable.Call + ) = CallLogEventSendJob( + Parameters.Builder() + .setQueue("CallLogEventSendJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .addConstraint(NetworkConstraint.KEY) + .build(), + SyncMessage.CallLogEvent( + timestamp = call.timestamp, + callId = call.callId, + conversationId = Recipient.resolved(call.peer).requireCallConversationId().toByteString(), + type = SyncMessage.CallLogEvent.Type.MARKED_AS_READ_IN_CONVERSATION + ) + ) } override fun serialize(): ByteArray = CallLogEventSendJobData.Builder() diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index 3e37771980..5846da2949 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -1246,20 +1246,20 @@ object SyncMessageProcessor { if (call != null) { log(envelopeTimestamp, "Synchronizing call log event with exact call data.") - synchronizeCallLogEventViaTimestamp(envelopeTimestamp, callLogEvent.type, call.timestamp) + synchronizeCallLogEventViaTimestamp(envelopeTimestamp, callLogEvent.type, call.timestamp, peer) return } } if (timestamp != null) { warn(envelopeTimestamp, "Synchronize call log event using timestamp instead of exact values") - synchronizeCallLogEventViaTimestamp(envelopeTimestamp, callLogEvent.type, timestamp) + synchronizeCallLogEventViaTimestamp(envelopeTimestamp, callLogEvent.type, timestamp, peer) } else { log(envelopeTimestamp, "Failed to synchronize call log event, not enough information.") } } - private fun synchronizeCallLogEventViaTimestamp(envelopeTimestamp: Long, eventType: CallLogEvent.Type?, timestamp: Long) { + private fun synchronizeCallLogEventViaTimestamp(envelopeTimestamp: Long, eventType: CallLogEvent.Type?, timestamp: Long, peer: RecipientId?) { when (eventType) { CallLogEvent.Type.CLEAR -> { SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(timestamp) @@ -1270,6 +1270,15 @@ object SyncMessageProcessor { SignalDatabase.calls.markAllCallEventsRead(timestamp) } + CallLogEvent.Type.MARKED_AS_READ_IN_CONVERSATION -> { + if (peer == null) { + warn(envelopeTimestamp, "Cannot synchronize conversation calls, missing peer.") + return + } + + SignalDatabase.calls.markAllCallEventsWithPeerBeforeTimestampRead(peer, timestamp) + } + else -> log(envelopeTimestamp, "Synchronize call log event has an invalid type $eventType, ignoring.") } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java index 5df2a5e9d5..2a4761f31c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java @@ -12,16 +12,17 @@ import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.database.CallTable; import org.thoughtcrime.securesms.database.MessageTable.ExpirationInfo; import org.thoughtcrime.securesms.database.MessageTable.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MessageTable.SyncMessageId; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.jobs.CallLogEventSendJob; import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob; import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; import org.thoughtcrime.securesms.notifications.v2.ConversationId; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; import java.util.ArrayList; import java.util.LinkedList; @@ -62,6 +63,7 @@ public void onReceive(final Context context, Intent intent) { } process(messageIdsCollection); + processCallEvents(threads, System.currentTimeMillis()); ApplicationDependencies.getMessageNotifier().updateNotification(context); finisher.finish(); @@ -102,6 +104,20 @@ public static void process(@NonNull List markedReadMessages) }); } + public static void processCallEvents(@NonNull List threads, long timestamp) { + List peers = SignalDatabase.threads().getRecipientIdsForThreadIds(threads.stream() + .filter(it -> it.getGroupStoryId() == null) + .map(ConversationId::getThreadId) + .collect(java.util.stream.Collectors.toList())); + + for (RecipientId peer : peers) { + CallTable.Call lastCallInThread = SignalDatabase.calls().markAllCallEventsWithPeerBeforeTimestampRead(peer, timestamp); + if (lastCallInThread != null) { + ApplicationDependencies.getJobManager().add(CallLogEventSendJob.forMarkedAsReadInConversation(lastCallInThread)); + } + } + } + private static void scheduleDeletion(@NonNull List expirationInfo) { if (expirationInfo.size() > 0) { long now = System.currentTimeMillis(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationStateProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationStateProvider.kt index 7bf9205f21..433b70178f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationStateProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationStateProvider.kt @@ -138,6 +138,7 @@ object NotificationStateProvider { ) { private val isGroupStoryReply: Boolean = thread.groupStoryId != null private val isUnreadIncoming: Boolean = isUnreadMessage && !messageRecord.isOutgoing && !isGroupStoryReply + private val isIncomingMissedCall: Boolean = !messageRecord.isOutgoing && (messageRecord.isMissedAudioCall || messageRecord.isMissedVideoCall) private val isNotifiableGroupStoryMessage: Boolean = isUnreadMessage && @@ -146,7 +147,7 @@ object NotificationStateProvider { (isParentStorySentBySelf || messageRecord.hasSelfMention() || (hasSelfRepliedToStory && !messageRecord.isStoryReaction())) fun includeMessage(notificationProfile: NotificationProfile?): MessageInclusion { - return if (isUnreadIncoming || stickyThread || isNotifiableGroupStoryMessage) { + return if (isUnreadIncoming || stickyThread || isNotifiableGroupStoryMessage || isIncomingMissedCall) { if (threadRecipient.isMuted && (threadRecipient.isDoNotNotifyMentions || !messageRecord.hasSelfMention())) { MessageInclusion.MUTE_FILTERED } else if (notificationProfile != null && !notificationProfile.isRecipientAllowed(threadRecipient.id) && !(notificationProfile.allowAllMentions && messageRecord.hasSelfMention())) { diff --git a/libsignal-service/src/main/protowire/SignalService.proto b/libsignal-service/src/main/protowire/SignalService.proto index 0707e787a7..c6d482d649 100644 --- a/libsignal-service/src/main/protowire/SignalService.proto +++ b/libsignal-service/src/main/protowire/SignalService.proto @@ -637,8 +637,9 @@ message SyncMessage { message CallLogEvent { enum Type { - CLEAR = 0; - MARKED_AS_READ = 1; + CLEAR = 0; + MARKED_AS_READ = 1; + MARKED_AS_READ_IN_CONVERSATION = 2; } optional Type type = 1; From d983265e08846478334b05287695e45c143ce31b Mon Sep 17 00:00:00 2001 From: Clark Date: Thu, 25 Apr 2024 09:23:36 -0400 Subject: [PATCH 084/113] Persist group state in backup. --- .../RecipientTableBackupExtensions.kt | 174 ++++++++++++++++-- app/src/main/protowire/Backup.proto | 68 +++++++ 2 files changed, 226 insertions(+), 16 deletions(-) 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 9a99d9bb86..3aab49c032 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 @@ -13,6 +13,7 @@ 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 @@ -23,7 +24,16 @@ 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.BackupState import org.thoughtcrime.securesms.backup.v2.proto.AccountData import org.thoughtcrime.securesms.backup.v2.proto.Contact @@ -37,13 +47,14 @@ import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras import org.thoughtcrime.securesms.dependencies.ApplicationDependencies 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 import org.whispersystems.signalservice.api.util.toByteArray @@ -103,7 +114,8 @@ fun RecipientTable.getGroupsForBackup(): BackupGroupIterator { "${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.TITLE}", + "${GroupTable.TABLE_NAME}.${GroupTable.V2_DECRYPTED_GROUP}" ) .from( """ @@ -219,9 +231,8 @@ private fun RecipientTable.restoreGroupFromBackup(group: Group): RecipientId { val masterKey = GroupMasterKey(group.masterKey.toByteArray()) val groupId = GroupId.v2(masterKey) - val placeholderState = DecryptedGroup.Builder() - .revision(GroupsV2StateProcessor.PLACEHOLDER_REVISION) - .build() + val operations = ApplicationDependencies.getGroupsV2Operations().forGroup(GroupSecretParams.deriveFromMasterKey(masterKey)) + val decryptedState = group.snapshot!!.toDecryptedGroup(operations) val values = ContentValues().apply { put(RecipientTable.GROUP_ID, groupId.toString()) @@ -236,20 +247,148 @@ private fun RecipientTable.restoreGroupFromBackup(group: Group): RecipientId { } val recipientId = writableDatabase.insert(RecipientTable.TABLE_NAME, null, values) - val groupValues = ContentValues().apply { - put(GroupTable.RECIPIENT_ID, recipientId) - put(GroupTable.GROUP_ID, groupId.toString()) - put(GroupTable.TITLE, group.name) - put(GroupTable.V2_MASTER_KEY, masterKey.serialize()) - put(GroupTable.V2_DECRYPTED_GROUP, placeholderState.encode()) - put(GroupTable.V2_REVISION, placeholderState.revision) - put(GroupTable.SHOW_AS_STORY_STATE, group.storySendMode.toGroupShowAsStoryState().code) - } - writableDatabase.insert(GroupTable.TABLE_NAME, null, groupValues) + SignalDatabase.groups.create(masterKey, decryptedState) return RecipientId.from(recipientId) } +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 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 { + return Group.GroupSnapshot( + title = title, + avatar = avatar, + disappearingMessagesTimer = disappearingMessagesTimer?.duration ?: 0, + accessControl = accessControl?.toSnapshot(), + version = revision, + members = members.map { it.toSnapshot() }, + membersPendingProfileKey = pendingMembers.map { it.toSnapshot() }, + membersPendingAdminApproval = requestingMembers.map { it.toSnapshot() }, + inviteLinkPassword = inviteLinkPassword, + description = description, + announcements_only = isAnnouncementGroup == EnabledState.ENABLED, + members_banned = bannedMembers.map { it.toSnapshot() } + ) +} + +private fun Group.Member.toLocal(): DecryptedMember { + return DecryptedMember(aciBytes = userId, role = role.toLocal(), profileKey = profileKey, joinedAtRevision = joinedAtVersion) +} + +private fun DecryptedMember.toSnapshot(): Group.Member { + return Group.Member(userId = aciBytes, role = role.toSnapshot(), profileKey = profileKey, 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 = serviceIdBytes, + role = role.toSnapshot() + ), + addedByUserId = addedByAci, + timestamp = timestamp + ) +} + +private fun Group.MemberPendingAdminApproval.toLocal(): DecryptedRequestingMember { + return DecryptedRequestingMember( + aciBytes = userId, + profileKey = profileKey, + timestamp = timestamp + ) +} + +private fun DecryptedRequestingMember.toSnapshot(): Group.MemberPendingAdminApproval { + return Group.MemberPendingAdminApproval( + userId = aciBytes, + profileKey = profileKey, + timestamp = timestamp + ) +} + +private fun Group.MemberBanned.toLocal(): DecryptedBannedMember { + return DecryptedBannedMember( + serviceIdBytes = userId, + timestamp = timestamp + ) +} + +private fun DecryptedBannedMember.toSnapshot(): Group.MemberBanned { + return Group.MemberBanned( + userId = serviceIdBytes, + timestamp = timestamp + ) +} + +private fun Group.GroupSnapshot.toDecryptedGroup(operations: GroupsV2Operations.GroupOperations): DecryptedGroup { + return DecryptedGroup( + title = title, + avatar = avatar, + disappearingMessagesTimer = DecryptedTimer(duration = disappearingMessagesTimer), + accessControl = accessControl?.toLocal(), + revision = version, + members = members.map { member -> member.toLocal() }, + pendingMembers = membersPendingProfileKey.map { pending -> pending.toLocal(operations) }, + requestingMembers = membersPendingAdminApproval.map { requesting -> requesting.toLocal() }, + inviteLinkPassword = inviteLinkPassword, + description = description, + isAnnouncementGroup = if (announcements_only) EnabledState.ENABLED else EnabledState.DISABLED, + bannedMembers = members_banned.map { it.toLocal() } + ) +} + private fun Contact.toLocalExtras(): RecipientExtras { return RecipientExtras( hideStory = this.hideStory @@ -331,6 +470,8 @@ class BackupGroupIterator(private val cursor: Cursor) : Iterator GroupSnapshot to avoid the naming conflict. + message GroupSnapshot { + bytes publicKey = 1; + string title = 2; + string description = 11; + string avatar = 3; + uint32 disappearingMessagesTimer = 4; + AccessControl accessControl = 5; + uint32 version = 6; + repeated Member members = 7; + repeated MemberPendingProfileKey membersPendingProfileKey = 8; + repeated MemberPendingAdminApproval membersPendingAdminApproval = 9; + bytes inviteLinkPassword = 10; + bool announcements_only = 12; + repeated MemberBanned members_banned = 13; + } + + message Member { + enum Role { + UNKNOWN = 0; + DEFAULT = 1; + ADMINISTRATOR = 2; + } + + bytes userId = 1; + Role role = 2; + bytes profileKey = 3; + bytes presentation = 4; + uint32 joinedAtVersion = 5; + } + + message MemberPendingProfileKey { + Member member = 1; + bytes addedByUserId = 2; + uint64 timestamp = 3; + } + + message MemberPendingAdminApproval { + bytes userId = 1; + bytes profileKey = 2; + bytes presentation = 3; + uint64 timestamp = 4; + } + + message MemberBanned { + bytes userId = 1; + uint64 timestamp = 2; + } + + message AccessControl { + enum AccessRequired { + UNKNOWN = 0; + ANY = 1; + MEMBER = 2; + ADMINISTRATOR = 3; + UNSATISFIABLE = 4; + } + + AccessRequired attributes = 1; + AccessRequired members = 2; + AccessRequired addFromInviteLink = 3; + } } message Self {} From 84e654efb24037aeba1691bb8c8c547ddbf953f4 Mon Sep 17 00:00:00 2001 From: Clark Date: Thu, 25 Apr 2024 12:25:15 -0400 Subject: [PATCH 085/113] Set archive transfer state when archive data is set. --- .../org/thoughtcrime/securesms/database/AttachmentTable.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 f10387e5f5..f0401b83c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -1367,7 +1367,8 @@ class AttachmentTable( .values( ARCHIVE_CDN to archiveCdn, ARCHIVE_MEDIA_ID to archiveMediaId, - ARCHIVE_MEDIA_NAME to archiveMediaName + ARCHIVE_MEDIA_NAME to archiveMediaName, + ARCHIVE_TRANSFER_STATE to ArchiveTransferState.FINISHED.value ) .where("$ID = ?", attachmentId.id) .run() From ffc1463cdab238dd15d727efee0f9c2968f49117 Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Thu, 25 Apr 2024 17:24:21 -0400 Subject: [PATCH 086/113] Add double tap editing feature. --- .../v2/items/V2ConversationItemShapeTest.kt | 2 + .../test/InternalConversationTestFragment.kt | 4 + .../securesms/BindableConversationItem.java | 1 + .../conversation/ConversationItem.java | 21 ++++++ .../ScheduledMessagesBottomSheet.kt | 1 + .../quotes/MessageQuotesBottomSheet.kt | 1 + .../ui/edit/EditMessageHistoryDialog.kt | 1 + .../conversation/v2/ConversationFragment.kt | 21 +++++- .../v2/DoubleTapEditEducationSheet.kt | 48 ++++++++++++ .../V2ConversationItemTextOnlyViewHolder.kt | 16 ++++ .../securesms/keyvalue/UiHints.java | 37 ++++++---- ...n_item_double_tap_edit_education_sheet.xml | 74 +++++++++++++++++++ app/src/main/res/values/strings.xml | 8 ++ 13 files changed, 220 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/DoubleTapEditEducationSheet.kt create mode 100644 app/src/main/res/layout/conversation_item_double_tap_edit_education_sheet.xml diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt index 9a21acf4f9..f3d65179e3 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt @@ -328,5 +328,7 @@ class V2ConversationItemShapeTest { override fun onReportSpamLearnMoreClicked() = Unit override fun onMessageRequestAcceptOptionsClicked() = Unit + + override fun onItemDoubleClick(item: MultiselectPart) = Unit } } diff --git a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt index 108c4e5a12..2996f0d0c7 100644 --- a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt +++ b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt @@ -300,6 +300,10 @@ class InternalConversationTestFragment : Fragment(R.layout.conversation_test_fra Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show() } + override fun onItemDoubleClick(item: MultiselectPart) { + Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show() + } + override fun onShowSafetyTips(forGroup: Boolean) { Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java index 8b5ff1aaca..1a914b5fb3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -126,5 +126,6 @@ interface EventListener { void onShowSafetyTips(boolean forGroup); void onReportSpamLearnMoreClicked(); void onMessageRequestAcceptOptionsClicked(); + void onItemDoubleClick(MultiselectPart multiselectPart); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index ce2221fc27..da423db299 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -40,6 +40,7 @@ import android.text.style.URLSpan; import android.util.AttributeSet; import android.util.TypedValue; +import android.view.GestureDetector; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.TouchDelegate; @@ -256,6 +257,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private final UrlClickListener urlClickListener = new UrlClickListener(); private final Rect thumbnailMaskingRect = new Rect(); private final TouchDelegateChangedListener touchDelegateChangedListener = new TouchDelegateChangedListener(); + private final DoubleTapEditTouchListener doubleTapEditTouchListener = new DoubleTapEditTouchListener(); private final GiftMessageViewCallback giftMessageViewCallback = new GiftMessageViewCallback(); private final Context context; @@ -351,6 +353,7 @@ protected void onFinishInflate() { setOnClickListener(new ClickListener(null)); + bodyText.setOnTouchListener(doubleTapEditTouchListener); bodyText.setOnLongClickListener(passthroughClickListener); bodyText.setOnClickListener(passthroughClickListener); footer.setOnTouchDelegateChangedListener(touchDelegateChangedListener); @@ -2438,6 +2441,24 @@ public void onClick(final View view) { } } + private class DoubleTapEditTouchListener implements View.OnTouchListener { + private final GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + if (eventListener != null && batchSelected.isEmpty()) { + eventListener.onItemDoubleClick(getMultiselectPartForLatestTouch()); + return true; + } + return false; + } + }); + + @Override + public boolean onTouch(View v, MotionEvent event) { + return gestureDetector.onTouchEvent(event); + } + } + private class AttachmentDownloadClickListener implements SlidesClickedListener { @Override public void onClick(View v, final List slides) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt index 82c7f9a6f8..4dbf8f5a17 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ScheduledMessagesBottomSheet.kt @@ -277,6 +277,7 @@ class ScheduledMessagesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment override fun onShowSafetyTips(forGroup: Boolean) = Unit override fun onReportSpamLearnMoreClicked() = Unit override fun onMessageRequestAcceptOptionsClicked() = Unit + override fun onItemDoubleClick(item: MultiselectPart) = Unit } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt index 169f2b37ea..0e050593c8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/quotes/MessageQuotesBottomSheet.kt @@ -261,6 +261,7 @@ class MessageQuotesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment() { override fun onShowSafetyTips(forGroup: Boolean) = Unit override fun onReportSpamLearnMoreClicked() = Unit override fun onMessageRequestAcceptOptionsClicked() = Unit + override fun onItemDoubleClick(item: MultiselectPart) = Unit } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/edit/EditMessageHistoryDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/edit/EditMessageHistoryDialog.kt index 8369cc4e8d..f9b2f6f3d1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/edit/EditMessageHistoryDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/edit/EditMessageHistoryDialog.kt @@ -168,6 +168,7 @@ class EditMessageHistoryDialog : FixedRoundedCornerBottomSheetDialogFragment() { override fun onShowSafetyTips(forGroup: Boolean) = Unit override fun onReportSpamLearnMoreClicked() = Unit override fun onMessageRequestAcceptOptionsClicked() = Unit + override fun onItemDoubleClick(item: MultiselectPart) = Unit } companion object { 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 b7f55bcc5d..171dd4ad20 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 @@ -351,7 +351,8 @@ class ConversationFragment : ConversationBottomSheetCallback, SafetyNumberBottomSheet.Callbacks, EnableCallNotificationSettingsDialog.Callback, - MultiselectForwardBottomSheet.Callback { + MultiselectForwardBottomSheet.Callback, + DoubleTapEditEducationSheet.Callback { companion object { private val TAG = Log.tag(ConversationFragment::class.java) @@ -2755,6 +2756,20 @@ class ConversationFragment : RecipientBottomSheetDialogFragment.show(childFragmentManager, recipientId, groupId) } + override fun onItemDoubleClick(item: MultiselectPart) { + Log.d(TAG, "onItemDoubleClick") + if (!isValidEditMessageSend(item.getMessageRecord(), System.currentTimeMillis())) { + return + } + + if (SignalStore.uiHints().hasSeenDoubleTapEditEducationSheet) { + onDoubleTapEditEducationSheetNext(item.conversationMessage) + return + } + + DoubleTapEditEducationSheet(item).show(childFragmentManager, DoubleTapEditEducationSheet.KEY) + } + override fun onMessageWithErrorClicked(messageRecord: MessageRecord) { val recipientId = viewModel.recipientSnapshot?.id ?: return if (messageRecord.isIdentityMismatchFailure) { @@ -4307,4 +4322,8 @@ class ConversationFragment : } } } + + override fun onDoubleTapEditEducationSheetNext(conversationMessage: ConversationMessage) { + handleEditMessage(conversationMessage) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/DoubleTapEditEducationSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/DoubleTapEditEducationSheet.kt new file mode 100644 index 0000000000..95a82a7b74 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/DoubleTapEditEducationSheet.kt @@ -0,0 +1,48 @@ +package org.thoughtcrime.securesms.conversation.v2 + +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.android.material.button.MaterialButton +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment +import org.thoughtcrime.securesms.conversation.ConversationMessage +import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.fragments.requireListener + +/** + * Shows an education sheet to users explaining how double tapping a sent message within 24hrs will allow them to edit it + */ +class DoubleTapEditEducationSheet(private val item: MultiselectPart) : FixedRoundedCornerBottomSheetDialogFragment() { + + companion object { + const val KEY = "DOUBLE_TAP_EDIT_EDU" + } + + override val peekHeightPercentage: Float = 1f + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.conversation_item_double_tap_edit_education_sheet, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + SignalStore.uiHints().hasSeenDoubleTapEditEducationSheet = true + + view.findViewById(R.id.got_it).setOnClickListener { + requireListener().onDoubleTapEditEducationSheetNext(item.conversationMessage) + dismissAllowingStateLoss() + } + } + + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + requireListener().onDoubleTapEditEducationSheetNext(item.conversationMessage) + } + + interface Callback { + fun onDoubleTapEditEducationSheetNext(conversationMessage: ConversationMessage) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt index 5ec98f4424..998ab89aa0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt @@ -18,6 +18,8 @@ import android.text.style.ClickableSpan import android.text.style.ForegroundColorSpan import android.text.style.URLSpan import android.util.TypedValue +import android.view.GestureDetector +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat @@ -110,6 +112,19 @@ open class V2ConversationItemTextOnlyViewHolder>( private val senderDrawable = ChatColorsDrawable(conversationContext::getChatColorsData) private val bodyBubbleLayoutTransition = BodyBubbleLayoutTransition() + private val gestureDetector = GestureDetector( + context, + object : GestureDetector.SimpleOnGestureListener() { + override fun onDoubleTap(e: MotionEvent): Boolean { + if (conversationContext.selectedItems.isEmpty()) { + conversationContext.clickListener.onItemDoubleClick(getMultiselectPartForLatestTouch()) + return true + } + return false + } + } + ) + protected lateinit var shape: V2ConversationItemShape.MessageShape private val replyDelegate = object : V2ConversationItemLayout.OnMeasureListener { @@ -139,6 +154,7 @@ open class V2ConversationItemTextOnlyViewHolder>( ) } + binding.body.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) } binding.root.setOnClickListener { onBubbleClicked() } binding.root.setOnLongClickListener { conversationContext.clickListener.onItemLongClick(binding.root, getMultiselectPartForLatestTouch()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java index 86b1b9d0f3..de3acba570 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java @@ -9,20 +9,21 @@ public class UiHints extends SignalStoreValues { private static final int NEVER_DISPLAY_PULL_TO_FILTER_TIP_THRESHOLD = 3; - private static final String HAS_SEEN_GROUP_SETTINGS_MENU_TOAST = "uihints.has_seen_group_settings_menu_toast"; - private static final String HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE = "uihints.has_confirmed_delete_for_everyone_once"; - private static final String HAS_SET_OR_SKIPPED_USERNAME_CREATION = "uihints.has_set_or_skipped_username_creation"; - private static final String NEVER_DISPLAY_PULL_TO_FILTER_TIP = "uihints.never_display_pull_to_filter_tip"; - private static final String HAS_SEEN_SCHEDULED_MESSAGES_INFO_ONCE = "uihints.has_seen_scheduled_messages_info_once"; - private static final String HAS_SEEN_TEXT_FORMATTING_ALERT = "uihints.text_formatting.has_seen_alert"; - private static final String HAS_NOT_SEEN_EDIT_MESSAGE_BETA_ALERT = "uihints.edit_message.has_not_seen_beta_alert"; - private static final String HAS_SEEN_SAFETY_NUMBER_NUX = "uihints.has_seen_safety_number_nux"; - private static final String DECLINED_NOTIFICATION_LOGS_PROMPT = "uihints.declined_notification_logs"; - private static final String LAST_NOTIFICATION_LOGS_PROMPT_TIME = "uihints.last_notification_logs_prompt"; - private static final String DISMISSED_BATTERY_SAVER_PROMPT = "uihints.declined_battery_saver_prompt"; - private static final String LAST_BATTERY_SAVER_PROMPT = "uihints.last_battery_saver_prompt"; - private static final String LAST_CRASH_PROMPT = "uihints.last_crash_prompt"; - private static final String HAS_COMPLETED_USERNAME_ONBOARDING = "uihints.has_completed_username_onboarding"; + private static final String HAS_SEEN_GROUP_SETTINGS_MENU_TOAST = "uihints.has_seen_group_settings_menu_toast"; + private static final String HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE = "uihints.has_confirmed_delete_for_everyone_once"; + private static final String HAS_SET_OR_SKIPPED_USERNAME_CREATION = "uihints.has_set_or_skipped_username_creation"; + private static final String NEVER_DISPLAY_PULL_TO_FILTER_TIP = "uihints.never_display_pull_to_filter_tip"; + private static final String HAS_SEEN_SCHEDULED_MESSAGES_INFO_ONCE = "uihints.has_seen_scheduled_messages_info_once"; + private static final String HAS_SEEN_TEXT_FORMATTING_ALERT = "uihints.text_formatting.has_seen_alert"; + private static final String HAS_NOT_SEEN_EDIT_MESSAGE_BETA_ALERT = "uihints.edit_message.has_not_seen_beta_alert"; + private static final String HAS_SEEN_SAFETY_NUMBER_NUX = "uihints.has_seen_safety_number_nux"; + private static final String DECLINED_NOTIFICATION_LOGS_PROMPT = "uihints.declined_notification_logs"; + private static final String LAST_NOTIFICATION_LOGS_PROMPT_TIME = "uihints.last_notification_logs_prompt"; + private static final String DISMISSED_BATTERY_SAVER_PROMPT = "uihints.declined_battery_saver_prompt"; + private static final String LAST_BATTERY_SAVER_PROMPT = "uihints.last_battery_saver_prompt"; + private static final String LAST_CRASH_PROMPT = "uihints.last_crash_prompt"; + private static final String HAS_COMPLETED_USERNAME_ONBOARDING = "uihints.has_completed_username_onboarding"; + private static final String HAS_SEEN_DOUBLE_TAP_EDIT_EDUCATION_SHEET = "uihints.has_seen_double_tap_edit_education_sheet"; UiHints(@NonNull KeyValueStore store) { super(store); @@ -158,4 +159,12 @@ public void setLastCrashPrompt(long time) { public long getLastCrashPrompt() { return getLong(LAST_CRASH_PROMPT, 0); } + + public void setHasSeenDoubleTapEditEducationSheet(boolean seen) { + putBoolean(HAS_SEEN_DOUBLE_TAP_EDIT_EDUCATION_SHEET, seen); + } + + public boolean getHasSeenDoubleTapEditEducationSheet() { + return getBoolean(HAS_SEEN_DOUBLE_TAP_EDIT_EDUCATION_SHEET, false); + } } diff --git a/app/src/main/res/layout/conversation_item_double_tap_edit_education_sheet.xml b/app/src/main/res/layout/conversation_item_double_tap_edit_education_sheet.xml new file mode 100644 index 0000000000..5ee2d608be --- /dev/null +++ b/app/src/main/res/layout/conversation_item_double_tap_edit_education_sheet.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 35987d413f..2fdedd61f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3691,6 +3691,14 @@ Chat Broadcast + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + New group Settings From 18e6c57e75e9346a8449141cf4c6bbc41765bdad Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Fri, 26 Apr 2024 09:52:25 -0400 Subject: [PATCH 087/113] Update location permission UI. --- .../v2/ConversationActivityResultContracts.kt | 24 ++- .../PermissionDeniedBottomSheet.kt | 160 ++++++++++++++++++ .../securesms/permissions/Permissions.java | 31 +++- .../res/drawable/ic_radio_button_checked.xml | 7 + .../permission_allow_location_dialog.xml | 35 ++++ app/src/main/res/values/strings.xml | 18 ++ 6 files changed, 263 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionDeniedBottomSheet.kt create mode 100644 app/src/main/res/drawable/ic_radio_button_checked.xml create mode 100644 app/src/main/res/layout/permission_allow_location_dialog.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt index e38cadc7b8..8c95352892 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt @@ -16,6 +16,7 @@ import android.widget.Toast import androidx.activity.result.contract.ActivityResultContract import androidx.core.content.IntentCompat import androidx.fragment.app.Fragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.location.SignalPlace @@ -106,12 +107,23 @@ class ConversationActivityResultContracts(private val fragment: Fragment, privat if (Permissions.hasAny(fragment.requireContext(), Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)) { selectLocationLauncher.launch(chatColors) } else { - Permissions.with(fragment) - .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) - .ifNecessary() - .withPermanentDenialDialog(fragment.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location)) - .onSomeGranted { selectLocationLauncher.launch(chatColors) } - .execute() + val dialog = MaterialAlertDialogBuilder(fragment.requireContext()) + .setView(R.layout.permission_allow_location_dialog) + .setPositiveButton(R.string.Permissions_continue) { _, _ -> + Permissions.with(fragment) + .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) + .ifNecessary() + .withPermanentDenialDialog(fragment.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location), null, R.string.AttachmentManager_signal_allow_access_location, R.string.AttachmentManager_signal_to_send_location, fragment.parentFragmentManager) + .onAnyDenied { Toast.makeText(fragment.requireContext(), R.string.AttachmentManager_signal_needs_location_access, Toast.LENGTH_LONG).show() } + .onSomeGranted { selectLocationLauncher.launch(chatColors) } + .execute() + } + .setNegativeButton(R.string.Permissions_not_now) { d, _ -> + Toast.makeText(fragment.requireContext(), R.string.AttachmentManager_signal_needs_location_access, Toast.LENGTH_LONG).show() + d.dismiss() + } + .create() + dialog.show() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionDeniedBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionDeniedBottomSheet.kt new file mode 100644 index 0000000000..1850351b10 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionDeniedBottomSheet.kt @@ -0,0 +1,160 @@ +package org.thoughtcrime.securesms.permissions + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.InlineTextContent +import androidx.compose.foundation.text.appendInlineContent +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +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.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.Placeholder +import androidx.compose.ui.text.PlaceholderVerticalAlign +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.os.bundleOf +import org.signal.core.ui.BottomSheets +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.compose.ComposeBottomSheetDialogFragment + +private const val PLACEHOLDER = "__RADIO_BUTTON_PLACEHOLDER__" + +/** + * Bottom sheet shown when a permission has been previously denied + * + * Displays rationale for the need of a permission and how to grant it + */ +class PermissionDeniedBottomSheet private constructor() : ComposeBottomSheetDialogFragment() { + + companion object { + private const val ARG_TITLE = "argument.title_res" + private const val ARG_SUBTITLE = "argument.subtitle_res" + + @JvmStatic + fun showPermissionFragment(titleRes: Int, subtitleRes: Int): ComposeBottomSheetDialogFragment { + return PermissionDeniedBottomSheet().apply { + arguments = bundleOf( + ARG_TITLE to titleRes, + ARG_SUBTITLE to subtitleRes + ) + } + } + } + + @Composable + override fun SheetContent() { + PermissionDeniedSheetContent( + titleRes = remember { requireArguments().getInt(ARG_TITLE) }, + subtitleRes = remember { requireArguments().getInt(ARG_SUBTITLE) }, + onSettingsClicked = this::goToSettings + ) + } + + private fun goToSettings() { + requireContext().startActivity(Permissions.getApplicationSettingsIntent(requireContext())) + dismissAllowingStateLoss() + } +} + +@SignalPreview +@Composable +private fun PermissionDeniedSheetContentPreview() { + Previews.BottomSheetPreview { + PermissionDeniedSheetContent( + titleRes = R.string.AttachmentManager_signal_allow_access_location, + subtitleRes = R.string.AttachmentManager_signal_to_send_location, + onSettingsClicked = {} + ) + } +} + +@Composable +private fun PermissionDeniedSheetContent( + titleRes: Int, + subtitleRes: Int, + onSettingsClicked: () -> Unit +) { + Column( + modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 12.dp, bottom = 32.dp) + ) { + BottomSheets.Handle( + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + + Text( + text = stringResource(titleRes), + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center, + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(bottom = 12.dp, top = 20.dp) + ) + + Text( + text = stringResource(subtitleRes), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(bottom = 32.dp) + ) + + Text( + text = stringResource(R.string.PermissionDeniedBottomSheet__1_tap_settings_below), + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(bottom = 24.dp) + ) + + val step2String = stringResource(id = R.string.PermissionDeniedBottomSheet__2_allow_permission, PLACEHOLDER) + val (step2Text, step2InlineContent) = remember(step2String) { + val parts = step2String.split(PLACEHOLDER) + val annotatedString = buildAnnotatedString { + append(parts[0]) + appendInlineContent("radio") + append(parts[1]) + } + + val inlineContentMap = mapOf( + "radio" to InlineTextContent(Placeholder(22.sp, 22.sp, PlaceholderVerticalAlign.Center)) { + Image( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_radio_button_checked), + contentDescription = null, + modifier = Modifier.fillMaxSize() + ) + } + ) + + annotatedString to inlineContentMap + } + + Text( + text = step2Text, + inlineContent = step2InlineContent, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(bottom = 32.dp) + ) + + Buttons.LargeTonal( + onClick = onSettingsClicked, + modifier = Modifier + .align(Alignment.CenterHorizontally) + .fillMaxWidth(1f) + ) { + Text(text = stringResource(id = R.string.PermissionDeniedBottomSheet__settings)) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java index 14c6d052d4..a8910f546a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -19,6 +19,7 @@ import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; import com.annimon.stream.Stream; import com.annimon.stream.function.Consumer; @@ -26,6 +27,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.BottomSheetUtil; import org.thoughtcrime.securesms.util.LRUCache; import org.thoughtcrime.securesms.util.ServiceUtil; @@ -113,7 +115,11 @@ public PermissionsBuilder withPermanentDenialDialog(@NonNull String message) { } public PermissionsBuilder withPermanentDenialDialog(@NonNull String message, @Nullable Runnable onDialogDismissed) { - return onAnyPermanentlyDenied(new SettingsDialogListener(permissionObject.getContext(), message, onDialogDismissed)); + return withPermanentDenialDialog(message, onDialogDismissed, 0, 0, null); + } + + public PermissionsBuilder withPermanentDenialDialog(@NonNull String message, @Nullable Runnable onDialogDismissed, int titleRes, int detailsRes, @Nullable FragmentManager fragmentManager) { + return onAnyPermanentlyDenied(new SettingsDialogListener(permissionObject.getContext(), message, onDialogDismissed, titleRes, detailsRes, fragmentManager)); } public PermissionsBuilder onAllGranted(Runnable allGrantedListener) { @@ -368,22 +374,34 @@ public void requestPermissions(int requestCode, String... permissions) { private static class SettingsDialogListener implements Runnable { - private final WeakReference context; - private final Runnable onDialogDismissed; - private final String message; + private final WeakReference context; + private final WeakReference fragmentManager; + private final Runnable onDialogDismissed; + private final String message; + private final int titleRes; + private final int detailsRes; + private final boolean useBottomSheet; - SettingsDialogListener(Context context, String message, @Nullable Runnable onDialogDismissed) { + SettingsDialogListener(Context context, String message, @Nullable Runnable onDialogDismissed, int titleRes, int detailsRes, @Nullable FragmentManager fragmentManager) { this.message = message; this.context = new WeakReference<>(context); this.onDialogDismissed = onDialogDismissed; + this.fragmentManager = new WeakReference<>(fragmentManager); + this.titleRes = titleRes; + this.detailsRes = detailsRes; + this.useBottomSheet = fragmentManager != null; } @Override public void run() { Context context = this.context.get(); + FragmentManager fragmentManager = this.fragmentManager.get(); if (context != null) { - new MaterialAlertDialogBuilder(context) + if (useBottomSheet && fragmentManager != null) { + PermissionDeniedBottomSheet.showPermissionFragment(titleRes, detailsRes).show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG); + } else if (!useBottomSheet){ + new MaterialAlertDialogBuilder(context) .setTitle(R.string.Permissions_permission_required) .setMessage(message) .setCancelable(false) @@ -395,6 +413,7 @@ public void run() { } }) .show(); + } } } } diff --git a/app/src/main/res/drawable/ic_radio_button_checked.xml b/app/src/main/res/drawable/ic_radio_button_checked.xml new file mode 100644 index 0000000000..7465d286ca --- /dev/null +++ b/app/src/main/res/drawable/ic_radio_button_checked.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/layout/permission_allow_location_dialog.xml b/app/src/main/res/layout/permission_allow_location_dialog.xml new file mode 100644 index 0000000000..40398b2b4d --- /dev/null +++ b/app/src/main/res/layout/permission_allow_location_dialog.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2fdedd61f2..669e328a84 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -89,6 +89,16 @@ Signal requires the Storage permission in order to attach photos, videos, or audio, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Storage\". Signal requires Contacts permission in order to attach contact information, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Contacts\". Signal requires Location permission in order to attach a location, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Location\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s hasn\'t activated Payments @@ -3437,6 +3447,14 @@ Send photos, videos and files from your device. + + + 1. Tap “Settings” below + + 2. %s Allow the permission + + Settings + Security setup From 97c08f0d5240e0b98862c33deee8c705c3e4f6b3 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 26 Apr 2024 12:07:54 -0400 Subject: [PATCH 088/113] Add additional validations to incremental attachment streams. --- .../crypto/AttachmentCipherInputStream.java | 8 +- ...ntalMacAdditionalValidationsInputStream.kt | 118 +++++++ .../api/crypto/AttachmentCipherTest.java | 288 ++++++++++-------- 3 files changed, 293 insertions(+), 121 deletions(-) create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java index 0ad0447092..e744df0609 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java @@ -12,7 +12,6 @@ import org.signal.libsignal.protocol.incrementalmac.IncrementalMacInputStream; import org.signal.libsignal.protocol.kdf.HKDF; import org.whispersystems.signalservice.api.backup.BackupKey; -import org.whispersystems.signalservice.api.backup.MediaId; import org.whispersystems.signalservice.internal.util.ContentLengthInputStream; import org.whispersystems.signalservice.internal.util.Util; @@ -88,7 +87,12 @@ public static InputStream createForAttachment(File file, long plaintextLength, b wrappedStream = new FileInputStream(file); } else { wrappedStream = new IncrementalMacInputStream( - new FileInputStream(file), + new IncrementalMacAdditionalValidationsInputStream( + new FileInputStream(file), + file.length(), + mac, + digest + ), parts[1], ChunkSizeChoice.everyNthByte(incrementalMacChunkSize), incrementalDigest); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt new file mode 100644 index 0000000000..a4ebd6f446 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.crypto + +import org.signal.libsignal.protocol.InvalidMessageException +import org.whispersystems.signalservice.internal.util.Util +import java.io.FilterInputStream +import java.io.InputStream +import java.security.MessageDigest +import javax.crypto.Mac + +/** + * This is meant as a helper stream to go along with [org.signal.libsignal.protocol.incrementalmac.IncrementalMacInputStream]. + * That class does not validate the overall digest, nor the overall MAC. This class does that for us. + * + * To use, wrap the IncremtalMacInputStream around this class, and then this class should wrap the lowest-level data stream. + */ +class IncrementalMacAdditionalValidationsInputStream( + wrapped: InputStream, + fileLength: Long, + private val mac: Mac, + private val theirDigest: ByteArray +) : FilterInputStream(wrapped) { + + private val digest: MessageDigest = MessageDigest.getInstance("SHA256") + private val macLength: Int = mac.macLength + private val macBuffer: ByteArray = ByteArray(macLength) + + private var validated = false + private var bytesRemaining: Int = fileLength.toInt() + private var macBufferPosition: Int = 0 + + override fun read(): Int { + throw UnsupportedOperationException() + } + + /** + * We need to be very careful to keep track of what data is part of the MAC and what isn't, based on how far we've read into the file. + * As a recap, the digest needs to ingest the entire file, while the MAC needs to ingest everything except the last [macLength] bytes. + * (Because the last [macLength] bytes represents the MAC we're going to verify against.) + * + * The wrapping stream may request the full length of the file, so we need to do some bookkeeping to remember the last [macLength] bytes + * for comparison purposes during [validate] while not ingesting them into the MAC that we're calculating. + */ + override fun read(buffer: ByteArray, offset: Int, length: Int): Int { + val bytesRead = super.read(buffer, offset, length) + if (bytesRead == -1) { + validate() + return bytesRead + } + + bytesRemaining -= bytesRead + + // This indicates we've read into the last [macLength] bytes of the file, so we need to start our bookkeeping + if (bytesRemaining < macLength) { + val bytesOfMacRead = macLength - bytesRemaining + val newBytesOfMacRead = bytesOfMacRead - macBufferPosition + + // There's a possibility that the reader has only partially read the last [macLength] bytes, so we need to keep track of a position in our + // MAC buffer and copy over just the new parts we've read + if (newBytesOfMacRead > 0) { + System.arraycopy(buffer, offset + bytesRead - newBytesOfMacRead, macBuffer, macBufferPosition, newBytesOfMacRead) + macBufferPosition += newBytesOfMacRead + } + + // Even though we're reading into the MAC, many of the bytes read in this method call could be non-MAC bytes, so we need to copy + // those over, while excluding the bytes that are part of the MAC. + mac.update(buffer, offset, bytesRead - bytesOfMacRead) + } else { + mac.update(buffer, offset, bytesRead) + } + + digest.update(buffer, offset, bytesRead) + + if (bytesRemaining == 0) { + validate() + } + + return bytesRead + } + + override fun close() { + // We only want to validate the digest if we've otherwise read the entire stream. + // It's valid to close the stream early, and in this case, we don't want to force reading the whole rest of the stream. + if (bytesRemaining > macLength) { + super.close() + return + } + + if (bytesRemaining > 0) { + Util.readFullyAsBytes(this) + } + + super.close() + } + + private fun validate() { + if (validated) { + return + } + validated = true + + val ourMac = mac.doFinal() + val theirMac = macBuffer + + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw InvalidMessageException("MAC doesn't match!") + } + + val ourDigest = digest.digest() + if (!MessageDigest.isEqual(ourDigest, theirDigest)) { + throw InvalidMessageException("Digest doesn't match!") + } + } +} diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java index 4ad0ea38e3..8f992701b6 100644 --- a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java @@ -2,12 +2,13 @@ import org.conscrypt.Conscrypt; import org.junit.Test; +import org.signal.core.util.StreamUtil; import org.signal.libsignal.protocol.InvalidMessageException; import org.signal.libsignal.protocol.incrementalmac.ChunkSizeChoice; import org.signal.libsignal.protocol.incrementalmac.InvalidMacException; import org.signal.libsignal.protocol.kdf.HKDFv3; +import org.signal.libsignal.protocol.util.ByteUtil; import org.whispersystems.signalservice.api.backup.BackupKey; -import org.whispersystems.signalservice.api.backup.MediaId; import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.internal.push.http.AttachmentCipherOutputStreamFactory; import org.whispersystems.signalservice.internal.util.Util; @@ -15,6 +16,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -37,11 +39,30 @@ public final class AttachmentCipherTest { } } + private static int MEBIBYTE = 1024 * 1024; + + @Test + public void attachment_encryptDecrypt_nonIncremental() throws IOException, InvalidMessageException { + attachment_encryptDecrypt(false, MEBIBYTE); + } + + @Test + public void attachment_encryptDecrypt_incremental() throws IOException, InvalidMessageException { + attachment_encryptDecrypt(true, MEBIBYTE); + } + @Test - public void attachment_encryptDecrypt() throws IOException, InvalidMessageException { + public void attachment_encryptDecrypt_incremental_manyFileSizes() throws IOException, InvalidMessageException { + // Designed to stress the various boundary conditions of reading the final mac + for (int i = 0; i < 100; i++) { + attachment_encryptDecrypt(true, MEBIBYTE + new Random().nextInt(1, 64 * 1024)); + } + } + + private void attachment_encryptDecrypt(boolean incremental, int fileSize) throws IOException, InvalidMessageException { byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Peter Parker".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, false); + byte[] plaintextInput = Util.getSecretBytes(fileSize); + EncryptResult encryptResult = encryptData(plaintextInput, key, incremental); File cipherFile = writeToFile(encryptResult.ciphertext); InputStream inputStream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest, encryptResult.incrementalDigest, encryptResult.chunkSizeChoice); byte[] plaintextOutput = readInputStreamFully(inputStream); @@ -52,10 +73,19 @@ public void attachment_encryptDecrypt() throws IOException, InvalidMessageExcept } @Test - public void attachment_encryptDecryptEmpty() throws IOException, InvalidMessageException { + public void attachment_encryptDecryptEmpty_nonIncremental() throws IOException, InvalidMessageException { + attachment_encryptDecryptEmpty(false); + } + + @Test + public void attachment_encryptDecryptEmpty_incremental() throws IOException, InvalidMessageException { + attachment_encryptDecryptEmpty(true); + } + + private void attachment_encryptDecryptEmpty(boolean incremental) throws IOException, InvalidMessageException { byte[] key = Util.getSecretBytes(64); byte[] plaintextInput = "".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); + EncryptResult encryptResult = encryptData(plaintextInput, key, incremental); File cipherFile = writeToFile(encryptResult.ciphertext); InputStream inputStream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest, encryptResult.incrementalDigest, encryptResult.chunkSizeChoice); byte[] plaintextOutput = readInputStreamFully(inputStream); @@ -65,110 +95,130 @@ public void attachment_encryptDecryptEmpty() throws IOException, InvalidMessageE cipherFile.delete(); } - @Test - public void attachment_decryptFailOnBadKey() throws IOException { - File cipherFile = null; - boolean hitCorrectException = false; + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnBadKey_nonIncremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnBadKey(false); + } + + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnBadKey_incremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnBadKey(true); + } + + private void attachment_decryptFailOnBadKey(boolean incremental) throws IOException, InvalidMessageException { + File cipherFile = null; try { byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Gwen Stacy".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); + byte[] plaintextInput = Util.getSecretBytes(MEBIBYTE); + EncryptResult encryptResult = encryptData(plaintextInput, key, incremental); byte[] badKey = new byte[64]; cipherFile = writeToFile(encryptResult.ciphertext); AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, badKey, encryptResult.digest, null, 0); - } catch (InvalidMessageException e) { - hitCorrectException = true; } finally { if (cipherFile != null) { cipherFile.delete(); } } + } + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnBadMac_nonIncremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnBadMac(false); + } - assertTrue(hitCorrectException); + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnBadMac_incremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnBadMac(true); } - @Test - public void archive_encryptDecrypt() throws IOException, InvalidMessageException { - byte[] key = Util.getSecretBytes(64); - BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); - byte[] plaintextInput = "Peter Parker".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, false); - File cipherFile = writeToFile(encryptResult.ciphertext); - InputStream inputStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); - byte[] plaintextOutput = readInputStreamFully(inputStream); + private void attachment_decryptFailOnBadMac(boolean incremental) throws IOException, InvalidMessageException { + File cipherFile = null; - assertArrayEquals(plaintextInput, plaintextOutput); + try { + byte[] key = Util.getSecretBytes(64); + byte[] plaintextInput = Util.getSecretBytes(MEBIBYTE); + EncryptResult encryptResult = encryptData(plaintextInput, key, incremental); + byte[] badMacCiphertext = Arrays.copyOf(encryptResult.ciphertext, encryptResult.ciphertext.length); - cipherFile.delete(); - } + badMacCiphertext[badMacCiphertext.length - 1] += 1; - @Test - public void archive_encryptDecryptEmpty() throws IOException, InvalidMessageException { - byte[] key = Util.getSecretBytes(64); - BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); - byte[] plaintextInput = "".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, false); - File cipherFile = writeToFile(encryptResult.ciphertext); - InputStream inputStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); - byte[] plaintextOutput = readInputStreamFully(inputStream); + cipherFile = writeToFile(badMacCiphertext); - assertArrayEquals(plaintextInput, plaintextOutput); + InputStream stream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest, encryptResult.incrementalDigest, encryptResult.chunkSizeChoice); - cipherFile.delete(); + // In incremental mode, we'll only check the digest after reading the whole thing + if (incremental) { + StreamUtil.readFully(stream); + } + } finally { + if (cipherFile != null) { + cipherFile.delete(); + } + } } - @Test - public void archive_decryptFailOnBadKey() throws IOException { - File cipherFile = null; - boolean hitCorrectException = false; + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnNullDigest_nonIncremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnNullDigest(false); + } + + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnNullDigest_incremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnNullDigest(true); + } + + private void attachment_decryptFailOnNullDigest(boolean incremental) throws IOException, InvalidMessageException { + File cipherFile = null; try { - byte[] key = Util.getSecretBytes(64); - byte[] badKey = Util.getSecretBytes(64); - BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), badKey, Util.getSecretBytes(16)); - byte[] plaintextInput = "Gwen Stacy".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, false); + byte[] key = Util.getSecretBytes(64); + byte[] plaintextInput = Util.getSecretBytes(MEBIBYTE); + EncryptResult encryptResult = encryptData(plaintextInput, key, incremental); cipherFile = writeToFile(encryptResult.ciphertext); - AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); - } catch (InvalidMessageException e) { - hitCorrectException = true; + AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, null, encryptResult.incrementalDigest, encryptResult.chunkSizeChoice); } finally { if (cipherFile != null) { cipherFile.delete(); } } + } - assertTrue(hitCorrectException); + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnBadDigest_nonIncremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnBadDigest(false); } - @Test - public void attachment_decryptFailOnBadDigest() throws IOException { - File cipherFile = null; - boolean hitCorrectException = false; + @Test(expected = InvalidMessageException.class) + public void attachment_decryptFailOnBadDigest_incremental() throws IOException, InvalidMessageException { + attachment_decryptFailOnBadDigest(true); + } + + private void attachment_decryptFailOnBadDigest(boolean incremental) throws IOException, InvalidMessageException { + File cipherFile = null; try { byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Mary Jane Watson".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); + byte[] plaintextInput = Util.getSecretBytes(MEBIBYTE); + EncryptResult encryptResult = encryptData(plaintextInput, key, incremental); byte[] badDigest = new byte[32]; cipherFile = writeToFile(encryptResult.ciphertext); - AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, badDigest, null, 0); - } catch (InvalidMessageException e) { - hitCorrectException = true; + InputStream stream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, badDigest, encryptResult.incrementalDigest, encryptResult.chunkSizeChoice); + + // In incremental mode, we'll only check the digest after reading the whole thing + if (incremental) { + StreamUtil.readFully(stream); + } } finally { if (cipherFile != null) { cipherFile.delete(); } } - - assertTrue(hitCorrectException); } @Test @@ -178,9 +228,7 @@ public void attachment_decryptFailOnBadIncrementalDigest() throws IOException { try { byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = new byte[1000000]; - - new Random().nextBytes(plaintextInput); + byte[] plaintextInput = Util.getSecretBytes(MEBIBYTE); EncryptResult encryptResult = encryptData(plaintextInput, key, true); byte[] badDigest = Util.getSecretBytes(encryptResult.incrementalDigest.length); @@ -242,6 +290,62 @@ public void attachment_encryptDecryptPaddedContent() throws IOException, Invalid } } + @Test + public void archive_encryptDecrypt() throws IOException, InvalidMessageException { + byte[] key = Util.getSecretBytes(64); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + byte[] plaintextInput = "Peter Parker".getBytes(); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); + File cipherFile = writeToFile(encryptResult.ciphertext); + InputStream inputStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); + byte[] plaintextOutput = readInputStreamFully(inputStream); + + assertArrayEquals(plaintextInput, plaintextOutput); + + cipherFile.delete(); + } + + @Test + public void archive_encryptDecryptEmpty() throws IOException, InvalidMessageException { + byte[] key = Util.getSecretBytes(64); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), key, Util.getSecretBytes(16)); + byte[] plaintextInput = "".getBytes(); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); + File cipherFile = writeToFile(encryptResult.ciphertext); + InputStream inputStream = AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); + byte[] plaintextOutput = readInputStreamFully(inputStream); + + assertArrayEquals(plaintextInput, plaintextOutput); + + cipherFile.delete(); + } + + @Test + public void archive_decryptFailOnBadKey() throws IOException { + File cipherFile = null; + boolean hitCorrectException = false; + + try { + byte[] key = Util.getSecretBytes(64); + byte[] badKey = Util.getSecretBytes(64); + BackupKey.MediaKeyMaterial keyMaterial = BackupKey.MediaKeyMaterial.forMedia(Util.getSecretBytes(15), badKey, Util.getSecretBytes(16)); + byte[] plaintextInput = "Gwen Stacy".getBytes(); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); + + cipherFile = writeToFile(encryptResult.ciphertext); + + AttachmentCipherInputStream.createForArchivedMedia(keyMaterial, cipherFile, plaintextInput.length); + } catch (InvalidMessageException e) { + hitCorrectException = true; + } finally { + if (cipherFile != null) { + cipherFile.delete(); + } + } + + assertTrue(hitCorrectException); + } + @Test public void archive_encryptDecryptPaddedContent() throws IOException, InvalidMessageException { int[] lengths = { 531, 600, 724, 1019, 1024 }; @@ -280,59 +384,6 @@ public void archive_encryptDecryptPaddedContent() throws IOException, InvalidMes } } - @Test - public void attachment_decryptFailOnNullDigest() throws IOException { - File cipherFile = null; - boolean hitCorrectException = false; - - try { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Aunt May".getBytes(); - ChunkSizeChoice sizeChoice = ChunkSizeChoice.inferChunkSize(plaintextInput.length); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); - - cipherFile = writeToFile(encryptResult.ciphertext); - - AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, null, encryptResult.incrementalDigest, encryptResult.chunkSizeChoice); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } finally { - if (cipherFile != null) { - cipherFile.delete(); - } - } - - assertTrue(hitCorrectException); - } - - @Test - public void attachment_decryptFailOnBadMac() throws IOException { - File cipherFile = null; - boolean hitCorrectException = false; - - try { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Uncle Ben".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); - byte[] badMacCiphertext = Arrays.copyOf(encryptResult.ciphertext, encryptResult.ciphertext.length); - - badMacCiphertext[badMacCiphertext.length - 1] += 1; - - cipherFile = writeToFile(badMacCiphertext); - - AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest, null, encryptResult.chunkSizeChoice); - fail(); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } finally { - if (cipherFile != null) { - cipherFile.delete(); - } - } - - assertTrue(hitCorrectException); - } - @Test public void archive_decryptFailOnBadMac() throws IOException { File cipherFile = null; @@ -444,7 +495,6 @@ private static EncryptResult encryptData(byte[] data, byte[] keyMaterial, boolea encryptStream = factory.createFor(outputStream); } - encryptStream.write(data); encryptStream.flush(); encryptStream.close(); From 7ef7aa65e6fd9cf8bb553f89b001642b7119871a Mon Sep 17 00:00:00 2001 From: moiseev-signal <122060238+moiseev-signal@users.noreply.github.com> Date: Mon, 29 Apr 2024 06:01:59 -0700 Subject: [PATCH 089/113] Upgrade to libsignal 0.45.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 0207db190e..5d22663a31 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.8.10") - version("libsignal-client", "0.45.0") + version("libsignal-client", "0.45.1") version("mp4parser", "1.9.39") version("android-gradle-plugin", "8.0.2") version("accompanist", "0.28.0") diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 1d268cb668..7fa08d000b 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5703,20 +5703,20 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + - - - + + + - - + + From 69c40a6835e8757d71720dd968e6b94d578858a2 Mon Sep 17 00:00:00 2001 From: Jim Gustafson Date: Mon, 29 Apr 2024 06:02:43 -0700 Subject: [PATCH 090/113] Update to RingRTC v2.41.0 --- dependencies.gradle.kts | 2 +- gradle/verification-metadata.xml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dependencies.gradle.kts b/dependencies.gradle.kts index 5d22663a31..fa13d25be6 100644 --- a/dependencies.gradle.kts +++ b/dependencies.gradle.kts @@ -120,7 +120,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.40.0") + library("signal-ringrtc", "org.signal:ringrtc-android:2.41.0") library("signal-android-database-sqlcipher", "org.signal:sqlcipher-android:4.5.4-S2") // Third Party diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 7fa08d000b..217330f807 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5719,12 +5719,12 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + From c5c0c432c44042f19a67eb6216353bf10406b396 Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Mon, 29 Apr 2024 09:39:07 -0400 Subject: [PATCH 091/113] Update microphone permission UI for voice messages. --- .../v2/ConversationActivityResultContracts.kt | 26 ++++--------- .../conversation/v2/ConversationFragment.kt | 5 ++- .../securesms/permissions/Permissions.java | 33 ++++++++++++---- .../permissions/RationaleDialog.java | 38 +++++++++++++++++++ ...dialog.xml => permission_allow_dialog.xml} | 27 +++++++------ app/src/main/res/values/strings.xml | 10 ++++- 6 files changed, 98 insertions(+), 41 deletions(-) rename app/src/main/res/layout/{permission_allow_location_dialog.xml => permission_allow_dialog.xml} (54%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt index 8c95352892..1b3c845b00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt @@ -16,7 +16,6 @@ import android.widget.Toast import androidx.activity.result.contract.ActivityResultContract import androidx.core.content.IntentCompat import androidx.fragment.app.Fragment -import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.location.SignalPlace @@ -107,23 +106,14 @@ class ConversationActivityResultContracts(private val fragment: Fragment, privat if (Permissions.hasAny(fragment.requireContext(), Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)) { selectLocationLauncher.launch(chatColors) } else { - val dialog = MaterialAlertDialogBuilder(fragment.requireContext()) - .setView(R.layout.permission_allow_location_dialog) - .setPositiveButton(R.string.Permissions_continue) { _, _ -> - Permissions.with(fragment) - .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) - .ifNecessary() - .withPermanentDenialDialog(fragment.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location), null, R.string.AttachmentManager_signal_allow_access_location, R.string.AttachmentManager_signal_to_send_location, fragment.parentFragmentManager) - .onAnyDenied { Toast.makeText(fragment.requireContext(), R.string.AttachmentManager_signal_needs_location_access, Toast.LENGTH_LONG).show() } - .onSomeGranted { selectLocationLauncher.launch(chatColors) } - .execute() - } - .setNegativeButton(R.string.Permissions_not_now) { d, _ -> - Toast.makeText(fragment.requireContext(), R.string.AttachmentManager_signal_needs_location_access, Toast.LENGTH_LONG).show() - d.dismiss() - } - .create() - dialog.show() + Permissions.with(fragment) + .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) + .ifNecessary() + .withRationaleDialog(fragment.getString(R.string.AttachmentManager_signal_allow_access_location), fragment.getString(R.string.AttachmentManager_signal_allow_signal_access_location), R.drawable.symbol_location_white_24) + .withPermanentDenialDialog(fragment.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location), null, R.string.AttachmentManager_signal_allow_access_location, R.string.AttachmentManager_signal_to_send_location, fragment.parentFragmentManager) + .onAnyDenied { Toast.makeText(fragment.requireContext(), R.string.AttachmentManager_signal_needs_location_access, Toast.LENGTH_LONG).show() } + .onSomeGranted { selectLocationLauncher.launch(chatColors) } + .execute() } } 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 171dd4ad20..044582a34e 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 @@ -3975,8 +3975,9 @@ class ConversationFragment : .with(this@ConversationFragment) .request(Manifest.permission.RECORD_AUDIO) .ifNecessary() - .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_mic_solid_24) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) + .withRationaleDialog(getString(R.string.ConversationActivity_allow_access_microphone), getString(R.string.ConversationActivity_to_send_voice_messages_allow_signal_access_to_your_microphone), R.drawable.ic_mic_24) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages), null, R.string.ConversationActivity_allow_access_microphone, R.string.ConversationActivity_signal_to_send_audio_messages, this@ConversationFragment.parentFragmentManager) + .onAnyDenied { Toast.makeText(this@ConversationFragment.requireContext(), R.string.ConversationActivity_signal_needs_microphone_access_voice_message, Toast.LENGTH_LONG).show() } .execute() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java index a8910f546a..fb30608b5a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -73,6 +73,8 @@ public static class PermissionsBuilder { private @DrawableRes int[] rationalDialogHeader; private String rationaleDialogMessage; + private String rationaleDialogTitle; + private String rationaleDialogDetails; private boolean rationaleDialogCancelable; private boolean ifNecesary; @@ -104,8 +106,18 @@ public PermissionsBuilder withRationaleDialog(@NonNull String message, @NonNull } public PermissionsBuilder withRationaleDialog(@NonNull String message, boolean cancelable, @NonNull @DrawableRes int... headers) { + return withRationaleDialog(message, null, null, cancelable, headers); + } + + public PermissionsBuilder withRationaleDialog(@NonNull String title, @NonNull String details, @NonNull @DrawableRes int... headers) { + return withRationaleDialog(null, title, details, true, headers); + } + + public PermissionsBuilder withRationaleDialog(@Nullable String message, @Nullable String title, @Nullable String details, boolean cancelable, @NonNull @DrawableRes int... headers) { this.rationalDialogHeader = headers; this.rationaleDialogMessage = message; + this.rationaleDialogTitle = title; + this.rationaleDialogDetails = details; this.rationaleDialogCancelable = cancelable; return this; } @@ -164,7 +176,8 @@ public void execute() { if (ifNecesary && (permissionObject.hasAll(requestedPermissions) || !condition)) { executePreGrantedPermissionsRequest(request); - } else if (rationaleDialogMessage != null && rationalDialogHeader != null) { + } else if ((rationaleDialogMessage != null || (rationaleDialogTitle != null && rationaleDialogDetails != null)) + && rationalDialogHeader != null) { executePermissionsRequestWithRationale(request); } else { executePermissionsRequest(request); @@ -180,13 +193,17 @@ private void executePreGrantedPermissionsRequest(PermissionsRequest request) { @SuppressWarnings("ConstantConditions") private void executePermissionsRequestWithRationale(PermissionsRequest request) { - RationaleDialog.createFor(permissionObject.getContext(), rationaleDialogMessage, rationalDialogHeader) - .setPositiveButton(R.string.Permissions_continue, (dialog, which) -> executePermissionsRequest(request)) - .setNegativeButton(R.string.Permissions_not_now, (dialog, which) -> executeNoPermissionsRequest(request)) - .setCancelable(rationaleDialogCancelable) - .show() - .getWindow() - .setLayout((int)(permissionObject.getWindowWidth() * .75), ViewGroup.LayoutParams.WRAP_CONTENT); + MaterialAlertDialogBuilder builder = (rationaleDialogMessage != null) + ? RationaleDialog.createFor(permissionObject.getContext(), rationaleDialogMessage, rationalDialogHeader) + : RationaleDialog.createFor(permissionObject.getContext(), rationaleDialogTitle, rationaleDialogDetails, rationalDialogHeader); + builder.setPositiveButton(R.string.Permissions_continue, (dialog, which) -> executePermissionsRequest(request)) + .setNegativeButton(R.string.Permissions_not_now, (dialog, which) -> executeNoPermissionsRequest(request)) + .setCancelable(rationaleDialogCancelable); + if (rationaleDialogMessage != null) { + builder.show().getWindow().setLayout((int)(permissionObject.getWindowWidth() * .75), ViewGroup.LayoutParams.WRAP_CONTENT); + } else { + builder.show(); + } } private void executePermissionsRequest(PermissionsRequest request) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java index f5f72e1790..2d3377b11b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java @@ -29,6 +29,44 @@ public class RationaleDialog { + public static MaterialAlertDialogBuilder createFor(@NonNull Context context, @NonNull String title, @NonNull String details, @DrawableRes int... drawables) { + View view = LayoutInflater.from(context).inflate(R.layout.permission_allow_dialog, null); + ViewGroup header = view.findViewById(R.id.permission_header_container); + TextView titleText = view.findViewById(R.id.permission_title); + TextView detailsText = view.findViewById(R.id.permission_details); + int iconSize = (int) DimensionUnit.DP.toPixels(32); + + for (int i = 0; i < drawables.length; i++) { + Drawable drawable = Objects.requireNonNull(ContextCompat.getDrawable(context, drawables[i])); + DrawableCompat.setTint(drawable, ContextCompat.getColor(context, R.color.signal_colorOnPrimaryContainer)); + + ImageView imageView = new ImageView(context); + imageView.setImageDrawable(drawable); + imageView.setLayoutParams(new LayoutParams(iconSize, iconSize)); + imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + + header.addView(imageView); + + if (i != drawables.length - 1) { + TextView plus = new TextView(context); + plus.setText("+"); + plus.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40); + plus.setTextColor(Color.WHITE); + + LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + layoutParams.setMargins(ViewUtil.dpToPx(context, 20), 0, ViewUtil.dpToPx(context, 20), 0); + + plus.setLayoutParams(layoutParams); + header.addView(plus); + } + } + + titleText.setText(title); + detailsText.setText(details); + + return new MaterialAlertDialogBuilder(context).setView(view); + } + public static MaterialAlertDialogBuilder createFor(@NonNull Context context, @NonNull String message, @DrawableRes int... drawables) { View view = LayoutInflater.from(context).inflate(R.layout.permissions_rationale_dialog, null); ViewGroup header = view.findViewById(R.id.header_container); diff --git a/app/src/main/res/layout/permission_allow_location_dialog.xml b/app/src/main/res/layout/permission_allow_dialog.xml similarity index 54% rename from app/src/main/res/layout/permission_allow_location_dialog.xml rename to app/src/main/res/layout/permission_allow_dialog.xml index 40398b2b4d..980ee79484 100644 --- a/app/src/main/res/layout/permission_allow_location_dialog.xml +++ b/app/src/main/res/layout/permission_allow_dialog.xml @@ -1,35 +1,38 @@ - + android:gravity="center" + android:layout_gravity="center" + android:orientation="horizontal" + android:layout_marginBottom="10dp"> + + + tools:text="@string/AttachmentManager_signal_allow_access_location" /> + tools:text="@string/AttachmentManager_signal_allow_signal_access_location" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 669e328a84..bcc23be6f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -396,8 +396,16 @@ Your request to join has been sent to the group admin. You\'ll be notified when they take action. Cancel Request - To send audio messages, allow Signal access to your microphone. + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal requires the Microphone permission in order to send audio messages, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\". + Signal needs the Microphone and Camera permissions in order to call %s, but they have been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". To capture photos and video, allow Signal access to the camera. Signal needs the Camera permission to take photos or video, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Camera\". From fd4864b3b15a956926fa33bc6acb7372429058fd Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Mon, 29 Apr 2024 10:22:11 -0400 Subject: [PATCH 092/113] Update microphone permission UI for calls. --- .../securesms/util/CommunicationActions.java | 33 +++++++++++-------- app/src/main/res/values/strings.xml | 8 ++++- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java index 363f93f1a2..d03a1ca774 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java @@ -19,6 +19,7 @@ import androidx.core.app.TaskStackBuilder; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -26,8 +27,6 @@ import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; -import org.signal.libsignal.usernames.BaseUsernameException; -import org.signal.libsignal.usernames.Username; import org.signal.ringrtc.CallLinkRootKey; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.WebRtcCallActivity; @@ -44,16 +43,13 @@ import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.profiles.manage.UsernameRepository; -import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameAciFetchResult; import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameLinkConversionResult; import org.thoughtcrime.securesms.proxy.ProxyBottomSheetFragment; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; -import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.UsernameLinkComponents; -import org.whispersystems.signalservice.internal.storage.protos.AccountRecord; import java.io.IOException; import java.util.Objects; @@ -74,7 +70,7 @@ public static void startVoiceCall(@NonNull Fragment fragment, @NonNull Recipient /** * Start a voice call. Assumes that permission request results will be routed to a handler on the Activity. */ - public static void startVoiceCall(@NonNull Activity activity, @NonNull Recipient recipient) { + public static void startVoiceCall(@NonNull FragmentActivity activity, @NonNull Recipient recipient) { startVoiceCall(new ActivityCallContext(activity), recipient); } @@ -118,7 +114,7 @@ public static void startVideoCall(@NonNull Fragment fragment, @NonNull Recipient /** * Start a video call. Assumes that permission request results will be routed to a handler on the Activity. */ - public static void startVideoCall(@NonNull Activity activity, @NonNull Recipient recipient) { + public static void startVideoCall(@NonNull FragmentActivity activity, @NonNull Recipient recipient) { startVideoCall(new ActivityCallContext(activity), recipient, false); } @@ -172,7 +168,7 @@ protected void onPostExecute(@NonNull Long threadId) { }.execute(); } - public static void startInsecureCall(@NonNull Activity activity, @NonNull Recipient recipient) { + public static void startInsecureCall(@NonNull FragmentActivity activity, @NonNull Recipient recipient) { startInsecureCall(new ActivityCallContext(activity), recipient); } @@ -392,9 +388,9 @@ private static void startAudioCallInternal(@NonNull CallContext callContext, @No callContext.getPermissionsBuilder() .request(Manifest.permission.RECORD_AUDIO) .ifNecessary() - .withRationaleDialog(callContext.getContext().getString(R.string.ConversationActivity__to_call_s_signal_needs_access_to_your_microphone, recipient.getDisplayName(callContext.getContext())), - R.drawable.ic_mic_solid_24) - .withPermanentDenialDialog(callContext.getContext().getString(R.string.ConversationActivity__to_call_s_signal_needs_access_to_your_microphone, recipient.getDisplayName(callContext.getContext()))) + .withRationaleDialog(callContext.getContext().getString(R.string.ConversationActivity_allow_access_microphone), callContext.getContext().getString(R.string.ConversationActivity__to_call_signal_needs_access_to_your_microphone), R.drawable.symbol_phone_24) + .withPermanentDenialDialog(callContext.getContext().getString(R.string.ConversationActivity__to_call_signal_needs_access_to_your_microphone), null, R.string.ConversationActivity_allow_access_microphone, R.string.ConversationActivity__to_start_call, callContext.getFragmentManager()) + .onAnyDenied(() -> Toast.makeText(callContext.getContext(), R.string.ConversationActivity_signal_needs_microphone_access_voice_call, Toast.LENGTH_LONG).show()) .onAllGranted(() -> { ApplicationDependencies.getSignalCallManager().startOutgoingAudioCall(recipient); @@ -496,12 +492,13 @@ private interface CallContext { @NonNull Permissions.PermissionsBuilder getPermissionsBuilder(); void startActivity(@NonNull Intent intent); @NonNull Context getContext(); + @NonNull FragmentManager getFragmentManager(); } private static class ActivityCallContext implements CallContext { - private final Activity activity; + private final FragmentActivity activity; - private ActivityCallContext(Activity activity) { + private ActivityCallContext(FragmentActivity activity) { this.activity = activity; } @@ -519,6 +516,11 @@ public void startActivity(@NonNull Intent intent) { public @NonNull Context getContext() { return activity; } + + @Override + public @NonNull FragmentManager getFragmentManager() { + return activity.getSupportFragmentManager(); + } } private static class FragmentCallContext implements CallContext { @@ -542,5 +544,10 @@ public void startActivity(@NonNull Intent intent) { public @NonNull Context getContext() { return fragment.requireContext(); } + + @Override + public @NonNull FragmentManager getFragmentManager() { + return fragment.getParentFragmentManager(); + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bcc23be6f2..8b2d3a25cd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -429,7 +429,13 @@ You will leave this group, and it will be deleted from all your devices. Delete Delete and leave - To call %1$s, Signal needs access to your microphone + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Join From f23476a4e92ca8978dea8411cab41f94e1952208 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Mon, 29 Apr 2024 15:17:22 -0400 Subject: [PATCH 093/113] Initial support for restoring backups and skipping SMS in registration v2. --- .../securesms/absbackup/SignalBackupAgent.kt | 1 + .../securesms/backup/FullBackupImporter.java | 7 - .../securesms/keyvalue/InternalValues.java | 2 +- .../pin/PinRestoreEntryFragment.java | 2 +- .../registration/VerifyAccountRepository.kt | 2 +- .../v2/data/RegistrationRepository.kt | 100 +++++-- .../v2/ui/RegistrationCheckpoint.kt | 1 + .../v2/ui/RegistrationV2Activity.kt | 44 ++++ .../registration/v2/ui/RegistrationV2State.kt | 10 +- .../v2/ui/RegistrationV2ViewModel.kt | 227 ++++++++++++---- .../v2/ui/entercode/EnterCodeV2Fragment.kt | 46 ---- .../GrantPermissionsV2Fragment.kt | 78 +++--- .../phonenumber/EnterPhoneNumberV2Fragment.kt | 10 +- .../ReRegisterWithPinV2Fragment.kt | 243 ++++++++++++++++++ .../ReRegisterWithPinV2State.kt | 12 + .../ReRegisterWithPinV2ViewModel.kt | 32 +++ .../v2/ui/welcome/WelcomeV2Fragment.kt | 37 ++- .../securesms/restore/RestoreActivity.kt | 8 + .../RestoreLocalBackupFragment.kt | 20 +- .../RestoreLocalBackupViewModel.kt | 4 +- .../securesms/util/FeatureFlags.java | 8 +- ...ment_registration_pin_restore_entry_v2.xml | 135 ++++++++++ .../fragment_registration_welcome_v2.xml | 1 - .../main/res/navigation/registration_v2.xml | 53 +--- .../signalservice/api/NetworkResult.kt | 12 + .../api/registration/RegistrationApi.kt | 7 + .../internal/push/PushServiceSocket.java | 6 +- 27 files changed, 878 insertions(+), 230 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2Fragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2State.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2ViewModel.kt create mode 100644 app/src/main/res/layout/fragment_registration_pin_restore_entry_v2.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/absbackup/SignalBackupAgent.kt b/app/src/main/java/org/thoughtcrime/securesms/absbackup/SignalBackupAgent.kt index 2b4692b95f..43bb72f15e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/absbackup/SignalBackupAgent.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/absbackup/SignalBackupAgent.kt @@ -54,6 +54,7 @@ class SignalBackupAgent : BackupAgent() { items.find { dataInput.key == it.getKey() }?.restoreData(buffer) } DataOutputStream(FileOutputStream(newState.fileDescriptor)).use { it.writeInt(cumulativeHashCode()) } + Log.i(TAG, "Android Backup Service complete.") } private fun cumulativeHashCode(): Int { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index 4fd2ef27ef..0787c666f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -271,13 +271,6 @@ private static void processKeyValue(KeyValue keyValue) { return; } - if (FeatureFlags.registrationV2()) { - if (SignalStore.account().getKeysToIncludeInBackup().contains(keyValue.key)) { - Log.i(TAG, "[regv2] skipping restore of " + keyValue.key); - return; - } - } - if (keyValue.blobValue != null) { dataSet.putBlob(keyValue.key, keyValue.blobValue.toByteArray()); } else if (keyValue.booleanValue != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java index f0236ed9ee..c1e33ba60d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java @@ -217,6 +217,6 @@ public void setForceEnterRestoreV2Flow(boolean enter) { } public boolean enterRestoreV2Flow() { - return FeatureFlags.registrationV2() && getBoolean(FORCE_ENTER_RESTORE_V2_FLOW, false); + return FeatureFlags.restoreAfterRegistration() && getBoolean(FORCE_ENTER_RESTORE_V2_FLOW, false); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java index 41f3c5ef6d..011e76c8e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java @@ -72,7 +72,7 @@ private void initViews(@NonNull View root) { RegistrationViewDelegate.setDebugLogSubmitMultiTapView(root.findViewById(R.id.pin_restore_pin_title)); pinEntry = root.findViewById(R.id.pin_restore_pin_input); - pinButton = root.findViewById(R.id.pin_restore_pin_confirm); + pinButton = root.findViewById(R.id.pin_restore_pin_continue); errorLabel = root.findViewById(R.id.pin_restore_pin_input_label); keyboardToggle = root.findViewById(R.id.pin_restore_keyboard_toggle); helpButton = root.findViewById(R.id.pin_restore_forgot_pin); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/VerifyAccountRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/VerifyAccountRepository.kt index 16e4cb8302..391c5ec7a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/VerifyAccountRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/VerifyAccountRepository.kt @@ -207,7 +207,7 @@ class VerifyAccountRepository(private val context: Application) { }.subscribeOn(Schedulers.io()) } - interface MasterKeyProducer { + fun interface MasterKeyProducer { @Throws(IOException::class, SvrWrongPinException::class, SvrNoDataException::class) fun produceMasterKey(): MasterKey } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt index 0307823580..b043ae431b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/data/RegistrationRepository.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.registration.v2.data +import android.app.backup.BackupManager import android.content.Context import androidx.annotation.WorkerThread import androidx.core.app.NotificationManagerCompat @@ -12,6 +13,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe +import org.signal.core.util.Base64 import org.signal.core.util.logging.Log import org.signal.libsignal.protocol.IdentityKeyPair import org.signal.libsignal.protocol.util.KeyHelper @@ -39,6 +41,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.registration.PushChallengeRequest import org.thoughtcrime.securesms.registration.RegistrationData import org.thoughtcrime.securesms.registration.VerifyAccountRepository +import org.thoughtcrime.securesms.registration.viewmodel.SvrAuthCredentialSet import org.thoughtcrime.securesms.service.DirectoryRefreshListener import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener import org.thoughtcrime.securesms.util.TextSecurePreferences @@ -47,13 +50,16 @@ import org.whispersystems.signalservice.api.account.AccountAttributes import org.whispersystems.signalservice.api.account.PreKeyCollection import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess import org.whispersystems.signalservice.api.kbs.MasterKey +import org.whispersystems.signalservice.api.kbs.PinHashUtil 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.push.SignalServiceAddress import org.whispersystems.signalservice.api.registration.RegistrationApi +import org.whispersystems.signalservice.internal.push.AuthCredentials import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataHeaders import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse +import java.nio.charset.StandardCharsets import java.util.Locale import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -227,6 +233,24 @@ object RegistrationRepository { metadataStore.lastResortKyberPreKeyRotationTime = System.currentTimeMillis() } + fun canUseLocalRecoveryPassword(): Boolean { + val recoveryPassword = SignalStore.svr().recoveryPassword + val pinHash = SignalStore.svr().localPinHash + return recoveryPassword != null && pinHash != null + } + + fun doesPinMatchLocalHash(pin: String): Boolean { + val pinHash = SignalStore.svr().localPinHash ?: throw IllegalStateException("Local PIN hash is not present!") + return PinHashUtil.verifyLocalPinHash(pinHash, pin) + } + + suspend fun fetchMasterKeyFromSvrRemote(pin: String, authCredentials: AuthCredentials): MasterKey = + withContext(Dispatchers.IO) { + val masterKey = SvrRepository.restoreMasterKeyPreRegistration(SvrAuthCredentialSet(null, authCredentials), pin) + SignalStore.svr().setMasterKey(masterKey, pin) + return@withContext masterKey + } + /** * Asks the service to send a verification code through one of our supported channels (SMS, phone call). * This requires two or more network calls: @@ -280,9 +304,9 @@ object RegistrationRepository { /** * Submit the necessary assets as a verified account so that the user can actually use the service. */ - suspend fun registerAccount(context: Context, e164: String, password: String, sessionId: String, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: VerifyAccountRepository.MasterKeyProducer? = null): NetworkResult = + suspend fun registerAccount(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: VerifyAccountRepository.MasterKeyProducer? = null): NetworkResult = withContext(Dispatchers.IO) { - val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password).registrationApi + val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, registrationData.e164, SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.password).registrationApi val universalUnidentifiedAccess: Boolean = TextSecurePreferences.isUniversalUnidentifiedAccess(context) val unidentifiedAccessKey: ByteArray = UnidentifiedAccess.deriveAccessKeyFrom(registrationData.profileKey) @@ -335,27 +359,32 @@ object RegistrationRepository { val eventBus = EventBus.getDefault() eventBus.register(subscriber) - val sessionCreationResponse = accountManager.createRegistrationSession(fcmToken, mcc, mnc) - if (sessionCreationResponse !is NetworkResult.Success) { - return@withContext sessionCreationResponse - } - - val receivedPush = subscriber.latch.await(PUSH_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS) - eventBus.unregister(subscriber) + try { + val sessionCreationResponse = accountManager.createRegistrationSession(fcmToken, mcc, mnc) + if (sessionCreationResponse !is NetworkResult.Success) { + return@withContext sessionCreationResponse + } - if (receivedPush) { - val challenge = subscriber.challenge - if (challenge != null) { - Log.w(TAG, "Push challenge token received.") - return@withContext accountManager.submitPushChallengeToken(sessionCreationResponse.result.body.id, challenge) + val receivedPush = subscriber.latch.await(PUSH_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS) + eventBus.unregister(subscriber) + + if (receivedPush) { + val challenge = subscriber.challenge + if (challenge != null) { + Log.w(TAG, "Push challenge token received.") + return@withContext accountManager.submitPushChallengeToken(sessionCreationResponse.result.body.id, challenge) + } else { + Log.w(TAG, "Push received but challenge token was null.") + } } else { - Log.w(TAG, "Push received but challenge token was null.") + Log.i(TAG, "Push challenge timed out.") } - } else { - Log.i(TAG, "Push challenge timed out.") + Log.i(TAG, "Push challenge unsuccessful. Updating registration state accordingly.") + return@withContext NetworkResult.ApplicationError(NullPointerException()) + } catch (ex: Exception) { + Log.w(TAG, "Exception caught, but the earlier try block should have caught it?", ex) // TODO [regv2]: figure out why this exception is not caught + return@withContext NetworkResult.ApplicationError(ex) } - Log.i(TAG, "Push challenge unsuccessful. Updating registration state accordingly.") - return@withContext NetworkResult.ApplicationError(NullPointerException()) } @JvmStatic @@ -368,6 +397,39 @@ object RegistrationRepository { return timestamp + deltaSeconds.seconds.inWholeMilliseconds } + suspend fun hasValidSvrAuthCredentials(context: Context, e164: String, password: String): AuthCredentials? = + withContext(Dispatchers.IO) { + val usernamePasswords = SignalStore.svr() + .authTokenList + .take(10) + .map { + it.replace("Basic ", "").trim() + } + .map { + Base64.decode(it) // TODO [regv2]: figure out why Android Studio doesn't like mapCatching + } + .map { + String(it, StandardCharsets.ISO_8859_1) + } + + if (usernamePasswords.isEmpty()) { + return@withContext null + } + val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password).registrationApi + + val authCheck = api.getSvrAuthCredential(e164, usernamePasswords) + if (authCheck !is NetworkResult.Success) { + return@withContext null + } + + val removedInvalidTokens = SignalStore.svr().removeAuthTokens(authCheck.result.invalid) + if (removedInvalidTokens) { + BackupManager(context).dataChanged() + } + + return@withContext authCheck.result.match + } + enum class Mode(val isSmsRetrieverSupported: Boolean) { SMS_WITH_LISTENER(true), SMS_WITHOUT_LISTENER(false), PHONE_CALL(false) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationCheckpoint.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationCheckpoint.kt index 1af548b418..f679d3fcd0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationCheckpoint.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationCheckpoint.kt @@ -17,6 +17,7 @@ enum class RegistrationCheckpoint { BACKUP_RESTORED, PUSH_NETWORK_AUDITED, PHONE_NUMBER_CONFIRMED, + PIN_CONFIRMED, VERIFICATION_CODE_REQUESTED, CHALLENGE_RECEIVED, CHALLENGE_COMPLETED, diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt index 032da33b02..163b4e4691 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2Activity.kt @@ -9,9 +9,16 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.activity.viewModels +import androidx.navigation.ActivityNavigator import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.BaseActivity +import org.thoughtcrime.securesms.MainActivity import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity +import org.thoughtcrime.securesms.profiles.AvatarHelper +import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity +import org.thoughtcrime.securesms.recipients.Recipient /** * Activity to hold the entire registration process. @@ -25,6 +32,43 @@ class RegistrationV2Activity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_registration_navigation_v2) + sharedViewModel.uiState.observe(this) { + if (it.registrationCheckpoint == RegistrationCheckpoint.SERVICE_REGISTRATION_COMPLETED) { + handleSuccessfulVerify() + } + } + } + + private fun handleSuccessfulVerify() { + // TODO [regv2]: add functionality of [RegistrationCompleteFragment] + val isProfileNameEmpty = Recipient.self().profileName.isEmpty + val isAvatarEmpty = !AvatarHelper.hasAvatar(this, Recipient.self().id) + val needsProfile = isProfileNameEmpty || isAvatarEmpty + val needsPin = !sharedViewModel.hasPin() + + Log.i(TAG, "Pin restore flow not required. Profile name: $isProfileNameEmpty | Profile avatar: $isAvatarEmpty | Needs PIN: $needsPin") + + SignalStore.internalValues().setForceEnterRestoreV2Flow(true) + + if (!needsProfile && !needsPin) { + sharedViewModel.completeRegistration() + } + sharedViewModel.setInProgress(false) + + val startIntent = MainActivity.clearTop(this).apply { + if (needsPin) { + putExtra("next_intent", CreateSvrPinActivity.getIntentForPinCreate(this@RegistrationV2Activity)) + } + + if (needsProfile) { + putExtra("next_intent", CreateProfileActivity.getIntentForUserProfile(this@RegistrationV2Activity)) + } + } + + Log.d(TAG, "Launching ${startIntent.component}") + startActivity(startIntent) + finish() + ActivityNavigator.applyPopAnimationsToPendingTransition(this) } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt index 6eba43dcd8..e39cb5697a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt @@ -6,6 +6,8 @@ package org.thoughtcrime.securesms.registration.v2.ui import com.google.i18n.phonenumbers.Phonenumber +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.whispersystems.signalservice.internal.push.AuthCredentials /** * State holder shared across all of registration. @@ -15,10 +17,16 @@ data class RegistrationV2State( val phoneNumber: Phonenumber.PhoneNumber? = null, val inProgress: Boolean = false, val isReRegister: Boolean = false, + val recoveryPassword: String? = SignalStore.svr().getRecoveryPassword(), val canSkipSms: Boolean = false, + val svrAuthCredentials: AuthCredentials? = null, + val svrTriesRemaining: Int = 10, + val isRegistrationLockEnabled: Boolean = false, + val userSkippedReregistration: Boolean = false, val isFcmSupported: Boolean = false, val fcmToken: String? = null, val nextSms: Long = 0L, val nextCall: Long = 0L, - val registrationCheckpoint: RegistrationCheckpoint = RegistrationCheckpoint.INITIALIZATION + val registrationCheckpoint: RegistrationCheckpoint = RegistrationCheckpoint.INITIALIZATION, + val networkError: Throwable? = null ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt index 8d75e5cf7f..4229b17622 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt @@ -23,12 +23,17 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob import org.thoughtcrime.securesms.jobs.ProfileUploadJob import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.pin.SvrWrongPinException import org.thoughtcrime.securesms.registration.RegistrationData import org.thoughtcrime.securesms.registration.RegistrationUtil import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.dualsim.MccMncProducer +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.SvrNoDataException +import org.whispersystems.signalservice.api.kbs.MasterKey +import org.whispersystems.signalservice.internal.push.LockedException import java.io.IOException /** @@ -78,50 +83,168 @@ class RegistrationV2ViewModel : ViewModel() { viewModelScope.launch { val fcmToken = RegistrationRepository.getFcmToken(context) store.update { - it.copy( - registrationCheckpoint = RegistrationCheckpoint.PUSH_NETWORK_AUDITED, - isFcmSupported = true, - fcmToken = fcmToken - ) + it.copy(registrationCheckpoint = RegistrationCheckpoint.PUSH_NETWORK_AUDITED, isFcmSupported = true, fcmToken = fcmToken) } } } + private suspend fun updateFcmToken(context: Context): String? { + val fcmToken = RegistrationRepository.getFcmToken(context) + store.update { + it.copy(fcmToken = fcmToken) + } + return fcmToken + } + + fun onBackupSuccessfullyRestored() { + val recoveryPassword = SignalStore.svr().recoveryPassword + store.update { + it.copy(registrationCheckpoint = RegistrationCheckpoint.BACKUP_RESTORED, recoveryPassword = SignalStore.svr().recoveryPassword, canSkipSms = recoveryPassword != null) + } + } + fun onUserConfirmedPhoneNumber(context: Context) { setRegistrationCheckpoint(RegistrationCheckpoint.PHONE_NUMBER_CONFIRMED) - // TODO [regv2]: check if can skip sms flow val state = store.value if (state.phoneNumber == null) { Log.w(TAG, "Phone number was null after confirmation.") onErrorOccurred() return } - if (state.canSkipSms) { - Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + + // TODO [regv2]: initialize Play Services sms retriever + val mccMncProducer = MccMncProducer(context) + val e164 = state.phoneNumber.toE164() + if (hasRecoveryPassword() && matchesSavedE164(e164)) { + // Re-registration when the local database is intact. + store.update { + it.copy(canSkipSms = true) + } } else { - // TODO [regv2]: initialize Play Services sms retriever - val mccMncProducer = MccMncProducer(context) - val e164 = state.phoneNumber.toE164() viewModelScope.launch { - val codeRequestResponse = RegistrationRepository.requestSmsCode(context, e164, password, mccMncProducer.mcc, mccMncProducer.mnc).successOrThrow() - store.update { - it.copy( - sessionId = codeRequestResponse.body.id, - nextSms = RegistrationRepository.deriveTimestamp(codeRequestResponse.headers, codeRequestResponse.body.nextSms), - nextCall = RegistrationRepository.deriveTimestamp(codeRequestResponse.headers, codeRequestResponse.body.nextCall), - registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_REQUESTED - ) + val svrCredentials = RegistrationRepository.hasValidSvrAuthCredentials(context, e164, password) + + if (svrCredentials != null) { + // Re-registration when credentials stored in backup. + store.update { + it.copy(canSkipSms = true, svrAuthCredentials = svrCredentials) + } + } else { + val codeRequestResponse = RegistrationRepository.requestSmsCode(context, e164, password, mccMncProducer.mcc, mccMncProducer.mnc).successOrThrow() + store.update { + it.copy(sessionId = codeRequestResponse.body.id, nextSms = RegistrationRepository.deriveTimestamp(codeRequestResponse.headers, codeRequestResponse.body.nextSms), nextCall = RegistrationRepository.deriveTimestamp(codeRequestResponse.headers, codeRequestResponse.body.nextCall), registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_REQUESTED) + } } } } } + private fun setRecoveryPassword(recoveryPassword: String?) { + store.update { + it.copy(recoveryPassword = recoveryPassword) + } + } + + private fun updateSvrTriesRemaining(remainingTries: Int) { + store.update { + it.copy(svrTriesRemaining = remainingTries) + } + } + + fun setUserSkippedReRegisterFlow(value: Boolean) { + store.update { + it.copy(userSkippedReregistration = value, canSkipSms = !value) + } + } + + fun verifyReRegisterWithPin(context: Context, pin: String, wrongPinHandler: () -> Unit) { + setInProgress(true) + + // Local recovery password + if (RegistrationRepository.canUseLocalRecoveryPassword()) { + if (RegistrationRepository.doesPinMatchLocalHash(pin)) { + Log.d(TAG, "Found recovery password, attempting to re-register.") + viewModelScope.launch { + verifyReRegisterInternal(context, pin, SignalStore.svr().getOrCreateMasterKey()) + setInProgress(false) + } + } else { + Log.d(TAG, "Entered PIN did not match local PIN hash.") + wrongPinHandler() + setInProgress(false) + } + return + } + + // remote recovery password + val authCredentials = store.value.svrAuthCredentials + if (authCredentials != null) { + Log.d(TAG, "Found SVR auth credentials, fetching recovery password from SVR.") + viewModelScope.launch { + try { + val masterKey = RegistrationRepository.fetchMasterKeyFromSvrRemote(pin, authCredentials) + setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword()) + updateSvrTriesRemaining(10) + verifyReRegisterInternal(context, pin, masterKey) + } catch (rejectedPin: SvrWrongPinException) { + Log.w(TAG, "Submitted PIN was rejected by SVR.", rejectedPin) + updateSvrTriesRemaining(rejectedPin.triesRemaining) + wrongPinHandler() + } catch (noData: SvrNoDataException) { + Log.w(TAG, "SVR has no data for these credentials. Aborting skip SMS flow.", noData) + setUserSkippedReRegisterFlow(true) + } + setInProgress(false) + } + return + } + + Log.w(TAG, "Could not get credentials to skip SMS registration, aborting!") + // TODO [regv2]: Investigate why in v1, this case throws a [IncorrectRegistrationRecoveryPasswordException], which seems weird. + store.update { + it.copy(canSkipSms = false, inProgress = false) + } + } + + private suspend fun verifyReRegisterInternal(context: Context, pin: String, masterKey: MasterKey) { + updateFcmToken(context) + + val registrationData = getRegistrationData("") + + val resultAndRegLockStatus = registerAccountInternal(context, null, registrationData, pin, masterKey) + val result = resultAndRegLockStatus.first + val reglockEnabled = resultAndRegLockStatus.second + + if (result !is NetworkResult.Success) { + Log.w(TAG, "Error during registration!", result.getCause()) + return + } + + onSuccessfulRegistration(context, registrationData, result.result, reglockEnabled) + } + + private suspend fun registerAccountInternal(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String?, masterKey: MasterKey): Pair, Boolean> { + val registrationResult = RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey } + + // TODO: check for wrong recovery password + + // Check if reg lock is enabled + if (registrationResult !is NetworkResult.StatusCodeError || registrationResult.exception !is LockedException) { + return Pair(registrationResult, false) + } + + Log.i(TAG, "Received a registration lock response when trying to register an account. Retrying with master key.") + val lockedException = registrationResult.exception as LockedException + store.update { + it.copy(svrAuthCredentials = lockedException.svr2Credentials) + } + + return Pair(RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey }, true) + } + fun verifyCodeWithoutRegistrationLock(context: Context, code: String) { store.update { - it.copy( - inProgress = true, - registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_ENTERED - ) + it.copy(inProgress = true, registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_ENTERED) } val sessionId = store.value.sessionId @@ -143,17 +266,18 @@ class RegistrationV2ViewModel : ViewModel() { setRegistrationCheckpoint(RegistrationCheckpoint.VERIFICATION_CODE_VALIDATED) - val registrationResponse = RegistrationRepository.registerAccount(context, e164, password, sessionId, registrationData).successOrThrow() + val registrationResponse = RegistrationRepository.registerAccount(context, sessionId, registrationData).successOrThrow() + onSuccessfulRegistration(context, registrationData, registrationResponse, false) + } + } - localRegisterAccount(context, registrationData, registrationResponse, false) + private suspend fun onSuccessfulRegistration(context: Context, registrationData: RegistrationData, remoteResult: RegistrationRepository.AccountRegistrationResult, reglockEnabled: Boolean) { + RegistrationRepository.registerAccountLocally(context, registrationData, remoteResult, reglockEnabled) - refreshFeatureFlags() + refreshFeatureFlags() - store.update { - it.copy( - registrationCheckpoint = RegistrationCheckpoint.SERVICE_REGISTRATION_COMPLETED - ) - } + store.update { + it.copy(registrationCheckpoint = RegistrationCheckpoint.SERVICE_REGISTRATION_COMPLETED) } } @@ -162,38 +286,31 @@ class RegistrationV2ViewModel : ViewModel() { } fun completeRegistration() { - ApplicationDependencies.getJobManager() - .startChain(ProfileUploadJob()) - .then(listOf(MultiDeviceProfileKeyUpdateJob(), MultiDeviceProfileContentUpdateJob())) - .enqueue() + ApplicationDependencies.getJobManager().startChain(ProfileUploadJob()).then(listOf(MultiDeviceProfileKeyUpdateJob(), MultiDeviceProfileContentUpdateJob())).enqueue() RegistrationUtil.maybeMarkRegistrationComplete() } - private fun getCurrentE164(): String? { - return store.value.phoneNumber?.toE164() + private fun matchesSavedE164(e164: String?): Boolean { + return if (e164 == null) { + false + } else { + e164 == SignalStore.account().e164 + } } - private suspend fun localRegisterAccount( - context: Context, - registrationData: RegistrationData, - remoteResult: RegistrationRepository.AccountRegistrationResult, - reglockEnabled: Boolean - ) { - RegistrationRepository.registerAccountLocally(context, registrationData, remoteResult, reglockEnabled) + private fun hasRecoveryPassword(): Boolean { + return store.value.recoveryPassword != null + } + + private fun getCurrentE164(): String? { + return store.value.phoneNumber?.toE164() } private suspend fun getRegistrationData(code: String): RegistrationData { - val e164: String = getCurrentE164() ?: throw IllegalStateException() - return RegistrationData( - code, - e164, - password, - RegistrationRepository.getRegistrationId(), - RegistrationRepository.getProfileKey(e164), - store.value.fcmToken, - RegistrationRepository.getPniRegistrationId(), - null // TODO [regv2]: recovery password - ) + val currentState = store.value + val e164: String = currentState.phoneNumber?.toE164() ?: throw IllegalStateException() + val recoveryPassword = if (currentState.sessionId == null) SignalStore.svr().getRecoveryPassword() else null + return RegistrationData(code, e164, password, RegistrationRepository.getRegistrationId(), RegistrationRepository.getProfileKey(e164), currentState.fcmToken, RegistrationRepository.getPniRegistrationId(), recoveryPassword) } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt index ee0b9f2e1e..ac31b27215 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/entercode/EnterCodeV2Fragment.kt @@ -9,19 +9,12 @@ import android.os.Bundle import android.view.View import androidx.activity.OnBackPressedCallback import androidx.fragment.app.activityViewModels -import androidx.navigation.ActivityNavigator import androidx.navigation.fragment.NavHostFragment import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.LoggingFragment -import org.thoughtcrime.securesms.MainActivity import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.ViewBinderDelegate import org.thoughtcrime.securesms.databinding.FragmentRegistrationEnterCodeV2Binding -import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity -import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity -import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint @@ -74,45 +67,6 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter } } } - - sharedViewModel.uiState.observe(viewLifecycleOwner) { - if (it.registrationCheckpoint == RegistrationCheckpoint.SERVICE_REGISTRATION_COMPLETED) { - handleSuccessfulVerify() - } - } - } - - private fun handleSuccessfulVerify() { - // TODO [regv2]: add functionality of [RegistrationCompleteFragment] - val activity = requireActivity() - val isProfileNameEmpty = Recipient.self().profileName.isEmpty - val isAvatarEmpty = !AvatarHelper.hasAvatar(activity, Recipient.self().id) - val needsProfile = isProfileNameEmpty || isAvatarEmpty - val needsPin = !sharedViewModel.hasPin() - - Log.i(TAG, "Pin restore flow not required. Profile name: $isProfileNameEmpty | Profile avatar: $isAvatarEmpty | Needs PIN: $needsPin") - - SignalStore.internalValues().setForceEnterRestoreV2Flow(true) - - if (!needsProfile && !needsPin) { - sharedViewModel.completeRegistration() - } - sharedViewModel.setInProgress(false) - - val startIntent = MainActivity.clearTop(activity).apply { - if (needsPin) { - putExtra("next_intent", CreateSvrPinActivity.getIntentForPinCreate(activity)) - } - - if (needsProfile) { - putExtra("next_intent", CreateProfileActivity.getIntentForUserProfile(activity)) - } - } - - Log.d(TAG, "Launching ${startIntent.component}") - activity.startActivity(startIntent) - activity.finish() - ActivityNavigator.applyPopAnimationsToPendingTransition(activity) } private fun popBackStack() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt index 903bd31a32..114e5ca311 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt @@ -5,15 +5,18 @@ package org.thoughtcrime.securesms.registration.v2.ui.grantpermissions +import android.app.Activity +import android.content.pm.PackageManager import android.os.Build import android.os.Bundle -import android.view.View +import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.platform.LocalContext +import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.navArgs @@ -22,8 +25,8 @@ import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.registration.compose.GrantPermissionsScreen import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint -import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel +import org.thoughtcrime.securesms.restore.RestoreActivity import org.thoughtcrime.securesms.util.BackupUtil import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -33,29 +36,31 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate @RequiresApi(23) class GrantPermissionsV2Fragment : ComposeFragment() { - private val TAG = Log.tag(GrantPermissionsV2Fragment::class.java) - private val sharedViewModel by activityViewModels() private val args by navArgs() private val isSearchingForBackup = mutableStateOf(false) private val requestPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions(), - ::permissionsGranted + ::onPermissionsGranted ) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - sharedViewModel.uiState.observe(viewLifecycleOwner) { - if (it.registrationCheckpoint >= RegistrationCheckpoint.PERMISSIONS_GRANTED) { - proceedToNextScreen(it) + private val launchRestoreActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> + when (val resultCode = result.resultCode) { + Activity.RESULT_OK -> { + sharedViewModel.onBackupSuccessfullyRestored() + NavHostFragment.findNavController(this).safeNavigate(GrantPermissionsV2FragmentDirections.actionEnterPhoneNumber()) } + Activity.RESULT_CANCELED -> Log.w(TAG, "Backup restoration canceled.") + else -> Log.w(TAG, "Backup restoration activity ended with unknown result code: $resultCode") } } - private fun proceedToNextScreen(it: RegistrationV2State) { - // TODO [nicholas]: conditionally go to backup flow - NavHostFragment.findNavController(this).safeNavigate(GrantPermissionsV2FragmentDirections.actionSkipRestore()) + private lateinit var welcomeAction: WelcomeAction + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + welcomeAction = args.welcomeAction } @Composable @@ -66,40 +71,41 @@ class GrantPermissionsV2Fragment : ComposeFragment() { deviceBuildVersion = Build.VERSION.SDK_INT, isSearchingForBackup = isSearchingForBackup, isBackupSelectionRequired = BackupUtil.isUserSelectionRequired(LocalContext.current), - onNextClicked = this::onNextClicked, - onNotNowClicked = this::onNotNowClicked + onNextClicked = this::launchPermissionRequests, + onNotNowClicked = this::proceedToNextScreen ) } - private fun onNextClicked() { - when (args.welcomeAction) { - WelcomeAction.CONTINUE -> continueNext() - WelcomeAction.RESTORE_BACKUP -> Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] - } - } - - private fun continueNext() { + private fun launchPermissionRequests() { val isUserSelectionRequired = BackupUtil.isUserSelectionRequired(requireContext()) - val requiredPermissions = WelcomePermissions.getWelcomePermissions(isUserSelectionRequired) - requestPermissionLauncher.launch(requiredPermissions) - } - private fun onNotNowClicked() { - when (args.welcomeAction) { - WelcomeAction.CONTINUE -> continueNotNow() - WelcomeAction.RESTORE_BACKUP -> Log.w(TAG, "Not yet implemented!", NotImplementedError()) // TODO [regv2] + val neededPermissions = WelcomePermissions.getWelcomePermissions(isUserSelectionRequired).filterNot { + ContextCompat.checkSelfPermission(requireContext(), it) == PackageManager.PERMISSION_GRANTED } - } - private fun continueNotNow() { - NavHostFragment.findNavController(this).popBackStack() + if (neededPermissions.isEmpty()) { + proceedToNextScreen() + } else { + requestPermissionLauncher.launch(neededPermissions.toTypedArray()) + } } - private fun permissionsGranted(permissions: Map) { + private fun onPermissionsGranted(permissions: Map) { permissions.forEach { Log.d(TAG, "${it.key} = ${it.value}") } sharedViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.PERMISSIONS_GRANTED) + proceedToNextScreen() + } + + private fun proceedToNextScreen() { + when (welcomeAction) { + WelcomeAction.CONTINUE -> NavHostFragment.findNavController(this).safeNavigate(GrantPermissionsV2FragmentDirections.actionEnterPhoneNumber()) + WelcomeAction.RESTORE_BACKUP -> { + val restoreIntent = RestoreActivity.getIntentForRestore(requireActivity()) + launchRestoreActivity.launch(restoreIntent) + } + } } /** @@ -110,4 +116,8 @@ class GrantPermissionsV2Fragment : ComposeFragment() { CONTINUE, RESTORE_BACKUP } + + companion object { + private val TAG = Log.tag(GrantPermissionsV2Fragment::class.java) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt index 90702878a0..d60b3ab968 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt @@ -24,6 +24,7 @@ import androidx.core.widget.addTextChangedListener import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.findNavController import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -95,7 +96,9 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio sharedViewModel.uiState.observe(viewLifecycleOwner) { sharedState -> presentRegisterButton(sharedState) presentProgressBar(sharedState.inProgress, sharedState.isReRegister) - if (sharedState.registrationCheckpoint >= RegistrationCheckpoint.VERIFICATION_CODE_REQUESTED) { + if (sharedState.registrationCheckpoint >= RegistrationCheckpoint.PHONE_NUMBER_CONFIRMED && sharedState.canSkipSms) { + moveToEnterPinScreen() + } else if (sharedState.registrationCheckpoint >= RegistrationCheckpoint.VERIFICATION_CODE_REQUESTED) { moveToVerificationEntryScreen() } } @@ -320,6 +323,11 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio }.show() } + private fun moveToEnterPinScreen() { + sharedViewModel.setInProgress(false) + findNavController().safeNavigate(EnterPhoneNumberV2FragmentDirections.actionReRegisterWithPinV2Fragment()) + } + private fun moveToVerificationEntryScreen() { NavHostFragment.findNavController(this).safeNavigate(EnterPhoneNumberV2FragmentDirections.actionEnterVerificationCode()) sharedViewModel.setInProgress(false) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2Fragment.kt new file mode 100644 index 0000000000..f9bc892c23 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2Fragment.kt @@ -0,0 +1,243 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.reregisterwithpin + +import android.os.Bundle +import android.text.InputType +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.LoggingFragment +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.databinding.FragmentRegistrationPinRestoreEntryV2Binding +import org.thoughtcrime.securesms.lock.v2.PinKeyboardType +import org.thoughtcrime.securesms.lock.v2.SvrConstants +import org.thoughtcrime.securesms.registration.fragments.BaseRegistrationLockFragment +import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel +import org.thoughtcrime.securesms.util.CommunicationActions +import org.thoughtcrime.securesms.util.SupportEmailUtil +import org.thoughtcrime.securesms.util.ViewUtil +import org.thoughtcrime.securesms.util.navigation.safeNavigate + +class ReRegisterWithPinV2Fragment : LoggingFragment(R.layout.fragment_registration_pin_restore_entry_v2) { + companion object { + private val TAG = Log.tag(ReRegisterWithPinV2Fragment::class.java) + } + + private val registrationViewModel by activityViewModels() + private val reRegisterViewModel by viewModels() + + private val binding: FragmentRegistrationPinRestoreEntryV2Binding by ViewBinderDelegate(FragmentRegistrationPinRestoreEntryV2Binding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + RegistrationViewDelegate.setDebugLogSubmitMultiTapView(binding.pinRestorePinTitle) + binding.pinRestorePinDescription.setText(R.string.RegistrationLockFragment__enter_the_pin_you_created_for_your_account) + + binding.pinRestoreForgotPin.visibility = View.GONE + binding.pinRestoreForgotPin.setOnClickListener { onNeedHelpClicked() } + + binding.pinRestoreSkipButton.setOnClickListener { onSkipClicked() } + + binding.pinRestorePinInput.imeOptions = EditorInfo.IME_ACTION_DONE + binding.pinRestorePinInput.setOnEditorActionListener { v, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + ViewUtil.hideKeyboard(requireContext(), v!!) + handlePinEntry() + return@setOnEditorActionListener true + } + false + } + + enableAndFocusPinEntry() + + binding.pinRestorePinContinue.setOnClickListener { + handlePinEntry() + } + + binding.pinRestoreKeyboardToggle.setOnClickListener { + val currentKeyboardType: PinKeyboardType = getPinEntryKeyboardType() + updateKeyboard(currentKeyboardType.other) + binding.pinRestoreKeyboardToggle.setIconResource(currentKeyboardType.iconResource) + } + + binding.pinRestoreKeyboardToggle.setIconResource(getPinEntryKeyboardType().other.iconResource) + + registrationViewModel.uiState.observe(viewLifecycleOwner, ::updateViewState) + } + + private fun updateViewState(state: RegistrationV2State) { + if (state.networkError != null) { + genericErrorDialog() + } else if (!state.canSkipSms) { + abortSkipSmsFlow() + } else if (state.isRegistrationLockEnabled && state.svrTriesRemaining == 0) { + Log.w(TAG, "Unable to continue skip flow, KBS is locked") + onAccountLocked() + } else { + presentProgress(state.inProgress) + presentTriesRemaining(state.svrTriesRemaining) + } + } + + private fun abortSkipSmsFlow() { + findNavController().safeNavigate(ReRegisterWithPinV2FragmentDirections.actionReRegisterWithPinFragmentToEnterPhoneNumberV2Fragment()) + } + + private fun presentProgress(inProgress: Boolean) { + if (inProgress) { + ViewUtil.hideKeyboard(requireContext(), binding.pinRestorePinInput) + binding.pinRestorePinInput.isEnabled = false + binding.pinRestorePinContinue.setSpinning() + } else { + binding.pinRestorePinInput.isEnabled = true + binding.pinRestorePinContinue.cancelSpinning() + } + } + + private fun handlePinEntry() { + val pin: String? = binding.pinRestorePinInput.text?.toString() + + if (pin.isNullOrBlank()) { + Toast.makeText(requireContext(), R.string.RegistrationActivity_you_must_enter_your_registration_lock_PIN, Toast.LENGTH_LONG).show() + enableAndFocusPinEntry() + return + } + + if (pin.trim().length < BaseRegistrationLockFragment.MINIMUM_PIN_LENGTH) { + Toast.makeText(requireContext(), getString(R.string.RegistrationActivity_your_pin_has_at_least_d_digits_or_characters, BaseRegistrationLockFragment.MINIMUM_PIN_LENGTH), Toast.LENGTH_LONG).show() + enableAndFocusPinEntry() + return + } + + registrationViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.PIN_CONFIRMED) + + registrationViewModel.verifyReRegisterWithPin(requireContext(), pin) { + reRegisterViewModel.markIncorrectGuess() + } + + // TODO [regv2]: check for registration lock + wrong pin and decrement SVR tries remaining + } + + private fun presentTriesRemaining(triesRemaining: Int) { + if (reRegisterViewModel.hasIncorrectGuess) { + if (triesRemaining == 1 && !reRegisterViewModel.isLocalVerification) { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.PinRestoreEntryFragment_incorrect_pin) + .setMessage(resources.getQuantityString(R.plurals.PinRestoreEntryFragment_you_have_d_attempt_remaining, triesRemaining, triesRemaining)) + .setPositiveButton(android.R.string.ok, null) + .show() + } + + if (triesRemaining > 5) { + binding.pinRestorePinInputLabel.setText(R.string.PinRestoreEntryFragment_incorrect_pin) + } else { + binding.pinRestorePinInputLabel.text = resources.getQuantityString(R.plurals.RegistrationLockFragment__incorrect_pin_d_attempts_remaining, triesRemaining, triesRemaining) + } + binding.pinRestoreForgotPin.visibility = View.VISIBLE + } else { + if (triesRemaining == 1) { + binding.pinRestoreForgotPin.visibility = View.VISIBLE + if (!reRegisterViewModel.isLocalVerification) { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(resources.getQuantityString(R.plurals.PinRestoreEntryFragment_you_have_d_attempt_remaining, triesRemaining, triesRemaining)) + .setPositiveButton(android.R.string.ok, null) + .show() + } + } + } + + if (triesRemaining == 0) { + Log.w(TAG, "Account locked. User out of attempts on KBS.") + onAccountLocked() + } + } + + private fun onAccountLocked() { + Log.d(TAG, "Showing Incorrect PIN dialog. Is local verification: ${reRegisterViewModel.isLocalVerification}") + val message = if (reRegisterViewModel.isLocalVerification) R.string.ReRegisterWithPinFragment_out_of_guesses_local else R.string.PinRestoreLockedFragment_youve_run_out_of_pin_guesses + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.PinRestoreEntryFragment_incorrect_pin) + .setMessage(message) + .setCancelable(false) + .setPositiveButton(R.string.ReRegisterWithPinFragment_send_sms_code) { _, _ -> onSkipPinEntry() } + .setNegativeButton(R.string.AccountLockedFragment__learn_more) { _, _ -> CommunicationActions.openBrowserLink(requireContext(), getString(R.string.PinRestoreLockedFragment_learn_more_url)) } + .show() + } + + private fun enableAndFocusPinEntry() { + binding.pinRestorePinInput.isEnabled = true + binding.pinRestorePinInput.isFocusable = true + ViewUtil.focusAndShowKeyboard(binding.pinRestorePinInput) + } + + private fun getPinEntryKeyboardType(): PinKeyboardType { + val isNumeric = binding.pinRestorePinInput.inputType and InputType.TYPE_MASK_CLASS == InputType.TYPE_CLASS_NUMBER + return if (isNumeric) PinKeyboardType.NUMERIC else PinKeyboardType.ALPHA_NUMERIC + } + + private fun updateKeyboard(keyboard: PinKeyboardType) { + val isAlphaNumeric = keyboard == PinKeyboardType.ALPHA_NUMERIC + binding.pinRestorePinInput.inputType = if (isAlphaNumeric) InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD else InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD + binding.pinRestorePinInput.text?.clear() + } + + private fun onNeedHelpClicked() { + val message = if (reRegisterViewModel.isLocalVerification) R.string.ReRegisterWithPinFragment_need_help_local else R.string.PinRestoreEntryFragment_your_pin_is_a_d_digit_code + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.PinRestoreEntryFragment_need_help) + .setMessage(getString(message, SvrConstants.MINIMUM_PIN_LENGTH)) + .setPositiveButton(R.string.PinRestoreEntryFragment_skip) { _, _ -> onSkipPinEntry() } + .setNeutralButton(R.string.PinRestoreEntryFragment_contact_support) { _, _ -> + val body = SupportEmailUtil.generateSupportEmailBody(requireContext(), R.string.ReRegisterWithPinFragment_support_email_subject, null, null) + + CommunicationActions.openEmail( + requireContext(), + SupportEmailUtil.getSupportEmailAddress(requireContext()), + getString(R.string.ReRegisterWithPinFragment_support_email_subject), + body + ) + } + .setNegativeButton(R.string.PinRestoreEntryFragment_cancel, null) + .show() + } + + private fun onSkipClicked() { + val message = if (reRegisterViewModel.isLocalVerification) R.string.ReRegisterWithPinFragment_skip_local else R.string.PinRestoreEntryFragment_if_you_cant_remember_your_pin + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.PinRestoreEntryFragment_skip_pin_entry) + .setMessage(message) + .setPositiveButton(R.string.PinRestoreEntryFragment_skip) { _, _ -> onSkipPinEntry() } + .setNegativeButton(R.string.PinRestoreEntryFragment_cancel, null) + .show() + } + + private fun onSkipPinEntry() { + Log.d(TAG, "User skipping PIN entry.") + registrationViewModel.setUserSkippedReRegisterFlow(true) + } + + private fun genericErrorDialog() { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(R.string.RegistrationActivity_error_connecting_to_service) + .setPositiveButton(android.R.string.ok, null) + .create() + .show() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2State.kt new file mode 100644 index 0000000000..7f8bf609fa --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2State.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.reregisterwithpin + +data class ReRegisterWithPinV2State( + val isLocalVerification: Boolean = false, + val hasIncorrectGuess: Boolean = false, + val localPinMatches: Boolean = false +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2ViewModel.kt new file mode 100644 index 0000000000..2f8b2b4098 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/reregisterwithpin/ReRegisterWithPinV2ViewModel.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.v2.ui.reregisterwithpin + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import org.signal.core.util.logging.Log + +class ReRegisterWithPinV2ViewModel : ViewModel() { + companion object { + private val TAG = Log.tag(ReRegisterWithPinV2ViewModel::class.java) + } + + private val store = MutableStateFlow(ReRegisterWithPinV2State()) + val uiState = store.asLiveData() + + val isLocalVerification: Boolean + get() = store.value.isLocalVerification + val hasIncorrectGuess: Boolean + get() = store.value.hasIncorrectGuess + + fun markIncorrectGuess() { + store.update { + it.copy(hasIncorrectGuess = true) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt index cf4ca9b2bf..d2a7d36ba1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/welcome/WelcomeV2Fragment.kt @@ -6,12 +6,15 @@ package org.thoughtcrime.securesms.registration.v2.ui.welcome import android.Manifest +import android.app.Activity import android.content.pm.PackageManager import android.os.Bundle import android.view.View +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels -import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.findNavController import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.LoggingFragment import org.thoughtcrime.securesms.R @@ -20,8 +23,10 @@ import org.thoughtcrime.securesms.databinding.FragmentRegistrationWelcomeV2Bindi import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions +import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel import org.thoughtcrime.securesms.registration.v2.ui.grantpermissions.GrantPermissionsV2Fragment +import org.thoughtcrime.securesms.restore.RestoreActivity import org.thoughtcrime.securesms.util.BackupUtil import org.thoughtcrime.securesms.util.CommunicationActions import org.thoughtcrime.securesms.util.TextSecurePreferences @@ -36,6 +41,20 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome private val sharedViewModel by activityViewModels() private val binding: FragmentRegistrationWelcomeV2Binding by ViewBinderDelegate(FragmentRegistrationWelcomeV2Binding::bind) + private val launchRestoreActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> + when (val resultCode = result.resultCode) { + Activity.RESULT_OK -> { + sharedViewModel.onBackupSuccessfullyRestored() + findNavController().safeNavigate(WelcomeV2FragmentDirections.actionGoToRegistration()) + } + Activity.RESULT_CANCELED -> { + Log.w(TAG, "Backup restoration canceled.") + findNavController().popBackStack() + } + else -> Log.w(TAG, "Backup restoration activity ended with unknown result code: $resultCode") + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) maybePrefillE164() @@ -43,12 +62,13 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome setDebugLogSubmitMultiTapView(binding.title) binding.welcomeContinueButton.setOnClickListener { onContinueClicked() } binding.welcomeTermsButton.setOnClickListener { onTermsClicked() } + binding.welcomeTransferOrRestore.setOnClickListener { onTransferOrRestoreClicked() } } private fun onContinueClicked() { TextSecurePreferences.setHasSeenWelcomeScreen(requireContext(), true) if (Permissions.isRuntimePermissionsRequired() && !hasAllPermissions()) { - NavHostFragment.findNavController(this).safeNavigate(WelcomeV2FragmentDirections.actionWelcomeFragmentToGrantPermissionsV2Fragment(GrantPermissionsV2Fragment.WelcomeAction.CONTINUE)) + findNavController().safeNavigate(WelcomeV2FragmentDirections.actionWelcomeFragmentToGrantPermissionsV2Fragment(GrantPermissionsV2Fragment.WelcomeAction.CONTINUE)) } else { skipRestore() } @@ -60,13 +80,24 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome } private fun skipRestore() { - NavHostFragment.findNavController(this).safeNavigate(WelcomeV2FragmentDirections.actionSkipRestore()) + findNavController().safeNavigate(WelcomeV2FragmentDirections.actionSkipRestore()) } private fun onTermsClicked() { CommunicationActions.openBrowserLink(requireContext(), TERMS_AND_CONDITIONS_URL) } + private fun onTransferOrRestoreClicked() { + if (Permissions.isRuntimePermissionsRequired() && !hasAllPermissions()) { + findNavController().safeNavigate(WelcomeV2FragmentDirections.actionWelcomeFragmentToGrantPermissionsV2Fragment(GrantPermissionsV2Fragment.WelcomeAction.RESTORE_BACKUP)) + } else { + sharedViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.PERMISSIONS_GRANTED) + + val restoreIntent = RestoreActivity.getIntentForRestore(requireActivity()) + launchRestoreActivity.launch(restoreIntent) + } + } + private fun maybePrefillE164() { if (Permissions.hasAll(requireContext(), Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_PHONE_NUMBERS)) { val localNumber = Util.getDeviceNumber(requireContext()).getOrNull() diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt index e28896c2df..fea5fb263b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt @@ -23,12 +23,20 @@ class RestoreActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + setResult(RESULT_CANCELED) + setContentView(R.layout.activity_restore) intent.getParcelableExtraCompat(PassphraseRequiredActivity.NEXT_INTENT_EXTRA, Intent::class.java)?.let { sharedViewModel.setNextIntent(it) } } + fun finishActivitySuccessfully() { + setResult(RESULT_OK) + finish() + } + companion object { @JvmStatic fun getIntentForRestore(context: Context): Intent { diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt index 576ff86fcb..276acfa927 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt @@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.databinding.FragmentRestoreLocalBackupV2Bindin import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView import org.thoughtcrime.securesms.registration.fragments.RestoreBackupFragment.PassphraseAsYouTypeFormatter +import org.thoughtcrime.securesms.restore.RestoreActivity import org.thoughtcrime.securesms.restore.RestoreRepository import org.thoughtcrime.securesms.restore.RestoreViewModel import org.thoughtcrime.securesms.util.DateUtils @@ -52,12 +53,11 @@ class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_loc setDebugLogSubmitMultiTapView(binding.verifyHeader) Log.i(TAG, "Backup restore.") + binding.restoreButton.setOnClickListener { presentBackupPassPhrasePromptDialog() } + restoreLocalBackupViewModel.uiState.observe(viewLifecycleOwner) { fragmentState -> fragmentState.backupInfo?.let { presentBackupFileInfo(backupSize = it.size, backupTimestamp = it.timestamp) - if (fragmentState.backupPassphrase.isEmpty()) { - presentBackupPassPhrasePromptDialog() - } } if (fragmentState.restoreInProgress) { @@ -71,23 +71,23 @@ class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_loc if (importResult == null) { onBackupCompletedSuccessfully() } else { - handleBackupImportResult(importResult) + handleBackupImportError(importResult) } } } - restoreLocalBackupViewModel.startRestore(requireContext()) + restoreLocalBackupViewModel.prepareRestore(requireContext()) } private fun onBackupCompletedSuccessfully() { Log.d(TAG, "onBackupCompletedSuccessfully()") SignalStore.internalValues().setForceEnterRestoreV2Flow(false) - val activity = requireActivity() + val activity = requireActivity() as RestoreActivity navigationViewModel.getNextIntent()?.let { Log.d(TAG, "Launching ${it.component}") activity.startActivity(it) } - activity.finish() + activity.finishActivitySuccessfully() } override fun onStart() { @@ -105,12 +105,12 @@ class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_loc restoreLocalBackupViewModel.onBackupProgressUpdate(event) } - private fun handleBackupImportResult(importResult: RestoreRepository.BackupImportResult) { + private fun handleBackupImportError(importResult: RestoreRepository.BackupImportResult) { when (importResult) { RestoreRepository.BackupImportResult.FAILURE_VERSION_DOWNGRADE -> Toast.makeText(requireContext(), R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show() RestoreRepository.BackupImportResult.FAILURE_FOREIGN_KEY -> Toast.makeText(requireContext(), R.string.RegistrationActivity_backup_failure_foreign_key, Toast.LENGTH_LONG).show() RestoreRepository.BackupImportResult.FAILURE_UNKNOWN -> Toast.makeText(requireContext(), R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show() - RestoreRepository.BackupImportResult.SUCCESS -> Log.w(TAG, "Successful backup import should not be handled here.", IllegalStateException()) + RestoreRepository.BackupImportResult.SUCCESS -> Log.w(TAG, "Successful backup import should not be handled in this function.", IllegalStateException()) } } @@ -143,7 +143,7 @@ class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_loc ViewUtil.hideKeyboard(requireContext(), prompt) val passphrase = prompt.getText().toString() - restoreLocalBackupViewModel.confirmPassphrase(requireContext(), passphrase) + restoreLocalBackupViewModel.confirmPassphraseAndBeginRestore(requireContext(), passphrase) } .setNegativeButton(android.R.string.cancel, null) .show() diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt index f8c2fee5d3..20c9f3f9b3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt @@ -24,7 +24,7 @@ class RestoreLocalBackupViewModel(fileBackupUri: Uri) : ViewModel() { private val store = MutableStateFlow(RestoreLocalBackupState(fileBackupUri)) val uiState = store.asLiveData() - fun startRestore(context: Context) { + fun prepareRestore(context: Context) { val backupFileUri = store.value.uri viewModelScope.launch { val backupInfo = RestoreRepository.getLocalBackupFromUri(context, backupFileUri) @@ -48,7 +48,7 @@ class RestoreLocalBackupViewModel(fileBackupUri: Uri) : ViewModel() { } } - fun confirmPassphrase(context: Context, passphrase: String) { + fun confirmPassphraseAndBeginRestore(context: Context, passphrase: String) { store.update { it.copy( backupPassphrase = passphrase, diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index b555f7a8e3..151766491e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -130,6 +130,7 @@ public final class FeatureFlags { private static final String CAMERAX_CUSTOM_CONTROLLER = "android.cameraXCustomController"; private static final String REGISTRATION_V2 = "android.registration.v2"; private static final String LIBSIGNAL_WEB_SOCKET_ENABLED = "android.libsignalWebSocketEnabled"; + private static final String RESTORE_POST_REGISTRATION = "android.registration.restorePostRegistration"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -214,7 +215,7 @@ public final class FeatureFlags { ); @VisibleForTesting - static final Set NOT_REMOTE_CAPABLE = SetUtil.newHashSet(MESSAGE_BACKUPS, REGISTRATION_V2); + static final Set NOT_REMOTE_CAPABLE = SetUtil.newHashSet(MESSAGE_BACKUPS, REGISTRATION_V2, RESTORE_POST_REGISTRATION); /** * Values in this map will take precedence over any value. This should only be used for local @@ -759,6 +760,11 @@ public static boolean registrationV2() { /** Whether unauthenticated chat web socket is backed by libsignal-net */ public static boolean libSignalWebSocketEnabled() { return getBoolean(LIBSIGNAL_WEB_SOCKET_ENABLED, false); } + /** Whether or not to launch the restore activity after registration is complete, rather than before. */ + public static boolean restoreAfterRegistration() { + return getBoolean(RESTORE_POST_REGISTRATION, false); + } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); diff --git a/app/src/main/res/layout/fragment_registration_pin_restore_entry_v2.xml b/app/src/main/res/layout/fragment_registration_pin_restore_entry_v2.xml new file mode 100644 index 0000000000..9f947c0d81 --- /dev/null +++ b/app/src/main/res/layout/fragment_registration_pin_restore_entry_v2.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_registration_welcome_v2.xml b/app/src/main/res/layout/fragment_registration_welcome_v2.xml index e4914c5f42..5f5a350be4 100644 --- a/app/src/main/res/layout/fragment_registration_welcome_v2.xml +++ b/app/src/main/res/layout/fragment_registration_welcome_v2.xml @@ -55,7 +55,6 @@ app:layout_goneMarginBottom="@dimen/registration_button_bottom_margin" app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal" /> - - - - - - - - - - - - { } } + /** + * Returns the [Throwable] associated with the result, or null if the result is successful. + */ + fun getCause(): Throwable? { + return when (this) { + is Success -> null + is NetworkError -> exception + is StatusCodeError -> exception + is ApplicationError -> throwable + } + } + /** * Takes the output of one [NetworkResult] and transforms it into another if the operation is successful. * If it's a failure, the original failure will be propagated. Useful for changing the type of a result. diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt index 80dce4bc2e..1eea96734c 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt @@ -8,6 +8,7 @@ package org.whispersystems.signalservice.api.registration import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.account.AccountAttributes import org.whispersystems.signalservice.api.account.PreKeyCollection +import org.whispersystems.signalservice.internal.push.BackupAuthCheckResponse import org.whispersystems.signalservice.internal.push.PushServiceSocket import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse import org.whispersystems.signalservice.internal.push.VerifyAccountResponse @@ -67,4 +68,10 @@ class RegistrationApi( pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, aciPreKeys, pniPreKeys, fcmToken, skipDeviceTransfer) } } + + fun getSvrAuthCredential(e164: String, usernamePasswords: List): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.checkBackupAuthCredentials(e164, usernamePasswords) + } + } } 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 b32df31fac..af6602b5d3 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 @@ -1137,6 +1137,11 @@ public Single> checkBackupAuthCredentia .onErrorReturn(ServiceResponse::forUnknownError); } + public BackupAuthCheckResponse checkBackupAuthCredentials(@Nullable String number, @Nonnull List passwords) throws IOException { + String response = makeServiceRequest(BACKUP_AUTH_CHECK, "POST", JsonUtil.toJson(new BackupAuthCheckRequest(number, passwords)), NO_HEADERS, UNOPINIONATED_HANDLER, Optional.empty()); + return JsonUtil.fromJson(response, BackupAuthCheckResponse.class); + } + private Single> createBackupAuthCheckSingle(@Nonnull String path, @Nonnull BackupAuthCheckRequest request, @Nonnull ResponseMapper responseMapper) @@ -3048,7 +3053,6 @@ public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResp } } - private static RegistrationSessionMetadataResponse parseSessionMetadataResponse(Response response) throws IOException { long serverDeliveredTimestamp = 0; try { From d20f58880231a9802ac60a798c98c851123de739 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Mon, 29 Apr 2024 15:36:15 -0400 Subject: [PATCH 094/113] Inline the group call reactions feature flag. --- .../components/webrtc/CallOverflowPopupWindow.kt | 11 ++++------- .../securesms/components/webrtc/WebRtcControls.java | 2 +- .../securesms/service/webrtc/SignalCallManager.java | 4 +--- .../org/thoughtcrime/securesms/util/FeatureFlags.java | 10 ---------- app/src/main/res/layout/call_overflow_holder.xml | 3 +-- 5 files changed, 7 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt index de2b5be2f2..0f5038bd1c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt @@ -35,13 +35,10 @@ class CallOverflowPopupWindow(private val activity: FragmentActivity, parentView init { val root = (contentView as LinearLayout) - if (FeatureFlags.groupCallReactions()) { - val reactionScrubber = root.findViewById(R.id.reaction_scrubber) - reactionScrubber.visible = true - reactionScrubber.initialize(activity.supportFragmentManager) { - ApplicationDependencies.getSignalCallManager().react(it) - dismiss() - } + val reactionScrubber = root.findViewById(R.id.reaction_scrubber) + reactionScrubber.initialize(activity.supportFragmentManager) { + ApplicationDependencies.getSignalCallManager().react(it) + dismiss() } if (FeatureFlags.groupCallRaiseHand()) { val raiseHand = root.findViewById(R.id.raise_hand_layout_parent) 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 7559176cb0..3fce3a64ce 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 @@ -159,7 +159,7 @@ public boolean displayEndCall() { } public boolean displayOverflow() { - return (FeatureFlags.groupCallReactions() || FeatureFlags.groupCallRaiseHand()) && isAtLeastOutgoing() && hasAtLeastOneRemote && isGroupCall(); + return isAtLeastOutgoing() && hasAtLeastOneRemote && isGroupCall(); } public boolean displayMuteAudio() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java index 8b71255ff6..6b3e234196 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java @@ -917,9 +917,7 @@ public void onLowBandwidthForVideo(@NonNull GroupCall groupCall, boolean recover @Override public void onReactions(@NonNull GroupCall groupCall, List reactions) { - if (FeatureFlags.groupCallReactions()) { - processStateless(s -> serviceState.getActionProcessor().handleGroupCallReaction(serviceState, s, reactions)); - } + processStateless(s -> serviceState.getActionProcessor().handleGroupCallReaction(serviceState, s, reactions)); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 151766491e..756f75ae4f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -113,7 +113,6 @@ public final class FeatureFlags { private static final String IDEAL_DONATIONS = "android.ideal.donations.5"; public static final String IDEAL_ENABLED_REGIONS = "global.donations.idealEnabledRegions"; public static final String SEPA_ENABLED_REGIONS = "global.donations.sepaEnabledRegions"; - private static final String CALLING_REACTIONS = "android.calling.reactions"; private static final String NOTIFICATION_THUMBNAIL_BLOCKLIST = "android.notificationThumbnailProductBlocklist"; private static final String CALLING_RAISE_HAND = "android.calling.raiseHand"; private static final String USE_ACTIVE_CALL_MANAGER = "android.calling.useActiveCallManager.5"; @@ -197,7 +196,6 @@ public final class FeatureFlags { IDEAL_DONATIONS, IDEAL_ENABLED_REGIONS, SEPA_ENABLED_REGIONS, - CALLING_REACTIONS, NOTIFICATION_THUMBNAIL_BLOCKLIST, CALLING_RAISE_HAND, USE_ACTIVE_CALL_MANAGER, @@ -278,7 +276,6 @@ public final class FeatureFlags { PROMPT_FOR_NOTIFICATION_CONFIG, PROMPT_BATTERY_SAVER, CRASH_PROMPT_CONFIG, - CALLING_REACTIONS, NOTIFICATION_THUMBNAIL_BLOCKLIST, CALLING_RAISE_HAND, VIDEO_RECORD_1X_ZOOM, @@ -679,13 +676,6 @@ public static String sepaEnabledRegions() { return getString(SEPA_ENABLED_REGIONS, ""); } - /** - * Whether or not group call reactions are enabled. - */ - public static boolean groupCallReactions() { - return getBoolean(CALLING_REACTIONS, false); - } - /** * Whether or not group call raise hand is enabled. */ diff --git a/app/src/main/res/layout/call_overflow_holder.xml b/app/src/main/res/layout/call_overflow_holder.xml index 0f07b86d9a..71d26510d5 100644 --- a/app/src/main/res/layout/call_overflow_holder.xml +++ b/app/src/main/res/layout/call_overflow_holder.xml @@ -15,8 +15,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/calling_reaction_emoji_height" android:background="@drawable/conversation_reaction_overlay_background" - android:elevation="4dp" - android:visibility="gone" /> + android:elevation="4dp"/> Date: Mon, 29 Apr 2024 14:57:01 -0700 Subject: [PATCH 095/113] Support proxy in connections managed by libsignal. --- .../dependencies/ApplicationDependencies.java | 15 +++++--- .../ApplicationDependencyProvider.java | 10 ++--- .../MockApplicationDependencyProvider.java | 6 +-- .../api/SignalServiceAccountManager.java | 3 +- .../api/services/CdsiV2Service.java | 3 +- .../internal/websocket/LibSignalNetwork.kt | 38 ++++++++++++++++++- 6 files changed, 58 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java index d067d7f088..4f3e7c37d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java @@ -8,7 +8,6 @@ import androidx.annotation.VisibleForTesting; import org.signal.core.util.concurrent.DeadlockDetector; -import org.signal.libsignal.net.Network; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations; import org.thoughtcrime.securesms.components.TypingStatusRepository; @@ -59,6 +58,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager; import org.whispersystems.signalservice.internal.util.Util; +import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -131,7 +131,7 @@ public class ApplicationDependencies { private static volatile DeadlockDetector deadlockDetector; private static volatile ClientZkReceiptOperations clientZkReceiptOperations; private static volatile ScheduledMessageManager scheduledMessagesManager; - private static volatile Network libsignalNetwork; + private static volatile LibSignalNetwork libsignalNetwork; @MainThread public static void init(@NonNull Application application, @NonNull Provider provider) { @@ -260,6 +260,9 @@ public static void closeConnections() { public static void resetAllNetworkConnections() { synchronized (LOCK) { closeConnections(); + if (libsignalNetwork != null) { + libsignalNetwork.resetSettings(getSignalServiceNetworkAccess().getConfiguration()); + } if (signalWebSocket != null) { signalWebSocket.forceNewWebSockets(); } @@ -688,11 +691,11 @@ public static void resetProtocolStores() { return deadlockDetector; } - public static @NonNull Network getLibsignalNetwork() { + public static @NonNull LibSignalNetwork getLibsignalNetwork() { if (libsignalNetwork == null) { synchronized (LIBSIGNAL_NETWORK_LOCK) { if (libsignalNetwork == null) { - libsignalNetwork = provider.provideLibsignalNetwork(); + libsignalNetwork = provider.provideLibsignalNetwork(getSignalServiceNetworkAccess().getConfiguration()); } } } @@ -726,7 +729,7 @@ public interface Provider { @NonNull SignalCallManager provideSignalCallManager(); @NonNull PendingRetryReceiptManager providePendingRetryReceiptManager(); @NonNull PendingRetryReceiptCache providePendingRetryReceiptCache(); - @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier); + @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier); @NonNull SignalServiceDataStoreImpl provideProtocolStore(); @NonNull GiphyMp4Cache provideGiphyMp4Cache(); @NonNull SimpleExoPlayerPool provideExoPlayerPool(); @@ -737,6 +740,6 @@ public interface Provider { @NonNull DeadlockDetector provideDeadlockDetector(); @NonNull ClientZkReceiptOperations provideClientZkReceiptOperations(@NonNull SignalServiceConfiguration signalServiceConfiguration); @NonNull ScheduledMessageManager provideScheduledMessageManager(); - @NonNull Network provideLibsignalNetwork(); + @NonNull LibSignalNetwork provideLibsignalNetwork(@NonNull SignalServiceConfiguration config); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 2bb2f43273..0ec99876a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -233,8 +233,8 @@ public ApplicationDependencyProvider(@NonNull Application context) { } @Override - public @NonNull Network provideLibsignalNetwork() { - return new Network(BuildConfig.LIBSIGNAL_NET_ENV); + public @NonNull LibSignalNetwork provideLibsignalNetwork(@NonNull SignalServiceConfiguration config) { + return new LibSignalNetwork(new Network(BuildConfig.LIBSIGNAL_NET_ENV), config); } @Override @@ -290,7 +290,7 @@ public ApplicationDependencyProvider(@NonNull Application context) { } @Override - public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier) { + public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier) { SleepTimer sleepTimer = !SignalStore.account().isFcmEnabled() || SignalStore.internalValues().isWebsocketModeForced() ? new AlarmSleepTimer(context) : new UptimeSleepTimer() ; SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(context, sleepTimer); SignalWebSocket signalWebSocket = new SignalWebSocket(provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor, libSignalNetworkSupplier)); @@ -400,7 +400,7 @@ public ApplicationDependencyProvider(@NonNull Application context) { return provideClientZkOperations(signalServiceConfiguration).getReceiptOperations(); } - @NonNull WebSocketFactory provideWebSocketFactory(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull SignalWebSocketHealthMonitor healthMonitor, @NonNull Supplier libSignalNetworkSupplier) { + @NonNull WebSocketFactory provideWebSocketFactory(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull SignalWebSocketHealthMonitor healthMonitor, @NonNull Supplier libSignalNetworkSupplier) { return new WebSocketFactory() { @Override public WebSocketConnection createWebSocket() { @@ -415,7 +415,7 @@ public WebSocketConnection createWebSocket() { @Override public WebSocketConnection createUnidentifiedWebSocket() { if (FeatureFlags.libSignalWebSocketEnabled()) { - var network = new LibSignalNetwork(libSignalNetworkSupplier.get()); + LibSignalNetwork network = libSignalNetworkSupplier.get(); return new LibSignalChatConnection( "libsignal-unauth", network.createChatService(null), diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java index 88df57b100..a166f99256 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.java @@ -3,7 +3,6 @@ import androidx.annotation.NonNull; import org.signal.core.util.concurrent.DeadlockDetector; -import org.signal.libsignal.net.Network; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations; import org.thoughtcrime.securesms.components.TypingStatusRepository; @@ -43,6 +42,7 @@ import org.whispersystems.signalservice.api.services.DonationsService; import org.whispersystems.signalservice.api.services.ProfileService; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork; import java.util.function.Supplier; @@ -181,7 +181,7 @@ public class MockApplicationDependencyProvider implements ApplicationDependencie } @Override - public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier) { + public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier signalServiceConfigurationSupplier, @NonNull Supplier libSignalNetworkSupplier) { return null; } @@ -235,7 +235,7 @@ public class MockApplicationDependencyProvider implements ApplicationDependencie } @Override - public @NonNull Network provideLibsignalNetwork() { + public @NonNull LibSignalNetwork provideLibsignalNetwork(@NonNull SignalServiceConfiguration config) { return null; } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java index 801615e3d5..7c271cbc89 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java @@ -87,6 +87,7 @@ import org.whispersystems.signalservice.internal.util.Util; import org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper; import org.signal.core.util.Base64; +import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork; import java.io.IOException; import java.security.MessageDigest; @@ -369,7 +370,7 @@ public CdsiV2Service.Response getRegisteredUsersWithCdsi(Set previousE16 Optional token, String mrEnclave, Long timeoutMs, - @Nullable Network libsignalNetwork, + @Nullable LibSignalNetwork libsignalNetwork, Consumer tokenSaver) throws IOException { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/CdsiV2Service.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/CdsiV2Service.java index 2549af2157..40d1bbc0e8 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/CdsiV2Service.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/CdsiV2Service.java @@ -19,6 +19,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.internal.ServiceResponse; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -55,7 +56,7 @@ public final class CdsiV2Service { private final CdsiRequestHandler cdsiRequestHandler; - public CdsiV2Service(SignalServiceConfiguration configuration, String mrEnclave, @Nullable Network network) { + public CdsiV2Service(SignalServiceConfiguration configuration, String mrEnclave, @Nullable LibSignalNetwork network) { if (network != null) { this.cdsiRequestHandler = (username, password, request, tokenSaver) -> { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt index 625ad2f44c..6d2134ea3c 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetwork.kt @@ -5,14 +5,27 @@ package org.whispersystems.signalservice.internal.websocket +import org.signal.core.util.orNull +import org.signal.libsignal.internal.CompletableFuture +import org.signal.libsignal.net.CdsiLookupRequest +import org.signal.libsignal.net.CdsiLookupResponse import org.signal.libsignal.net.ChatService import org.signal.libsignal.net.Network import org.whispersystems.signalservice.api.util.CredentialsProvider +import org.whispersystems.signalservice.internal.configuration.SignalProxy +import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration +import java.io.IOException +import java.util.concurrent.ExecutionException +import java.util.function.Consumer /** * Makes Network API more ergonomic to use with Android client types */ -class LibSignalNetwork(private val inner: Network) { +class LibSignalNetwork(private val inner: Network, config: SignalServiceConfiguration) { + init { + resetSettings(config) + } + fun createChatService( credentialsProvider: CredentialsProvider? = null ): ChatService { @@ -20,4 +33,27 @@ class LibSignalNetwork(private val inner: Network) { val password = credentialsProvider?.password ?: "" return inner.createChatService(username, password) } + + fun resetSettings(config: SignalServiceConfiguration) { + resetProxy(config.signalProxy.orNull()) + } + + private fun resetProxy(proxy: SignalProxy?) { + if (proxy == null) { + inner.clearProxy() + } else { + inner.setProxy(proxy.host, proxy.port) + } + } + + // Delegates + @Throws(IOException::class, InterruptedException::class, ExecutionException::class) + fun cdsiLookup( + username: String?, + password: String?, + request: CdsiLookupRequest?, + tokenConsumer: Consumer + ): CompletableFuture? { + return inner.cdsiLookup(username, password, request, tokenConsumer) + } } From 95a683598813012fdd83fb0796a9d7416a9b61eb Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 29 Apr 2024 19:22:17 -0400 Subject: [PATCH 096/113] Improve handling of backup initialization. --- .../securesms/backup/v2/BackupRepository.kt | 81 ++++--- .../securesms/keyvalue/BackupValues.kt | 16 +- .../signalservice/api/NetworkResult.kt | 96 +++++++- .../signalservice/api/NetworkResultTest.kt | 221 ++++++++++++++++++ 4 files changed, 360 insertions(+), 54 deletions(-) create mode 100644 libsignal-service/src/test/java/org/whispersystems/signalservice/api/NetworkResultTest.kt 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 fd83f9ede0..8193585d4b 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 @@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.RecipientId import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.StatusCodeErrorAction import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse import org.whispersystems.signalservice.api.archive.ArchiveMediaRequest import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential @@ -61,6 +62,13 @@ object BackupRepository { private val TAG = Log.tag(BackupRepository::class.java) private const val VERSION = 1L + private val resetInitializedStateErrorAction: StatusCodeErrorAction = { error -> + if (error.code == 401) { + Log.i(TAG, "Resetting initialized state due to 401.") + SignalStore.backup().backupsInitialized = false + } + } + fun export(outputStream: OutputStream, append: (ByteArray) -> Unit, plaintext: Boolean = false) { val eventTimer = EventTimer() val writer: BackupExportWriter = if (plaintext) { @@ -229,13 +237,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } - .then { credential -> - api.setPublicKey(backupKey, credential) - .map { credential } - } + return initBackupAndFetchAuth(backupKey) .then { credential -> api.getArchiveMediaItemsPage(backupKey, credential, limit, cursor) } @@ -248,14 +250,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } - .then { credential -> - api.setPublicKey(backupKey, credential) - .also { Log.i(TAG, "PublicKeyResult: $it") } - .map { credential } - } + return initBackupAndFetchAuth(backupKey) .then { credential -> api.getBackupInfo(backupKey, credential) .map { it to credential } @@ -282,14 +277,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } - .then { credential -> - api.setPublicKey(backupKey, credential) - .also { Log.i(TAG, "PublicKeyResult: $it") } - .map { credential } - } + return initBackupAndFetchAuth(backupKey) .then { credential -> api.getMessageBackupUploadForm(backupKey, credential) .also { Log.i(TAG, "UploadFormResult: $it") } @@ -311,9 +299,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } + return initBackupAndFetchAuth(backupKey) .then { credential -> api.getBackupInfo(backupKey, credential) } @@ -332,9 +318,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } + return initBackupAndFetchAuth(backupKey) .then { credential -> api.debugGetUploadedMediaItemMetadata(backupKey, credential) } @@ -347,9 +331,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } + return initBackupAndFetchAuth(backupKey) .then { credential -> api.getMediaUploadForm(backupKey, credential) } @@ -362,9 +344,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } + return initBackupAndFetchAuth(backupKey) .then { credential -> api.setPublicKey(backupKey, credential) .map { credential } @@ -390,9 +370,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return api - .triggerBackupIdReservation(backupKey) - .then { getAuthCredential() } + return initBackupAndFetchAuth(backupKey) .then { credential -> val requests = mutableListOf() val mediaIdToAttachmentId = mutableMapOf() @@ -445,7 +423,7 @@ object BackupRepository { return NetworkResult.Success(Unit) } - return getAuthCredential() + return initBackupAndFetchAuth(backupKey) .then { credential -> api.deleteArchivedMedia( backupKey = backupKey, @@ -476,7 +454,7 @@ object BackupRepository { return NetworkResult.Success(Unit) } - return getAuthCredential() + return initBackupAndFetchAuth(backupKey) .then { credential -> api.deleteArchivedMedia( backupKey = backupKey, @@ -533,7 +511,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return getAuthCredential() + return initBackupAndFetchAuth(backupKey) .then { credential -> api.getCdnReadCredentials( cdnNumber = cdnNumber, @@ -570,7 +548,7 @@ object BackupRepository { val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() - return getAuthCredential() + return initBackupAndFetchAuth(backupKey) .then { credential -> api.getBackupInfo(backupKey, credential).map { BackupDirectories(it.backupDir!!, it.mediaDir!!) @@ -584,6 +562,25 @@ object BackupRepository { } } + /** + * Ensures that the backupId has been reserved and that your public key has been set, while also returning an auth credential. + * Should be the basis of all backup operations. + */ + private fun initBackupAndFetchAuth(backupKey: BackupKey): NetworkResult { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + + return if (SignalStore.backup().backupsInitialized) { + getAuthCredential().runOnStatusCodeError(resetInitializedStateErrorAction) + } else { + return api + .triggerBackupIdReservation(backupKey) + .then { getAuthCredential() } + .then { credential -> api.setPublicKey(backupKey, credential).map { credential } } + .runIfSuccessful { SignalStore.backup().backupsInitialized = true } + .runOnStatusCodeError(resetInitializedStateErrorAction) + } + } + /** * Retrieves an auth credential, preferring a cached value if available. */ 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 f32f06a27d..3d73cedc34 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -26,6 +26,7 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { private const val KEY_CDN_BACKUP_MEDIA_DIRECTORY = "backup.cdn.mediaDirectory" private const val KEY_OPTIMIZE_STORAGE = "backup.optimizeStorage" + private const val KEY_BACKUPS_INITIALIZED = "backup.initialized" /** * Specifies whether remote backups are enabled on this device. @@ -49,7 +50,20 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { var nextBackupTime: Long by longValue(KEY_NEXT_BACKUP_TIME, -1) - var areBackupsEnabled: Boolean by booleanValue(KEY_BACKUPS_ENABLED, false) + var areBackupsEnabled: Boolean + get() { + return getBoolean(KEY_BACKUPS_ENABLED, false) + } + set(value) { + store + .beginWrite() + .putBoolean(KEY_BACKUPS_ENABLED, value) + .putLong(KEY_NEXT_BACKUP_TIME, -1) + .putBoolean(KEY_BACKUPS_INITIALIZED, false) + .apply() + } + + var backupsInitialized: Boolean by booleanValue(KEY_BACKUPS_INITIALIZED, false) /** * Retrieves the stored credentials, mapped by the day they're valid. The day is represented as diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt index d8fa217fbd..6465bd5f88 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/NetworkResult.kt @@ -8,6 +8,8 @@ package org.whispersystems.signalservice.api import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException import java.io.IOException +typealias StatusCodeErrorAction = (NetworkResult.StatusCodeError<*>) -> Unit + /** * A helper class that wraps the result of a network request, turning common exceptions * into sealed classes, with optional request chaining. @@ -22,7 +24,9 @@ import java.io.IOException * sealed class. However, for the majority of requests which just require getting a model from * the success case and the status code of the error, this can be quite convenient. */ -sealed class NetworkResult { +sealed class NetworkResult( + private val statusCodeErrorActions: MutableSet = mutableSetOf() +) { companion object { /** * A convenience method to capture the common case of making a request. @@ -54,6 +58,8 @@ sealed class NetworkResult { /** * Returns the result if successful, otherwise turns the result back into an exception and throws it. + * + * Useful for bridging to Java, where you may want to use try-catch. */ fun successOrThrow(): T { when (this) { @@ -78,27 +84,95 @@ sealed class NetworkResult { /** * Takes the output of one [NetworkResult] and transforms it into another if the operation is successful. - * If it's a failure, the original failure will be propagated. Useful for changing the type of a result. + * If it's non-successful, [transform] lambda is not run, and instead the original failure will be propagated. + * Useful for changing the type of a result. + * + * ```kotlin + * val user: NetworkResult = NetworkResult + * .fromFetch { fetchRemoteUserModel() } + * .map { it.toLocalUserModel() } + * ``` */ fun map(transform: (T) -> R): NetworkResult { return when (this) { - is Success -> Success(transform(this.result)) - is NetworkError -> NetworkError(exception) - is StatusCodeError -> StatusCodeError(code, body, exception) - is ApplicationError -> ApplicationError(throwable) + is Success -> Success(transform(this.result)).runOnStatusCodeError(statusCodeErrorActions) + is NetworkError -> NetworkError(exception).runOnStatusCodeError(statusCodeErrorActions) + is ApplicationError -> ApplicationError(throwable).runOnStatusCodeError(statusCodeErrorActions) + is StatusCodeError -> StatusCodeError(code, body, exception).runOnStatusCodeError(statusCodeErrorActions) } } /** * Takes the output of one [NetworkResult] and passes it as the input to another if the operation is successful. - * If it's a failure, the original failure will be propagated. Useful for chaining operations together. + * If it's non-successful, the [result] lambda is not run, and instead the original failure will be propagated. + * Useful for chaining operations together. + * + * ```kotlin + * val networkResult: NetworkResult = NetworkResult + * .fromFetch { fetchAuthCredential() } + * .then { + * NetworkResult.fromFetch { credential -> fetchData(credential) } + * } + * ``` */ fun then(result: (T) -> NetworkResult): NetworkResult { return when (this) { - is Success -> result(this.result) - is NetworkError -> NetworkError(exception) - is StatusCodeError -> StatusCodeError(code, body, exception) - is ApplicationError -> ApplicationError(throwable) + is Success -> result(this.result).runOnStatusCodeError(statusCodeErrorActions) + is NetworkError -> NetworkError(exception).runOnStatusCodeError(statusCodeErrorActions) + is ApplicationError -> ApplicationError(throwable).runOnStatusCodeError(statusCodeErrorActions) + is StatusCodeError -> StatusCodeError(code, body, exception).runOnStatusCodeError(statusCodeErrorActions) } } + + /** + * Will perform an operation if the result at this point in the chain is successful. Note that it runs if the chain is _currently_ successful. It does not + * depend on anything futher down the chain. + * + * ```kotlin + * val networkResult: NetworkResult = NetworkResult + * .fromFetch { fetchAuthCredential() } + * .runIfSuccessful { storeMyCredential(it) } + * ``` + */ + fun runIfSuccessful(result: (T) -> Unit): NetworkResult { + if (this is Success) { + result(this.result) + } + return this + } + + /** + * Specify an action to be run when a status code error occurs. When a result is a [StatusCodeError] or is transformed into one further down the chain via + * a future [map] or [then], this code will be run. There can only ever be a single status code error in a chain, and therefore this lambda will only ever + * be run a single time. + * + * This is a low-visibility way of doing things, so use sparingly. + * + * ```kotlin + * val result = NetworkResult + * .fromFetch { getAuth() } + * .runOnStatusCodeError { error -> logError(error) } + * .then { credential -> + * NetworkResult.fromFetch { fetchUserDetails(credential) } + * } + * ``` + */ + fun runOnStatusCodeError(action: StatusCodeErrorAction): NetworkResult { + return runOnStatusCodeError(setOf(action)) + } + + internal fun runOnStatusCodeError(actions: Collection): NetworkResult { + if (actions.isEmpty()) { + return this + } + + statusCodeErrorActions += actions + + if (this is StatusCodeError) { + statusCodeErrorActions.forEach { it.invoke(this) } + statusCodeErrorActions.clear() + } + + return this + } } diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/NetworkResultTest.kt b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/NetworkResultTest.kt new file mode 100644 index 0000000000..591ee17d11 --- /dev/null +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/NetworkResultTest.kt @@ -0,0 +1,221 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException + +class NetworkResultTest { + @Test + fun `generic success`() { + val result = NetworkResult.fromFetch {} + + assertTrue(result is NetworkResult.Success) + } + + @Test + fun `generic non-successful status code`() { + val exception = NonSuccessfulResponseCodeException(404, "not found", "body") + + val result = NetworkResult.fromFetch { + throw exception + } + + check(result is NetworkResult.StatusCodeError) + assertEquals(exception, result.exception) + assertEquals(404, result.code) + assertEquals("body", result.body) + } + + @Test + fun `generic network error`() { + val exception = PushNetworkException("general exception") + + val result = NetworkResult.fromFetch { + throw exception + } + + assertTrue(result is NetworkResult.NetworkError) + assertEquals(exception, (result as NetworkResult.NetworkError).exception) + } + + @Test + fun `generic application error`() { + val throwable = RuntimeException("test") + + val result = NetworkResult.fromFetch { + throw throwable + } + + assertTrue(result is NetworkResult.ApplicationError) + assertEquals(throwable, (result as NetworkResult.ApplicationError).throwable) + } + + @Test + fun `then - generic`() { + val result = NetworkResult + .fromFetch { NetworkResult.Success(1) } + .then { NetworkResult.Success(2) } + + assertTrue(result is NetworkResult.Success) + assertEquals(2, (result as NetworkResult.Success).result) + } + + @Test + fun `then - doesn't run on error`() { + val throwable = RuntimeException("test") + var run = false + + val result = NetworkResult + .fromFetch { throw throwable } + .then { + run = true + NetworkResult.Success(1) + } + + assertTrue(result is NetworkResult.ApplicationError) + assertFalse(run) + } + + @Test + fun `map - generic`() { + val result = NetworkResult + .fromFetch { NetworkResult.Success(1) } + .map { 2 } + + assertTrue(result is NetworkResult.Success) + assertEquals(2, (result as NetworkResult.Success).result) + } + + @Test + fun `map - doesn't run on error`() { + val throwable = RuntimeException("test") + var run = false + + val result = NetworkResult + .fromFetch { throw throwable } + .map { + run = true + 1 + } + + assertTrue(result is NetworkResult.ApplicationError) + assertFalse(run) + } + + @Test + fun `runIfSuccessful - doesn't run on error`() { + val throwable = RuntimeException("test") + var run = false + + val result = NetworkResult + .fromFetch { throw throwable } + .runIfSuccessful { run = true } + + assertTrue(result is NetworkResult.ApplicationError) + assertFalse(run) + } + + @Test + fun `runIfSuccessful - runs on success`() { + var run = false + + NetworkResult + .fromFetch { NetworkResult.Success(1) } + .runIfSuccessful { run = true } + + assertTrue(run) + } + + @Test + fun `runIfSuccessful - runs before error`() { + val throwable = RuntimeException("test") + var run = false + + val result = NetworkResult + .fromFetch { NetworkResult.Success(Unit) } + .runIfSuccessful { run = true } + .then { NetworkResult.ApplicationError(throwable) } + + assertTrue(result is NetworkResult.ApplicationError) + assertTrue(run) + } + + @Test + fun `runOnStatusCodeError - simple call`() { + var handled = false + + NetworkResult + .fromFetch { throw NonSuccessfulResponseCodeException(404, "not found", "body") } + .runOnStatusCodeError { handled = true } + + assertTrue(handled) + } + + @Test + fun `runOnStatusCodeError - ensure only called once`() { + var handleCount = 0 + + NetworkResult + .fromFetch { throw NonSuccessfulResponseCodeException(404, "not found", "body") } + .runOnStatusCodeError { handleCount++ } + .map { 1 } + .then { NetworkResult.Success(2) } + .map { 3 } + + assertEquals(1, handleCount) + } + + @Test + fun `runOnStatusCodeError - called when placed before a failing then`() { + var handled = false + + val result = NetworkResult + .fromFetch { } + .runOnStatusCodeError { handled = true } + .then { NetworkResult.fromFetch { throw NonSuccessfulResponseCodeException(404, "not found", "body") } } + + assertTrue(handled) + assertTrue(result is NetworkResult.StatusCodeError) + } + + @Test + fun `runOnStatusCodeError - called when placed two spots before a failing then`() { + var handled = false + + val result = NetworkResult + .fromFetch { } + .runOnStatusCodeError { handled = true } + .then { NetworkResult.Success(Unit) } + .then { NetworkResult.fromFetch { throw NonSuccessfulResponseCodeException(404, "not found", "body") } } + + assertTrue(handled) + assertTrue(result is NetworkResult.StatusCodeError) + } + + @Test + fun `runOnStatusCodeError - should not be called for successful results`() { + var handled = false + + NetworkResult + .fromFetch {} + .runOnStatusCodeError { handled = true } + + NetworkResult + .fromFetch { throw RuntimeException("application error") } + .runOnStatusCodeError { handled = true } + + NetworkResult + .fromFetch { throw PushNetworkException("network error") } + .runOnStatusCodeError { handled = true } + + assertFalse(handled) + } +} From c6f4a010014ce04d143ba59f37e83eca42350658 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 29 Apr 2024 21:58:11 -0400 Subject: [PATCH 097/113] Hopeful fix for crash in SimpleProgressDialog. --- .../util/views/SimpleProgressDialog.java | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/views/SimpleProgressDialog.java b/app/src/main/java/org/thoughtcrime/securesms/util/views/SimpleProgressDialog.java index 261e64fa08..a5e4e872e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/views/SimpleProgressDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/views/SimpleProgressDialog.java @@ -1,11 +1,14 @@ package org.thoughtcrime.securesms.util.views; +import android.app.Activity; import android.content.Context; import androidx.annotation.AnyThread; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.Lifecycle; import org.signal.core.util.ThreadUtil; import org.signal.core.util.logging.Log; @@ -61,6 +64,11 @@ private SimpleProgressDialog() {} AtomicLong shownAt = new AtomicLong(); Runnable showRunnable = () -> { + if (!isContextValid(context)) { + Log.w(TAG, "Context is no longer valid. Not showing dialog."); + return; + } + Log.i(TAG, "Taking some time. Showing a progress dialog."); shownAt.set(System.currentTimeMillis()); dialogAtomicReference.set(show(context)); @@ -73,13 +81,25 @@ private SimpleProgressDialog() {} public void dismiss() { ThreadUtil.cancelRunnableOnMain(showRunnable); ThreadUtil.runOnMain(() -> { + if (!isContextValid(context)) { + Log.w(TAG, "Context is no longer valid. Not dismissing dialog."); + return; + } + AlertDialog alertDialog = dialogAtomicReference.getAndSet(null); if (alertDialog != null) { long beenShowingForMs = System.currentTimeMillis() - shownAt.get(); long remainingTimeMs = minimumShowTimeMs - beenShowingForMs; if (remainingTimeMs > 0) { - ThreadUtil.runOnMainDelayed(alertDialog::dismiss, remainingTimeMs); + ThreadUtil.runOnMainDelayed(() -> { + if (!isContextValid(context)) { + Log.w(TAG, "Context is no longer valid. Not dismissing dialog."); + return; + } + + alertDialog.dismiss(); + }, remainingTimeMs); } else { alertDialog.dismiss(); } @@ -100,6 +120,18 @@ public void dismissNow() { }; } + private static boolean isContextValid(@NonNull Context context) { + if (context instanceof AppCompatActivity) { + AppCompatActivity activity = (AppCompatActivity) context; + return !activity.isFinishing() && !activity.isDestroyed() && activity.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED); + } else if (context instanceof Activity) { + Activity activity = (Activity) context; + return !activity.isFinishing() && !activity.isDestroyed(); + } else { + return true; + } + } + public interface DismissibleDialog { @AnyThread void dismiss(); From f7763a5b829049d92810835ab8eedf0de0105674 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 29 Apr 2024 21:58:37 -0400 Subject: [PATCH 098/113] Be more lenient around long-int conversion in SignalStore. --- .../securesms/keyvalue/KeyValueDataSet.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java index d18ec98b60..f21191a3bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java @@ -2,6 +2,8 @@ import androidx.annotation.NonNull; +import org.thoughtcrime.securesms.util.Util; + import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -119,12 +121,30 @@ public Class getType(@NonNull String key) { return types.get(key); } - private E readValueAsType(@NonNull String key, Class type, boolean nullable) { + private E readValueAsType(@NonNull String key, Class expectedType, boolean nullable) { Object value = values.get(key); - if ((value == null && nullable) || (value != null && value.getClass() == type)) { - return type.cast(value); - } else { - throw new IllegalArgumentException("Type mismatch!"); + if (value == null && nullable) { + return null; + } + + if (value == null) { + throw new IllegalArgumentException("Nullability mismatch!"); + } + + if (value.getClass() == expectedType) { + return expectedType.cast(value); } + + if (expectedType == Integer.class && value instanceof Long) { + long longValue = (long) value; + return expectedType.cast(Util.toIntExact(longValue)); + } + + if (expectedType == Long.class && value instanceof Integer) { + int intValue = (int) value; + return expectedType.cast((long) intValue); + } + + throw new IllegalArgumentException("Type mismatch!"); } } From dde2a8b63a5e79b7eeb1323ef578610fea91beef Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 29 Apr 2024 19:36:44 -0400 Subject: [PATCH 099/113] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 4 +- app/src/main/res/values-ar/strings.xml | 4 +- app/src/main/res/values-az/strings.xml | 4 +- app/src/main/res/values-bg/strings.xml | 4 +- app/src/main/res/values-bn/strings.xml | 4 +- app/src/main/res/values-bs/strings.xml | 4 +- app/src/main/res/values-ca/strings.xml | 4 +- app/src/main/res/values-cs/strings.xml | 4 +- app/src/main/res/values-da/strings.xml | 4 +- app/src/main/res/values-de/strings.xml | 140 ++++++++++----------- app/src/main/res/values-el/strings.xml | 4 +- app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-et/strings.xml | 4 +- app/src/main/res/values-eu/strings.xml | 4 +- app/src/main/res/values-fa/strings.xml | 4 +- app/src/main/res/values-fi/strings.xml | 4 +- app/src/main/res/values-fr/strings.xml | 56 ++++----- app/src/main/res/values-ga/strings.xml | 4 +- app/src/main/res/values-gl/strings.xml | 4 +- app/src/main/res/values-gu/strings.xml | 20 +-- app/src/main/res/values-hi/strings.xml | 4 +- app/src/main/res/values-hr/strings.xml | 4 +- app/src/main/res/values-hu/strings.xml | 4 +- app/src/main/res/values-in/strings.xml | 4 +- app/src/main/res/values-it/strings.xml | 4 +- app/src/main/res/values-iw/strings.xml | 4 +- app/src/main/res/values-ja/strings.xml | 4 +- app/src/main/res/values-ka/strings.xml | 4 +- app/src/main/res/values-kk/strings.xml | 4 +- app/src/main/res/values-km/strings.xml | 4 +- app/src/main/res/values-kn/strings.xml | 4 +- app/src/main/res/values-ko/strings.xml | 4 +- app/src/main/res/values-ky/strings.xml | 4 +- app/src/main/res/values-lt/strings.xml | 4 +- app/src/main/res/values-lv/strings.xml | 4 +- app/src/main/res/values-mk/strings.xml | 4 +- app/src/main/res/values-ml/strings.xml | 4 +- app/src/main/res/values-mr/strings.xml | 4 +- app/src/main/res/values-ms/strings.xml | 4 +- app/src/main/res/values-my/strings.xml | 14 +-- app/src/main/res/values-nb/strings.xml | 4 +- app/src/main/res/values-nl/strings.xml | 34 ++--- app/src/main/res/values-pa/strings.xml | 4 +- app/src/main/res/values-pl/strings.xml | 4 +- app/src/main/res/values-pt-rBR/strings.xml | 4 +- app/src/main/res/values-pt/strings.xml | 4 +- app/src/main/res/values-ro/strings.xml | 4 +- app/src/main/res/values-ru/strings.xml | 4 +- app/src/main/res/values-sk/strings.xml | 4 +- app/src/main/res/values-sl/strings.xml | 4 +- app/src/main/res/values-sq/strings.xml | 4 +- app/src/main/res/values-sr/strings.xml | 4 +- app/src/main/res/values-sv/strings.xml | 4 +- app/src/main/res/values-sw/strings.xml | 4 +- app/src/main/res/values-ta/strings.xml | 4 +- app/src/main/res/values-te/strings.xml | 4 +- app/src/main/res/values-th/strings.xml | 4 +- app/src/main/res/values-tl/strings.xml | 4 +- app/src/main/res/values-tr/strings.xml | 4 +- app/src/main/res/values-ug/strings.xml | 4 +- app/src/main/res/values-uk/strings.xml | 42 +++---- app/src/main/res/values-ur/strings.xml | 4 +- app/src/main/res/values-vi/strings.xml | 4 +- app/src/main/res/values-yue/strings.xml | 4 +- app/src/main/res/values-zh-rCN/strings.xml | 4 +- app/src/main/res/values-zh-rHK/strings.xml | 4 +- app/src/main/res/values-zh-rTW/strings.xml | 4 +- app/src/main/res/values/strings.xml | 18 +-- app/static-ips.gradle.kts | 6 +- 69 files changed, 287 insertions(+), 287 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index eb875e87a4..586df2fc44 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -386,7 +386,7 @@ Jou aansluitingsversoek is na die groepadmin gestuur. Jy sal laat weet word wanneer hulle aksie geneem het. Kanselleer versoek - Om klankboodskappe te stuur, laat Signal toegang tot jou mikrofoon toe. + Signal benodig toestemming vir die Mikrofoon om klankboodskappe te stuur, maar dit is permanent geweier. Gaan asseblief na die programinstellings, kies \"Toestemmings\" en aktiveer \"Mikrofoon\". Signal benodig toestemming vir die Mikrofoon en Kamera om %1$s te kan bel, maar dit is permanent geweier. Gaan asseblief na die programinstellings, kies \"Toestemmings\" en skakel \"Mikrofoon\" en \"Kamera\" aan. Laat Signal toegang tot die kamera om foto\'s en video op te neem. @@ -411,7 +411,7 @@ Jy sal hierdie groep verlaat, en dit sal van al jou toestelle geskrap word. Skrap Skrap en verlaat - Om %1$s te skakel, het Signal toegang tot jou mikrofoon nodig. + Sluit aan diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d8f89a0d42..8b2336cae3 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -398,7 +398,7 @@ تم إرسال طلبك للانضمام إلى المشرفين على المجموعة. سيتم تنبيهك عند اتخاذهم الإجراء المناسب لهم. إلغاء الطلب - لإرسال الرسائل الصوتية، يرجى السماح لسيجنال بالوصول إلى الميكروفون. + يحتاج سيجنال إلى إذن الوصول إلى الميكروفون لإرسال الرسائل الصوتية، ولكن تم إيقاف الإذن على نحو دائم. الرّجاء الاطلاع على إعدادات التطبيق ثم اختيار \"الأذونات\" وتفعيل \"الميكروفون\". يحتاج سيجنال إلى إذنَي الوصول إلى الميكروفون والكاميرا من أجل الاتصال بـ%1$s ولكن تم إيقافهما على نحو دائم. الرجاء الاطلاع على إعدادات التطبيق ثم اختيار \"الأذونات\" وتفعيل \"الميكروفون\" و\"الكاميرا\". لالتقاط الصور والفيديوهات، الرجاء السماح لسيجنال بالوصول إلي الكاميرا. @@ -423,7 +423,7 @@ سَتغادر هذه المجموعة، ثم ستُحذَف من كل أجهزتك. حذف حذف ثم المغادرة - للاتصال بـ %1$s، يحتاج سيجنال الوصول إلى ميكروفونك + انضم diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 7458806506..f5d0a0f225 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -386,7 +386,7 @@ Qoşulma tələbiniz qrup admininə göndərildi. Tələbiniz cavablandırılanda bildiriş alacaqsınız. Tələbdən imtina - Səsli mesaj göndərmək üçün, Signal-ın mikrofonunuza müraciətinə icazə verin. + Signal, səsli mesaj göndərmək üçün Mikrofon icazəsini tələb edir, ancaq bu icazə birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələrində \"İcazələr\"i seçib \"Mikrofon\"u fəallaşdırın. Signal-ın, %1$s əlaqəsinə zəng etmək üçün Mikrofon və Kamera icazələrinə ehtiyacı var, ancaq bu icazələr birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələrində \"İcazələr\"i seçib \"Mikrofon\" və \"Kamera\"nı fəallaşdırın. Foto və video çəkmək üçün, Signal-ın kameraya müraciətinə icazə verin. @@ -411,7 +411,7 @@ Bu qrupu tərk edəcəksiniz və bütün cihazlarınızdan silinəcək. Sil Sil və tərk et - %1$s əlaqəsinə zəng etmək üçün, Signal-ın mikrofonunuza müraciətinə ehtiyac var + Qoşul diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 78c678fa06..c21954633e 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -386,7 +386,7 @@ Заявката ви за присъединяване към групата е изпратена до администратора ѝ. Ще бъдете уведомени, когата те вземат решение. Отказване на заявката - За да изпратите аудио съобщение, разрешете достъпа на Signal до микрофона. + Signal се нуждае от достъп до микрофона Ви, за да може да изпраща аудио съобщения, но той му е отказан. Моля, отидете в настройки в менюто и изберете \"Разрешения\" и \"Микрофон\". Signal се нуждае от достъп до микрофона и камерта Ви, за да може да се обади на %1$s, но той му е отказан. Моля, отидете на настройки в менюто и изберете \"Разрешения\", \"Микрофон\" и \"Камера\". За да прави снимки и видеа, Signal се нуждае от достъп до камерта Ви. @@ -411,7 +411,7 @@ Ще напуснете тази група и тя ще бъде изтрита от всичките ви устройства. Изтриване Изтриване и напускане - За да се обадите на %1$s, Signal се нуждае от достъп до микрофона ви + Присъедини се diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 042ffe47b4..b190b6874b 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -386,7 +386,7 @@ আপনার গ্রুপে ঢোকার অনুরোধ গ্রুপের অ্যাডমিনদের কাছে পাঠানো হয়েছে। তারা কোনো পদক্ষেপ নিলে আপনাকে জানানো হবে। অনুরোধ বাতিল করুন - অডিও বার্তা পাঠাতে Signal কে আপনার মাইক্রোফোন ব্যাবহারের অনুমতি দিন। + অডিও বার্তাগুলি প্রেরণের জন্য Signal এর মাইক্রোফোনের অনুমতি প্রয়োজন, কিন্তু এর উপর স্থায়ীভাবে নিষেধাজ্ঞা আরোপ করা হয়েছে। দয়া করে অ্যাপ্লিকেশন সেটিংসে যান, \"অনুমতিগুলি\" নির্বাচন করুন এবং \"মাইক্রোফোন\" সক্ষম করুন| %1$s কে ফোন করতে Signal এর মাইক্রোফোন এবং ক্যামেরা ব্যাবহারের অনুমতির প্রয়োজন, কিন্তু এর উপর স্থায়ীভাবে নিষেধাজ্ঞা আরোপ করা হয়েছে। দয়া করে এপ সেটিংসে যান, \"অনুমতি\" নির্বাচন করুন এবং \"মাইক্রোফোন\" ও \"ক্যামেরা\" এর অনুমতি সক্ষম করুন। ছবি ও ভিডিও তুলতে Signal কে ক্যামেরা ব্যাবহারের অনুমতি দিন। @@ -411,7 +411,7 @@ আপনি এই গ্রুপ ছেড়ে চলে যাবেন এবং এটি আপনার সকল ডিভাইস থেকে মুছে ফেলা হবে। মুছে ফেলুন মুছুন এবং ছেড়ে যান - %1$s কে ফোন করতে Signal কে আপনার মাইক্রোফোন ব্যাবহার করতে হবে। + যোগদান করুন diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index a7ddf84c45..b3165cd961 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -392,7 +392,7 @@ Vaš zahtjev za pristupanje poslan je administratoru grupe. Bit ćete obaviješteni kad ga on/a razmotri. Poništi zahtjev - Da biste mogli slati zvučne poruke, dozvolite Signalu pristup Vašem mikrofonu. + Signalu je potrebno dopuštenje da pristupi mikrofonu kako bi mogao slati zvučne poruke, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavku \"Mikrofon\". Signalu je potrebno dopuštenje da pristupi mikrofonu i kameri kako bi mogao nazvati %1$s, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavke \"Mikrofon\" i \"Kamera\". Da biste slikali i snimali, dozvolite Signalu da pristupi kameri. @@ -417,7 +417,7 @@ Napustit ćete ovu grupu i ona će biti izbrisana sa svih Vaših uređaja. Izbriši Izbriši i napusti - Da biste nazvali %1$s, Signalu je potreban pristup Vašem mikrofonu + Pristupi diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 0da94aba50..c2d350e9c9 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -386,7 +386,7 @@ La sol·licitud per afegir-vos-hi s\'ha enviat a l\'administrador/a del grup. Rebreu una notificació quan la resolguin. Cancel·la la sol·licitud - Per enviar missatges d\'àudio, permeteu que el Signal tingui accés al micròfon. + El Signal necessita el permís del micròfon per tal d\'enviar missatges d\'àudio, però s\'ha denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi el micròfon. El Signal necessita el permís del micròfon i de la càmera per tal de trucar a %1$s, però s\'han denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi el micròfon i la càmera. Per captar fotografies i vídeos, permeteu que el Signal tingui accés a la càmera. @@ -411,7 +411,7 @@ Abandonaràs aquest grup i s\'esborrarà de tots els dispositius. Esborra Esborra i surt - Per trucar a %1$s, el Signal necessita accés al micròfon. + Afegeix-m\'hi diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 9d23caa65d..def7046e55 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -392,7 +392,7 @@ Váš požadavek na připojení ke skupině byl zaslán správci. Budete upozorněni, jakmile zareaguje. Zrušit požadavek - Pro posílání audio zpráv potřebuje Signal přístup k mikrofonu. + Signal potřebuje oprávnění pro přístup k mikrofonu, aby mohl poslat audio zprávu, ale toto oprávnění je nyní zakázáno. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Mikrofon\". Signal potřebuje oprávnění pro přístup k mikrofonu a fotoaparátu, abyste mohli volat %1$s, ale tato oprávnění jsou nyní zakázána. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Mikrofon\" a \"Fotoaparát\". Pro pořizování fotografií nebo videa potřebuje Signal přístup k fotoaparátu. @@ -417,7 +417,7 @@ Tuto skupinu opustíte a bude odstraněna ze všech vašich zařízení. Odstranit Odstranit a opustit - Signal potřebuje přístup k vašemu mikrofonu pro volání %1$s + Připojit diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 098c920a32..4851f21d91 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -386,7 +386,7 @@ Din anmodning om at blive medlem er sendt til gruppeadministratoren. Du får besked, når den er behandlet. Annullér anmodning - For at sende talebeskeder skal du give Signal tilladelse til at tilgå mikrofonen. + Signal beder om tilladelse til at tilgå mikrofonen for at kunne sende lydfiler, hvilket er blevet nægtet. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Mikrofon\". Signal beder om tilladelse til at tilgå mikrofon og kamera, for at kunne ringe til %1$s, men er permanent blevet afvist. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Mikrofon\" og \"Kamera\". Giv Signal tilladelse til at tilgå dit kamera for at tage billeder og optage video. @@ -411,7 +411,7 @@ Du forlader gruppen, og den vil blive slettet fra alle dine enheder. Slet Slet og forlad - For at ringe til %1$s kræver Signal tilladelse til at tilgå din mikrofon + Deltag diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index acabc274be..b35be66d38 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -386,7 +386,7 @@ Deine Beitrittsanfrage wurde an den Gruppen-Admin gesendet. Du wirst über die Entscheidung benachrichtigt. Anfrage abbrechen - Erlaube Signal Zugriff auf dein Mikrofon, um Sprachnachrichten zu versenden. + Signal benötigt die Berechtigung »Mikrofon« für das Senden von Sprachnachrichten, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Mikrofon«. Signal benötigt die Berechtigungen »Mikrofon« und »Kamera«, um %1$s anzurufen, diese wurden jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Mikrofon« und »Kamera«. Zum Aufnehmen von Fotos und Videos erlaube Signal Zugriff auf deine Kamera. @@ -411,7 +411,7 @@ Du wirst diese Gruppe verlassen und sie wird von all deinen Geräten gelöscht werden. Löschen Löschen und verlassen - Um %1$s anzurufen, benötigt Signal Zugriff auf dein Mikrofon + Beitreten @@ -561,7 +561,7 @@ Vage oder irrelevante Nachrichten - Spammer beginnen oft mit einer einfachen Nachricht wie „Hallo“, um dich anzulocken. Wenn du antwortest, wirst du möglicherweise weiter kontaktiert. + Spammer beginnen oft mit einer einfachen Nachricht wie »Hallo«, um dich anzulocken. Wenn du antwortest, wirst du möglicherweise weiter kontaktiert. Nachrichten mit Links @@ -679,7 +679,7 @@ Aus Sicherung wiederherstellen? Stelle deine Nachrichten und Medieninhalte aus einer lokalen Datensicherung wieder her. Falls du sie jetzt nicht wiederherstellst, wirst du dies später nicht mehr nachholen können. - \"Aus Datensicherung wiederherstellen\"-Symbol + »Aus Datensicherung wiederherstellen«-Symbol Datensicherung wählen Mehr erfahren Kein Dateimanager verfügbar @@ -1310,8 +1310,8 @@ Signal-Anruf wird gestartet - Den Signal Anrufdienst starten - Signals Anrufdienst wird gestoppt + Den Signal-Anrufdienst starten + Signal-Anrufdienst wird gestoppt Anruf abbrechen @@ -1367,10 +1367,10 @@ %1$s hat die Ablaufzeit verschwindender Nachrichten auf %2$s festgelegt. Die Ablaufzeit verschwindender Nachrichten wurde auf %1$s festgelegt. Diese Gruppe wurde auf eine Gruppe neuen Typs aktualisiert. - Du konntest der Gruppe neuen Typs nicht hinzugefügt werden und wurdest daher eingeladen, beizutreten. + Du konntest der Gruppe nicht hinzugefügt werden und wurdest daher eingeladen, beizutreten. Chat-Sitzung aktualisiert - Ein Mitglied konnte der Gruppe neuen Typs nicht hinzugefügt werden und wurde daher eingeladen, beizutreten. + Ein Mitglied konnte der Gruppe nicht hinzugefügt werden und wurde daher eingeladen, beizutreten. %1$s Mitglieder konnten der Gruppe neuen Typs nicht hinzugefügt werden und wurden daher eingeladen, beizutreten. @@ -1472,14 +1472,14 @@ Der Gruppenavatar wurde geändert. - Du hast die Bearbeitungsberechtigten für Gruppendetails zu »%1$s« geändert. - %1$s hat die Bearbeitungsberechtigten für Gruppendetails zu »%2$s« geändert. - Die Bearbeitungsberechtigten für Gruppendetails wurden zu »%1$s« geändert. + Du hast die Berechtigung »Gruppendetails bearbeiten« zu »%1$s« geändert. + %1$s hat die Berechtigung »Gruppendetails bearbeiten« zu »%2$s« geändert. + Die Berechtigung »Gruppendetails bearbeiten« wurde zu »%1$s« geändert. - Du hast die Bearbeitungsberechtigten für die Gruppenmitgliedschaft zu »%1$s« geändert. - %1$s hat die Bearbeitungsberechtigten für die Gruppenmitgliedschaft zu »%2$s« geändert. - Die Bearbeitungsberechtigten für die Gruppenmitgliedschaft wurden zu »%1$s« geändert. + Du hast die Berechtigung »Mitglieder hinzufügen« zu »%1$s« geändert. + %1$s hat die Berechtigung »Mitglieder hinzufügen« zu »%2$s« geändert. + Die Berechtigung »Mitglieder hinzufügen« wurde zu »%1$s« geändert. Du hast die Gruppen-Einstellungen geändert, um allen Mitgliedern das Senden von Nachrichten zu erlauben. @@ -1535,8 +1535,8 @@ Deine Gruppenbeitrittsanfrage wurde von einem Admin abgelehnt. %1$s hat eine Gruppenbeitrittsanfrage von %2$s abgelehnt. Eine Gruppenbeitrittsanfrage von %1$s wurde abgelehnt. - Du hast deine Gruppenbeitrittsanfrage abgebrochen. - %1$s hat die Gruppenbeitrittsanfrage abgebrochen. + Du hast deine Gruppenbeitrittsanfrage widerrufen. + %1$s hat die Gruppenbeitrittsanfrage widerrufen. @@ -1635,7 +1635,7 @@ Freigeben Darf dir %1$s Nachrichten schreiben und deinen Namen und dein Foto sehen? Diese Person wurde bereits entfernt. - Darf %1$s dir Nachrichten schreiben und deinen Namen und dein Foto sehen? Der Nutzer weiß nicht, dass du seine Nachricht gesehen hast, bis du die Anfrage annimmst. + Darf %1$s dir Nachrichten schreiben und deinen Namen und dein Foto sehen? Die Person weiß nicht, dass du ihre Nachricht gesehen hast, bis du die Anfrage annimmst. 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. @@ -1647,7 +1647,7 @@ Chat mit %1$s fortsetzen und deinen Namen und dein Foto mit diesem Nutzer/dieser Nutzerin teilen? Möchtest du dieser Gruppe beitreten und deinen Namen und dein Foto mit ihren Mitgliedern teilen? Diese wissen nicht, dass du ihre Nachrichten gesehen hast, bis du die Anfrage annimmst. Möchtest du dieser Gruppe beitreten und deinen Namen und dein Foto mit ihren Mitgliedern teilen? Du kannst deren Nachrichten nicht sehen, bis du die Anfrage annimmst. - Dieser Gruppe beitreten? Solange du nicht beitrittst, werden die Gruppenmitglieder nicht wissen, dass du ihre Nachrichten gesehen hast. + Dieser Gruppe beitreten? Die Gruppenmitglieder wissen nicht, dass du ihre Nachrichten gesehen hast, bis du die Anfrage annimmst. Möchtest du diese Gruppe freigeben und deinen Namen und dein Foto mit deren Mitgliedern teilen? Du wirst keine Nachrichten erhalten, es sei denn, du erteilst eine Freigabe. Anzeigen @@ -1695,9 +1695,9 @@ Netzwerkfehler. Ungültiger QR-Code. Es sind bereits zu viele Geräte gekoppelt. Bitte entferne mindestens ein Gerät. - Dies ist leider kein gültiger QR-Code zur Gerätekopplung. + Dies ist kein gültiger QR-Code zur Gerätekopplung. Signal-Gerät koppeln? - Du versuchst offenbar, ein Signal-Gerät mithilfe einer fremden Scanner-App zu koppeln. Bitte scanne den QR-Code zu deinem Schutz direkt aus Signal ein. + Du versuchst offenbar, ein Signal-Gerät mithilfe einer fremden Scanner-App zu koppeln. Bitte scanne den QR-Code zu deinem Schutz direkt in Signal ein. Signal benötigt die Berechtigung »Kamera« für das Einscannen von QR-Codes, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Kamera«. QR-Codes können ohne die Berechtigung »Kamera« nicht eingescannt werden. @@ -1731,7 +1731,7 @@ Falsche PIN PIN-Eingabe überspringen? Hilfe benötigt? - Deine PIN ist ein von dir erstellter, numerischer oder alphanumerischer Code von %1$d+ Zeichen.\n\nFalls du deine PIN vergessen hast, kannst du eine neue erstellen. Du kannst dich registrieren und dein Nutzerkonto verwenden, wirst aber einige gespeicherte Einstellungen, wie z. B. deine Profilinformationen verlieren. + Deine PIN ist ein von dir erstellter, numerischer oder alphanumerischer Code aus %1$d+ Zeichen.\n\nFalls du deine PIN vergessen hast, kannst du eine neue erstellen. Du kannst dich registrieren und dein Nutzerkonto verwenden, wirst aber einige gespeicherte Einstellungen, wie z. B. deine Profilinformationen verlieren. Falls du deine PIN vergessen hast, kannst du eine neue erstellen. Du kannst dich registrieren und dein Nutzerkonto verwenden, wirst aber einige gespeicherte Einstellungen, wie z. B. deine Profilinformationen verlieren. Neue PIN erstellen Support kontaktieren @@ -1767,7 +1767,7 @@ Bewerte diese App - Hilf uns durch deine Bewertung, falls dir die App gefällt. + Bitte hilf uns durch deine Bewertung, falls dir die App gefällt. Jetzt bewerten! Nein danke Später @@ -1782,9 +1782,9 @@ Du - Verifizieren zum Senden weiterer Nachrichten - Hilf dabei, Spam auf Signal zu verhindern, und schließe die Verifikation ab. - Nach abgeschlossener Verifikation kannst du wieder Nachrichten senden. Alle pausierten Nachrichten werden dann automatisch versendet. + Verifizieren um weiter Nachrichten zu versenden + Hilf dabei, Spam auf Signal zu verhindern, und schließe die Verifizierung ab. + Nach abgeschlossener Verifizierung kannst du wieder Nachrichten senden. Alle pausierten Nachrichten werden dann automatisch versendet. Du @@ -1815,7 +1815,7 @@ - Hier antippen, um deine Videoübertragung anzuschalten + Hier antippen, um Videoübertragung zu aktivieren Um %1$s anzurufen, benötigt Signal Zugriff auf deine Kamera Signal %1$s Anrufen … @@ -1907,23 +1907,23 @@ Klingelt gerade bei %1$s - Klingelt gerade bei %1$s und %2$s + Klingelt bei %1$s und %2$s - Klingelt gerade bei %1$s, %2$s und %3$d weiteren Person - Klingelt gerade bei %1$s, %2$s und %3$d weiteren Personen + Klingelt bei %1$s, %2$s und %3$d weiteren Person + Klingelt bei %1$s, %2$s und %3$d weiteren Personen - %1$s ruft dich gerade an - %1$s ruft dich und %2$s gerade an - %1$s ruft dich, %2$s und %3$s gerade an + %1$s ruft dich an + %1$s ruft dich und %2$s an + %1$s ruft dich, %2$s und %3$s an - %1$s ruft dich, %2$s, %3$s und %4$d weitere Person gerade an - %1$s ruft dich, %2$s, %3$s und %4$d weitere Personen gerade an + %1$s ruft dich, %2$s, %3$s und %4$d weitere Person an + %1$s ruft dich, %2$s, %3$s und %4$d weitere Personen an Niemand anderes ist hier %1$s nimmt an diesem Anruf teil - %1$s nimmst an diesem Anruf teil + %1$s nimmt an diesem Anruf teil %1$s und %2$s nehmen an diesem Anruf teil @@ -2033,8 +2033,8 @@ %1$s ist blockiert Mehr Details - Weder du noch dieser Teilnehmer werden gegenseitig Audio- oder Videodaten empfangen. - Audio- & Videodaten von %1$s können nicht empfangen werden + Du und dieser Teilnehmer werden gegenseitig keine Audio- oder Videodaten empfangen. + Audio- und Videodaten von %1$s können nicht empfangen werden Audio- und Videodaten von %1$s können nicht empfangen werden Dies kann daran liegen, dass der Teilnehmer die Änderung deiner Sicherheitsnummer nicht verifiziert hat, ein Problem mit dessen Gerät besteht oder er dich blockiert hat. @@ -2050,7 +2050,7 @@ Übermittlung gescheitert - Verifikation abschließen + Verifizierung abschließen Wähle dein Land aus @@ -2067,7 +2067,7 @@ Zusätzliche Verifizierung erforderlich Ein Verifizierungscode wird an diese Nummer gesendet. Es können Tarife anfallen. - Du erhälst einen Anruf zur Verifikation dieser Telefonnummer. + Du erhältst einen Anruf zur Verifizierung dieser Telefonnummer. Telefonnummer bearbeiten Fehlende Google-Play-Dienste Auf diesem Gerät fehlen die Google-Play-Dienste. Signal kann immer noch verwendet werden, allerdings beeinträchtigt diese Konfiguration eventuell die Zuverlässigkeit oder die Leistung.\n\nFalls du kein erfahrener Nutzer bist, kein Custom-ROM verwendest oder glaubst, dass diese Meldung irrtümlich angezeigt wird, kontaktiere bitte support@signal.org für Hilfe bei der Problembehandlung. @@ -2090,23 +2090,23 @@ Die eingegebene Telefonnummer (%1$s) scheint nicht im Standardformat zu sein.\n\nHast du %2$s gemeint? Signal Android - Telefonnummernformat - Anruf angefragt + Anruf angefordert SMS angefordert - Verifikationscode angefordert + Verifizierungscode angefordert Nur noch %1$d Schritt, und du kannst ein Diagnoseprotokoll übermitteln. Nur noch %1$d Schritte, und du kannst ein Diagnoseprotokoll übermitteln. - Bist du ein Mensch? + Wir müssen sicherstellen, dass du ein Mensch bist. Sprachanruf Abbrechen Weiter Weiter - Nimm Privatsphäre mit dir.\nSei du selbst in jeder Nachricht. + Privatsphäre immer dabei.\nSei du selbst in jeder Nachricht. Telefonnummer @@ -2116,7 +2116,7 @@ Telefonnummer Landesvorwahl Mich anrufen - Verifikationscode + Verifizierungscode Code erneut senden Probleme bei der Anmeldung? @@ -2132,7 +2132,7 @@ Registrierungssperre einschalten? Registrierungssperre ausschalten? - Falls du beim erneuten Registrieren von Signal deine Signal-PIN vergisst, wird dein Konto für 7 Tage gesperrt. + Falls du beim erneuten Registrieren von Signal deine Signal-PIN vergisst, wird dein Konto für sieben Tage gesperrt. Einschalten Ausschalten @@ -2228,7 +2228,7 @@ Erfolgreich! Bitte kopiere diese Internetadresse und füge sie deinem Fehlerbericht oder deiner E-Mail an den Support hinzu:\n\n%1$s Teilen - Dieses Diagnoseprotokoll wird im Internet veröffentlicht, sodass Mitwirkende es einsehen können. Bevor du das Protokoll hochlädst, kannst du es noch einmal überprüfen. + Dieses Diagnoseprotokoll wird online gestellt, sodass Mitwirkende es einsehen können. Bevor du das Protokoll hochlädst, kannst du es noch einmal überprüfen. @@ -2327,7 +2327,7 @@ Zu viele Versuche. Bitte versuche es später erneut. Dieser Nutzername ist vergeben. - Nutzernamen dürfen nur a–Z, 0–9 und Unterstriche beinhalten. + Nutzernamen dürfen nur Buchstaben (a-Z), 0-9 und Unterstriche beinhalten. Nutzernamen dürfen nicht mit einer Ziffer beginnen. Nutzername ist ungültig. Nutzernamen dürfen aus %1$d bis %2$d Zeichen bestehen. @@ -2367,15 +2367,15 @@ Einen Nutzernamen-Link kopieren oder teilen - Dein Kontakt verwendet eine neuere Signal-Version mit einem inkompatiblen QR-Code-Format. Zum Vergleichen bitte aktualisieren. - Der eingescannte QR-Code ist kein korrekt formatierter Verifikationscode einer Sicherheitsnummer. Bitte versuche es erneut. + Dein Kontakt verwendet eine neuere Signal-Version mit einem inkompatiblen QR-Code-Format. Zum Vergleichen bitte Signal aktualisieren. + Der eingescannte QR-Code ist kein korrekt formatierter Verifizierungscode einer Sicherheitsnummer. Bitte versuche es erneut. Sicherheitsnummer teilen über … Unsere Signal-Sicherheitsnummer: Anscheinend hast du keine zum Teilen geeigneten Apps installiert. Keine zu vergleichende Sicherheitsnummer in der Zwischenablage gefunden Signal benötigt die Berechtigung »Kamera« für das Einscannen von QR-Codes, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Kamera«. QR-Codes können ohne die Berechtigung »Kamera« nicht eingescannt werden. - Du musst zuerst Nachrichten mit %1$s austauschen, um eure Sicherheitsnummer sehen zu können. + Du musst zuerst Nachrichten mit %1$s austauschen, um die Sicherheitsnummer sehen zu können. Nachdem du mit dieser Person Nachrichten ausgetauscht hast, wird eine Sicherheitsnummer erstellt. @@ -2392,11 +2392,11 @@ - Nachricht wurde für eine nicht bestehende sichere Sitzung verschlüsselt + Nachricht wurde für eine nicht bestehende Sitzung verschlüsselt Fehlerhaft verschlüsselte MMS - MMS wurde für eine nicht bestehende sichere Sitzung verschlüsselt + MMS wurde für eine nicht bestehende Sitzung verschlüsselt Stummschalten @@ -2454,7 +2454,7 @@ Nachricht konnte nicht zugestellt werden. Fehler beim Zustellen der Nachricht Nachrichtenzustellung pausiert. - Verifizieren zum Senden weiterer Nachrichten auf Signal. + Verifizieren zum Senden weiterer Nachrichten mit Signal. Alle gelesen Gelesen Diese Benachrichtigungen ausschalten @@ -2484,7 +2484,7 @@ Um Benachrichtigungen über neue Nachrichten zu erhalten: - 1. Tippe unten auf „Einstellungen“ + 1. Tippe unten auf »Einstellungen« 2. %1$s Benachrichtigungen einschalten @@ -2607,7 +2607,7 @@ Jemand mit einer geänderten Sicherheitsnummer ist diesem Anruf beigetreten. - Zum Ansichtswechsel nach oben wischen + Nach oben wischen um Ansicht zu wechseln @@ -2705,8 +2705,8 @@ SIM %1$d Senden Nachricht verfassen - Emoji-Tastatur aktivieren - Anhangsvorschaubild + Emoji-Tastatur anzeigen + Vorschaubild Anhang Direktaufnahme Sprachnachricht aufnehmen und senden Audioaufnahme arretieren @@ -2745,7 +2745,7 @@ Keine gemeinsamen Gruppen. Prüfe Anfragen sorgfältig. Keine Kontakte in dieser Gruppe. Prüfe Anfragen sorgfältig. Anzeigen - Die Ablaufzeit für verschwindende Nachrichten wird auf %1$s festgelegt, sobald du dem Nutzer eine Nachricht sendest. + Die Ablaufzeit für verschwindende Nachrichten wird auf %1$s festgelegt, sobald du der Person eine Nachricht sendest. Spenden @@ -2780,7 +2780,7 @@ Spende für einen Freund - Zum Ende scrollen + Nach unten scrollen @@ -2797,7 +2797,7 @@ Anruf beitreten Anruf fortsetzen Anruf verlassen - Die folgenden Personen haben Signal vielleicht erneut installiert oder das Gerät gewechselt. Verifiziert eure gemeinsame Sicherheitsnummer zur Sicherstellung der Privatsphäre. + Die folgenden Personen haben Signal vielleicht erneut installiert oder das Gerät gewechselt. Verifiziert eure gemeinsame Sicherheitsnummer, um eure Privatsphäre sicherzustellen. Anzeigen Zuvor verifiziert @@ -2969,7 +2969,7 @@ Vorname Nachname (optional) Speichern - Profilname konnte aufgrund Netzproblemen nicht gespeichert werden. Bitte versuche es später erneut. + Profilname konnte aufgrund von Netzproblemen nicht gespeichert werden. Bitte versuche es später erneut. Geteilte Medieninhalte @@ -2984,7 +2984,7 @@ Sicherheitsnummer konnte nicht verifiziert werden Wird geladen … Als verifiziert markieren - Verifikation entfernen + Verifizierung entfernen Scanne den QR-Code deines Kontakts ein. @@ -3043,8 +3043,8 @@ - Supportinformationen - Supportanfrage für Signal Android + Support-Informationen + Support-Anfrage für Signal Android Diagnoseprotokoll: Protokoll konnte nicht hochgeladen werden Bitte beschreibe das Problem so genau wie möglich, damit wir es besser verstehen können. @@ -3792,7 +3792,7 @@ PIN eingeben - Gib die PIN ein, die du für dein Konto erstellt hast. Sie unterscheidet sich von deinem SMS-Verifikationscode. + Gib die PIN ein, die du für dein Konto erstellt hast. Sie unterscheidet sich von deinem SMS-Verifizierungscode. Gib die PIN ein, die du für dein Konto festgelegt hast. @@ -3914,7 +3914,7 @@ Code erneut senden (%1$02d:%2$02d) Signal-Support kontaktieren - Signal-Registrierung – Verifikationscode für Android + Signal-Registrierung – Verifizierungscode für Android Falscher Code Nie Unbekannt @@ -4926,7 +4926,7 @@ - „%1$s“ zur Gruppen-Story hinzufügen + »%1$s« zur Gruppen-Story hinzufügen Zu Story hinzufügen Füg eine Nachricht hinzu @@ -5909,7 +5909,7 @@ Prüfung der Sicherheitsnummer abgeschlossen - Alle Verbindungen wurden geprüft, tippe auf „Senden“, um fortzufahren. + Alle Verbindungen wurden geprüft, tippe auf »Senden«, um fortzufahren. %1$d Kontakt hat Signal möglicherweise erneut installiert oder das Gerät gewechselt. Optional kannst du dessen Sicherheitsnummer vor dem Senden überprüfen. @@ -6706,8 +6706,8 @@ Inaktive gekoppelte Geräte - Öffne Signal auf dem Gerät innerhalb %2$d Tages, damit \"%1$s\" weiterhin gekoppelt bleibt. - Öffne Signal auf dem Gerät innerhalb von %2$d Tagen, damit \"%1$s\" weiterhin gekoppelt bleibt. + Öffne Signal auf dem Gerät innerhalb %2$d Tages, damit »%1$s« weiterhin gekoppelt bleibt. + Öffne Signal auf dem Gerät innerhalb von %2$d Tagen, damit »%1$s« weiterhin gekoppelt bleibt. Mich nicht daran erinnern diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 02fcab0d3f..48bec2e4ac 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -386,7 +386,7 @@ Το αίτημά σου για να μπεις στην ομάδα στάλθηκε στον διαχειριστή/τρια της ομάδας. Θα ενημερωθείς όταν λάβει απόφαση. Ακύρωση αιτήματος - Για να στείλεις μηνύματα ήχου, δώσε στο Signal πρόσβαση στο μικρόφωνο. + Το Signal χρειάζεται τα δικαιώματα μικροφώνου για την αποστολή μηνυμάτων ήχου, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επίλεξε τα «Δικαιώματα», και ενεργοποίησε το «Μικρόφωνο». Το Signal χρειάζεται τα δικαιώματα μικροφώνου και κάμερας για να καλέσεις τον/την %1$s, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επέλεξε τα «Δικαιώματα», και ενεργοποίησε το «Μικρόφωνο» και «Κάμερα». Για να τραβήξεις φωτογραφίες και βίντεο, δώσε στο Signal πρόσβαση στην κάμερα. @@ -411,7 +411,7 @@ Θα αποχωρήσεις από αυτή την ομάδα και θα διαγραφεί από όλες τις συσκευές σου. Διαγραφή Διαγραφή και αποχώρηση - Για να καλέσεις τον/την %1$s, το Signal χρειάζεται πρόσβαση στο μικρόφωνό σου. + Μπες στην κλήση diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 35589f0f3d..24270fa9d0 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -386,7 +386,7 @@ Se ha enviado tu solicitud para unirte al grupo al admin. Te llegará una notificación con su respuesta. Cancelar solicitud - Para enviar notas de voz y hacer llamadas, permite a Signal acceder al micrófono. + Signal necesita acceso al micrófono para enviar notas de voz. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Micrófono». Signal necesita acceso al micrófono y cámara para llamar a %1$s. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Micrófono» y «Cámara». Para hacer fotos y vídeos, permite el acceso de Signal a la cámara. @@ -411,7 +411,7 @@ Abandonarás este grupo, que se eliminará de todos tus dispositivos. Eliminar Eliminar y abandonar - Signal necesita acceder a tu micrófono para llamar a %1$s. + Unirse diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index c8f579ddeb..83bc6b5bb7 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -386,7 +386,7 @@ Sinu taotlus grupiga liitumiseks on grupi administraatoritele saadetud. Sulle antakse teada, kui nad vastavad. Tühista taotlus - Audiosõnumite saatmiseks luba Signalile juurdepääs seadme mikrofonile. + Signal vajab audiosõnumite saatmiseks ligipääsu seadme mikrofonile, kuid see on püsivalt keelatud. Palun ava rakenduse sätete menüü, vali \"Õigused\" ja luba \"Mikrofon\". Signal vajab kasutajale %1$s helistamiseks ligipääsu kaamerale ja mikrofonile, kuid need on püsivalt keelatud. Palun ava rakenduse sätete menüü, vali \"Õigused\" ja luba \"Mikrofon\" ning \"Kaamera\". Luba Signalile ligipääs kaamerale fotode ja videote salvestamiseks. @@ -411,7 +411,7 @@ Sa lahkud sellest grupist ja see kustutatakse kõigist sinu seadmetest. Kustuta Kustuta ja lahku - Signal vajab kasutajale %1$s helistamiseks juurdepääsu seadme mikrofonile + Liitu diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 1d1fd0e929..88a90dd83c 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -386,7 +386,7 @@ Taldean sartzeko zure eskaera taldeko administratzaileari bidali zaio. Jakinaraziko zaizu erabaki bat hartzen duenean. Bertan behera utzi eskaera - Audio mezuak bidaltzeko, zure mikrofonoa erabiltzeko baimena eman Signal-i. + Signalek Mikrofonorako baimena behar du audio mezuak bidaltzeko, baina ukatu egin diozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Mikrofonoa\" Signal aplikazioak Mikronofoa eta Kamera baimenak behar ditu %1$skontaktuari deitzeko, baina ukatu egin dizkiozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Mikrofonoa\" eta \"Kamera\" Argazkiak eta bideoak grabatzeko, eman baimena Signali kamera erabiltzeko. @@ -411,7 +411,7 @@ Talde honetatik irtengo zara, eta gailu guztietatik ezabatuko da. Ezabatu Ezabatu eta irten - %1$s erabiltzaileari deitzeko, Signalek zure mikrofonoa erabiltzeko baimena behar du + Batu diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 13624afbf3..29eb38755e 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -386,7 +386,7 @@ درخواست شما برای پیوستن به مدیر گروه ارسال شد. هنگامی که اقدامی انجام دهند مطلع خواهید شد. لغو درخواست - برای ارسال پیام‌های صوتی، به سیگنال اجازه دهید تا به میکروفون شما دسترسی داشته باشد. + سیگنال برای ارسال پیام های صوتی نیاز به مجوز میکروفون دارد، ولی این اجازه به صورت دائم رد شده است. لطفاً به تنظیمات برنامه رفته، «مجوزها» را انتخاب کنید و «میکروفون» را فعال کنید. سیگنال نیاز به مجوزها‌ی دوربین و میکروفون دارد تا بتواند با %1$s تماس بگیرد، اما دسترسی به آن‌ها به صورت دائم رد شده است. لطفاً به قسمت تنظیمات برنامه رفته، «مجوزها» را انتخاب کرده، «دوربین» و «میکروفون» را فعال کنید. برای ضبط عکس و ویدئو، به سیگنال اجازه دهید به دوربین شما دسترسی داشته باشد. @@ -411,7 +411,7 @@ شما این گروه را ترک خواهید کرد، و گروه از روی تمام دستگاه‌های شما پاک خواهد شد. پاک کردن پاک کردن و ترک گروه - برای تماس با %1$s، سیگنال به دسترسی میکروفون شما نیاز دارد. + پیوستن diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 2fc8a2e4ed..50291a8735 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -386,7 +386,7 @@ Liittymispyyntösi on lähetetty ryhmän ylläpitäjälle. Saat ilmoituksen, kun he tekevät päätöksen. Peruuta pyyntö - Ääniviestin lähettäminen edellyttää mikrofonin käyttöoikeuden myöntämistä Signalille. + Signal tarvitsee luvan käyttää mikrofonia äänitiedostojen lähettämistä varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Mikrofoni\". Signal tarvitsee luvan käyttää laitteesi mikrofonia ja kameraa yhteystiedolle %1$s soittamista varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Mikrofoni\" ja \"Kamera\". Kuvien ja videoiden ottaminen edellyttää, että annat Signalille kameran käyttöoikeuden. @@ -411,7 +411,7 @@ Poistut tästä ryhmästä ja se poistetaan kaikilta laitteiltasi. Poista Poista ja poistu ryhmästä - Jotta voit soittaa käyttäjälle %1$s, Signal tarvitsee luvan käyttää mikrofonia. + Liity diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f294cdba39..8542b14486 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -43,7 +43,7 @@ Vous n’avez pas encore défini de phrase de passe. Désactiver la phrase de passe ? - Signal et les notifications de message seront déverrouillées en permanence. + Signal et les notifications de message seront accessibles en permanence. Désactiver Erreur de connexion au serveur. Le blocage d’inscription nécessite l’utilisation d’un code PIN. Pour désactiver le code PIN, commencez par désactiver le blocage d’inscription. @@ -86,9 +86,9 @@ Impossible de trouver une appli pour sélectionner le média. - Pour joindre des photos, des vidéos ou de l’audio, Signal doit accéder au stockage de l’appareil, mais vous lui en avez interdit l’accès. Pour l’autoriser à y accéder, ouvrez l’application « Paramètres » de votre appareil, appuyez sur « Applications », puis sélectionnez « Autorisations » et activez « Stockage ». - Signal exige l’autorisation Contacts afin de joindre des coordonnées de contact, mais elle a été refusée définitivement. Veuillez accéder au menu des paramètres des applis, sélectionner « Autorisations » et activer « Contacts ». - Signal exige l’autorisation Position afin de joindre une position géographique, mais elle a été refusée définitivement. Veuillez accéder au menu des paramètres des applis, sélectionner « Autorisations » et activer « Position ». + Pour joindre des photos, des vidéos ou de l’audio, Signal doit accéder au stockage de l’appareil, mais vous lui en avez interdit l’accès. Ouvrez l’application « Paramètres » de votre appareil, puis touchez Applications > Signal > Autorisations > Stockage. + Pour joindre les coordonnées d’un contact, Signal doit accéder à l’application Contacts mais vous lui en avez interdit l’accès. Ouvrez l’application « Paramètres » de votre appareil, puis touchez Applications > Signal > Autorisations > Contacts > Autoriser. + Pour joindre une position géographique, Signal doit accéder à l’application Position mais vous lui en avez interdit l’accès. Ouvrez l’application « Paramètres » de votre appareil, puis touchez Applications > Signal > Autorisations > Position et activer l’autorisation appropriée. %1$s n’a pas activé les paiements @@ -221,8 +221,8 @@ Sélectionner les destinataires Signal Aucun contact Signal Vous ne pouvez utiliser le bouton Appareil photo que pour envoyer des photos à des contacts Signal. - Éprouvez-vous des difficultés à trouver qui vous cherchiez ? - Inviter un contact à se joindre à Signal + Vous ne trouvez pas ce que vous recherchez ? + Inviter un contact à rejoindre Signal Rechercher @@ -270,7 +270,7 @@ Mes stories - Nouvelle Story + Nouvelle story Conversations @@ -386,7 +386,7 @@ Votre demande de vous joindre au groupe a été envoyée à son administrateur. Vous serez averti de sa décision. Annuler la demande - Pour envoyer des messages audio, autorisez l’accès de Signal à votre microphone. + Pour envoyer des messages audio, Signal doit être autorisé à accéder à l’application Microphone, mais vous lui en avez interdit l’accès. Pour l’autoriser à y accéder, ouvrez l’application « Paramètres » de votre appareil, appuyez sur « Applications », puis sélectionnez « Autorisations » et activez « Microphone ». Signal a besoin des autorisations Microphone et Appareil photo pour appeler %1$s, mais elles ont été refusées définitivement. Veuillez accéder aux paramètres de l’appli, sélectionner Autorisations et activer Microphone et Appareil photo. Pour prendre des photos et des vidéos, autorisez l’accès de Signal à l’appareil photo. @@ -411,7 +411,7 @@ Vous quitterez ce groupe et il sera supprimé de tous vos appareils. Supprimer Supprimer et quitter - Pour appeler %1$s, Signal a besoin d’accéder à votre microphone + Me joindre @@ -1789,7 +1789,7 @@ Vous - Ma Story + Ma story Appel Signal @@ -2335,7 +2335,7 @@ Les noms d’utilisateur sont toujours associés à une série de chiffres. Qu\'est-ce que ce numéro ? - Ces chiffres servent à préserver la confidentialité de votre identité correspondant au nom d\'utilisatuer choisi et ainsi d\'éviter les messages indésirables. Ne partagez votre nom d’utilisateur qu\'avec les personnes et les groupes avec lesquels vous souhaitez échanger. Si vous changez votre nom d’utilisateur, un nouvel ensemble de chiffres vous sera attribué. + Ces chiffres servent à protéger la confidentialité de votre nom d\'utilisateur, vous évitant ainsi de recevoir des messages indésirables. Ne partagez votre nom d’utilisateur qu’avec les personnes et les groupes avec lesquels vous voulez discuter. Si vous changez de nom d’utilisateur, une nouvelle série de chiffres vous sera attribuée. Ignorer @@ -2693,8 +2693,8 @@ - %1$d spectateur - %1$d spectateurs + %1$d personne + %1$d personnes @@ -5569,7 +5569,7 @@ Effacer - Ma Story + Ma story %1$d spectateur @@ -5578,13 +5578,13 @@ Afficher - Qui peut voir cette Story ? + Qui peut voir cette story ? - Tous les contacts Signal + Tous mes contacts Signal - Tout sauf… + Tous mes contacts Signal, sauf… Cacher votre Story de certains contacts @@ -5593,22 +5593,22 @@ %1$d personnes exclues - Partager uniquement avec… + Ne partager qu’avec… - Partager uniquement avec les contacts sélectionnés + Ne partager qu’avec les contacts sélectionnés %1$d personne %1$d personnes - Choisissez qui peut voir votre Story. Les modifications n\'affecteront pas les Stories déjà postées. + Choisissez qui peut voir votre story. Cela ne modifiera pas l’audience des stories déjà publiées. Réponses et réactions Autoriser les réponses et réactions - Laisser les personnes qui peuvent voir votre Story réagir et y répondre + Autoriser les contacts qui peuvent voir votre story à y réagir et à y répondre Contacts Signal @@ -5620,7 +5620,7 @@ Il s’agit de contacts enregistrés sur votre téléphone - "Vos connexions peuvent voir vos nom et photo ainsi que vos publications dans « Mes stories » à moins que vous ne cachiez ces publications pour elles." + "Vos contacts Signal peuvent voir votre nom et votre photo. Ils peuvent aussi afficher vos stories, sauf si vous décidez de les masquer." Ajouter un spectateur @@ -5659,9 +5659,9 @@ Veuillez saisir un lien valide. - Tout sauf… + Tous mes contacts Signal, sauf… - Partager uniquement avec… + Ne partager qu’avec… Terminé @@ -5946,11 +5946,11 @@ Choisissez les contacts autorisés à voir votre story. Vous pouvez modifier ces préférences à tout moment dans les paramètres. - Tous les contacts Signal + Tous mes contacts Signal - Tout sauf… + Tous mes contacts Signal, sauf… - Partager uniquement avec… + Ne partager qu’avec… Envoyé @@ -5993,7 +5993,7 @@ - Qui peut voir cette Story ? + Qui peut voir cette story ? "Les membres de ce groupe \" %1$s \" peuvent consulter et commenter votre Story. Vous pouvez modifier les participants du groupe en question." diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index bfd46de2ad..7deef29c90 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -395,7 +395,7 @@ Seoladh d\'iarratas chun a bheith mar bhall chuig riarthóir na baicle. Seolfar fógra chugat nuair a dhéanfaidh sé beart faoi d\'iarratas. Cealaigh an tIarratas - Ceadaigh Signal do mhicreafón a úsáid chun teachtaireachtaí fuaime a sheoladh. + Tá gá ag Signal le cead micreafóin chun teachtaireachtaí fuaime a sheoladh, ach ní ceadaítear é go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Micreafón\". Tá gá ag Signal le cead micreafóin agus cheamara chun %1$s a ghlaoigh, ach ní ceadaítear iad go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Micreafón\" agus \"Ceamara\". Ceadaigh Signal do cheamara a úsáid chun grianghraif a thógáil nó físéan a dhéanamh. @@ -420,7 +420,7 @@ Imeoidh tú as an ngrúpa seo, agus scriosfar ó gach gléas leat é. Scrios Scrios agus imigh as - Chun glaoch ar %1$s, ní mór do Signal rochtain a fháil ar do mhicreafón + Téigh le diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index bb7b6d1fb6..bb1b4864be 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -386,7 +386,7 @@ Enviouse a solicitude á administración do grupo. Recibirás unha notificación cando a resolvan. Cancelar solicitude - Para enviar mensaxes de son, Signal necesita acceder ao teu micrófono. + Signal require permiso para poder enviar mensaxes de son, pero este denegouse de forma permanente. Vai a configuración da aplicación, selecciona «Permisos» e activa «Micrófono». Signal necesita permisos para acceder ao micrófono e á cámara para poder chamar a %1$s, pero denegáronse de forma permanente. Vai a configuración da aplicación, selecciona «Permisos» e activa «Micrófono» e «Cámara». Para tirar fotografías e facer vídeos, Signal necesita acceder á cámara. @@ -411,7 +411,7 @@ Abandonarás este grupo e eliminarase de todos os teus dispositivos. Eliminar Eliminar e saír - Para chamar a %1$s, Signal necesita acceder ao teu micrófono. + Unirse diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index b898918bb2..0bf29cd4d0 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -386,7 +386,7 @@ તમારી જૂથમાં જોડાવવાની વિનંતી એડમિનને મોકલવામાં આવી છે. જ્યારે તેઓ મંજૂર કરશે ત્યારે તમને જાણ કરવામાં આવશે. વિનંતી રદ કરો - ઓડિયો મેસેજ મોકલવા માટે, તમારા માઇક્રોફોન પર Signal એક્સેસની મંજૂરી આપો. + ઓડિયો મેસેજ મોકલવા માટે Signal ને માઇક્રોફોન પરવાનગીની આવશ્યકતા છે, પરંતુ તે કાયમી ધોરણે નામંજૂર કરવામાં આવી છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"માઇક્રોફોન\" સક્ષમ કરો. કૉલ કરવા માટે Signal ને માઇક્રોફોન અને કેમેરાની પરવાનગીની જરૂર છે %1$s, પરંતુ તેઓને કાયમી નામંજૂર કરવામાં આવ્યા છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"માઇક્રોફોન\" અને \"કેમેરો\" સક્ષમ કરો. ફોટા અને વિડિયો મેળવવા માટે, કેમેરામાં Signal એક્સેસની મંજૂરી આપો. @@ -411,7 +411,7 @@ તમે આ ગ્રુપને છોડી દો, અને તે તમારા બધા ડિવાઇસમાંથી ડિલીટ કરવામાં આવશે. ડિલીટ કરો ડિલીટ કરો અને છોડો - %1$s ને કોલ કરવા માટે, સિગ્નલને તમારા માઇક્રોફોનની ઍક્સેસની જરૂર છે + જોડાઓ @@ -1582,15 +1582,15 @@ વીડિયો કૉલ સમાપ્ત થયો છે · %1$s - મિસ્ડ વિડિયો કૉલ + મિસ્ડ વીડિયો કૉલ - મિસ્ડ વિડિયો કૉલ · %1$s + મિસ્ડ વીડિયો કૉલ · %1$s - ઈનકમિંગ વિડિયો કૉલ + ઇનકમિંગ વીડિયો કૉલ ઇનકમિંગ વીડિયો કૉલ · %1$s - આઉટગોઈંગ વિડિયો કૉલ + આઉટગોઈંગ વીડિયો કૉલ આઉટગોઇંગ વીડિયો કૉલ · %1$s @@ -1606,14 +1606,14 @@ - %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 અન્યો આ કૉલમાં છે - %1$s, %2$s, અને %3$d અન્યો આ ગ્રુપ કૉલમાં છે + %1$s, %2$s અને અન્ય %3$d આ કૉલમાં છે + %1$s, %2$s અને અન્ય %3$d આ ગ્રૂપ કૉલમાં છે diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 0005cd472c..b452a2deb8 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -386,7 +386,7 @@ समूह से जुड़ने का आपका अनुरोध समूह संचालक को भेज दिया गया है। जब वे जवाब देंगे तो आपको सूचित किया जाएगा। निवेदन रद्द करें - ऑडियो मेसेज भेजने के लिए, अपने माइक्रोफ़ोन पर Signal पहुंच की अनुमति दें। + ऑडियो मेसेज को भेजने के लिए Signal को माइक्रोफ़ोन अनुमति की आवश्यकता होती है, लेकिन इसे स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स जारी रखें, \"अनुमतियां\" चुनें, और \"माइक्रोफ़ोन\" सक्षम करें। %1$s को कॉल करने के लिए Signal को माइक्रोफ़ोन और कैमरा अनुमतियों की आवश्यकता होती है, लेकिन उन्हें स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स जारी रखें, \"अनुमतियां\" चुनें, और \"माइक्रोफ़ोन\" और \"कैमरा\" सक्षम करें। फोटो और वीडियो कैप्चर करने के लिए, कैमरे को Signal पहुंच की अनुमति दें। @@ -411,7 +411,7 @@ आप इस ग्रूप को छोङ देंगे और यह ग्रूप आपके सभी डिवाइस से डिलीट कर दिया जाएगा। डिलीट करें डिलीट करें और छोड़ दें - %1$s को कॉल करने के लिए, Signal को आपके माइक्रोफ़ोन तक पहुंच की आवश्यकता है। + जुड़ें diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 312b3b0998..82930971f0 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -392,7 +392,7 @@ Vaš zahtjev za pridruživanje poslan je administratoru grupe. Biti će te obaviješteni kada nešto poduzmu. Poništi zahtjev - Za slanje zvučnih poruka, omogućite Signalu pristup vašem mikrofonu. + Signal zahtijeva dopuštenje za mikrofon za slanje zvučnih poruka, ali je trajno odbijen. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Mikrofon\". Signal zahtijeva dopuštenja za mikrofon i kameru kako bi nazvao %1$s, ali ona su trajno odbijena. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Mikrofon\" i \"Kamera\". Za snimanje slika i video zapisa, omogućite Signalu pristup vašoj kameri. @@ -417,7 +417,7 @@ Napustit ćete ovu grupu i ona će biti izbrisana sa svih vaših uređaja. Izbriši Izbriši i napusti grupu - Da biste uputili poziv za %1$s, omogućite Signalu pristup vašem mikrofonu + Pridruži se diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 9f5b388cfd..e5aef7089f 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -386,7 +386,7 @@ A csatlakozási kérésed el lett küldve a csoportadmin részére. Értesítést fogsz kapni, amint reagálnak rá. Kérés törlése - Hangüzenetek küldéséhez engedélyezd, hogy a Signal hozzáférhessen a mikrofonhoz! + A Signalnak szüksége van a Mikrofon engedélyre, hogy hangüzeneteket küldhessen, de ez jelenleg nincs megadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd az \"Mikrofon\"-t. A Signalnak szüksége van Mikrofon és Kamera engedélyekre %1$s felhívásához, de ez jelenleg nincs megadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd a \"Mikrofon\"-t és a \"Kamera\"-t. Fotók és videók készítéséhez engedélyezd a Signalnak a kamerához való hozzáférést! @@ -411,7 +411,7 @@ Ha kilépsz a csoportból, akkor a csoport a többi társított eszközödön is törölve lesz. Törlés Törlés és kilépés - %1$s hívásához a Signalnak hozzá kell férnie mikrofonodhoz + Csatlakozás diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 32f285e1ae..9c4c46425e 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -383,7 +383,7 @@ Permintaan Anda untuk bergabung telah terkirim kepada admin grup. Anda akan diberitahu saat mereka meresponnya. Batalkan Permintaan - Untuk mengirim pesan audio, izinkan Signal mengakses mikrofon Anda. + Signal memerlukan izin Mikrofon untuk mengirim pesan audio, tetapi saat ini izin ditolak secara permanen. Mohon lanjutkan ke pengaturan aplikasi, pilih \"Izin\" dan aktifkan \"Mikrofon\". Signal memerlukan izin Mikrofon dan Kamera untuk memanggil %1$s, tetapi saat ini izin ditolak secara permanen. Mohon lanjutkan ke pengaturan aplikasi, pilih \"Izin\" dan aktifkan \"Mikrofon\" serta \"Kamera\". Untuk mengambil foto dan video, izinkan Signal mengakses kamera. @@ -408,7 +408,7 @@ Anda akan keluar dari grup ini, dan grup akan dihapus dari semua perangkat Anda. Hapus Hapus dan keluar - Untuk menelepon %1$s, Signal memerlukan akses mikrofon Anda + Bergabung diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index f4d672a0bd..14d50efdf6 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -386,7 +386,7 @@ La tua richiesta di unirti è stata inviata agli amministratori del gruppo. Riceverai una notifica quando interverranno. Annulla richiesta - Per poter mandare un messaggio audio, permetti a Signal di accedere al tuo microfono. + Signal richiede l\'autorizzazione all\'uso del microfono per inviare messaggi audio, ma è stata negata in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Microfono\". Signal richiede le autorizzazioni all\'uso del microfono e della fotocamera per chiamare %1$s, ma sono state negate in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Microfono\" e \"Fotocamera\". Per poter catturare foto e video, permetti a Signal di accedere alla fotocamera del tuo dispositivo @@ -411,7 +411,7 @@ Quando abbandonerai il gruppo, verrà eliminato da tutti i tuoi dispositivi. Elimina Elimina e abbandona - Per chiamare %1$s, Signal necessita dell\'accesso al tuo microfono + Unisciti diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 850a9d5eb2..d8da804517 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -392,7 +392,7 @@ בקשתך להצטרף נשלחה אל מנהלן הקבוצה. תיודע כאשר הוא מחליט. בטל בקשה - כדי לשלוח הודעות שמע, התר אל Signal לקבל גישה אל המיקרופון שלך. + Signal דורש את הרשאת המיקרופון על מנת לשלוח הודעות שמע, אבל היא נדחתה לצמיתות. אנא המשך אל הגדרות היישום, בחר \"הרשאות\" ואפשר את \"מיקרופון\". Signal צריך את ההרשאות של המיקרופון והמצלמה על מנת לחייג אל %1$s, אבל הן נדחו לצמיתות. אנא המשך אל הגדרות היישום, בחר \"הרשאות\" ואפשר את \"מיקרופון\" ואת \"מצלמה\". כדי ללכוד תצלומים וסרטונים, התר אל Signal גישה אל המצלמה. @@ -417,7 +417,7 @@ אתם תעזבו קבוצה זו, והיא תימחק מכל המכשירים שלכם. מחיקה מחיקה ועזיבה - כדי להתקשר אל %1$s, היישום Signal צריך גישה אל המיקרופון שלך + הצטרף diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index bf834d6b76..a1197533b7 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -383,7 +383,7 @@ あなたの参加申請はグループ管理者に送信されました。応答があると通知されます。 申請をキャンセル - 音声メッセージを送るには、Signalのマイクへのアクセスを許可してください。 + 音声メッセージを添付するには、Signalにマイクへのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アプリの権限」で「マイク」を有効にしてください。 %1$s と通話するには、Signalにマイクとカメラへのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アプリの権限」で「マイク」と「カメラ」を有効にしてください。 写真や動画を撮るには、Signalにカメラへのアクセスを許可してください。 @@ -408,7 +408,7 @@ このグループから抜けて、すべての端末から消去します。 消去する 消去して抜ける - %1$sと通話するには、Signalにマイクへのアクセスを許可してください。 + 参加する diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 83c6b446bf..d979da4a54 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -386,7 +386,7 @@ შენი გაწევრიანების მოთხოვნა იქნა გაგზავნილი ჯგუფის ადმინთან. შენ მიიღებ შეტყობინებას, როდესაც ისინი რამეს მოიმოქმედებენ. მოთხოვნის გაუქმება - აუდიო შეტყობინებების გასაგზავნად, მიეცი Signal-ს წვდომა შენს მიკროფონზე. + აუდიო შეტყობინებების გადასაგზავნად Signal-ს მიკროფონზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ, გადახვიდე აპის პარამეტრების მენიუში, აირჩიო \"უფლებები\" და ჩართო \"მიკროფონი\". %1$s-თან დასარეკად Signal-ს მიკროფონსა და კამერაზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ, გადახვიდე აპის პარამეტრების მენიუში, აირჩიო \"უფლებები\" და ჩართოთ \"მიკროფონი\" და \"კამერა\". სურათების და ვიდეოების გადასაღებად, მიეცი Signal-ს წვდომა კამერაზე. @@ -411,7 +411,7 @@ ამ ჯგუფს დატოვებ და ის შენი ყველა მოწყობილობიდან წაიშლება. წაშლა წაშლა და დატოვება - %1$s-თან დასარეკად, Signal-ს შენს მიკროფონზე წვდომა სჭირდება + შემოუერთდი diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 7a96275924..7632fab0d8 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -386,7 +386,7 @@ Топқа қосылу туралы өтінішіңіз топ әкімшісіне жіберілді. Олар шара қолданғанда, сізге хабарландыру келеді. Өтініштен бас тарту - Аудио хаттар жіберу үшін Signal қолданбасына микрофонды пайдалануына рұқсат етіңіз. + Аудио хаттар жіберу үшін Signal-ға микрофон ашық болу керек, бірақ параметрлерде микрофонды пайдалануға рұқсат берілмеген. Қолданба параметрлеріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Микрофон\" параметрін қосыңыз. %1$s қоңырау шалу үшін Signal қолданбасына микрофон мен камераны пайдалануға рұқсат керек, бірақ параметрлерде оларды пайдалануға рұқсат берілмеген. Қолданба параметрлеріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Микрофон\" және \"Камера\" параметрлерін қосыңыз. Фотосуреттер мен видео түсіру үшін Signal қолданбасына камера ашық болу керек. @@ -411,7 +411,7 @@ Сіз бұл топтан шығып кетесіз және ол сіздің барлық құрылғыларыңыздан жойылады. Жою Жойып, шығып кету - %1$s қоңырау шалу үшін Signal қолданбасына микрофонды пайдалануға рұқсат керек + Қосылу diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index 30d8d5863c..ebeb0a2bc4 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -383,7 +383,7 @@ ការស្នើសុំរបស់អ្នកដើម្បីចូលក្រុម ត្រូវបានផ្ញើទៅកាន់អ្នកគ្រប់គ្រងក្រុម។ អ្នកនឹងទទួលដំណឹង នៅពេលគេមានសកម្មភាព។ បោះបង់ការស្នើសុំ - ដើម្បីផ្ញើសារជាសំឡេង អនុញ្ញាតឱ្យ Signal ចូលទៅប្រើប្រាស់មីក្រូហ្វូនរបស់អ្នក។ + Signal សុំសិទ្ធិប្រើប្រាស់ម៉ៃក្រូហ្វូន ដើម្បីផ្ញើសារជាសំឡេង, ប៉ុន្តែ វាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅកាន់ ការកំណត់កម្មវិធី, ជ្រើសរើស \"ការអនុញ្ញាត\", និងបើក \"ប្រដាប់ស្រូបសំឡេង\"។ Signalត្រូវការសិទ្ធិប្រើប្រាស់ម៉ៃក្រូហ្វូន និងកាមេរ៉ាដើម្បីហៅទៅកាន់ %1$s, ប៉ុន្តែពួកវាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅការកំណត់ជ្រើសរើស \"អនុញ្ញាត\" ហើយបើក \"ម៉ៃក្រូហ្វូន\" និង \"កាមេរ៉ា\"។ ដើម្បីថតរូបភាព និងវីដេអូ, សូមអនុញ្ញាត Signal ចូលប្រើប្រាស់កាមេរ៉ា។ @@ -408,7 +408,7 @@ អ្នកនឹងចាកចេញពីក្រុមនេះ ហើយវានឹងត្រូវបានលុបចេញពីឧបករណ៍របស់អ្នកទាំងអស់។ លុប លុប និងចាកចេញ - ដើម្បីហៅទៅ %1$s, Signal ត្រូវការចូលប្រើប្រាស់ម៉ៃក្រូហ្វូនរបស់អ្នក។ + ចូលរួម diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 1c89373a33..a0db312de2 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -386,7 +386,7 @@ ಗುಂಪು ಸೇರಲು ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ನಿರ್ವಾಹಕರಿಗೆ ಕಳುಹಿಸಲಾಗಿದೆ. ಅವರು ಕ್ರಮ ಕೈಗೊಂಡಾಗ ನಿಮಗೆ ಸೂಚಿಸಲಾಗುತ್ತದೆ. ಕೋರಿಕೆಯನ್ನು ರದ್ದುಮಾಡಿ - ಆಡಿಯೋ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು, Signal ಗೆ ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಉಪಯೋಗಿಸಲು ಅನುಮತಿ ನೀಡಿ. + ಆಡಿಯೊ ಸಂದೇಶ ಕಳುಹಿಸುವ ಸಲುವಾಗಿ Signal ಗೆ ಮೈಕ್ರೊಫೋನ್ ಅನುಮತಿ ಅಗತ್ಯವಿದೆ, ಆದರೆ ಅದನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಅಪ್ಲಿಕೇಶನ್ ಸಂಯೋಜನೆಗೆ ಮುಂದುವರಿಸಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆ ಮಾಡಿ, ಮತ್ತು \"ಮೈಕ್ರೊಫೋನ್\" ಸಕ್ರಿಯಗೊಳಿಸಿ. %1$s ಗೆ ಕರೆ ಮಾಡಲು Signal ಗೆ ಮೈಕ್ರೊಫೋನ್ ಹಾಗೂ ಕ್ಯಾಮರಾ ಅನುಮತಿಗಳು ಅಗತ್ಯವಿರುತ್ತವೆ, ಆದರೆ ಅವುಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ ಗಳಿಗೆ ಮುಂದುವರಿಯಿರಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆ ಮಾಡಿ, ಮತ್ತು \"ಮೈಕ್ರೊಫೋನ್\" ಮತ್ತು \"ಕ್ಯಾಮೆರಾ\" ಸಕ್ರಿಯಗೊಳಿಸಿ. ಫೋಟೋಗಳನ್ನು ಮತ್ತು ವಿಡಿಯೊ ಸೆರೆಹಿಡಿಯಲು, Signal ಗೆ ಕ್ಯಾಮರಾ ಪ್ರವೇಶ ಅನುಮತಿಸಿ. @@ -411,7 +411,7 @@ ನೀವು ಈ ಗ್ರೂಪ್ ಅನ್ನು ತೊರೆಯುತ್ತೀರಿ ಮತ್ತು ಇದನ್ನು ನಿಮ್ಮ ಎಲ್ಲ ಸಾಧನಗಳಿಂದಲೂ ಅಳಿಸಲಾಗುವುದು. ಅಳಿಸಿ ಅಳಿಸಿ ಮತ್ತು ತೊರೆಯಿರಿ - %1$s ಗೆ ಕರೆ ಮಾಡಲು, Signal ಗೆ ನಿಮ್ಮ ಮೈಕ್ರೊಫೋನ್ ಪ್ರವೇಶ ಅಗತ್ಯವಿದೆ. + ಸೇರು diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e2fd08eef2..d5963cff24 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -383,7 +383,7 @@ 참가 요청이 그룹 관리자에게 전송되었습니다. 요청이 처리되면 알림을 받게 됩니다. 요청 취소하기 - 음성 메시지를 보내려면 Signal이 마이크를 사용할 수 있도록 허용해 주세요. + Signal에서 오디오 메시지를 보내려면 마이크 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'마이크\' 항목을 허용해 주세요. Signal에서 %1$s에게 전화하려면 마이크와 카메라 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'마이크\'와 \'카메라\' 항목을 허용해 주세요. Signal에서 사진과 동영상을 찍으려면 카메라 권한이 필요합니다. @@ -408,7 +408,7 @@ 당신은 그룹을 떠날 것이며, 당신의 모든 기기에서 삭제될 겁니다. 삭제 삭제 후 나가기 - %1$s에게 통화하려면, Signal에서 마이크 권한이 필요합니다. + 참가 diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index a8878ea351..8c62dc3d11 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -383,7 +383,7 @@ Кошулуу өтүнүчүңүз топтун администраторуна жөнөтүлдү. Анын чечими тууралуу билдирме аласыз. Өтүнүчтү жокко чыгаруу - Аудио билдирүүлөрдү жөнөтүү үчүн Signal колдонмосуна микрофонуңузду жеткиликтүү кылышыңыз керек. + Signal колдонмосуна аудио билдирүүлөрдү жөнөтүү үчүн микрофонду колдонууга уруксат беришиңиз керек, бирок сиз андан баш тарткансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Микрофон\" дегенди иштетиңиз. Signal колдонмосуна %1$s чалуу үчүн микрофон менен камераны колдонууга уруксат беришиңиз керек, бирок сиз андан баш тарткансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Микрофон\" жана \"Камера\" дегенди иштетиңиз. Сүрөт же видео тартуу үчүн Signal колдонмосуна камераны колдонууга уруксат беришиңиз керек. @@ -408,7 +408,7 @@ Бул топтон чыгасыз жана ал бардык түзмөктөрүңүздөн өчүрүлөт. Өчүрүү Өчүрүп, топтон чыгам - %1$s чалуу үчүн Signal колдонмосуна микрофонуңуз керек + Кошулуу diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 6ce6feb61f..6164ee8f4f 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -392,7 +392,7 @@ Jūsų prašymas prisijungti prie grupės buvo išsiųstas grupės administratoriui. Jums bus pranešta, kai jis atliks tam tikrus veiksmus. Panaikinti prašymą - Norėdami siųsti garso žinutes, leiskite Signal prieigą prie mikrofono. + Norint siųsti garso žinutes, Signal reikia mikrofono leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymus, pasirinkite „Leidimai“ ir įjunkite „Mikrofoną“. Norint skambinti %1$s, programai Signal reikia mikrofono ir kameros leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymus, pasirinkite „Leidimai“ ir įjunkite „Mikrofoną“ ir „Kamerą“. Norėdami fotografuoti ir filmuoti vaizdo įrašus, leiskite Signal prieigą prie kameros. @@ -417,7 +417,7 @@ Tu išeisi iš šios grupės ir ji bus ištrinta iš visų tavo įrenginių. Ištrinti Ištrinti ir išeiti - Norint skambinti %1$s, Signal reikia prieigos prie jūsų mikrofono + Prisijungti diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 5da86da11b..3bec7b447f 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -389,7 +389,7 @@ Jūsu pieprasījums pievienoties ir nosūtīts grupas administratoriem. Jums tiks paziņots, kad viņi veiks kādu darbību. Atcelt pieprasījumu - Lai nosūtītu audio ziņu, ļaujiet Signal piekļūt ierīces mikrofonam. + Lai nosūtītu audio ziņas, Signal nepieciešama atļauja pieejai mikrofonam, bet tā ir liegta. Dodieties uz lietotnes iestatījumiem, izvēlieties \"Atļaujas\" un iespējojiet \"Mikrofons\". Lai zvanītu %1$s, Signal nepieciešama atļauja piekļuvei mikrofonam un kamerai, bet tā ir liegta. Dodieties uz lietotnes iestatījumiem, izvēlieties \"Atļaujas\" un iespējojiet \"Mikrofons\" un \"Kamera\". Lai uzņemtu fotogrāfijas un video, ļaujiet Signal piekļūt kamerai. @@ -414,7 +414,7 @@ Jūs pametīsiet šo grupu, un tā tiks izdzēsta no visām jūsu ierīcēm. Dzēst Dzēst un pamest - Lai veiktu zvanu %1$s, Signal ir nepieciešama piekļuve jūsu mikrofonam. + Pievienoties diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index e560fec4d3..9e7c3210e0 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -386,7 +386,7 @@ Вашето барање за да се приклучите е испратено до администраторот на групата. Ќе бидете известени штом преземат нешто околу тоа. Откажи барање - За да испраќате аудио пораки, дозволете му на Signal пристап до Вашиот микрофон. + Signal има потреба од дозвола до микрофонот за да може да испраќа аудио пораки. Оваа дозвола е трајно одбиена. Ве молиме продолжете до менито за поставувања, изберете „Дозволи“ и вклучете „Микрофон“. Signal има потреба од дозвола до микрофонот и камерата за да може да воспостави повик со %1$s. Овие дозволи се трајно одбиени. Ве молиме продолжете до менито за поставувањата, изберете „Дозволи“ и вклучете „Микрофон“ и „Камера“. За да снимате фотографии и видеа, дозволете му на Signal пристап до камерата. @@ -411,7 +411,7 @@ Ќе ја напуштите оваа група и таа ќе биде избришана од сите ваши уреди. Избриши Избриши и напушти - За да го/ја повикате %1$s, на Signal му треба пристап до микрофонот + Приклучи сѐ diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index f53c3eb944..75e4337663 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -386,7 +386,7 @@ ഗ്രൂപ്പില്‍ ചേരുന്നതിനുള്ള നിങ്ങളുടെ അഭ്യർത്ഥന അഡ്മിന് അയച്ചു. അവർ നടപടിയെടുക്കുമ്പോൾ നിങ്ങളെ അറിയിക്കും. അഭ്യർത്ഥന റദ്ദാക്കുക - ഓഡിയോ സന്ദേശങ്ങള്‍ അയയ്‌ക്കാൻ, Signal-ന് നിങ്ങളുടെ മൈക്രോഫോണിലേക്ക് ആക്‌സസ്സ് അനുവദിക്കുക. + ഓഡിയോ സന്ദേശങ്ങൾ അയയ്‌ക്കാൻ Signal-ന് മൈക്രോഫോൺ അനുമതി ആവശ്യമാണ്, പക്ഷേ ഇത് ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങളിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"മൈക്രോഫോൺ\" പ്രവർത്തനക്ഷമമാക്കുക. %1$s എന്നയാളെ വിളിക്കുന്നതിന് Signal-ന് മൈക്രോഫോൺ, ക്യാമറ അനുമതികൾ ആവശ്യമാണ്, പക്ഷേ അവ ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങളിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"മൈക്രോഫോൺ\", \"ക്യാമറ\" എന്നിവ പ്രവർത്തനക്ഷമമാക്കുക. ഫോട്ടോകളും വീഡിയോയും എടുക്കാൻ ക്യാമറയിലേക്ക് Signal-ന് ആക്സസ് അനുവദിക്കുക. @@ -411,7 +411,7 @@ നിങ്ങൾ ഈ ഗ്രൂപ്പിൽ നിന്ന് പുറത്തുകടക്കും, ഇത് നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിൽ നിന്നും ഇല്ലാതാക്കുകയും ചെയ്യും. ഇല്ലാതാക്കൂ ഇല്ലാതാക്കിയ ശേഷം പുറത്തുകടക്കുക - %1$s-നെ വിളിക്കാൻ, Signal-ന് നിങ്ങളുടെ മൈക്രോഫോണിലേക്ക് ആക്സസ് ആവശ്യമാണ് + ചേരുക diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index 0560e77efd..e40de4509a 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -386,7 +386,7 @@ आपली सामील होण्याची विनंती गट प्रशासकाकडे पाठविली गेली आहे. जेव्हा ते कारवाई करतील तेव्हा आपल्याला सूचित केले जाईल. विनंती रद्द करा - ऑडिओ संदेश पाठविण्यासाठी, Signal ला आपले मायक्रोफोन अॅक्सेस करण्याची अनुमती द्या. + ऑडिओ संदेश पाठवण्यासाठी Signal ला मायक्रोफोन परवानगीची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"मायक्रोफोन\" सक्षम करा. %1$s ला कॉल करण्यासाठी Signal ला मायक्रोफोन आणि कॅमेरा परवानग्यांची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"मायक्रोफोन\" आणि \"कॅमेरा\" सक्षम करा. फोटो आणि व्हिडिओ कॅप्चर करण्यासाठी, Signal ला कॅमेरा अॅक्सेसची अनुमती द्या. @@ -411,7 +411,7 @@ आपण हा ग्रुप सोडाल , आणि तो आपल्या सर्व डिव्हाइसेस वरून हटविला जाईल. हटवा ग्रुप हटवा आणि सोडून द्या - %1$s ला कॉल करण्यासाठी, Signal ला आपल्या मायक्रोफोनचा अॅक्सेस हवा आहे + सामील व्हा diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 6f5389a3f3..ba2dc71d51 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -383,7 +383,7 @@ Permintaan anda untuk join telah dihantar kepada admin group. Anda akan diberitahu sekiranya mereka telah mengambil tindakan. Batalkan Permintaan - Untuk menghantar mesej audio, benarkan Signal untuk mengakses mikrofon anda. + Signal memerlukan kebenaran Mikrofon untuk menghantar mesej audio, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Mikrofon\". Signal memerlukan kebenaran Mikrofon dan Kamera untuk memanggil %1$s, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Mikrofon\" dan \"Kamera\". Untuk menangkap foto dan video, benarkan Signal untuk mengakses kamera anda. @@ -408,7 +408,7 @@ Anda akan meninggalkan kumpulan ini, dan ia akan dipadam daripada semua peranti anda. Padam Padam dan tinggalkan - Untuk memanggil %1$s, Signal memerlukan akses kepada mikrofon anda + Sertai diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index a8a37a84ed..45034981ca 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -383,7 +383,7 @@ သင်၏အဖွဲ့ဝင်ရန် တောင်းဆိုမှုကို အဖွဲ့အက်မင်ထံသို့ ပို့ပြီးပါပြီ။ သူတို့ဆောင်ရွက်ချက်ကို အကြောင်းပြန်ပေးပါမည်။ တောင်းဆိုမှုကိုပယ်ဖျက်သည်။ - အသံဖိုင်ပို့နိုင်ရန် မိမိမိုက်ခရိုဖုန်းကို Signal အား အသုံးပြုခွင့်ပေးပါ။ + အသံဖိုင်များပို့နိုင်ရန် Signalမှ မိုက်ခရိုဖုန်းအား အသုံးပြုခွင့်ရရန် လိုအပ်သည်။ သို့သော် လုံးဝခွင့်မပြုပါ ဟုရွေးထားပြီး ဖြစ်နေသဖြင့် အပ်ပလီကေးရှင်း အပြင်အဆင်သို့ သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ မိုက်ခရိုဖုန်းကို အသုံးပြုနိုင်အောင် ပြုလုပ်ပါ။ %1$sဖုန်းခေါ်ဆိုမှုပြုနိုင်ရန် Signalမှ မိုက်ခရိုဖုန်း နှင့် ကင်မရာအား အသုံးပြုခွင့်ရရန်လိုအပ်သည်။ သို့သော် အမြဲတမ်းတွက် ခွင့်မပြုပါ ဟုရွေးထားပြီး ဖြစ်နေသဖြင့် အပ်ပလီကေးရှင်း အပြင်အဆင်သို့ သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ မိုက်ခရိုဖုန်း နှင့် ကင်မရာကို အသုံးပြုနိုင်အောင် ပြုလုပ်ပါ။ ဓါတ်ပုံနှင့် ဗီဒီယိုရိုက်နိုင်ရန် Signal မှ မိမိကင်မရာကို အသုံးပြုခွင့်ပေးပါ။ @@ -408,7 +408,7 @@ သင်သည် ဤအဖွဲ့မှ ထွက်မည်ဖြစ်ပြီး ယင်းအဖွဲ့ကို သင်၏ အခြားစက်များအားလုံးမှလည်း ဖျက်သွားပါမည်။ ဖျက်ရန် ဖျက်ပြီး ထွက်ရန် - ခေါ်ရန်%1$s Signal သည်သင်၏မိုက်ခရိုဖုန်းကိုအသုံးပြုရန်လိုအပ်သည် + ပူးပေါင်း @@ -1517,7 +1517,7 @@ သင်သည် ကောလ်ထဲတွင် ရှိနေသည် · %1$s - %1$s နှင့် %2$s သည် ကောလ်ထဲ တွင်ရှိနေသည် · %3$s + %1$s နှင့် %2$s သည် ကောလ်ထဲ တွင်ရှိနေသည် · %3$s %1$s သည် ကောလ်ထဲတွင် ရှိနေသည် @@ -1531,7 +1531,7 @@ လွတ်သွားသော ဗီဒီယိုကောလ် - လွဲချော်ခဲ့သော ဗီဒီယိုခေါ်ဆိုမှု · %1$s + လွတ်သွားသော ဗီဒီယိုကောလ် · %1$s အဝင် ဗီဒီယိုကောလ် @@ -1541,7 +1541,7 @@ အထွက်ဗီဒီယိုကောလ် · %1$s - သင်သည် ဗီဒီယိုကောလ်ခေါ်ဆိုမှု စတင်ခဲ့သည် + သင်သည် ဗီဒီယိုကောလ်ခေါ်ဆိုမှုကို စတင်ခဲ့သည် သင်သည် ဗီဒီယိုကောလ်ခေါ်ဆိုမှုကို စတင်ခဲ့သည် · %1$s @@ -1553,12 +1553,12 @@ - %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်များသည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိပါသည် · %4$s + %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်သည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိသည် · %4$s - %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်များသည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိပါသည် + %1$s၊ %2$s၊ နှင့် အခြား %3$d ယောက်သည် ၎င်းအဖွဲ့လိုက်ခေါ်ဆိုမှုထဲတွင် ရှိသည် diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 133975bc68..e450f67258 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -386,7 +386,7 @@ Din forespørsel om å bli med har blitt sendt til gruppeadministratoren. Du vil bli varslet når de tar aksjon. Avbryt forespørsel - For å sende lydmeldinger, gi Signal tilgang til mikrofonen din. + Signal krever tillatelse fra systemet for å kunne bruke mikrofonen, men du har valgt å avslå dette permanent. Gå til «Apper»-menyen på systemet og slå på tillatelsen «Mikrofon». Signal krever tillatelser fra systemet for å kunne ringe %1$s, men du har valgt å avslå minst én av disse permanent. Gå til «Apper»-menyen på systemet og slå på tillatelser for «Mikrofon» og «Kamera». Du må gi Signal «Kamera»-tillatelse på systemet for å kunne filme og ta bilder. @@ -411,7 +411,7 @@ Du forlater denne gruppen, og den blir slettet fra alle enhetene dine. Slett Slett og forlat - Signal trenger tilgang til mikrofonen din for å ringe %1$s + Bli med diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index e53d09058d..cd0a3c5af2 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -73,7 +73,7 @@ (video) (locatie) (citaat) - (Audiobericht) + (Spraakbericht) Galerij @@ -386,8 +386,8 @@ Je verzoek om lid te worden van de groep is doorgestuurd naar een beheerder. Je krijgt een melding zodra deze een besluit heeft genomen. Verzoek annuleren - Om audioberichten op te nemen, moet je Signal toegang geven tot je microfoon. - Signal heeft toegang tot de microfoon nodig om audioberichten te kunnen opnemen, maar deze is pertinent 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 deze is pertinent geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Microfoon’ in. Signal heeft toegang tot de microfoon en de camera nodig om %1$s te kunnen bellen, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, 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 opnemen, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Camera’ in. @@ -411,7 +411,7 @@ Je zult deze groep verlaten en het gesprek zal op al je eigen apparaten worden verwijderd. Verwijderen Verwijderen en verlaten - Signal heeft toegang tot je microfoon nodig om %1$s te bellen. + Deelnemen @@ -1336,7 +1336,7 @@ Camera - Gebruiker niet bekend + Onbekend Dit bericht gebruikt verouderde versleuteling van een Signal-versie die niet langer wordt ondersteund. Vraag de afzender om bij te werken naar de meest recente versie en het bericht opnieuw te verzenden. Je hebt de groep verlaten. Je hebt de groep aangepast. @@ -2274,7 +2274,7 @@ Je hebt deze persoon verborgen. Als je hem of haar opnieuw een bericht stuurt, wordt diegene weer aan je lijst toegevoegd. Afbeelding GIF - Audiobericht + Spraakbericht Bestand Video Chatsessie vernieuwd @@ -2500,7 +2500,7 @@ Anders Chats Onbekend - Audioberichten + Spraakberichten Nieuw persoon bereikbaar via Signal Geen ‘activiteit’ beschikbaar om instellingen voor meldingskanalen mee te openen. @@ -2888,7 +2888,7 @@ - GIFs zoeken + GIF\'s zoeken Niets gevonden @@ -3424,7 +3424,7 @@ Contacten - Vind mensen die je kent. Je contacten zijn versleuteld en niet zichtbaar voor de Signal-service. + Vind mensen die je kent. Je contacten zijn versleuteld en niet zichtbaar voor de Signal-dienst. Telefoonoproepen @@ -3749,7 +3749,7 @@ Nieuwe pincode aanmaken Je kunt je pincode nu nog wijzigen omdat je telefoonnummer nog voor dit apparaat geregistreerd staat. Wanneer je telefoonnummer niet meer geregistreerd staat voor dit apparaat, dan kun je de pincode niet langer wijzigen en ook niet teruglezen. Het is daarom belangrijk dat je je pincode onthoudt. Pincode aanmaken - Pincodes kunnen je helpen je account te herstellen en je informatie versleuteld te houden met Signal. + Pincodes kunnen je helpen je account te herstellen en je informatie versleuteld te houden op Signal. Verzin een sterkere pincode @@ -4166,10 +4166,10 @@ De link is momenteel niet actief - Het afspelen van het audiobericht is mislukt + Het afspelen van het spraakbericht is mislukt - Audiobericht · %1$s + Spraakbericht · %1$s %1$s naar %2$s @@ -4726,7 +4726,7 @@ GIF zoeken Stickers Backspace - GIFs + GIF\'s Emoji zoeken Terug naar emoji-overzicht Zoekveld leegmaken @@ -4858,11 +4858,11 @@ · %1$s - Audiobericht afspelen stoppen + Spraakbericht afspelen stoppen Audioafspeelsnelheid aanpassen - Audiobericht pauzeren - Audiobericht afspelen - Naar audiobericht navigeren + Spraakbericht pauzeren + Spraakbericht afspelen + Naar spraakbericht navigeren diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index d51515e769..7b8f4bbbeb 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -386,7 +386,7 @@ ਤੁਹਾਡੀ ਸ਼ਾਮਲ ਹੋਣ ਦੀ ਬੇਨਤੀ ਗਰੁੱਪ ਪਰਸ਼ਾਸ਼ਕ ਨੂੰ ਭੇਜ ਦਿੱਤੀ ਗਈ ਹੈ। ਜਦੋਂ ਉਹ ਕਾਰਵਾਈ ਕਰਨਗੇ ਤਾਂ ਤੂਹਾਨੂੰ ਸੂਚਿਤ ਕੀਤਾ ਜਾਵੇਗਾ। ਬੇਨਤੀ ਨੂੰ ਰੱਦ ਕਰੋ - ਆਡੀਓ ਸੁਨੇਹੇ ਭੇਜਣ ਲਈ, Signal ਨੂੰ ਆਪਣੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਤਕ ਪਹੁੰਚ ਦਿਓ। + ਆਡੀਓ ਸੁਨੇਹੇ ਭੇਜਣ ਲਈ Signal ਨੂੰ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਸਨੂੰ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ| ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ, ਅਤੇ \"ਮਾਈਕ੍ਰੋਫ਼ੋਨ\" ਨੂੰ ਸਮਰੱਥ ਕਰੋ। %1$s ਨੂੰ ਕਾਲ ਕਰਨ ਲਈ Signal ਨੂੰ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਅਤੇ ਕੈਮਰਾ ਇਜਾਜ਼ਤਾਂ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਹਨਾਂ ਲਈ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ | ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ, ਅਤੇ \"ਮਾਈਕ੍ਰੋਫ਼ੋਨ\" ਅਤੇ \"ਕੈਮਰਾ\" ਨੂੰ ਸਮਰੱਥ ਕਰੋ। ਵੀਡੀਓ ਅਤੇ ਫ਼ੋਟੋਆਂ ਖਿੱਚਣ ਲਈ Signal ਨੂੰ ਕੈਮਰੇ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ। @@ -411,7 +411,7 @@ ਤੁਸੀਂ ਇਸ ਗਰੁੱਪ ਨੂੰ ਛੱਡ ਦਿਓਗੇ, ਅਤੇ ਇਸ ਨੂੰ ਤੁਹਾਡੇ ਸਾਰੇ ਡਿਵਾਈਸਾਂ ਵਿੱਚੋਂ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਮਿਟਾਓ ਹਟਾਓ ਅਤੇ ਛੱਡੋ - %1$s ਨੂੰ ਕਾਲ ਕਰਨ ਲਈ, Signal ਨੂੰ ਤੁਹਾਡੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਤੱਕ ਪਹੁੰਚ ਦੀ ਲੋੜ ਹੈ + ਸ਼ਾਮਲ ਹੋਵੋ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 86dd383bda..fdfd3c33fc 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -392,7 +392,7 @@ Twoja prośba o przyjęcie do grupy została wysłana do administratora. Zostaniesz powiadomiony(a), gdy podejmie decyzję. Anuluj prośbę - Aby wysyłać wiadomości głosowe, zezwól Signal na dostęp do mikrofonu. + Signal wymaga pozwolenia na dostęp do mikrofonu w celu umożliwienia wysyłania wiadomości głosowych, ale zostało one na stałe odrzucone. Przejdź do ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Mikrofon\". Signal wymaga pozwolenia na dostęp do mikrofonu i aparatu, aby zadzwonić do %1$s, ale zostały one na stałe odrzucone. Przejdź do ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Aparat\" oraz \"Mikrofon\". Aby móc robić zdjęcia i nagrywać wideo, zezwól Signal na dostęp do aparatu. @@ -417,7 +417,7 @@ Opuścisz tę grupę i zostanie ona usunięta ze wszystkich Twoich urządzeń. Usuń Usuń i opuść - Aby zadzwonić do %1$s, Signal potrzebuje dostępu do mikrofonu + Dołącz diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 156a665915..e4e5718c71 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -386,7 +386,7 @@ Seu pedido para participar foi enviado ao administrador do grupo. Você será notificado quando a decisão for tomada. Cancelar pedido - Para enviar mensagens de áudio, permita ao Signal acesso ao seu microfone. + O Signal precisa da permissão Microfone para enviar mensagens de áudio, mas ela foi permanentemente negada. Favor ir no menu de configurações de aplicativos, selecionar \"Permissões\", e habilitar \"Microfone\". O Signal precisa das permissões Microfone e Câmera para chamar %1$s, mas elas foram permanentemente negadas. Favor ir no menu de configurações de aplicativos, selecionar \"Permissões\", e habilitar \"Microfone\" e \"Câmera\". Para tirar fotos e fazer vídeos, permita ao Signal acessar a câmera. @@ -411,7 +411,7 @@ Você deixará este grupo, que será apagada de todos os seus dispositivos. Apagar Apagar e sair - Para chamar %1$s, Signal precisa acessar seu microfone + Entrar diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 88aec03d2c..db82da488a 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -386,7 +386,7 @@ O seu pedido para entrar foi enviado ao administrador do grupo. Será notificado quando ele tomar uma decisão. Cancelar pedido - Para poder enviar mensagens de áudio, permita que o Signal aceda ao microfone. + O Signal requer permissão de acesso ao microfone para enviar mensagens áudio, mas esta foi negada permanentemente. Por favor, aceda às definições das aplicações do telemóvel, selecione a aplicação Signal e nas \"Permissões\" ative \"Microfone\". O Signal requer permissão de acesso ao microfone e câmara, para poder ligar a %1$s, mas esta foi negada permanentemente. Por favor, aceda às definições das aplicações do telemóvel, selecione a aplicação Signal e nas \"Permissões\" ative o \"Microfone\" e a \"Câmara\". Para tirar fotografias e filmar vídeos, dê permissões ao Signal para acesso à câmara. @@ -411,7 +411,7 @@ Irá abandonar este grupo, e ele será eliminado de todos os seus dispositivos. Eliminar Eliminar e abandonar - Para ligar a %1$s, o Signal necessita de ter acesso ao microfone. + Entrar diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index befa79123a..541784253e 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -389,7 +389,7 @@ Solicitarea ta de a te alătura grupului a fost trimisă administratorului. Vei fi notificat când acesta ia o decizie. Anulează Cerere - Pentru a trimite mesaje audio, permite-i aplicației Signal accesul la microfonul tău. + Signal are nevoie de permisiunea pentru Microfon pentru a putea trimite mesaje audio dar i-a fost refuzat accesul permanent. Te rugăm să continui în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \"Microfon\". Signal are nevoie de permisiunile pentru Microfon și Cameră pentru a putea apela pe %1$s, dar i-a fost refuzat accesul permanent. Te rugăm continuă în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \"Microfon\" și \"Camera\". Pentru a captura poze sau filme, permite aplicației Signal să acceseze camera. @@ -414,7 +414,7 @@ Vei părăsi acest grup și va fi șters de pe toate dispozitivele tale. Șterge Șterge și părăsește - Pentru a apela pe %1$s, Signal are nevoie de acces la microfonul tău. + Alătură-te diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index b9787e3e44..b1bef2dfdf 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -392,7 +392,7 @@ Ваш запрос на присоединение был отправлен администратору группы. Вы будете уведомлены, когда он примет решение. Отменить запрос - Для отправки аудиосообщений разрешите Signal доступ к микрофону. + Signal требуется разрешение на доступ к микрофону для отправки аудиосообщений, но оно было вами отклонено. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Микрофон». Signal требуются разрешения на доступ к микрофону и камере, чтобы позвонить %1$s, но они были вами отклонены. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Микрофон» и «Камера». Для создания фото и видео разрешите Signal доступ к камере. @@ -417,7 +417,7 @@ Вы покинете эту группу, и она будет удалена со всех ваших устройств. Удалить Удалить и покинуть - Чтобы позвонить %1$s, Signal необходим доступ к вашему микрофону + Войти diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 14489968f3..f184d1b320 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -392,7 +392,7 @@ Vaša žiadosť o členstvo bola odoslaná administrátorovi skupiny. Keď na ňu zareaguje, dostanete oznámenie. Zrušiť žiadosť - Pre posielanie zvukových správ potrebuje Signal prístup k mikrofónu. + Signal potrebuje prístup k mikrofónu aby mohol posielať zvukové správy, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Mikrofón\". Signal potrebuje prístup k mikrofónu a fotoaparátu aby mohol zavolať %1$s, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Mikrofón\" a \"Fotoaparát\". Pre fotenie a nahrávanie videa potrebuje Signal prístup k fotoaparátu. @@ -417,7 +417,7 @@ Opustíte túto skupinu a vymažete ju zo všetkých vašich zariadení. Vymazať Vymazať a opustiť - Pre volanie %1$s potrebuje Signal prístup k vášmu mikrofónu + Pridať sa diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index ba88c31f5f..f054560684 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -392,7 +392,7 @@ Vaša prošnja za pridružitev je bila posredovana skrbniku_ci skupine. Ob njegovem_njenem odzivu boste obveščeni. Preklic prošnje - Za pošiljanje zvočnih sporočil dovolite aplikaciji Signal dostop do mikrofona naprave. + Dostop do mikrofona je bil trajno onemogočen. Aplikacija Signal potrebuje dovoljenje za dostop do mikrofona naprave za pošiljanje zvokovnih sporočil. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenje pod postavko \"Mikrofon\". Dostop do mikrofona in kamere je bil trajno onemogočen. Za klic uporabnika_ce %1$spotrebuje Aplikacija Signal dovoljenje za dostop do mikrofona in kamere naprave. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenji pod postavkama \"Mikrofon\" in \"Kamera\". Za zajemanje videa in fotografij dovolite aplikaciji Signal dostop do kamere naprave. @@ -417,7 +417,7 @@ Zapustili boste skupino in ta bo izbrisana na vseh vaših napravah. Izbriši Izbriši in zapusti - Za klic uporabnika_ce %1$s, Signal potrebuje dostop do mikrofona + Pridruži se diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 5c73bf125b..f011808e41 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -386,7 +386,7 @@ Kërkesa juaj për t\'ju bashkuar grupit i është dërguar administratorit të grupit. Do të njoftoheni kur ata të ndërmarrin veprime. Anuloje kërkesën - Për të dërguar mesazhe audio, lejo qasjen e Signal-it në mikrofonin tënd. + Signal kërkon lejen e aktivizimit të Mikrofonit për të dërguar mesazhe audio, por është i mbyllur përgjithmonë. Ju lutemi, vazhdoni te parametrat e aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Mikrofoni\". Signal ka nevojë për lejet e aktivizimit të mikrofonit dhe të kamerës për të telefonuar%1$s, por janë mbyllur përgjithmonë. Ju lutemi, vazhdoni te parametrat e aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Mikrofoni\" dhe \"Kamera\". Për të bërë foto dhe video, lejo qasjen e Signal-it në kamerë. @@ -411,7 +411,7 @@ Do të largohesh nga ky grup dhe do të fshihet nga të gjitha pajisjet e tua. Fshije Fshije dhe dil - Për të telefonuar %1$s, Signal ka nevojë për qasje te mikrofoni juaj + Bashkohu diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 3f19b1e087..0dd5292b67 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -386,7 +386,7 @@ Ваш захтев за придруживање је послат администратору групе. Бићете обавештени када администратор предузме мере. Откажи захтев - Да бисте послали аудио поруку, дозволите Signal-у приступ за коришћење микрофона. + Signal-у је потребна дозвола за приступ микрофону да би могао да шаље аудио поруке, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опцију „Микрофон“. Signal-у је потребна дозвола за приступ микрофону и камери да би могао да успостави позив са корисником %1$s, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опције „Микрофон“ и „Камера“. Да бисте могли да снимате фотографије и видео, дозволите Signal-у приступ камери. @@ -411,7 +411,7 @@ Напустићете групу и она ће бити избрисана на свим вашим уређајима. Избриши Избриши и напусти - Да бисте позвали контакт %1$s, Signal-у је потребан приступ вашем микрофону + Придружите се diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index fed5af6ce9..8f253585a4 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -386,7 +386,7 @@ Din förfrågan om att gå med har skickats till gruppadministratören. Du får ett meddelande när administratören vidtar åtgärder. Avbryt förfrågan - För att skicka ljudmeddelanden, tillåt att Signal får åtkomst till din mikrofon. + Signal behöver behörigheten Mikrofon för att skicka ljudmeddelanden men den avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Mikrofon\". Signal behöver behörigheterna Mikrofon och Kamera för att ringa%1$s men de har avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Mikrofon\" och Kamera\". För att fånga fotografier och video, tillåt Signal att tillgå kameran. @@ -411,7 +411,7 @@ Du lämnar denna grupp och den tas bort från alla dina enheter. Ta bort Ta bort och lämna - För att ringa %1$s behöver Signal åtkomst till din mikrofon + Gå med diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index fddbc5b91d..a0101ec885 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -386,7 +386,7 @@ Ombi lako la kujiunga limetumwa kwa msimamizi wa kikundi. Utaarifiwa atakapochukua hatua. Ghairi Ombi - Kutuma jumbe za sauti, ruhusu ufikiaji wa Signal kwenye kipaza sauti chako. + Signal inahitaji idhini ya Kipaza sauti ili kutuma ujumbe wa sauti, lakini imekataliwa kabisa. Tafadhali endelea kwenye mipangilio ya programu, chagua \"Ruhusa\", na uwezeshe \"Kipaza sauti\". Signal inahitaji ruhusa ya Kipaza sauti na Kamera ili kupiga%1$s , lakini zimekataliwa kabisa. Tafadhali endelea kwenye mipangilio ya programu, chagua \"Ruhusa\", na uwezeshe \"Kipaza sauti\" na \"Kamera\". Ili kupiga picha na video, ruhusu ufikiaji wa Signal kwa kamera. @@ -411,7 +411,7 @@ Utaondoka kundi hili, na litafutwa kutoka kwa vifaa vyako vyote. Futa Futa na uondoke - Ili kumpigia %1$s simu, Signal inahitaji kufikia maikrofoni yako + Jiunge diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index e33c457209..7df938113e 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -386,7 +386,7 @@ குழுவில் சேர உங்கள் கோரிக்கை குழு நிர்வாகிக்கு அனுப்பப்பட்டுள்ளது. அவர்கள் நடவடிக்கை எடுக்கும்போது உங்களுக்கு அறிவிக்கப்படும். கோரிக்கையை ரத்துசெய் - ஆடியோ செய்திகளை அனுப்ப, உங்கள் மைக்ரோஃபோனுக்கு Signal அணுகலை அனுமதிக்கவும். + ஆடியோ செய்திகளை அனுப்ப Signal க்கு மைக்ரோஃபோன் அனுமதி தேவைப்படுகிறது, ஆனால் அது நிரந்தரமாக மறுக்கப்பட்டது. பயன்பாடு அமைப்புகளைத் தொடரவும், \"அனுமதிகள்\" என்பதைத் தேர்ந்தெடுத்து, \"மைக்ரோஃபோனை\" இயக்கவும். Signal லை அழைக்க மைக்ரோஃபோன் மற்றும் கேமரா அனுமதிகள் தேவை%1$s, ஆனால் அவை நிரந்தரமாக மறுக்கப்பட்டுள்ளன. பயன்பாடு அமைப்புகளைத் தொடரவும், \"அனுமதி\" என்பதைத் தேர்ந்தெடுத்து, \"மைக்ரோஃபோன்\" மற்றும் \"கேமரா\" ஐ இயக்கவும். புகைப்படங்கள் மற்றும் காணொளி பிடிக்க, Signal-ஐ கேமராவை அணுக அனுமதிக்கவும். @@ -411,7 +411,7 @@ நீங்கள் இந்த குழுவை விட்டு வெளியேறும், மற்றும் இது எல்லாவற்றிலிருந்தும் நீக்கப்படும் உங்கள் சாதனங்கள். நீக்கு நீக்கி வெளியேறுக - %1$sஐ அழைக்க, சிக்னலுக்கு உங்கள் மைக்ரோஃபோனுக்கு அனுமதி தேவை microphone access and permission for Signal + சேரவும் diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 1891f4127d..62307322d9 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -386,7 +386,7 @@ గ్రూప్‌లో చేరడానికి మీ అభ్యర్థన నిర్వాహకుడికి పంపబడింది. వారు చర్య తీసుకున్నప్పుడు మీకు తెలియజేయబడుతుంది. అభ్యర్ధన రద్దు చేయి - ఆడియో సందేశాలను పంపడానికి, మీ మైక్రోఫోన్‌కు Signal ప్రాప్తిని అనుమతించండి. + ఆడియో సందేశాలను పంపడానికి Signalకు మైక్రోఫోన్ అనుమతి అవసరం, కానీ ఇది శాశ్వతంగా తిరస్కరించబడింది. దయచేసి అనువర్తనం సెట్టింగ్లకు కొనసాగించండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"మైక్రోఫోన్\" ని ప్రారంభించండి. Signal కాల్ చేయడానికి మైక్రోఫోన్ మరియు కెమెరా అనుమతులు అవసరం %1$s,కానీ అవి శాశ్వతంగా తిరస్కరించబడ్డాయి. దయచేసి అనువర్తనం సెట్టింగ్లకు కొనసాగించండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"మైక్రోఫోన్\" మరియు \"కెమెరా\" ని ప్రారంభించండి. చిత్రాలు మరియు వీడియోలను సంగ్రహించడానికి, కెమెరాకి Signal ప్రాప్తిని అనుమతించండి. @@ -411,7 +411,7 @@ మీరు ఈ గ్రూప్‌ను వదిలి వెళతారు మరియు ఇది మీ పరికరాలు అన్నింటి నుండి తొలగించబడుతుంది. తొలగించండి తొలగించండి మరియు వదిలివేయండి - %1$s కి కాల్ చేయడానికి, Signal కు మీ మైక్రోఫోన్‌ ప్రాప్యత అవసరం + చేరండి diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 6803ec89d3..e5eef8a7a4 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -383,7 +383,7 @@ คำขอเข้ากลุ่มของคุณถูกส่งไปยังผู้ดูแลกลุ่มแล้ว คุณจะได้รับแจ้งเมื่อผู้ดูแลดำเนินการ ยกเลิกคำขอ - เพื่อจะส่งข้อความเสียง คุณต้องอนุญาตให้ Signal เข้าถึงไมโครโฟน + เพื่อจะส่งข้อความเสียง Signal ต้องได้รับอนุญาตให้เข้าถึงไมโครโฟน แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ไมโครโฟน\" เพื่อที่จะโทรหา %1$s Signal ต้องได้รับอนุญาตให้เข้าถึงไมโครโฟนและกล้อง แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ไมโครโฟน\" และ \"กล้อง\" อนุญาต Signal ให้ใช้กล้องเพื่อถ่ายรูปและวิดีโอ @@ -408,7 +408,7 @@ คุณจะออกจากกลุ่มนี้และระบบจะลบกลุ่มออกจากอุปกรณ์ทุกเครื่องของคุณ ลบ ลบและออก - เพื่อจะโทรหา %1$s Signal จำเป็นต้องเข้าถึงไมโครโฟนของคุณ + เข้าร่วม diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 047c7ebe3b..752d34ab87 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -386,7 +386,7 @@ Ang iyong request to join ay na-send na sa group admin. You\'ll be notified when they take action. I-cancel ang Request - Upang magpadala ng mensaheng audio, payagan ang Signal na magamit ang iyong mikropono. + Kailangan ng Signal ang pahintulot sa Mikropono upang makapagpadala ng mensaheng audio, ngunit permanente itong ipinagbabawal. Pumunta sa app settings, piliin ang \"Mga Pahintulot\", at i-enable ang \"Mikropono\". Kailangan ng Signal ang pahintulot sa Mikropono at Camera upang matawagan si %1$s, ngunit ito ay permaneteng ipinagbabawal. Pumunta sa app settings, piliin ang \"Pahintulot\", at i-enable ang \"Mikropono\" at \"Camera\". Upang makakuha ng larawan at video, payagan ang Signal na gamitin ang camera. @@ -411,7 +411,7 @@ Aalis ka sa grupong ito, at ito\'y mabubura sa lahat ng iyong mga device. Burahin Burahin at umalis - Para tawagan si %1$s, kailangan ng Signal ng access sa iyong microphone + Mag-join diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index b6094645fa..a5fbee3257 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -386,7 +386,7 @@ Katılma isteğiniz grup yöneticisine gönderildi. İsteğiniz yanıtlandığı zaman haberdar edileceksiniz. İsteği İptal Et - Sesli ileti göndermek için Signal\'in mikrofonunuza erişmesine izin verin. + Signal, sesli iletiler göndermek için Mikrofon iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Mikrofon\"u etkinleştirin. Signal, %1$s ile arama yapmanız için Mikrofon ve Kamera iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, \"Mikrofon\" ve \"Kamera\"yı etkinleştirin. Fotoğraf veya video çekmek için, Signal\'in kameraya erişmesine izin verin. @@ -411,7 +411,7 @@ Bu gruptan ayrılacaksın ve grup tüm cihazlarından silinecek. Sil Sil ve ayrıl - %1$s ile arama yapmanız için Signal\'in mikrofonunuza erişime ihtiyacı var. + Katıl diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 2b85f4d866..db3d8d30f5 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -383,7 +383,7 @@ قوشۇلۇش تەلىپىڭىز گۇرۇپپا باشقۇرغۇچىسىغا ئەۋەتىلدى. ئۇلار قارار قىلغاندا سىزگە ئۇقتۇرۇلىدۇ. ئىلتىماستىن ۋاز كەچ - ئۈنلىك ئۇچۇر يوللاش ئۈچۈن، Signal نىڭ مىكروفونىڭىزنى زىيارەت قىلىشىغا رۇخسەت قىلىڭ. + ئۈنلىك ئۇچۇر يوللاش ئۈچۈن Signal مىكروفون ھوقۇقى تەلەپ قىلىدۇ، ئەمما ئۇ مەڭگۈلۈك رەت قىلىندى. ئەپ تەڭشىكى تىزىملىكىدىن «ھوقۇقلار» نى تاللاپ ۋە «مىكروفون» نى قوزغىتىڭ. %1$s گە چاقىرىق قىلىش ئۈچۈن Signal مىكروفون ۋە كامېرا ھوقۇقىنى ئىشلىتىشى كېرەك، لېكىن ئۇ ھوقۇقلار مەڭگۈلۈك چەكلەنگەن. ئەپ تەڭشىكىگە كىرىپ، «ھوقۇقلار» نى تاللاپ، «مىكروفون» ۋە «كامېرا» نى قوزغىتىڭ. رەسىم ۋە سىنغا ئېلىش ئۈچۈن ، Signal نىڭ كامېرانى زىيارەت قىلىشىغا يول قويۇڭ. @@ -408,7 +408,7 @@ بۇ گۇرۇپپىدىن چېكىنىپ چىقىسىز، شۇنداقلا بۇ گۇرۇپپا بارلىق ئۈسكۈنىلىرىڭىزدىن ئۆچۈرۈلىدۇ. ئۆچۈر ئۆچۈر ۋە ئايرىل - %1$sنى چاقىرىش ئۈچۈن Signal مىكروفونىڭىزنى ئىشلىتەلىشى كېرەك + قوشۇل diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8b6a78749e..eb87567613 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -392,13 +392,13 @@ Ваш запит на приєднання надіслано до адміністраторів групи. Ви отримаєте повідомлення, коли вони відреагують. Відкликати запит - Щоб надсилати аудіоповідомлення, надайте Signal доступ до мікрофона. + Signal потребує дозволу «Мікрофон», щоб відправляти аудіоповідомлення, але наразі доступу немає. Будь ласка, перейдіть до налаштувань застосунку, оберіть «Дозволи» та увімкніть «Мікрофон». Signal потребує дозволів «Мікрофон» та «Камера», щоб подзвонити до %1$s, але наразі доступу немає. Будь ласка, перейдіть до налаштувань застосунку, оберіть «Дозволи» та увімкніть «Мікрофон» та «Камера». Щоб знімати фото і відео, надайте Signal доступ до камери. Signal потребує дозволу \"Камера\", щоб фотографувати або знімати відео, але наразі доступу немає. Будь ласка, перейдіть до налаштувань додатку, оберіть \"Дозволи\", та увімкніть \"Камера\". Signal потребує дозволу «Камера», щоб фотографувати або фільмувати. - Надайте доступ до мікрофону, щоб записувати відео зі звуком. + Надайте доступ до мікрофона, щоб записувати відео зі звуком. Signal потрібен дозвіл на доступ до мікрофона, щоб знімати відео, але воно було вами відхилено. Будь ласка, натисніть «Продовжити», щоб перейти в налаштування програми, відкрийте \"Дозволи\" і включіть \"Мікрофон\" та \"Камера\". Signal потребує доступу до мікрофона для запису відео. @@ -417,7 +417,7 @@ Ви покинете цю групу, та її буде видалено з усіх ваших пристроїв. Видалити Видалити і покинути - Щоб зателефонувати %1$s, Signal має отримати доступ до мікрофона + Приєднатись @@ -1002,7 +1002,7 @@ Вибрати адміністратора - Немає попереднього перегляду посилання + Попередній вигляд вебсторінки недоступний Посилання групи неактивне %1$s · %2$s @@ -1451,11 +1451,11 @@ %1$s · %2$s %1$s оновив групу. %1$s приєднався до Signal! - Ви вимкнули зникаючі повідомлення - %1$s вимкнув зникаючі повідомлення. - Ви встановлюєте час зникнення повідомлення на %1$s. - %1$sвстановлює час зникання повідомлень на %2$s. - Таймер часу зникнення повідомлення було встановлено на %1$s. + Ви вимкнули тимчасові повідомлення. + Користувач %1$s вимкнув тимчасові повідомлення. + Ви встановили таймер тимчасових повідомлень на %1$s. + Користувач %1$s встановив таймер тимчасових повідомлень на %2$s. + Таймер тимчасових повідомлень встановлено на %1$s. Цю групу оновлено до Нової групи. Вас не вдалось додати до Нової групи, а тому було запрошено приєднатись. Сесію чату оновлено @@ -2421,8 +2421,8 @@ %1$s тепер може приймати платежі %1$s приєднався до Signal! - Зникаючі повідомлення заборонені - Час зникнення повідомлень встановлено на %1$s + Тимчасові повідомлення вимкнено + Таймер тимчасових повідомлень встановлено на %1$s Код безпеки змінився Код безпеки для чату з %1$s змінився. Ви перевірили @@ -2919,7 +2919,7 @@ Спільних груп немає. Переглядайте запити уважно. У цій групі немає контактів. Переглядайте запити уважно. Детальніше - Час зникання повідомлення буде встановлено на %1$s, коли ви його відправите їм. + Таймер цього тимчасового повідомлення буде встановлено на %1$s, коли ви його відправите. Задонатити @@ -3321,8 +3321,8 @@ Залишати стишені чати заархівованими Стишені заархівовані чати залишатимуться заархівованими після отримання нового повідомлення. - Генерувати посилання попереднього перегляду - Для надісланих повідомлень отримувати дані попереднього перегляду посилань напряму з веб-сайтів. + Генерувати попередній вигляд вебсторінок + Показувати попередній вигляд вебсторінок за посиланнями, які ви надсилаєте в повідомленнях. Змінити фразу-пароль Змінить вашу фразу-пароль Увімкнути фразу-пароль @@ -3794,7 +3794,7 @@ - Зникнення повідомлень + Тимчасові повідомлення @@ -4813,13 +4813,13 @@ Заблоковані %1$d контактів Листування - Зникаючі повідомлення + Тимчасові повідомлення Безпека програми Блокувати знімки екрану в списку недавніх і в застосунку Повідомлення і виклики в Signal, завжди ретранслювати виклики, захищений відправник Таймер за умовчанням для нових чатів - Встановіть таймер зникнення повідомлень за замовчуванням для усіх нових чатів, які ви створите. + Установіть стандартний таймер для тимчасових повідомлень у всіх нових чатах, які ви починаєте. Увімкніть розблокування екрана Android чи розблокування відбитком пальця для переказу коштів @@ -4847,8 +4847,8 @@ 1 година 5 хвилин 30 секунд - Власний час - Задати + Свій час + Установити Зберегти секунди @@ -4987,7 +4987,7 @@ Сповіщення вимкнені Пошук - Зникаючі повідомлення + Тимчасові повідомлення Звуки & сповіщення Дані з контактів телефона @@ -6603,7 +6603,7 @@ хв - Задати + Установити Мінімальний час до застосування блокування екрана становить 1 хвилину. diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 18227f2c6a..1a3fd6383b 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -386,7 +386,7 @@ آپ کی شمولیت کی درخواست گروپ منتظم کو بھیج دی گئی ہے۔ جب وہ کارروائی کریں گے تو آپ کو مطلع کیا جائے گا۔ درخواست منسوخ - آڈیو پیغامات بھیجنے کے لیے Signal کو مائکروفون تک رسائی کی اجازت دیں۔ + Signal کو آڈیو پیغامات بھیجنے کے لیے مائکروفون کی اجازت کی ضرورت ہے، لیکن اس کی مستقل طور پر نفی کردی گئی ہے ۔ برائے مہربانی ایپ کی ترتیبات میں جائیں، \"منظوری\"، منتخب کریں، اور \"مائکروفون\" فعال کریں۔ %1$s کال کیلئے Signal کو مائکروفون اور کیمرہ کی منظوری کی ضرورت ہوتی ہے، لیکن ان کی مستقل طور پر نفی کر دی گئی ہے ۔ برائے مہربانی ایپ کی ترتیبات میں جائیں، \"منظوری\" منتخب کریں، اور \"مائکروفون\" اور \"کیمرہ\"کو فعال کریں۔ تصویریں اور ویڈیو بنانے کے لیے ، Signal کو کیمرہ تک رسائی کی ضرورت ہے @@ -411,7 +411,7 @@ آپ اس گروپ کو چھوڑ دیں گے ، اور یہ آپ کے سبھی devices سے حذف ہوجائے گا۔ حذف کریں حذف کریں اور چھوڑیں - %1$s کو فون کرنے کے لئے ، Signal کو آپ کے مائکروفون تک رسائی کی ضرورت ہے + شامل ہوں diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 9b3888da9b..0efe449b07 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -383,7 +383,7 @@ Yêu cầu tham gia nhóm của bạn đã được gửi tới quản trị viên nhóm. Bạn sẽ được thông báo khi họ đưa ra quyết định. Hủy yêu cầu - Để gửi tin nhắn âm thanh, hãy cho phép Signal truy cập micro điện thoại. + Signal cần quyền truy cập Micro để gửi tin nhắn âm thanh, nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Micro\". Signal cần quyền truy cập Micro và Máy ảnh để gọi %1$s, nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Micro\" và \"Máy ảnh\" Để chụp ảnh, hãy cho phép Signal truy cập máy ảnh. @@ -408,7 +408,7 @@ Bạn sẽ rời nhóm, và lịch sử trò chuyện sẽ bị xóa trên tất cả các thiết bị của bạn. Xóa Xóa và rời khỏi - Để gọi %1$s, Signal cần quyền truy cập micro của bạn + Tham gia diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index 5fec9c0254..dbd67f6b14 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -383,7 +383,7 @@ 已經話咗畀個谷嘅話事人知,您要求加入呢個谷。一有消息會通知您㗎喇。 取消請求 - 要傳送語音訊息嘅話,請允許 Signal 存取您部機個咪。 + Signal 要攞「麥克風」權限,先可以傳送語音訊息,但權限已被永久拒絕。請到呢個 app 嘅應用程式設定,揀選「權限」,然後啟用「麥克風」。 Signal 要攞「麥克風」同「相機」權限,先可以同 %1$s 通話,但權限已被永久拒絕。請到呢個 app 嘅應用程式設定,揀選「權限」,然後啟用「麥克風」同「相機」。 要影相或者拍片嘅話,請允許 Signal 存取您部機嘅相機。 @@ -408,7 +408,7 @@ 你會退出呢個群組,而呢個群組亦都會喺你所有裝置上面刪除。 刪除 刪除,然後退出 - 要同 %1$s 通話嘅話,Signal 需要存取您部機個咪 + 摻埋我 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 66f450c9cc..122403ab9b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -383,7 +383,7 @@ 您的入群申请已发送给群组管理员,您将在管理员作出决定后收到通知。 撤回申请 - 如需发送语音,请允许 Signal 使用麦克风。 + Signal 需“麦克风”权限,来发送音频,但该权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“麦克风”。 Signal 需“麦克风”和“相机”权限,来呼叫 %1$s,但这些权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“麦克风”和“相机”。 要拍照或录像,请允许 Signal 访问相机。 @@ -408,7 +408,7 @@ 你将离开这个组,它将从你所有的设备中删除。 删除 删除并离开 - Signal 需要访问您的麦克风来呼叫 %1$s + 加入 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 21f8d5539d..0fc6a20ba5 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -383,7 +383,7 @@ 您欲加入群組的請求已傳送至此群組的管理員。一經處理,將會通知您。 取消請求 - 如要傳送語音訊息,請允許Signal存取您的咪高峰。 + Signal需要存取咪高峰來傳送語音訊息,但您已經永久拒絕。請前往應用程式設定功能表,按「權限」,然後開啟「咪高峰」。 Signal需要存取咪高峰及相機來呼叫%1$s,但您已經永久拒絕。請前往應用程式設定功能表,按「權限」,然後開啟「咪高峰」和「相機」。 要拍照或錄影,請允許Signal存取相機。 @@ -408,7 +408,7 @@ 您將退出此群組,並從您所有的裝置上刪除此群組。 刪除 刪除並退出 - Signal 需要存取您的咪高峰,以便與 %1$s 通話。 + 加入 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 5eee03d2fb..cd7e383eb4 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -383,7 +383,7 @@ 你的加入要求已傳送到群組管理員。 他們採取行動時會通知你。 取消要求 - 若要傳送語音訊息,請允許 Signal 使用您的麥克風 + Signal 需要麥克風的權限來傳送語音訊息,但是現在系統設定為總是拒絕 Signal。請到 Signal 的應用程式設定中,點選「權限」,並啟用「麥克風」的權限。 Signal 需要麥克風和相機的權限來打電話給 %1$s,但是現在系統設定為總是拒絕 Signal。請到 Signal 的應用程式設定中,點選「權限」,並啟用麥克風和相機的權限。 請授予 Signal 使用相機的權限,才能拍攝照片和影片 @@ -408,7 +408,7 @@ 你將離開此群組,及它將從你所有的裝置上被刪除。 刪除 刪除並離開 - 如要與 %1$s 通話,Signal 需要存取你的麥克風 + 加入 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b2d3a25cd..77299d3f76 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -96,7 +96,7 @@ To send your location: Allow Signal access to send your location. - + Signal needs location access to send your location. @@ -402,7 +402,7 @@ To send audio messages: To send voice messages, allow Signal access to your microphone. - + Signal needs microphone access to record a voice message. Signal requires the Microphone permission in order to send audio messages, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\". @@ -434,7 +434,7 @@ To start a call: To start a call, allow Signal access to your microphone. - + Signal needs microphone access to start a call. @@ -6792,17 +6792,17 @@ Downloading backup data… - + Credit or debit card - + iDEAL - + Google Pay - + Bank transfer - + PayPal - + Unknown diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index 9c00a899a8..44fdecc9ad 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.250.80.115"}""" -rootProject.extra["cdn_ips"] = """new String[]{"18.238.55.2","18.238.55.54","18.238.55.7","18.238.55.78"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.251.40.243"}""" +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[]{"34.36.104.134"}""" +rootProject.extra["sfu_ips"] = """new String[]{"34.49.178.219"}""" 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 7241283be2fa192af2024c161d767b7d9e7e0002 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 29 Apr 2024 21:02:33 -0400 Subject: [PATCH 100/113] Update baseline profile. --- app/src/main/baseline-prof.txt | 488 +++++++++++++++++++-------------- 1 file changed, 284 insertions(+), 204 deletions(-) diff --git a/app/src/main/baseline-prof.txt b/app/src/main/baseline-prof.txt index 5319b7b893..cf9d8b2cb9 100644 --- a/app/src/main/baseline-prof.txt +++ b/app/src/main/baseline-prof.txt @@ -1,46 +1,38 @@ HPLandroidx/appcompat/widget/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -HPLandroidx/core/view/ViewGroupKt$descendants$1;->(Landroid/view/ViewGroup;Lkotlin/coroutines/Continuation;)V +HPLandroidx/constraintlayout/core/ArrayRow;->(Landroidx/constraintlayout/core/Cache;)V HPLandroidx/core/view/ViewGroupKt$descendants$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; HPLandroidx/customview/poolingcontainer/PoolingContainer;->callPoolingContainerOnRelease(Landroid/view/View;)V HPLandroidx/fragment/app/FragmentManager;->saveAllStateInternal()Landroid/os/Bundle; -HPLandroidx/fragment/app/FragmentStateManager;->saveState()Landroid/os/Bundle; -HPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areItemsTheSame(II)Z -HPLandroidx/recyclerview/widget/DiffUtil;->forward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; -HPLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->buildAdapterChangeFlagsForAnimations(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)I +HPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areContentsTheSame(II)Z +HPLandroidx/recyclerview/widget/BatchingListUpdateCallback;->onChanged(IILjava/lang/Object;)V HPLandroidx/recyclerview/widget/RecyclerView;->viewRangeUpdate(IILjava/lang/Object;)V HPLandroidx/recyclerview/widget/ViewInfoStore;->addToPreLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)V +HPLandroidx/recyclerview/widget/ViewInfoStore;->isDisappearing(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Z HPLandroidx/recyclerview/widget/ViewInfoStore;->popFromLayoutStep(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;I)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; -HPLandroidx/savedstate/SavedStateRegistry;->performSave(Landroid/os/Bundle;)V -HPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I -HPLio/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable;->subscribeActual(Lio/reactivex/rxjava3/core/MaybeObserver;)V HPLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver;->innerSuccess(Lio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver$InnerObserver;Ljava/lang/Object;)V HPLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver;->onNext(Ljava/lang/Object;)V -HPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->removeFirst()V -HPLkotlin/sequences/SequenceBuilderIterator;->yieldAll(Ljava/util/Iterator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -HPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getCount()I -HPLnet/zetetic/database/sqlcipher/SQLiteCursor;->setWindow(Landroid/database/CursorWindow;)V -HPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda0;->rejectedExecution(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V -HPLorg/signal/core/util/concurrent/SignalExecutors;->lambda$newCachedBoundedExecutor$1(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V -HPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V -HPLorg/signal/paging/FixedSizePagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V +HPLkotlin/collections/AbstractList$IteratorImpl;->(Lkotlin/collections/AbstractList;)V +HPLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;)V +HPLkotlin/text/StringsKt__StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange; +HPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->wakeConnectionWaitersLocked()V +HPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->inTransaction()Z +HPLorg/signal/core/util/tracing/TracePacket$Builder;->()V +HPLorg/signal/core/util/tracing/TracePacket$Builder;->timestamp(Ljava/lang/Long;)Lorg/signal/core/util/tracing/TracePacket$Builder; +HPLorg/signal/libsignal/protocol/ecc/ECPublicKey;->equals(Ljava/lang/Object;)Z HPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->animateChange(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)Z HPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->animatePersistence(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)Z HPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeConversationThreadUi$8;->invoke()Ljava/lang/Boolean; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->doAfterFirstRender()V HPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getReminder$lambda$10(Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;Lorg/thoughtcrime/securesms/database/model/GroupRecord;)Lj$/util/Optional; +HPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getRequestReviewState$lambda$15(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getRequestReviewState(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;)Lio/reactivex/rxjava3/core/Single; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$13;->apply(Lj$/util/Optional;)Lio/reactivex/rxjava3/core/MaybeSource; HPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Lorg/thoughtcrime/securesms/recipients/Recipient;)Ljava/lang/Boolean; +HPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)Lio/reactivex/rxjava3/core/SingleSource; HPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->equals(Ljava/lang/Object;)Z HPLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z -HPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$unregisterObserver$17(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HPLorg/thoughtcrime/securesms/database/model/IdentityRecord;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->equals(Ljava/lang/Object;)Z HPLorg/thoughtcrime/securesms/mms/Slide;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/profiles/ProfileName;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/recipients/Recipient;->hasSameContent(Lorg/thoughtcrime/securesms/recipients/Recipient;)Z HPLorg/thoughtcrime/securesms/util/BubbleUtil;->canBubble(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Long;)Z HSPLandroid/support/v4/media/MediaBrowserCompat$MediaBrowserImplApi21$$ExternalSyntheticThrowCCEIfNotNull0;->m(Ljava/lang/Object;)V HSPLandroid/support/v4/media/session/IMediaSession$Stub;->()V @@ -412,7 +404,6 @@ HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->setItemInvoker(Landroidx/a HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->setPopupCallback(Landroidx/appcompat/view/menu/ActionMenuItemView$PopupCallback;)V HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->setTitle(Ljava/lang/CharSequence;)V HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->shouldAllowTextWithIcon()Z -HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->updateTextButtonVisibility()V HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->(Landroid/content/Context;II)V HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->addItemView(Landroid/view/View;I)V HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->createItemView(Landroid/view/ViewGroup;)Landroidx/appcompat/view/menu/MenuView$ItemView; @@ -420,7 +411,6 @@ HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->filterLeftoverView(Landroid HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->getItemView(Landroidx/appcompat/view/menu/MenuItemImpl;Landroid/view/View;Landroid/view/ViewGroup;)Landroid/view/View; HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->initForMenu(Landroid/content/Context;Landroidx/appcompat/view/menu/MenuBuilder;)V HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->setCallback(Landroidx/appcompat/view/menu/MenuPresenter$Callback;)V -HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->updateMenuView(Z)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->()V HSPLandroidx/appcompat/view/menu/MenuBuilder;->(Landroid/content/Context;)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->add(IIILjava/lang/CharSequence;)Landroid/view/MenuItem; @@ -503,7 +493,6 @@ HSPLandroidx/appcompat/widget/ActionMenuPresenter;->setExpandedActionViewsExclus HSPLandroidx/appcompat/widget/ActionMenuPresenter;->setMenuView(Landroidx/appcompat/widget/ActionMenuView;)V HSPLandroidx/appcompat/widget/ActionMenuPresenter;->setReserveOverflow(Z)V HSPLandroidx/appcompat/widget/ActionMenuPresenter;->shouldIncludeItem(ILandroidx/appcompat/view/menu/MenuItemImpl;)Z -HSPLandroidx/appcompat/widget/ActionMenuPresenter;->updateMenuView(Z)V HSPLandroidx/appcompat/widget/ActionMenuView$ActionMenuPresenterCallback;->()V HSPLandroidx/appcompat/widget/ActionMenuView$LayoutParams;->(II)V HSPLandroidx/appcompat/widget/ActionMenuView$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V @@ -1100,8 +1089,6 @@ HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getVariable(I)Landroid HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getVariableValue(I)F HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->invert()V HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->remove(Landroidx/constraintlayout/core/SolverVariable;Z)F -HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->use(Landroidx/constraintlayout/core/ArrayRow;Z)F -HSPLandroidx/constraintlayout/core/ArrayRow;->(Landroidx/constraintlayout/core/Cache;)V HSPLandroidx/constraintlayout/core/ArrayRow;->addError(Landroidx/constraintlayout/core/LinearSystem;I)Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/ArrayRow;->addSingleError(Landroidx/constraintlayout/core/SolverVariable;I)Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/ArrayRow;->chooseSubject(Landroidx/constraintlayout/core/LinearSystem;)Z @@ -1121,12 +1108,14 @@ HSPLandroidx/constraintlayout/core/ArrayRow;->isNew(Landroidx/constraintlayout/c HSPLandroidx/constraintlayout/core/ArrayRow;->pivot(Landroidx/constraintlayout/core/SolverVariable;)V HSPLandroidx/constraintlayout/core/ArrayRow;->reset()V HSPLandroidx/constraintlayout/core/ArrayRow;->updateFromFinalVariable(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/SolverVariable;Z)V -HSPLandroidx/constraintlayout/core/ArrayRow;->updateFromRow(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/ArrayRow;Z)V +HSPLandroidx/constraintlayout/core/ArrayRow;->updateFromSystem(Landroidx/constraintlayout/core/LinearSystem;)V HSPLandroidx/constraintlayout/core/Cache;->()V HSPLandroidx/constraintlayout/core/LinearSystem;->()V HSPLandroidx/constraintlayout/core/LinearSystem;->()V +HSPLandroidx/constraintlayout/core/LinearSystem;->acquireSolverVariable(Landroidx/constraintlayout/core/SolverVariable$Type;Ljava/lang/String;)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/LinearSystem;->addCentering(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;IFLandroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)V HSPLandroidx/constraintlayout/core/LinearSystem;->addConstraint(Landroidx/constraintlayout/core/ArrayRow;)V +HSPLandroidx/constraintlayout/core/LinearSystem;->addEquality(Landroidx/constraintlayout/core/SolverVariable;I)V HSPLandroidx/constraintlayout/core/LinearSystem;->addEquality(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/LinearSystem;->addGreaterBarrier(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;IZ)V HSPLandroidx/constraintlayout/core/LinearSystem;->addGreaterThan(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)V @@ -1135,9 +1124,11 @@ HSPLandroidx/constraintlayout/core/LinearSystem;->addLowerThan(Landroidx/constra HSPLandroidx/constraintlayout/core/LinearSystem;->addRow(Landroidx/constraintlayout/core/ArrayRow;)V HSPLandroidx/constraintlayout/core/LinearSystem;->addSingleError(Landroidx/constraintlayout/core/ArrayRow;II)V HSPLandroidx/constraintlayout/core/LinearSystem;->computeValues()V +HSPLandroidx/constraintlayout/core/LinearSystem;->createErrorVariable(ILjava/lang/String;)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/LinearSystem;->createObjectVariable(Ljava/lang/Object;)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/LinearSystem;->createRow()Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/LinearSystem;->createSlackVariable()Landroidx/constraintlayout/core/SolverVariable; +HSPLandroidx/constraintlayout/core/LinearSystem;->enforceBFS(Landroidx/constraintlayout/core/LinearSystem$Row;)I HSPLandroidx/constraintlayout/core/LinearSystem;->getCache()Landroidx/constraintlayout/core/Cache; HSPLandroidx/constraintlayout/core/LinearSystem;->getMetrics()Landroidx/constraintlayout/core/Metrics; HSPLandroidx/constraintlayout/core/LinearSystem;->getObjectVariableValue(Ljava/lang/Object;)I @@ -1371,6 +1362,7 @@ HSPLandroidx/constraintlayout/core/widgets/analyzer/DependencyGraph;->invalidate HSPLandroidx/constraintlayout/core/widgets/analyzer/DependencyGraph;->setMeasurer(Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->()V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->canMeasure(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;)Z +HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->horizontalSolvingPass(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveBarrier(ILandroidx/constraintlayout/core/widgets/Barrier;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;IZ)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveHorizontalCenterConstraints(ILandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveHorizontalMatchConstraint(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V @@ -1401,6 +1393,7 @@ HSPLandroidx/constraintlayout/widget/ConstraintHelper;->validateParams()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$1;->()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams$Table;->()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->resolveLayoutDirection(I)V HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->validate()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->(Landroidx/constraintlayout/widget/ConstraintLayout;Landroidx/constraintlayout/widget/ConstraintLayout;)V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->captureLayoutInfo(IIIIII)V @@ -1577,7 +1570,6 @@ HSPLandroidx/core/app/NotificationManagerCompat;->(Landroid/content/Contex HSPLandroidx/core/app/NotificationManagerCompat;->cancel(I)V HSPLandroidx/core/app/NotificationManagerCompat;->cancel(Ljava/lang/String;I)V HSPLandroidx/core/app/NotificationManagerCompat;->from(Landroid/content/Context;)Landroidx/core/app/NotificationManagerCompat; -HSPLandroidx/core/content/ContentValuesKt;->contentValuesOf([Lkotlin/Pair;)Landroid/content/ContentValues; HSPLandroidx/core/content/ContextCompat$Api21Impl;->getDrawable(Landroid/content/Context;I)Landroid/graphics/drawable/Drawable; HSPLandroidx/core/content/ContextCompat$Api23Impl$$ExternalSyntheticApiModelOutline1;->m(Landroid/content/Context;I)I HSPLandroidx/core/content/ContextCompat$Api23Impl;->getColor(Landroid/content/Context;I)I @@ -5046,6 +5038,9 @@ HSPLandroidx/recyclerview/widget/LinearLayoutManager;->assertNotInLayoutOrScroll HSPLandroidx/recyclerview/widget/LinearLayoutManager;->calculateExtraLayoutSpace(Landroidx/recyclerview/widget/RecyclerView$State;[I)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->canScrollHorizontally()Z HSPLandroidx/recyclerview/widget/LinearLayoutManager;->canScrollVertically()Z +HSPLandroidx/recyclerview/widget/LinearLayoutManager;->computeHorizontalScrollExtent(Landroidx/recyclerview/widget/RecyclerView$State;)I +HSPLandroidx/recyclerview/widget/LinearLayoutManager;->computeHorizontalScrollOffset(Landroidx/recyclerview/widget/RecyclerView$State;)I +HSPLandroidx/recyclerview/widget/LinearLayoutManager;->computeHorizontalScrollRange(Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/LinearLayoutManager;->computeScrollExtent(Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/LinearLayoutManager;->computeScrollOffset(Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/LinearLayoutManager;->computeScrollRange(Landroidx/recyclerview/widget/RecyclerView$State;)I @@ -5109,8 +5104,10 @@ HSPLandroidx/recyclerview/widget/OpReorderer;->(Landroidx/recyclerview/wid HSPLandroidx/recyclerview/widget/OpReorderer;->getLastMoveOutOfOrder(Ljava/util/List;)I HSPLandroidx/recyclerview/widget/OpReorderer;->reorderOps(Ljava/util/List;)V HSPLandroidx/recyclerview/widget/OrientationHelper$1;->(Landroidx/recyclerview/widget/RecyclerView$LayoutManager;)V +HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedEnd(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedMeasurement(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedMeasurementInOther(Landroid/view/View;)I +HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedStart(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getEndAfterPadding()I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getEndPadding()I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getMode()I @@ -5198,6 +5195,11 @@ HSPLandroidx/recyclerview/widget/RecyclerView$ItemDecoration;->onDraw(Landroid/g HSPLandroidx/recyclerview/widget/RecyclerView$ItemDecoration;->onDrawOver(Landroid/graphics/Canvas;Landroidx/recyclerview/widget/RecyclerView;)V HSPLandroidx/recyclerview/widget/RecyclerView$ItemDecoration;->onDrawOver(Landroid/graphics/Canvas;Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$State;)V HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$1;->(Landroidx/recyclerview/widget/RecyclerView$LayoutManager;)V +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$1;->getChildAt(I)Landroid/view/View; +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$1;->getChildEnd(Landroid/view/View;)I +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$1;->getChildStart(Landroid/view/View;)I +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$1;->getParentEnd()I +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$1;->getParentStart()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$2;->(Landroidx/recyclerview/widget/RecyclerView$LayoutManager;)V HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$2;->getChildAt(I)Landroid/view/View; HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager$2;->getChildEnd(Landroid/view/View;)I @@ -5226,14 +5228,17 @@ HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getChildMeasureSpe HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getColumnCountForAccessibility(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedBottom(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedBoundsWithMargins(Landroid/view/View;Landroid/graphics/Rect;)V +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedLeft(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedMeasuredHeight(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedMeasuredWidth(Landroid/view/View;)I +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedRight(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedTop(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getFocusedChild()Landroid/view/View; HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getHeight()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getHeightMode()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getItemCount()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getLayoutDirection()I +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getLeftDecorationWidth(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getMinimumHeight()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getMinimumWidth()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getPaddingBottom()I @@ -5242,6 +5247,7 @@ HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getPaddingRight()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getPaddingTop()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getPosition(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getProperties(Landroid/content/Context;Landroid/util/AttributeSet;II)Landroidx/recyclerview/widget/RecyclerView$LayoutManager$Properties; +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getRightDecorationWidth(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getRowCountForAccessibility(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getSelectionModeForAccessibility(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getTopDecorationHeight(Landroid/view/View;)I @@ -5558,6 +5564,7 @@ HSPLandroidx/savedstate/SavedStateRegistryController;->performAttach()V HSPLandroidx/savedstate/SavedStateRegistryController;->performRestore(Landroid/os/Bundle;)V HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner;->set(Landroid/view/View;Landroidx/savedstate/SavedStateRegistryOwner;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->(Ljava/lang/String;[Ljava/lang/Object;)V +HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->bind(Landroidx/sqlite/db/SupportSQLiteProgram;ILjava/lang/Object;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->bind(Landroidx/sqlite/db/SupportSQLiteProgram;[Ljava/lang/Object;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->bindTo(Landroidx/sqlite/db/SupportSQLiteProgram;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->getSql()Ljava/lang/String; @@ -6086,9 +6093,9 @@ HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->findName(Ljava/lang/String;L HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->hasNext()Z HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->isLiteral(I)Z HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextBoolean()Z -HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextDouble()D HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextInt()I HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextName()Ljava/lang/String; +HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextNonWhitespace(Z)I HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextQuotedValue(Lokio/ByteString;)Ljava/lang/String; HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextString()Ljava/lang/String; HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peek()Lcom/airbnb/lottie/parser/moshi/JsonReader$Token; @@ -7340,13 +7347,13 @@ HSPLcom/fasterxml/jackson/core/base/GeneratorBase;->isEnabled(Lcom/fasterxml/jac HSPLcom/fasterxml/jackson/core/base/ParserBase;->()V HSPLcom/fasterxml/jackson/core/base/ParserBase;->(Lcom/fasterxml/jackson/core/io/IOContext;I)V HSPLcom/fasterxml/jackson/core/base/ParserBase;->_getSourceReference()Ljava/lang/Object; +HSPLcom/fasterxml/jackson/core/base/ParserBase;->_parseIntValue()I HSPLcom/fasterxml/jackson/core/base/ParserBase;->_parseNumericValue(I)V HSPLcom/fasterxml/jackson/core/base/ParserBase;->_releaseBuffers()V HSPLcom/fasterxml/jackson/core/base/ParserBase;->_validJsonTokenList()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/base/ParserBase;->_validJsonValueList()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/base/ParserBase;->close()V HSPLcom/fasterxml/jackson/core/base/ParserBase;->convertNumberToLong()V -HSPLcom/fasterxml/jackson/core/base/ParserBase;->getCurrentName()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/base/ParserBase;->getIntValue()I HSPLcom/fasterxml/jackson/core/base/ParserBase;->getLongValue()J HSPLcom/fasterxml/jackson/core/base/ParserBase;->getNumberType()Lcom/fasterxml/jackson/core/JsonParser$NumberType; @@ -7355,7 +7362,7 @@ HSPLcom/fasterxml/jackson/core/base/ParserBase;->resetInt(ZI)Lcom/fasterxml/jack HSPLcom/fasterxml/jackson/core/base/ParserBase;->setCurrentValue(Ljava/lang/Object;)V HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->()V HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->(I)V -HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->_reportError(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V +HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->_reportError(Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->currentToken()Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->currentTokenId()I HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->getValueAsString()Ljava/lang/String; @@ -7452,6 +7459,7 @@ HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_loadMore()Z HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchFalse()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchNull()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchTrue()V +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_nextAfterName()Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parseName2(III)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parseNumber2(ZI)Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_releaseBuffers()V @@ -7460,7 +7468,6 @@ HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipColon()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipColon2(Z)I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipComma(I)I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipString()V -HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipWSOrEnd()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipWSOrEnd2()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_updateLocation()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_updateNameLocation()V @@ -7468,8 +7475,6 @@ HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_verifyNoLeadingZero HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->getReadCapabilities()Lcom/fasterxml/jackson/core/util/JacksonFeatureSet; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->getText()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->getValueAsString()Ljava/lang/String; -HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->nextFieldName()Ljava/lang/String; -HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->nextToken()Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->()V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->(Lcom/fasterxml/jackson/core/io/IOContext;ILcom/fasterxml/jackson/core/ObjectCodec;Ljava/io/OutputStream;C)V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->_flushBuffer()V @@ -7490,6 +7495,7 @@ HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeString(Ljava/lang/S HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeString([CII)V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->(Lcom/fasterxml/jackson/core/io/IOContext;ILjava/io/InputStream;Lcom/fasterxml/jackson/core/ObjectCodec;Lcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;[BIIIZ)V +HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_closeArrayScope()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_closeInput()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_closeObjectScope()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_decodeCharForError(I)I @@ -7498,10 +7504,12 @@ HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_handleUnexpectedValu HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_loadMore()Z HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_loadMoreGuaranteed()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_nextAfterName()Lcom/fasterxml/jackson/core/JsonToken; +HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_nextTokenNotInObject(I)Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_padLastQuad(II)I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_parseName(I)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_parsePosNumber(I)Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_releaseBuffers()V +HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_reportInvalidOther(I)V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_reportInvalidToken(Ljava/lang/String;Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_skipColon()I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_skipWS()I @@ -7515,7 +7523,7 @@ HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName(IIII)Ljava/l HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName([IIII)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getCurrentLocation()Lcom/fasterxml/jackson/core/JsonLocation; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getReadCapabilities()Lcom/fasterxml/jackson/core/util/JacksonFeatureSet; -HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getText()Ljava/lang/String; +HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->nextByte()I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->nextFieldName()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->parseEscapedName([IIIII)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->parseLongName(III)Ljava/lang/String; @@ -7528,6 +7536,7 @@ HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_flushBuffer()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_releaseBuffers()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_verifyValueWrite(Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeBinary(Lcom/fasterxml/jackson/core/Base64Variant;[BII)V +HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeFieldName(Lcom/fasterxml/jackson/core/SerializableString;Z)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeFieldName(Ljava/lang/String;Z)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeNull()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeString(Ljava/lang/String;)V @@ -7556,7 +7565,6 @@ HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_calcTertiaryShift(I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_checkNeedForRehash()Z HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_findOffsetForAdd(I)I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_findSecondary(III)Ljava/lang/String; -HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_findSecondary(IIII)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_resizeAndFindOffsetForAdd(I)I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_spilloverStart()I HSPLcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;->_verifyLongName([III)Z @@ -7586,6 +7594,7 @@ HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer$TableInfo;->createIn HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->(I)V HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->(Lcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;IILcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer$TableInfo;)V HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_addSymbol([CIIII)Ljava/lang/String; +HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_findSymbol2([CIILcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer$Bucket;)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_hashToIndex(I)I HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->_thresholdSize(I)I HSPLcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;->copyArrays()V @@ -8270,6 +8279,7 @@ HSPLcom/fasterxml/jackson/databind/deser/impl/FailingDeserializer;->(Ljava HSPLcom/fasterxml/jackson/databind/deser/impl/FailingDeserializer;->(Ljava/lang/String;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->(Lcom/fasterxml/jackson/databind/deser/impl/FieldProperty;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->(Lcom/fasterxml/jackson/databind/introspect/BeanPropertyDefinition;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/util/Annotations;Lcom/fasterxml/jackson/databind/introspect/AnnotatedField;)V +HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->deserializeAndSet(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/lang/Object;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->fixAccess(Lcom/fasterxml/jackson/databind/DeserializationConfig;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->getMember()Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember; HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->withValueDeserializer(Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty; @@ -8426,6 +8436,7 @@ HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->with HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; +HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/String; HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->isCachable()Z HSPLcom/fasterxml/jackson/databind/deser/std/UUIDDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/UUIDDeserializer;->()V @@ -9104,14 +9115,12 @@ HSPLcom/fasterxml/jackson/databind/ser/impl/IndexedListSerializer;->serializeCon HSPLcom/fasterxml/jackson/databind/ser/impl/IndexedListSerializer;->serializeContentsUsing(Ljava/util/List;Lcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;Lcom/fasterxml/jackson/databind/JsonSerializer;)V HSPLcom/fasterxml/jackson/databind/ser/impl/IndexedListSerializer;->withResolved(Lcom/fasterxml/jackson/databind/BeanProperty;Lcom/fasterxml/jackson/databind/jsontype/TypeSerializer;Lcom/fasterxml/jackson/databind/JsonSerializer;Ljava/lang/Boolean;)Lcom/fasterxml/jackson/databind/ser/impl/IndexedListSerializer; HSPLcom/fasterxml/jackson/databind/ser/impl/IndexedListSerializer;->withResolved(Lcom/fasterxml/jackson/databind/BeanProperty;Lcom/fasterxml/jackson/databind/jsontype/TypeSerializer;Lcom/fasterxml/jackson/databind/JsonSerializer;Ljava/lang/Boolean;)Lcom/fasterxml/jackson/databind/ser/std/AsArraySerializerBase; -HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Double;->(Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap;Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;)V HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Empty;->()V HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Empty;->(Z)V HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Empty;->newWith(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;)Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap; HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Empty;->serializerFor(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JsonSerializer; HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$SerializerAndMapResult;->(Lcom/fasterxml/jackson/databind/JsonSerializer;Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap;)V HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Single;->(Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap;Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;)V -HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Single;->newWith(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;)Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap; HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Single;->serializerFor(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JsonSerializer; HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap;->(Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap;)V HSPLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap;->(Z)V @@ -11543,6 +11552,7 @@ HSPLcom/squareup/wire/internal/Internal;->countNonNull(Ljava/lang/Object;Ljava/l HSPLcom/squareup/wire/internal/Internal;->immutableCopyOf(Ljava/lang/String;Ljava/util/List;)Ljava/util/List; HSPLcom/squareup/wire/internal/Internal__InternalKt;->checkElementsNotNull(Ljava/util/List;)V HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;)I +HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)I HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->callRequireNonNull(Ljava/util/concurrent/Callable;)Lio/reactivex/rxjava3/core/Scheduler; HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->initMainThreadScheduler(Ljava/util/concurrent/Callable;)Lio/reactivex/rxjava3/core/Scheduler; @@ -11703,6 +11713,7 @@ HSPLio/reactivex/rxjava3/exceptions/Exceptions;->throwIfFatal(Ljava/lang/Throwab HSPLio/reactivex/rxjava3/flowables/ConnectableFlowable;->()V HSPLio/reactivex/rxjava3/flowables/ConnectableFlowable;->refCount()Lio/reactivex/rxjava3/core/Flowable; HSPLio/reactivex/rxjava3/internal/disposables/CancellableDisposable;->(Lio/reactivex/rxjava3/functions/Cancellable;)V +HSPLio/reactivex/rxjava3/internal/disposables/CancellableDisposable;->dispose()V HSPLio/reactivex/rxjava3/internal/disposables/DisposableHelper;->()V HSPLio/reactivex/rxjava3/internal/disposables/DisposableHelper;->(Ljava/lang/String;I)V HSPLio/reactivex/rxjava3/internal/disposables/DisposableHelper;->dispose(Ljava/util/concurrent/atomic/AtomicReference;)Z @@ -11893,7 +11904,9 @@ HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedRepla HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->getHead()Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->leaveTransform(Ljava/lang/Object;)Ljava/lang/Object; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->next(Ljava/lang/Object;)V +HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->removeFirst()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->replay(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;)V +HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$DefaultUnboundedFactory;->()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscriber;Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->index()Ljava/lang/Object; @@ -12013,6 +12026,7 @@ HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap$Conca HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap;->(Lio/reactivex/rxjava3/core/ObservableSource;Lio/reactivex/rxjava3/functions/Function;ILio/reactivex/rxjava3/internal/util/ErrorMode;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap;->subscribeActual(Lio/reactivex/rxjava3/core/Observer;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->(Lio/reactivex/rxjava3/core/Observer;)V +HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->dispose()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->isDisposed()Z HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->onNext(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->setCancellable(Lio/reactivex/rxjava3/functions/Cancellable;)V @@ -12093,7 +12107,9 @@ HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedR HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->getHead()Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$Node; HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->leaveTransform(Ljava/lang/Object;)Ljava/lang/Object; HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->next(Ljava/lang/Object;)V +HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->removeFirst()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->replay(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;)V +HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$Node;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;->(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$ReplayObserver;Lio/reactivex/rxjava3/core/Observer;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;->dispose()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;->index()Ljava/lang/Object; @@ -12228,7 +12244,9 @@ HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode;->soNext HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode;->spValue(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->()V HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->clear()V +HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->isEmpty()Z HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lpConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; +HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvProducerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->offer(Ljava/lang/Object;)Z HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->poll()Ljava/lang/Object; @@ -12265,7 +12283,6 @@ HSPLio/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue;->soProducerIndex(J HSPLio/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue;->writeToQueue(Ljava/util/concurrent/atomic/AtomicReferenceArray;Ljava/lang/Object;JI)Z HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->()V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->(Ljava/lang/Runnable;Z)V -HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->cancelFuture(Ljava/util/concurrent/Future;)V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->dispose()V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->setFuture(Ljava/util/concurrent/Future;)V HSPLio/reactivex/rxjava3/internal/schedulers/DisposeOnCancel;->(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -12349,9 +12366,6 @@ HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->setOnce(Lja HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->setOnce(Ljava/util/concurrent/atomic/AtomicReference;Lorg/reactivestreams/Subscription;J)Z HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->validate(J)Z HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->validate(Lorg/reactivestreams/Subscription;Lorg/reactivestreams/Subscription;)Z -HSPLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->(I)V -HSPLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->add(Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->forEachWhile(Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList$NonThrowingPredicate;)V HSPLio/reactivex/rxjava3/internal/util/ArrayListSupplier;->()V HSPLio/reactivex/rxjava3/internal/util/ArrayListSupplier;->(Ljava/lang/String;I)V HSPLio/reactivex/rxjava3/internal/util/ArrayListSupplier;->asSupplier()Lio/reactivex/rxjava3/functions/Supplier; @@ -12467,7 +12481,6 @@ HSPLio/reactivex/rxjava3/processors/BehaviorProcessor;->subscribeActual(Lorg/rea HSPLio/reactivex/rxjava3/processors/FlowableProcessor;->()V HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->(Lorg/reactivestreams/Subscriber;Lio/reactivex/rxjava3/processors/PublishProcessor;)V HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->isCancelled()Z -HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->onNext(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->request(J)V HSPLio/reactivex/rxjava3/processors/PublishProcessor;->()V HSPLio/reactivex/rxjava3/processors/PublishProcessor;->()V @@ -12796,7 +12809,6 @@ HSPLj$/util/c;->()V HSPLj$/util/concurrent/ConcurrentHashMap;->()V HSPLj$/util/concurrent/ConcurrentHashMap;->()V HSPLj$/util/concurrent/ConcurrentHashMap;->(IFI)V -HSPLj$/util/concurrent/ConcurrentHashMap;->addCount(JI)V HSPLj$/util/concurrent/ConcurrentHashMap;->casTabAt([Lj$/util/concurrent/l;ILj$/util/concurrent/l;Lj$/util/concurrent/l;)Z HSPLj$/util/concurrent/ConcurrentHashMap;->clear()V HSPLj$/util/concurrent/ConcurrentHashMap;->comparableClassFor(Ljava/lang/Object;)Ljava/lang/Class; @@ -12820,6 +12832,7 @@ HSPLj$/util/concurrent/ConcurrentHashMap;->tabAt([Lj$/util/concurrent/l;I)Lj$/ut HSPLj$/util/concurrent/ConcurrentHashMap;->tableSizeFor(I)I HSPLj$/util/concurrent/ConcurrentHashMap;->transfer([Lj$/util/concurrent/l;[Lj$/util/concurrent/l;)V HSPLj$/util/concurrent/ConcurrentHashMap;->treeifyBin([Lj$/util/concurrent/l;I)V +HSPLj$/util/concurrent/ConcurrentHashMap;->untreeify(Lj$/util/concurrent/l;)Lj$/util/concurrent/l; HSPLj$/util/concurrent/a;->([Lj$/util/concurrent/l;IILj$/util/concurrent/ConcurrentHashMap;)V HSPLj$/util/concurrent/a;->hasNext()Z HSPLj$/util/concurrent/b;->(Lj$/util/concurrent/ConcurrentHashMap;)V @@ -12839,6 +12852,7 @@ HSPLj$/util/concurrent/q;->()V HSPLj$/util/concurrent/q;->(Lj$/util/concurrent/r;)V HSPLj$/util/concurrent/q;->a(Ljava/lang/Object;I)Lj$/util/concurrent/l; HSPLj$/util/concurrent/q;->c(Lj$/util/concurrent/r;Lj$/util/concurrent/r;)Lj$/util/concurrent/r; +HSPLj$/util/concurrent/q;->e()V HSPLj$/util/concurrent/q;->f(ILjava/lang/Object;Ljava/lang/Object;)Lj$/util/concurrent/r; HSPLj$/util/concurrent/q;->h(Lj$/util/concurrent/r;Lj$/util/concurrent/r;)Lj$/util/concurrent/r; HSPLj$/util/concurrent/q;->i(Lj$/util/concurrent/r;Lj$/util/concurrent/r;)Lj$/util/concurrent/r; @@ -13057,13 +13071,12 @@ HSPLkotlin/UnsafeLazyImpl;->(Lkotlin/jvm/functions/Function0;)V HSPLkotlin/UnsafeLazyImpl;->getValue()Ljava/lang/Object; HSPLkotlin/collections/AbstractCollection$toString$1;->(Lkotlin/collections/AbstractCollection;)V HSPLkotlin/collections/AbstractCollection;->()V +HSPLkotlin/collections/AbstractCollection;->isEmpty()Z +HSPLkotlin/collections/AbstractCollection;->size()I HSPLkotlin/collections/AbstractCollection;->toString()Ljava/lang/String; HSPLkotlin/collections/AbstractList$Companion;->()V HSPLkotlin/collections/AbstractList$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLkotlin/collections/AbstractList$Companion;->checkElementIndex$kotlin_stdlib(II)V -HSPLkotlin/collections/AbstractList$IteratorImpl;->(Lkotlin/collections/AbstractList;)V -HSPLkotlin/collections/AbstractList$IteratorImpl;->hasNext()Z -HSPLkotlin/collections/AbstractList$IteratorImpl;->next()Ljava/lang/Object; HSPLkotlin/collections/AbstractList;->()V HSPLkotlin/collections/AbstractList;->()V HSPLkotlin/collections/AbstractList;->iterator()Ljava/util/Iterator; @@ -13324,7 +13337,6 @@ HSPLkotlin/collections/MapsKt;->mapCapacity(I)I HSPLkotlin/collections/MapsKt;->mapOf(Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt;->mapOf([Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt;->plus(Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map; -HSPLkotlin/collections/MapsKt;->plus(Ljava/util/Map;Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt;->putAll(Ljava/util/Map;[Lkotlin/Pair;)V HSPLkotlin/collections/MapsKt;->toMap(Ljava/lang/Iterable;)Ljava/util/Map; HSPLkotlin/collections/MapsKt;->toMap(Ljava/util/Map;)Ljava/util/Map; @@ -13337,7 +13349,6 @@ HSPLkotlin/collections/MapsKt__MapsKt;->emptyMap()Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsKt;->linkedMapOf([Lkotlin/Pair;)Ljava/util/LinkedHashMap; HSPLkotlin/collections/MapsKt__MapsKt;->mapOf([Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsKt;->plus(Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map; -HSPLkotlin/collections/MapsKt__MapsKt;->plus(Ljava/util/Map;Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsKt;->putAll(Ljava/util/Map;Ljava/lang/Iterable;)V HSPLkotlin/collections/MapsKt__MapsKt;->putAll(Ljava/util/Map;[Lkotlin/Pair;)V HSPLkotlin/collections/MapsKt__MapsKt;->toMap(Ljava/lang/Iterable;)Ljava/util/Map; @@ -13453,7 +13464,6 @@ HSPLkotlin/jvm/internal/FunctionReference;->(ILjava/lang/Object;Ljava/lang HSPLkotlin/jvm/internal/FunctionReferenceImpl;->(ILjava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V HSPLkotlin/jvm/internal/FunctionReferenceImpl;->(ILjava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V HSPLkotlin/jvm/internal/Intrinsics;->areEqual(Ljava/lang/Object;Ljava/lang/Object;)Z -HSPLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;)V HSPLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;Ljava/lang/String;)V HSPLkotlin/jvm/internal/Intrinsics;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V HSPLkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V @@ -13823,8 +13833,6 @@ HSPLkotlin/reflect/jvm/internal/KTypeImpl;->getJavaType()Ljava/lang/reflect/Type HSPLkotlin/reflect/jvm/internal/KTypeImpl;->isMarkedNullable()Z HSPLkotlin/reflect/jvm/internal/ModuleByClassLoaderKt;->()V HSPLkotlin/reflect/jvm/internal/ModuleByClassLoaderKt;->getOrCreateModule(Ljava/lang/Class;)Lkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/RuntimeModuleData; -HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazySoftVal;->(Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V -HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazySoftVal;->invoke()Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazyVal;->(Lkotlin/jvm/functions/Function0;)V HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazyVal;->invoke()Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/ReflectProperties$Val$1;->()V @@ -15010,6 +15018,7 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->merg HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->mergeFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite$Builder; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->setId(I)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->()V +HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$Builder;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$Builder;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V @@ -15118,7 +15127,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->()V -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->(Z)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getContextReceiverTypeCount()I @@ -15133,7 +15141,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getOldFlags()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReceiverType()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReturnType()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReturnTypeId()I -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getSerializedSize()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameter(I)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeParameter; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameterCount()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameterList()Ljava/util/List; @@ -15150,7 +15157,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasReturnType( HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasReturnTypeId()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasTypeTable()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->initFields()V -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->writeTo(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind;->(Ljava/lang/String;III)V @@ -15295,6 +15301,7 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type$Argument;->initField HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type$Argument;->isInitialized()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type$Argument;->writeTo(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->()V +HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->(Z)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->getArgument(I)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type$Argument; @@ -15307,7 +15314,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->getDefaultInstance HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->getFlags()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->getNullable()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->getOuterType()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type; -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->getSerializedSize()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->hasAbbreviatedType()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->hasAbbreviatedTypeId()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;->hasClassName()Z @@ -15784,8 +15790,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readBytes()Lkot HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readDouble()D HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readEnum()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readFloat()F -HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readMessage(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite$Builder;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V -HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readMessage(Lkotlin/reflect/jvm/internal/impl/protobuf/Parser;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite; HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawByte()B HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawLittleEndian32()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawLittleEndian64()J @@ -15846,7 +15850,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->getField(Lkotlin/reflec HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->getSerializedSize()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->getWireFormatForFieldType(Lkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;Z)I HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->hasField(Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet$FieldDescriptorLite;)Z -HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->isInitialized()Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->iterator()Ljava/util/Iterator; HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->makeImmutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->newFieldSet()Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet; @@ -15869,7 +15872,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMes HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage;->extensionsSerializedSize()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage;->getExtension(Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$GeneratedExtension;)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage;->hasExtension(Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$GeneratedExtension;)Z -HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage;->makeExtensionsImmutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage;->newExtensionWriter()Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage$ExtensionWriter; HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage;->parseUnknownField(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;I)Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$ExtendableMessage;->verifyExtensionContainingType(Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$GeneratedExtension;)V @@ -15894,7 +15896,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->access$100( HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->makeExtensionsImmutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->newRepeatedGeneratedExtension(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/Internal$EnumLiteMap;ILkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;ZLjava/lang/Class;)Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$GeneratedExtension; HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->newSingularGeneratedExtension(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Ljava/lang/Object;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/Internal$EnumLiteMap;ILkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;Ljava/lang/Class;)Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$GeneratedExtension; -HSPLkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite;->parseUnknownField(Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;I)Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/LazyStringArrayList;->()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/LazyStringArrayList;->()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/LazyStringArrayList;->add(Lkotlin/reflect/jvm/internal/impl/protobuf/ByteString;)V @@ -16374,6 +16375,7 @@ HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/Deserializati HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationConfiguration$Default;->getSkipMetadataVersionCheck()Z HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationConfiguration$Default;->getSkipPrereleaseCheck()Z HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationConfiguration$Default;->getTypeAliasesAllowed()Z +HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationContext;->(Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationComponents;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;Lkotlin/reflect/jvm/internal/impl/descriptors/DeclarationDescriptor;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/TypeTable;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/VersionRequirementTable;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/BinaryVersion;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/descriptors/DeserializedContainerSource;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;Ljava/util/List;)V HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationContext;->childContext$default(Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationContext;Lkotlin/reflect/jvm/internal/impl/descriptors/DeclarationDescriptor;Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/TypeTable;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/VersionRequirementTable;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/BinaryVersion;ILjava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationContext; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationContext;->childContext(Lkotlin/reflect/jvm/internal/impl/descriptors/DeclarationDescriptor;Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/TypeTable;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/VersionRequirementTable;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/BinaryVersion;)Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationContext; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationContext;->getComponents()Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/DeserializationComponents; @@ -16429,8 +16431,8 @@ HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeseria HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadConstructor(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Constructor;Z)Lkotlin/reflect/jvm/internal/impl/descriptors/ClassConstructorDescriptor; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadFunction(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;)Lkotlin/reflect/jvm/internal/impl/descriptors/SimpleFunctionDescriptor; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadOldFlags(I)I +HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadProperty(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;)Lkotlin/reflect/jvm/internal/impl/descriptors/PropertyDescriptor; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->valueParameters(Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/AnnotatedCallableKind;)Ljava/util/List; -HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/NameResolverUtilKt;->getClassId(Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;I)Lkotlin/reflect/jvm/internal/impl/name/ClassId; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/NameResolverUtilKt;->getName(Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;I)Lkotlin/reflect/jvm/internal/impl/name/Name; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/ProtoBasedClassDataFinder;->(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$PackageFragment;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/BinaryVersion;Lkotlin/jvm/functions/Function1;)V HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/ProtoBasedClassDataFinder;->findClassData(Lkotlin/reflect/jvm/internal/impl/name/ClassId;)Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/ClassData; @@ -16466,7 +16468,6 @@ HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeseriali HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->computeLocalClassifierReplacementType(I)Lkotlin/reflect/jvm/internal/impl/types/SimpleType; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->getOwnTypeParameters()Ljava/util/List; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->simpleType$collectAllArguments(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;)Ljava/util/List; -HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->simpleType(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;Z)Lkotlin/reflect/jvm/internal/impl/types/SimpleType; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->toAttributes(Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/descriptors/annotations/Annotations;Lkotlin/reflect/jvm/internal/impl/types/TypeConstructor;Lkotlin/reflect/jvm/internal/impl/descriptors/DeclarationDescriptor;)Lkotlin/reflect/jvm/internal/impl/types/TypeAttributes; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->type(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;)Lkotlin/reflect/jvm/internal/impl/types/KotlinType; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/TypeDeserializer;->typeArgument(Lkotlin/reflect/jvm/internal/impl/descriptors/TypeParameterDescriptor;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type$Argument;)Lkotlin/reflect/jvm/internal/impl/types/TypeProjection; @@ -16692,7 +16693,6 @@ HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$KeyWithComp HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$KeyWithComputation;->equals(Ljava/lang/Object;)Z HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$KeyWithComputation;->hashCode()I HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValue;->(Lkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager;Lkotlin/jvm/functions/Function0;)V -HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValue;->invoke()Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValue;->postCompute(Ljava/lang/Object;)V HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValueWithPostCompute;->(Lkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager;Lkotlin/jvm/functions/Function0;)V HSPLkotlin/reflect/jvm/internal/impl/storage/LockBasedStorageManager$LockBasedLazyValueWithPostCompute;->invoke()Ljava/lang/Object; @@ -17036,6 +17036,7 @@ HSPLkotlin/sequences/GeneratorSequence;->access$getGetNextValue$p(Lkotlin/sequen HSPLkotlin/sequences/GeneratorSequence;->iterator()Ljava/util/Iterator; HSPLkotlin/sequences/SequenceBuilderIterator;->()V HSPLkotlin/sequences/SequenceBuilderIterator;->getContext()Lkotlin/coroutines/CoroutineContext; +HSPLkotlin/sequences/SequenceBuilderIterator;->hasNext()Z HSPLkotlin/sequences/SequenceBuilderIterator;->next()Ljava/lang/Object; HSPLkotlin/sequences/SequenceBuilderIterator;->resumeWith(Ljava/lang/Object;)V HSPLkotlin/sequences/SequenceBuilderIterator;->setNextStep(Lkotlin/coroutines/Continuation;)V @@ -17089,6 +17090,7 @@ HSPLkotlin/sequences/SequencesKt___SequencesKt;->flatMapIterable(Lkotlin/sequenc HSPLkotlin/sequences/SequencesKt___SequencesKt;->map(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; HSPLkotlin/sequences/SequencesKt___SequencesKt;->mapNotNull(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; HSPLkotlin/sequences/SequencesKt___SequencesKt;->sortedWith(Lkotlin/sequences/Sequence;Ljava/util/Comparator;)Lkotlin/sequences/Sequence; +HSPLkotlin/sequences/SequencesKt___SequencesKt;->toCollection(Lkotlin/sequences/Sequence;Ljava/util/Collection;)Ljava/util/Collection; HSPLkotlin/sequences/SequencesKt___SequencesKt;->toList(Lkotlin/sequences/Sequence;)Ljava/util/List; HSPLkotlin/sequences/SequencesKt___SequencesKt;->toMutableList(Lkotlin/sequences/Sequence;)Ljava/util/List; HSPLkotlin/sequences/TransformingSequence$iterator$1;->(Lkotlin/sequences/TransformingSequence;)V @@ -17141,6 +17143,7 @@ HSPLkotlin/text/StringsKt;->contains$default(Ljava/lang/CharSequence;CZILjava/la HSPLkotlin/text/StringsKt;->contains$default(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt;->contains(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z HSPLkotlin/text/StringsKt;->drop(Ljava/lang/String;I)Ljava/lang/String; +HSPLkotlin/text/StringsKt;->endsWith$default(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange; HSPLkotlin/text/StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I HSPLkotlin/text/StringsKt;->indexOf$default(Ljava/lang/CharSequence;CIZILjava/lang/Object;)I @@ -17170,7 +17173,8 @@ HSPLkotlin/text/StringsKt__IndentKt;->replaceIndent(Ljava/lang/String;Ljava/lang HSPLkotlin/text/StringsKt__IndentKt;->trimIndent(Ljava/lang/String;)Ljava/lang/String; HSPLkotlin/text/StringsKt__StringNumberConversionsKt;->toLongOrNull(Ljava/lang/String;)Ljava/lang/Long; HSPLkotlin/text/StringsKt__StringNumberConversionsKt;->toLongOrNull(Ljava/lang/String;I)Ljava/lang/Long; -HSPLkotlin/text/StringsKt__StringsJVMKt;->isBlank(Ljava/lang/CharSequence;)Z +HSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith$default(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Z +HSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith(Ljava/lang/String;Ljava/lang/String;Z)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->regionMatches(Ljava/lang/String;ILjava/lang/String;IIZ)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->replace$default(Ljava/lang/String;CCZILjava/lang/Object;)Ljava/lang/String; HSPLkotlin/text/StringsKt__StringsJVMKt;->replace$default(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Ljava/lang/String; @@ -17189,8 +17193,6 @@ HSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence; HSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt__StringsKt;->contains(Ljava/lang/CharSequence;CZ)Z HSPLkotlin/text/StringsKt__StringsKt;->contains(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z -HSPLkotlin/text/StringsKt__StringsKt;->findAnyOf$StringsKt__StringsKt(Ljava/lang/CharSequence;Ljava/util/Collection;IZZ)Lkotlin/Pair; -HSPLkotlin/text/StringsKt__StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange; HSPLkotlin/text/StringsKt__StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I HSPLkotlin/text/StringsKt__StringsKt;->indexOf$default(Ljava/lang/CharSequence;CIZILjava/lang/Object;)I HSPLkotlin/text/StringsKt__StringsKt;->indexOf$default(Ljava/lang/CharSequence;Ljava/lang/String;IZILjava/lang/Object;)I @@ -17347,7 +17349,6 @@ HSPLnet/zetetic/database/sqlcipher/CloseGuard;->get()Lnet/zetetic/database/sqlci HSPLnet/zetetic/database/sqlcipher/CloseGuard;->open(Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteClosable;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteClosable;->acquireReference()V -HSPLnet/zetetic/database/sqlcipher/SQLiteClosable;->releaseReference()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection$Operation;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection$Operation;->(Lnet/zetetic/database/sqlcipher/SQLiteConnection$1;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection$OperationLog;->()V @@ -17373,9 +17374,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->attachCancellationSignal(L HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->bindArguments(Lnet/zetetic/database/sqlcipher/SQLiteConnection$PreparedStatement;[Ljava/lang/Object;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->canonicalizeSyncMode(Ljava/lang/String;)Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->detachCancellationSignal(Landroid/os/CancellationSignal;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->execute(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForChangedRowCount(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)I -HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForLastInsertedRowId(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)J HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForLong(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)J HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForString(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->finalizePreparedStatement(Lnet/zetetic/database/sqlcipher/SQLiteConnection$PreparedStatement;)V @@ -17407,7 +17405,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->acquireConnection(Ljava/lang/String;ILandroid/os/CancellationSignal;)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->closeExcessConnectionsAndLogExceptionsLocked()V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->finishAcquireConnectionLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnection;I)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->getPriority(I)I HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->markAcquiredConnectionsLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$AcquiredConnectionStatus;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->obtainConnectionWaiterLocked(Ljava/lang/Thread;JIZLjava/lang/String;I)Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$ConnectionWaiter; @@ -17423,18 +17420,17 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->setMaxConnectionPoolSi HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->throwIfClosedLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->tryAcquirePrimaryConnectionLocked(I)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->waitForConnection(Ljava/lang/String;ILandroid/os/CancellationSignal;)Lnet/zetetic/database/sqlcipher/SQLiteConnection; -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->wakeConnectionWaitersLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor$$ExternalSyntheticApiModelOutline0;->m(Ljava/lang/String;J)Landroid/database/CursorWindow; HSPLnet/zetetic/database/sqlcipher/SQLiteCursor$$ExternalSyntheticApiModelOutline1;->m()V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->()V -HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->(Lnet/zetetic/database/sqlcipher/SQLiteCursorDriver;Ljava/lang/String;Lnet/zetetic/database/sqlcipher/SQLiteQuery;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->awc_clearOrCreateWindow(Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->close()V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->finalize()V HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getColumnIndex(Ljava/lang/String;)I HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getColumnNames()[Ljava/lang/String; +HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getCount()I HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->getDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->onMove(II)Z +HSPLnet/zetetic/database/sqlcipher/SQLiteCursor;->setWindow(Landroid/database/CursorWindow;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase$1;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase$1;->initialValue()Ljava/lang/Object; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase$1;->initialValue()Lnet/zetetic/database/sqlcipher/SQLiteSession; @@ -17456,10 +17452,8 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getPath()Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getThreadDefaultConnectionFlags(Z)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getVersion()I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->hasCodec()Z -HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->inTransaction()Z HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->insert(Ljava/lang/String;ILandroid/content/ContentValues;)J HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->insert(Ljava/lang/String;Ljava/lang/String;Landroid/content/ContentValues;)J -HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->insertWithOnConflict(Ljava/lang/String;Ljava/lang/String;Landroid/content/ContentValues;I)J HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->isMainThread()Z HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->isOpen()Z HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->isReadOnly()Z @@ -17484,6 +17478,7 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->setVersion(I)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->throwIfNotOpenLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->update(Ljava/lang/String;ILandroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/Object;)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->update(Ljava/lang/String;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I +HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->updateWithOnConflict(Ljava/lang/String;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;I)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->(Ljava/lang/String;I[BLnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)V @@ -17494,7 +17489,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDebug;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteDirectCursorDriver;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/lang/String;Ljava/lang/String;Landroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDirectCursorDriver;->cursorClosed()V HSPLnet/zetetic/database/sqlcipher/SQLiteDirectCursorDriver;->query(Lnet/zetetic/database/sqlcipher/SQLiteDatabase$CursorFactory;[Ljava/lang/Object;)Landroid/database/Cursor; -HSPLnet/zetetic/database/sqlcipher/SQLiteDirectCursorDriver;->query(Lnet/zetetic/database/sqlcipher/SQLiteDatabase$CursorFactory;[Ljava/lang/String;)Landroid/database/Cursor; HSPLnet/zetetic/database/sqlcipher/SQLiteGlobal;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteGlobal;->getJournalSizeLimit()I HSPLnet/zetetic/database/sqlcipher/SQLiteGlobal;->getWALAutoCheckpoint()I @@ -17504,12 +17498,10 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Lnet/zetetic/database/sqlcipher/SQLiteDatabase$CursorFactory;IILnet/zetetic/database/DatabaseErrorHandler;Lnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;Z)V HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->(Landroid/content/Context;Ljava/lang/String;[BLnet/zetetic/database/sqlcipher/SQLiteDatabase$CursorFactory;IILnet/zetetic/database/DatabaseErrorHandler;Lnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;Z)V HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getBytes(Ljava/lang/String;)[B -HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getDatabaseLocked(Z)Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getReadableDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->getWritableDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteOpenHelper;->onConfigure(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->()V -HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->bind(ILjava/lang/Object;)V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->bindAllArgs([Ljava/lang/Object;)V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->bindAllArgsAsStrings([Ljava/lang/String;)V @@ -17521,9 +17513,7 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getConnectionFlags()I HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSession()Lnet/zetetic/database/sqlcipher/SQLiteSession; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSql()Ljava/lang/String; -HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->onAllReferencesReleased()V HSPLnet/zetetic/database/sqlcipher/SQLiteQuery;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/lang/String;Landroid/os/CancellationSignal;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteQuery;->fillWindow(Landroid/database/CursorWindow;IIZ)I HSPLnet/zetetic/database/sqlcipher/SQLiteQueryBuilder;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteQueryBuilder;->appendClause(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteQueryBuilder;->appendColumns(Ljava/lang/StringBuilder;[Ljava/lang/String;)V @@ -17543,7 +17533,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->executeForLong(Ljava/lang/Str HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->executeSpecial(Ljava/lang/String;[Ljava/lang/Object;ILandroid/os/CancellationSignal;)Z HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->hasTransaction()Z HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->obtainTransaction(ILnet/zetetic/database/sqlcipher/SQLiteTransactionListener;)Lnet/zetetic/database/sqlcipher/SQLiteSession$Transaction; -HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->prepare(Ljava/lang/String;ILandroid/os/CancellationSignal;Lnet/zetetic/database/sqlcipher/SQLiteStatementInfo;)V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->recycleTransaction(Lnet/zetetic/database/sqlcipher/SQLiteSession$Transaction;)V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->releaseConnection()V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->setTransactionSuccessful()V @@ -17856,6 +17845,7 @@ HSPLokhttp3/Response;->isSuccessful()Z HSPLokhttp3/Response;->message()Ljava/lang/String; HSPLokhttp3/Response;->newBuilder()Lokhttp3/Response$Builder; HSPLokhttp3/Response;->request()Lokhttp3/Request; +HSPLokhttp3/Response;->toString()Ljava/lang/String; HSPLokhttp3/ResponseBody$1;->(Lokhttp3/MediaType;JLokio/BufferedSource;)V HSPLokhttp3/ResponseBody$1;->source()Lokio/BufferedSource; HSPLokhttp3/ResponseBody;->()V @@ -18124,7 +18114,6 @@ HSPLokio/Buffer;->clear()V HSPLokio/Buffer;->close()V HSPLokio/Buffer;->copyTo(Lokio/Buffer;JJ)Lokio/Buffer; HSPLokio/Buffer;->exhausted()Z -HSPLokio/Buffer;->getByte(J)B HSPLokio/Buffer;->indexOf(BJJ)J HSPLokio/Buffer;->indexOfElement(Lokio/ByteString;J)J HSPLokio/Buffer;->read(Lokio/Buffer;J)J @@ -18144,8 +18133,8 @@ HSPLokio/Buffer;->readString(Ljava/nio/charset/Charset;)Ljava/lang/String; HSPLokio/Buffer;->readUtf8(J)Ljava/lang/String; HSPLokio/Buffer;->setSize$okio(J)V HSPLokio/Buffer;->size()J +HSPLokio/Buffer;->skip(J)V HSPLokio/Buffer;->writableSegment$okio(I)Lokio/Segment; -HSPLokio/Buffer;->write(Lokio/Buffer;J)V HSPLokio/Buffer;->write([B)Lokio/Buffer; HSPLokio/Buffer;->write([BII)Lokio/Buffer; HSPLokio/Buffer;->writeAll(Lokio/Source;)J @@ -18209,7 +18198,6 @@ HSPLokio/InflaterSource;->refill()Z HSPLokio/InflaterSource;->releaseBytesAfterInflate()V HSPLokio/InputStreamSource;->(Ljava/io/InputStream;Lokio/Timeout;)V HSPLokio/InputStreamSource;->close()V -HSPLokio/InputStreamSource;->read(Lokio/Buffer;J)J HSPLokio/Okio;->blackhole()Lokio/Sink; HSPLokio/Okio;->buffer(Lokio/Sink;)Lokio/BufferedSink; HSPLokio/Okio;->buffer(Lokio/Source;)Lokio/BufferedSource; @@ -18240,7 +18228,6 @@ HSPLokio/Options;->of([Lokio/ByteString;)Lokio/Options; HSPLokio/OutputStreamSink;->(Ljava/io/OutputStream;Lokio/Timeout;)V HSPLokio/OutputStreamSink;->close()V HSPLokio/OutputStreamSink;->flush()V -HSPLokio/OutputStreamSink;->write(Lokio/Buffer;J)V HSPLokio/PeekSource;->(Lokio/BufferedSource;)V HSPLokio/PeekSource;->read(Lokio/Buffer;J)J HSPLokio/RealBufferedSink;->(Lokio/Sink;)V @@ -18282,7 +18269,6 @@ HSPLokio/Segment$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarke HSPLokio/Segment;->()V HSPLokio/Segment;->()V HSPLokio/Segment;->([BIIZZ)V -HSPLokio/Segment;->compact()V HSPLokio/Segment;->pop()Lokio/Segment; HSPLokio/Segment;->push(Lokio/Segment;)Lokio/Segment; HSPLokio/Segment;->sharedCopy()Lokio/Segment; @@ -18450,7 +18436,6 @@ HSPLorg/conscrypt/ConscryptEngine;->singleDstBuffer(Ljava/nio/ByteBuffer;)[Ljava HSPLorg/conscrypt/ConscryptEngine;->singleSrcBuffer(Ljava/nio/ByteBuffer;)[Ljava/nio/ByteBuffer; HSPLorg/conscrypt/ConscryptEngine;->transitionTo(I)V HSPLorg/conscrypt/ConscryptEngine;->unwrap(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult; -HSPLorg/conscrypt/ConscryptEngine;->unwrap([Ljava/nio/ByteBuffer;II[Ljava/nio/ByteBuffer;II)Ljavax/net/ssl/SSLEngineResult; HSPLorg/conscrypt/ConscryptEngine;->unwrap([Ljava/nio/ByteBuffer;[Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult; HSPLorg/conscrypt/ConscryptEngine;->verifyCertificateChain([[BLjava/lang/String;)V HSPLorg/conscrypt/ConscryptEngine;->wrap(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult; @@ -18469,7 +18454,6 @@ HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->(Lorg/conscrypt/C HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->access$100(Lorg/conscrypt/ConscryptEngineSocket$SSLInputStream;[BII)I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->init()V HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->isHandshaking(Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;)Z -HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->processDataFromSocket([BII)I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->read([BII)I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->readFromSocket()I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->readUntilDataAvailable([BII)I @@ -18577,7 +18561,6 @@ HSPLorg/conscrypt/NativeSsl$BioWrapper;->(Lorg/conscrypt/NativeSsl;)V HSPLorg/conscrypt/NativeSsl$BioWrapper;->(Lorg/conscrypt/NativeSsl;Lorg/conscrypt/NativeSsl$1;)V HSPLorg/conscrypt/NativeSsl$BioWrapper;->getPendingWrittenBytes()I HSPLorg/conscrypt/NativeSsl$BioWrapper;->readDirectByteBuffer(JI)I -HSPLorg/conscrypt/NativeSsl$BioWrapper;->writeDirectByteBuffer(JI)I HSPLorg/conscrypt/NativeSsl;->(JLorg/conscrypt/SSLParametersImpl;Lorg/conscrypt/NativeCrypto$SSLHandshakeCallbacks;Lorg/conscrypt/SSLParametersImpl$AliasChooser;Lorg/conscrypt/SSLParametersImpl$PSKCallbacks;)V HSPLorg/conscrypt/NativeSsl;->access$100(Lorg/conscrypt/NativeSsl;)J HSPLorg/conscrypt/NativeSsl;->access$200(Lorg/conscrypt/NativeSsl;)Ljava/util/concurrent/locks/ReadWriteLock; @@ -18743,6 +18726,7 @@ HSPLorg/conscrypt/OpenSSLX509CertPath;->getCertificates()Ljava/util/List; HSPLorg/conscrypt/OpenSSLX509Certificate;->(J)V HSPLorg/conscrypt/OpenSSLX509Certificate;->alternativeNameArrayToList([[Ljava/lang/Object;)Ljava/util/Collection; HSPLorg/conscrypt/OpenSSLX509Certificate;->checkValidity(Ljava/util/Date;)V +HSPLorg/conscrypt/OpenSSLX509Certificate;->equals(Ljava/lang/Object;)Z HSPLorg/conscrypt/OpenSSLX509Certificate;->finalize()V HSPLorg/conscrypt/OpenSSLX509Certificate;->fromX509DerInputStream(Ljava/io/InputStream;)Lorg/conscrypt/OpenSSLX509Certificate; HSPLorg/conscrypt/OpenSSLX509Certificate;->getCriticalExtensionOIDs()Ljava/util/Set; @@ -18987,6 +18971,7 @@ HSPLorg/signal/core/util/CursorExtensionsKt;->requireBlob(Landroid/database/Curs HSPLorg/signal/core/util/CursorExtensionsKt;->requireBoolean(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorExtensionsKt;->requireInt(Landroid/database/Cursor;Ljava/lang/String;)I HSPLorg/signal/core/util/CursorExtensionsKt;->requireLong(Landroid/database/Cursor;Ljava/lang/String;)J +HSPLorg/signal/core/util/CursorExtensionsKt;->requireNonNullString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/CursorExtensionsKt;->requireObject(Landroid/database/Cursor;Ljava/lang/String;Lorg/signal/core/util/IntSerializer;)Ljava/lang/Object; HSPLorg/signal/core/util/CursorExtensionsKt;->requireString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/CursorExtensionsKt;->toInt(Z)I @@ -18999,6 +18984,7 @@ HSPLorg/signal/core/util/CursorUtil;->isNull(Landroid/database/Cursor;Ljava/lang HSPLorg/signal/core/util/CursorUtil;->requireBlob(Landroid/database/Cursor;Ljava/lang/String;)[B HSPLorg/signal/core/util/CursorUtil;->requireBoolean(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorUtil;->requireInt(Landroid/database/Cursor;Ljava/lang/String;)I +HSPLorg/signal/core/util/CursorUtil;->requireLong(Landroid/database/Cursor;Ljava/lang/String;)J HSPLorg/signal/core/util/CursorUtil;->requireString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/DeleteBuilderPart1;->(Landroidx/sqlite/db/SupportSQLiteDatabase;Ljava/lang/String;)V HSPLorg/signal/core/util/DeleteBuilderPart1;->where(Ljava/lang/String;[Ljava/lang/Object;)Lorg/signal/core/util/DeleteBuilderPart2; @@ -19098,7 +19084,6 @@ HSPLorg/signal/core/util/SqlUtil;->()V HSPLorg/signal/core/util/SqlUtil;->access$buildSingleCustomCollectionQuery(Lorg/signal/core/util/SqlUtil;Ljava/lang/String;Ljava/util/List;)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/signal/core/util/SqlUtil;->appendArg([Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String; HSPLorg/signal/core/util/SqlUtil;->buildArgs(J)[Ljava/lang/String; -HSPLorg/signal/core/util/SqlUtil;->buildArgs([Ljava/lang/Object;)[Ljava/lang/String; HSPLorg/signal/core/util/SqlUtil;->buildCollectionQuery$default(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;ILorg/signal/core/util/SqlUtil$CollectionOperator;ILjava/lang/Object;)Ljava/util/List; HSPLorg/signal/core/util/SqlUtil;->buildCollectionQuery(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;ILorg/signal/core/util/SqlUtil$CollectionOperator;)Ljava/util/List; HSPLorg/signal/core/util/SqlUtil;->buildCustomCollectionQuery$lambda$11(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Lorg/signal/core/util/SqlUtil$Query; @@ -19207,6 +19192,7 @@ HSPLorg/signal/core/util/concurrent/SettableFuture;->notifyAllListeners()V HSPLorg/signal/core/util/concurrent/SettableFuture;->set(Ljava/lang/Object;)Z HSPLorg/signal/core/util/concurrent/SettableFuture;->setException(Ljava/lang/Throwable;)Z HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda0;->()V +HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda0;->rejectedExecution(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda1;->(Ljava/lang/String;I)V HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda1;->newThread(Ljava/lang/Runnable;)Ljava/lang/Thread; HSPLorg/signal/core/util/concurrent/SignalExecutors$1;->(Ljava/lang/Runnable;Ljava/lang/String;I)V @@ -19219,9 +19205,11 @@ HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory$1;->ru HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;->-$$Nest$fgetpriority(Lorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;)I HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;->(Ljava/lang/String;I)V HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;->newThread(Ljava/lang/Runnable;)Ljava/lang/Thread; +HSPLorg/signal/core/util/concurrent/SignalExecutors;->$r8$lambda$0Q0afsv1raKIrq3aP-SuMcT2Ad0(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V HSPLorg/signal/core/util/concurrent/SignalExecutors;->$r8$lambda$QcfzSx3VRxairlCydtFL9hAJp4M(Ljava/lang/String;ILjava/lang/Runnable;)Ljava/lang/Thread; HSPLorg/signal/core/util/concurrent/SignalExecutors;->()V HSPLorg/signal/core/util/concurrent/SignalExecutors;->getAndStartHandlerThread(Ljava/lang/String;I)Landroid/os/HandlerThread; +HSPLorg/signal/core/util/concurrent/SignalExecutors;->lambda$newCachedBoundedExecutor$1(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V HSPLorg/signal/core/util/concurrent/SignalExecutors;->lambda$newCachedSingleThreadExecutor$0(Ljava/lang/String;ILjava/lang/Runnable;)Ljava/lang/Thread; HSPLorg/signal/core/util/concurrent/SignalExecutors;->newCachedBoundedExecutor(Ljava/lang/String;IIII)Ljava/util/concurrent/ExecutorService; HSPLorg/signal/core/util/concurrent/SignalExecutors;->newCachedSingleThreadExecutor(Ljava/lang/String;I)Ljava/util/concurrent/ExecutorService; @@ -19312,6 +19300,7 @@ HSPLorg/signal/core/util/logging/Scrubber;->access$getTOP_100_TLDS$p()Ljava/util HSPLorg/signal/core/util/logging/Scrubber;->access$hash(Lorg/signal/core/util/logging/Scrubber;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/logging/Scrubber;->hash(Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/logging/Scrubber;->scrub(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; +HSPLorg/signal/core/util/logging/Scrubber;->scrub(Ljava/lang/CharSequence;Ljava/util/regex/Pattern;Lkotlin/jvm/functions/Function2;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubCallLinkKeys(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubDomains(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubE164(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; @@ -19326,7 +19315,6 @@ HSPLorg/signal/core/util/logging/Scrubber;->scrubUuids(Ljava/lang/CharSequence;) HSPLorg/signal/core/util/logging/Scrubber;->setIdentifierHmacKeyProvider(Lkotlin/jvm/functions/Function0;)V HSPLorg/signal/core/util/stream/TruncatingInputStream$$ExternalSyntheticBackport0;->m(J)I HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->()V -HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->build()Lorg/signal/core/util/tracing/DebugAnnotation; HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->name(Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation$Builder; HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->string_value(Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation$Builder; HSPLorg/signal/core/util/tracing/DebugAnnotation$Companion$ADAPTER$1;->(Lcom/squareup/wire/FieldEncoding;Lkotlin/reflect/KClass;Lcom/squareup/wire/Syntax;)V @@ -19335,9 +19323,7 @@ HSPLorg/signal/core/util/tracing/DebugAnnotation$Companion;->(Lkotlin/jvm/ HSPLorg/signal/core/util/tracing/DebugAnnotation;->()V HSPLorg/signal/core/util/tracing/DebugAnnotation;->(Ljava/lang/Long;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/Long;Lorg/signal/core/util/tracing/DebugAnnotation$NestedValue;Lokio/ByteString;)V HSPLorg/signal/core/util/tracing/DebugAnnotation;->equals(Ljava/lang/Object;)Z -HSPLorg/signal/core/util/tracing/TracePacket$Builder;->()V HSPLorg/signal/core/util/tracing/TracePacket$Builder;->synchronization_marker(Lokio/ByteString;)Lorg/signal/core/util/tracing/TracePacket$Builder; -HSPLorg/signal/core/util/tracing/TracePacket$Builder;->timestamp(Ljava/lang/Long;)Lorg/signal/core/util/tracing/TracePacket$Builder; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->track_descriptor(Lorg/signal/core/util/tracing/TrackDescriptor;)Lorg/signal/core/util/tracing/TracePacket$Builder; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->track_event(Lorg/signal/core/util/tracing/TrackEvent;)Lorg/signal/core/util/tracing/TracePacket$Builder; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->trusted_packet_sequence_id(Ljava/lang/Integer;)Lorg/signal/core/util/tracing/TracePacket$Builder; @@ -19351,14 +19337,14 @@ HSPLorg/signal/core/util/tracing/Tracer$$ExternalSyntheticLambda0;->getTimeNanos HSPLorg/signal/core/util/tracing/Tracer;->()V HSPLorg/signal/core/util/tracing/Tracer;->()V HSPLorg/signal/core/util/tracing/Tracer;->addPacket(Lorg/signal/core/util/tracing/TracePacket;)V -HSPLorg/signal/core/util/tracing/Tracer;->debugAnnotation(Ljava/lang/String;Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation; HSPLorg/signal/core/util/tracing/Tracer;->end(Ljava/lang/String;J)V -HSPLorg/signal/core/util/tracing/Tracer;->forMethodStart(Ljava/lang/String;JJLjava/util/Map;)Lorg/signal/core/util/tracing/TracePacket; +HSPLorg/signal/core/util/tracing/Tracer;->forMethodEnd(Ljava/lang/String;JJ)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forSynchronization(J)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forTrack(JLjava/lang/String;)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forTrackId(J)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->getInstance()Lorg/signal/core/util/tracing/Tracer; HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;)V +HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;Ljava/util/Map;)V HSPLorg/signal/core/util/tracing/Tracer;->toByteArray(Ljava/util/UUID;)[B @@ -19398,6 +19384,8 @@ HSPLorg/signal/libsignal/internal/FilterExceptions;->filterExceptions(Lorg/signa HSPLorg/signal/libsignal/internal/FilterExceptions;->filterExceptions(Lorg/signal/libsignal/internal/FilterExceptions$ThrowingNativeOperation;)Ljava/lang/Object; HSPLorg/signal/libsignal/internal/Native;->()V HSPLorg/signal/libsignal/internal/Native;->loadLibrary()V +HSPLorg/signal/libsignal/internal/NativeHandleGuard$SimpleOwner;->(J)V +HSPLorg/signal/libsignal/internal/NativeHandleGuard$SimpleOwner;->finalize()V HSPLorg/signal/libsignal/internal/NativeHandleGuard;->(Lorg/signal/libsignal/internal/NativeHandleGuard$Owner;)V HSPLorg/signal/libsignal/internal/NativeHandleGuard;->close()V HSPLorg/signal/libsignal/internal/NativeHandleGuard;->nativeHandle()J @@ -19549,10 +19537,11 @@ HSPLorg/signal/libsignal/protocol/util/Pair;->(Ljava/lang/Object;Ljava/lan HSPLorg/signal/libsignal/protocol/util/Pair;->first()Ljava/lang/Object; HSPLorg/signal/libsignal/protocol/util/Pair;->second()Ljava/lang/Object; HSPLorg/signal/libsignal/zkgroup/ServerPublicParams$$ExternalSyntheticLambda0;->([B)V -HSPLorg/signal/libsignal/zkgroup/ServerPublicParams$$ExternalSyntheticLambda0;->run()V -HSPLorg/signal/libsignal/zkgroup/ServerPublicParams;->$r8$lambda$R936UR66SIUnVcbjr9dIro_sBME([B)V +HSPLorg/signal/libsignal/zkgroup/ServerPublicParams$$ExternalSyntheticLambda0;->run()J +HSPLorg/signal/libsignal/zkgroup/ServerPublicParams;->$r8$lambda$seZa-GNqGGj1AGfCKLVPf3_WvAQ([B)J HSPLorg/signal/libsignal/zkgroup/ServerPublicParams;->([B)V -HSPLorg/signal/libsignal/zkgroup/ServerPublicParams;->lambda$new$0([B)V +HSPLorg/signal/libsignal/zkgroup/ServerPublicParams;->lambda$new$0([B)J +HSPLorg/signal/libsignal/zkgroup/ServerPublicParams;->release(J)V HSPLorg/signal/libsignal/zkgroup/auth/ClientZkAuthOperations;->(Lorg/signal/libsignal/zkgroup/ServerPublicParams;)V HSPLorg/signal/libsignal/zkgroup/internal/ByteArray$UncheckedAndUncloned;->$values()[Lorg/signal/libsignal/zkgroup/internal/ByteArray$UncheckedAndUncloned; HSPLorg/signal/libsignal/zkgroup/internal/ByteArray$UncheckedAndUncloned;->()V @@ -19576,14 +19565,19 @@ HSPLorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion;->serialize()Ljava/l HSPLorg/signal/libsignal/zkgroup/receipts/ClientZkReceiptOperations;->(Lorg/signal/libsignal/zkgroup/ServerPublicParams;)V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda1;->(Lorg/signal/paging/BufferedPagingController;I)V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda1;->run()V +HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V +HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->run()V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda3;->(Lorg/signal/paging/BufferedPagingController;)V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda3;->run()V +HSPLorg/signal/paging/BufferedPagingController;->$r8$lambda$GxlLAxjfERBgyqmyvxteAPWaQkA(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V HSPLorg/signal/paging/BufferedPagingController;->$r8$lambda$LZo0__w5_DH31X-e0IJhSqYlhmM(Lorg/signal/paging/BufferedPagingController;I)V HSPLorg/signal/paging/BufferedPagingController;->$r8$lambda$rEsvvMe_LeaFyq_h5sQ9KWDvIFs(Lorg/signal/paging/BufferedPagingController;)V HSPLorg/signal/paging/BufferedPagingController;->(Lorg/signal/paging/PagedDataSource;Lorg/signal/paging/PagingConfig;Lorg/signal/paging/DataStream;)V HSPLorg/signal/paging/BufferedPagingController;->lambda$onDataInvalidated$1()V +HSPLorg/signal/paging/BufferedPagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V HSPLorg/signal/paging/BufferedPagingController;->lambda$onDataNeededAroundIndex$0(I)V HSPLorg/signal/paging/BufferedPagingController;->onDataInvalidated()V +HSPLorg/signal/paging/BufferedPagingController;->onDataItemChanged(Ljava/lang/Object;)V HSPLorg/signal/paging/BufferedPagingController;->onDataNeededAroundIndex(I)V HSPLorg/signal/paging/CompressedList;->(I)V HSPLorg/signal/paging/CompressedList;->(Ljava/util/List;)V @@ -19594,22 +19588,27 @@ HSPLorg/signal/paging/DataStatus;->()V HSPLorg/signal/paging/DataStatus;->(ILjava/util/BitSet;)V HSPLorg/signal/paging/DataStatus;->getEarliestUnmarkedIndexInRange(II)I HSPLorg/signal/paging/DataStatus;->getLatestUnmarkedIndexInRange(II)I +HSPLorg/signal/paging/DataStatus;->mark(I)V HSPLorg/signal/paging/DataStatus;->markRange(II)V HSPLorg/signal/paging/DataStatus;->obtain(I)Lorg/signal/paging/DataStatus; HSPLorg/signal/paging/DataStatus;->recycle()V HSPLorg/signal/paging/DataStatus;->size()I +HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V +HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->run()V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda2;->(Lorg/signal/paging/FixedSizePagingController;IIII)V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda2;->run()V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda3;->(Lorg/signal/paging/FixedSizePagingController;)V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda3;->isCanceled()Z +HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$2jZFFAhs3dG0IThMmzJQSvWvcd0(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$S1N9oMReIFywjAgkTfXskOOFzyk(Lorg/signal/paging/FixedSizePagingController;)Z HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$pQWvrV6w7QQq3SnkCgnHNDTtP_I(Lorg/signal/paging/FixedSizePagingController;IIII)V HSPLorg/signal/paging/FixedSizePagingController;->()V HSPLorg/signal/paging/FixedSizePagingController;->(Lorg/signal/paging/PagedDataSource;Lorg/signal/paging/PagingConfig;Lorg/signal/paging/DataStream;I)V -HSPLorg/signal/paging/FixedSizePagingController;->buildDataNeededLog(ILjava/lang/String;)Ljava/lang/String; +HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataNeededAroundIndex$0()Z HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataNeededAroundIndex$1(IIII)V HSPLorg/signal/paging/FixedSizePagingController;->onDataInvalidated()V +HSPLorg/signal/paging/FixedSizePagingController;->onDataItemChanged(Ljava/lang/Object;)V HSPLorg/signal/paging/FixedSizePagingController;->onDataNeededAroundIndex(I)V HSPLorg/signal/paging/LivePagedData;->(Landroidx/lifecycle/LiveData;Lorg/signal/paging/PagingController;)V HSPLorg/signal/paging/LivePagedData;->getData()Landroidx/lifecycle/LiveData; @@ -19638,6 +19637,7 @@ HSPLorg/signal/paging/PagingConfig;->pageSize()I HSPLorg/signal/paging/PagingConfig;->startIndex()I HSPLorg/signal/paging/ProxyPagingController;->()V HSPLorg/signal/paging/ProxyPagingController;->onDataInvalidated()V +HSPLorg/signal/paging/ProxyPagingController;->onDataItemChanged(Ljava/lang/Object;)V HSPLorg/signal/paging/ProxyPagingController;->onDataNeededAroundIndex(I)V HSPLorg/signal/paging/ProxyPagingController;->set(Lorg/signal/paging/PagingController;)V HSPLorg/signal/ringrtc/BuildInfo;->()V @@ -20197,13 +20197,13 @@ HSPLorg/thoughtcrime/securesms/components/ComposeText$CommitContentListener;->(IZ)V HSPLorg/thoughtcrime/securesms/components/ComposeText;->()V HSPLorg/thoughtcrime/securesms/components/ComposeText;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/ComposeText;->canFilter(Landroid/text/Editable;)Z HSPLorg/thoughtcrime/securesms/components/ComposeText;->changeSelectionForPartialMentions(Landroid/text/Spanned;II)Z HSPLorg/thoughtcrime/securesms/components/ComposeText;->clearInlineQuery()V HSPLorg/thoughtcrime/securesms/components/ComposeText;->doAfterCursorChange(Landroid/text/Editable;)V HSPLorg/thoughtcrime/securesms/components/ComposeText;->ellipsizeToWidth(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; -HSPLorg/thoughtcrime/securesms/components/ComposeText;->enoughToFilter(Landroid/text/Editable;Z)Z +HSPLorg/thoughtcrime/securesms/components/ComposeText;->findQueryStart(Ljava/lang/CharSequence;I)Lorg/thoughtcrime/securesms/components/ComposeText$QueryStart; HSPLorg/thoughtcrime/securesms/components/ComposeText;->findQueryStart(Ljava/lang/CharSequence;IC)I -HSPLorg/thoughtcrime/securesms/components/ComposeText;->findQueryStart(Ljava/lang/CharSequence;IZ)Lorg/thoughtcrime/securesms/components/ComposeText$QueryStart; HSPLorg/thoughtcrime/securesms/components/ComposeText;->initialize()V HSPLorg/thoughtcrime/securesms/components/ComposeText;->isLandscape()Z HSPLorg/thoughtcrime/securesms/components/ComposeText;->onCreateInputConnection(Landroid/view/inputmethod/EditorInfo;)Landroid/view/inputmethod/InputConnection; @@ -21159,6 +21159,9 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$1;->(Lorg/tho HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$ClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$DoubleTapEditTouchListener$1;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem$DoubleTapEditTouchListener;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$DoubleTapEditTouchListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$DoubleTapEditTouchListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$DoubleTapEditTouchListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$GiftMessageViewCallback;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$GiftMessageViewCallback;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$GiftMessageViewCallback-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$LinkPreviewClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V @@ -21185,6 +21188,7 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$UrlClickListener;-> HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$UrlClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$UrlClickListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$ViewOnceMessageClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$ViewOnceMessageClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$ViewOnceMessageClickListener-IA;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->-$$Nest$fgetcontext(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)Landroid/content/Context; HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->()V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->bind(Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/conversation/ConversationMessage;Lj$/util/Optional;Lj$/util/Optional;Lcom/bumptech/glide/RequestManager;Ljava/util/Locale;Ljava/util/Set;Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/String;ZZZZLorg/thoughtcrime/securesms/conversation/colors/Colorizer;Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode;)V @@ -21512,6 +21516,7 @@ HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->findAdapterBridge(Landroidx/recyclerview/widget/RecyclerView;)Lorg/thoughtcrime/securesms/conversation/ConversationAdapterBridge; HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->getCurrentSelection(Landroidx/recyclerview/widget/RecyclerView;)Ljava/util/Set; HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->getDifferenceForPart(Ljava/util/Set;Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectPart;)Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration$Difference; +HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->getItemOffsets(Landroid/graphics/Rect;Landroid/view/View;Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$State;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->hasRunningPulseRequestAnimators()Z HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->invalidateIfEnterExitAnimatorsAreRunning(Landroidx/recyclerview/widget/RecyclerView;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->invalidateIfPulseRequestAnimatorsAreRunning(Landroidx/recyclerview/widget/RecyclerView;)V @@ -22053,6 +22058,8 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->( HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$8;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$8;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$8;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @@ -22114,6 +22121,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->(Lor HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/database/model/GroupRecord;ZLorg/thoughtcrime/securesms/database/identity/IdentityRecordList;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/database/model/GroupRecord;ZZZ)V +HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->getConversationRecipient()Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->getMessageRequestState()Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState; HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->isActiveGroup()Ljava/lang/Boolean; @@ -22139,9 +22147,15 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->get HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->getSizeInternal()I HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->getThreadRecipient()Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(IIILorg/signal/paging/PagedDataSource$CancellationSignal;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->loadThreadHeader()Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->size()I HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->toMappingModel(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel; +HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->getThreadHeader()Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey; +HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)V HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->getConversationMessage()Lorg/thoughtcrime/securesms/conversation/ConversationMessage; @@ -22222,12 +22236,8 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewM HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$1;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Lkotlin/Unit;Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState; HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$filteredState$1;->()V @@ -22342,8 +22352,9 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyV HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/conversation/ConversationAdapter$ItemClickListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda5;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda8;->(Lorg/thoughtcrime/securesms/conversation/ConversationAdapter$ItemClickListener;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$PassthroughClickListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V @@ -22354,6 +22365,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyV HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->invoke()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1;->invoke()Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$ChatColorsData; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$gestureDetector$1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$replyDelegate$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$senderDrawable$1;->(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->$r8$lambda$ocilDMoff9b132TfYhzB6ol1qqk(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;Landroid/view/View;IIIIIIII)V @@ -22829,6 +22841,7 @@ HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type;->(Ljava/lang/String;I)V HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type;->values()[Lorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type; HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;)V +HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->getThreadRecord()Lorg/thoughtcrime/securesms/database/model/ThreadRecord; HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->getType()Lorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type; HSPLorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter;->$values()[Lorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter; @@ -23005,6 +23018,7 @@ HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertUndownloadedAttach HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertUndownloadedAttachment$attachmentId$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)Lorg/thoughtcrime/securesms/attachments/AttachmentId; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->()V HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;Lorg/thoughtcrime/securesms/crypto/AttachmentSecret;)V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->containsStickerPackId(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->deleteAbandonedPreuploadedAttachments()I HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachment(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachment(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; @@ -23072,6 +23086,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambd HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda11;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17;->run()V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda22;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -23086,6 +23102,10 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambd HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda29;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30;->run()V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32;->run()V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda40;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Ljava/lang/Runnable;)V @@ -23094,6 +23114,7 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambd HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda4;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda5;->run()V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$6H_TtixOHSa7Tr30medlqcHry2c(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$6mdIgDDCV4XFVFnyxH8Vj4a6MqU(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Aq7iz6-OcN5qdEpvMz8WyoOoHtc(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$FLqOSncPM9UHAHmQfH7ITyYgYis(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -23102,9 +23123,11 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Q9T3e0x03- HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Rd4ts_QPgl4yKrcyZyl-dxASD8g(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$XcpL0fyOGdTr1sc4d0z4i8eoe14(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$XpAe1b_YlxfSEkV3hD_v20iDkHw(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$dh6RWMfCAixhY74q-duAcBwIwmU(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$mv9tymw4eNQuLtAMo52Pot0i2c4(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$nM9Xevlg3i5jd4hhWqCSJ8V0APs(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$oXFDlhvhHFY1OBIQHYp3Oanmq-k(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$v9-I7k7VKIptUuQHIpRZcaVjlwY(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$wnm9BEANNc03FZmWKcqOLSgrT_U(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$wtkgxGON7fTcqqEso3BleXuYIA8(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$zacSulZCbj18KAJ4fsL5guxghT4(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -23113,6 +23136,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyAttachme HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyConversationListListeners$22()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyConversationListeners$19(J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyRecipientChanged$34(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStickerObservers$26()V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStickerPackObservers$27()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStoryObservers$35(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerAttachmentObserver$9(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerConversationListObserver$0(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -23123,12 +23148,15 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerNotifi HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerStoryObserver$13(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerVerboseConversationObserver$2(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$runPostSuccessfulTransaction$40(Ljava/lang/Runnable;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$unregisterObserver$17(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyAttachmentObservers()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyConversationListListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyConversationListeners(J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyMapped(Ljava/util/Map;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyRecipientChanged(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifySet(Ljava/util/Set;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStickerObservers()V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStickerPackObservers()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStoryObservers(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerAttachmentObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerConversationListObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -23140,6 +23168,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerNotificationP HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerStoryObserver(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerVerboseConversationObserver(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->runPostSuccessfulTransaction(Ljava/lang/String;Ljava/lang/Runnable;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterMapped(Ljava/util/Map;Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->getReadableDatabase()Lorg/thoughtcrime/securesms/database/SQLiteDatabase; @@ -23147,6 +23177,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->getWritableDatabase()Lor HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyAttachmentListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyConversationListListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyConversationListeners(J)V +HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyStickerListeners()V +HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyStickerPackListeners()V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$Companion;->()V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$ListTable;->()V @@ -23235,6 +23267,9 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase$deleteJobs$1;->invoke(Lnet/z HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->(Ljava/util/List;Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->invoke(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V +HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->(Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->invoke(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->$r8$lambda$ou_p531IVGikC2LNueT6qnVrWyc(Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->()V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->(Landroid/app/Application;Lorg/thoughtcrime/securesms/crypto/DatabaseSecret;)V @@ -23243,6 +23278,7 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertConstraintSpe HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertDependencySpecs(Lorg/thoughtcrime/securesms/database/JobDatabase;Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertJobSpec(Lorg/thoughtcrime/securesms/database/JobDatabase;Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$setInstance$cp(Lorg/thoughtcrime/securesms/database/JobDatabase;)V +HSPLorg/thoughtcrime/securesms/database/JobDatabase;->constraintSpecFromCursor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec; HSPLorg/thoughtcrime/securesms/database/JobDatabase;->deleteJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->dropTableIfPresent(Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->getAllConstraintSpecs()Ljava/util/List; @@ -23253,11 +23289,13 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertConstraintSpecs(Lnet HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertDependencySpecs(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertJobSpec(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertJobs(Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/database/JobDatabase;->jobSpecFromCursor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/database/JobDatabase;->markJobAsRunning(Ljava/lang/String;J)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->onOpen$lambda$0(Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->onOpen(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateAllJobsToBePending()V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateJobAfterRetry(Ljava/lang/String;JIJ[B)V +HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0;->run()V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$1;->()V @@ -23782,6 +23820,7 @@ HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getExtras(Lan HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getRecipientExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/databaseprotos/RecipientExtras; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getRecord(Landroid/content/Context;Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getSyncExtras$lambda$6(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/Boolean; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getSyncExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord$SyncExtras; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->parseBadgeList([B)Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->readCapabilities(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities; HSPLorg/thoughtcrime/securesms/database/RemappedRecordTables$Companion;->()V @@ -23797,6 +23836,7 @@ HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLam HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda0;->subscribe(Lio/reactivex/rxjava3/core/FlowableEmitter;)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->(Lio/reactivex/rxjava3/core/Emitter;)V +HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->onChanged()V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->prime()V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$conversation$1;->(J)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$conversation$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -24052,9 +24092,17 @@ HSPLorg/thoughtcrime/securesms/database/SqlCipherErrorHandler;->(Ljava/lan HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->()V HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->()V HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->load()V +HSPLorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader;->(Landroid/database/Cursor;)V +HSPLorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader;->getNext()Lorg/thoughtcrime/securesms/database/model/StickerPackRecord; HSPLorg/thoughtcrime/securesms/database/StickerTable;->()V HSPLorg/thoughtcrime/securesms/database/StickerTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;Lorg/thoughtcrime/securesms/crypto/AttachmentSecret;)V +HSPLorg/thoughtcrime/securesms/database/StickerTable;->deleteOrphanedPacks()V +HSPLorg/thoughtcrime/securesms/database/StickerTable;->deleteStickersInPackExceptCover(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/StickerTable;->getAllStickerPacks(Ljava/lang/String;)Landroid/database/Cursor; +HSPLorg/thoughtcrime/securesms/database/StickerTable;->getStickerPack(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/model/StickerPackRecord; +HSPLorg/thoughtcrime/securesms/database/StickerTable;->isPackAvailableAsReference(Ljava/lang/String;)Z +HSPLorg/thoughtcrime/securesms/database/StickerTable;->uninstallPack(Ljava/lang/String;)V +HSPLorg/thoughtcrime/securesms/database/StickerTable;->updatePackInstalled(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;Ljava/lang/String;ZZ)V HSPLorg/thoughtcrime/securesms/database/StorySendTable$Companion;->()V HSPLorg/thoughtcrime/securesms/database/StorySendTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/StorySendTable;->()V @@ -24117,6 +24165,7 @@ HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1$isPinned$2;-> HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1$shouldDelete$2;->(ZJLkotlin/Lazy;)V HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1;->(JLorg/thoughtcrime/securesms/database/ThreadTable;ZZZ)V HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)Ljava/lang/Boolean; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->()V HSPLorg/thoughtcrime/securesms/database/ThreadTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/ThreadTable;->access$createThreadForRecipient(Lorg/thoughtcrime/securesms/database/ThreadTable;Lorg/thoughtcrime/securesms/recipients/RecipientId;ZI)J @@ -24363,6 +24412,7 @@ HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails$Companion HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->()V HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->(JJ)V +HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->getDiskCacheKeyBytes()[B HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->hashCode()I HSPLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->toString()Ljava/lang/String; @@ -24441,10 +24491,13 @@ HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$External HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda1;->subscribe(Lio/reactivex/rxjava3/core/ObservableEmitter;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda2;->(Lio/reactivex/rxjava3/core/ObservableEmitter;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda3;->cancel()V +HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->$r8$lambda$_YM1i9V93JIKhbRirbAeb_98VJw(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->$r8$lambda$qlVsO3gJogFXwiR82wyUg4D6NdU(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lio/reactivex/rxjava3/core/ObservableEmitter;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->()V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getForRecipientId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lio/reactivex/rxjava3/core/Observable; +HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3$lambda$2(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3$refresh(Lio/reactivex/rxjava3/core/ObservableEmitter;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lio/reactivex/rxjava3/core/ObservableEmitter;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lio/reactivex/rxjava3/core/Observable; @@ -24497,6 +24550,7 @@ HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setUnreadCo HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setUnreadSelfMentionsCount(I)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)V HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;Lorg/thoughtcrime/securesms/database/model/ThreadRecord-IA;)V +HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getBody()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getBodyRanges()Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getDate()J @@ -24547,6 +24601,7 @@ HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBin HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->getRoot()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0;->get()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda1;->()V HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->$r8$lambda$TTNxYGZvGlMOp1oidmVJeKiRxZs()Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->()V HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->closeConnections()V @@ -24588,7 +24643,7 @@ HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$$Exter HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$$ExternalSyntheticLambda0;->get()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$$ExternalSyntheticLambda1;->()V HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$$ExternalSyntheticLambda1;->get()Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$1;->(Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;Lj$/util/function/Supplier;Lorg/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor;)V +HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$1;->(Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;Lj$/util/function/Supplier;Lorg/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor;Lj$/util/function/Supplier;)V HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$1;->createUnidentifiedWebSocket()Lorg/whispersystems/signalservice/internal/websocket/WebSocketConnection; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$1;->createWebSocket()Lorg/whispersystems/signalservice/internal/websocket/WebSocketConnection; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$DynamicCredentialsProvider;->()V @@ -24627,10 +24682,10 @@ HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->prov HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalServiceAccountManager(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations;)Lorg/whispersystems/signalservice/api/SignalServiceAccountManager; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalServiceMessageReceiver(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;)Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalServiceNetworkAccess()Lorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess; -HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalWebSocket(Lj$/util/function/Supplier;)Lorg/whispersystems/signalservice/api/SignalWebSocket; +HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalWebSocket(Lj$/util/function/Supplier;Lj$/util/function/Supplier;)Lorg/whispersystems/signalservice/api/SignalWebSocket; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideTypingStatusRepository()Lorg/thoughtcrime/securesms/components/TypingStatusRepository; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideViewOnceMessageManager()Lorg/thoughtcrime/securesms/revealable/ViewOnceMessageManager; -HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideWebSocketFactory(Lj$/util/function/Supplier;Lorg/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor;)Lorg/whispersystems/signalservice/api/websocket/WebSocketFactory; +HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideWebSocketFactory(Lj$/util/function/Supplier;Lorg/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor;Lj$/util/function/Supplier;)Lorg/whispersystems/signalservice/api/websocket/WebSocketFactory; HSPLorg/thoughtcrime/securesms/emoji/EmojiCategory$Companion;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiCategory$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiCategory$Companion;->forKey(Ljava/lang/String;)Lorg/thoughtcrime/securesms/emoji/EmojiCategory; @@ -24750,7 +24805,6 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->access$createPage(Lorg/thoughtcrime/securesms/emoji/EmojiJsonParser;Ljava/lang/String;Ljava/lang/String;Lcom/fasterxml/jackson/databind/JsonNode;Lkotlin/jvm/functions/Function2;)Lorg/thoughtcrime/securesms/components/emoji/EmojiPageModel; HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->buildEmojiSourceFromNode(Lcom/fasterxml/jackson/databind/JsonNode;Lkotlin/jvm/functions/Function2;)Lorg/thoughtcrime/securesms/emoji/ParsedEmojiData; -HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->createPage(Ljava/lang/String;Ljava/lang/String;Lcom/fasterxml/jackson/databind/JsonNode;Lkotlin/jvm/functions/Function2;)Lorg/thoughtcrime/securesms/components/emoji/EmojiPageModel; HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->getDataPages$lambda$0(Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->getDataPages(Ljava/lang/String;Lcom/fasterxml/jackson/databind/JsonNode;Lkotlin/jvm/functions/Function2;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/emoji/EmojiJsonParser;->getJumboPages(Lcom/fasterxml/jackson/databind/JsonNode;)Ljava/util/Map; @@ -24896,7 +24950,6 @@ HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->()V HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->(Ljava/util/Map;)V HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->access$getObjectMapper$cp()Lcom/fasterxml/jackson/databind/ObjectMapper; HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->access$getTAG$cp()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->copy(Ljava/util/Map;)Lorg/thoughtcrime/securesms/fonts/FontFileMap; HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->getMap()Ljava/util/Map; HSPLorg/thoughtcrime/securesms/fonts/FontManifest$Companion;->()V HSPLorg/thoughtcrime/securesms/fonts/FontManifest$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -25192,8 +25245,6 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda20;->test(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda22;->(Lorg/thoughtcrime/securesms/jobmanager/JobPredicate;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda22;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda23;->(Lorg/thoughtcrime/securesms/jobmanager/JobController$Callback;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda23;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/Job;)V @@ -25245,8 +25296,6 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda10; HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda10;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda11;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda11;->run()V -HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda16;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda16;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda17;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda17;->run()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda19;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Ljava/lang/Runnable;)V @@ -25258,7 +25307,6 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda6;- HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda8;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda8;->shouldRunOnExecutor()Z HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda9;->onEmpty()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;->enqueue()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;->getJobListChain()Ljava/util/List; @@ -25287,9 +25335,7 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$88wGNi-R9qqqYZ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$B0N_1IwVaEFBHupl4Ii7Rtasq5s(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$LRMTzBAnZzMhc-JGu7V6yfyQyoc(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$EmptyQueueListener;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$XT2SMZJxrQFMSviwHBjZhWrZ8u4(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Configuration;Landroid/app/Application;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$f8CXlOogPUV2rL47HPh17prqg70(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$n2_WnBTSP7W-v9B5XsgvQbvhaaA(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->$r8$lambda$sAhOIfgF3Tsjgqk6ejbaAJGNqJA(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->-$$Nest$menqueueChain(Lorg/thoughtcrime/securesms/jobmanager/JobManager;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->(Landroid/app/Application;Lorg/thoughtcrime/securesms/jobmanager/JobManager$Configuration;)V @@ -25301,10 +25347,8 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$addOnEmptyQueueLis HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$beginJobLoop$1()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$enqueueChain$13(Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$new$0(Lorg/thoughtcrime/securesms/jobmanager/JobManager$Configuration;Landroid/app/Application;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$onEmptyQueue$14()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->lambda$runOnExecutor$15(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->onConstraintMet(Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->onEmptyQueue()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->runOnExecutor(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->startChain(Lorg/thoughtcrime/securesms/jobmanager/Job;)Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain; HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->waitUntilInitialized()V @@ -25993,6 +26037,14 @@ HSPLorg/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StickerDownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob; +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->()V +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Ljava/lang/String;Ljava/lang/String;ZZ)V +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Ljava/lang/String;Ljava/lang/String;ZZLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob-IA;)V +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onFailure()V +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onRun()V +HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onShouldRetry(Ljava/lang/Exception;)Z HSPLorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob;->()V HSPLorg/thoughtcrime/securesms/jobs/StorageForcePushJob$Factory;->()V @@ -26490,10 +26542,8 @@ HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->()V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->(Landroid/app/Application;)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->d(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->flush()V -HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->getThreadString()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->w(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V -HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->write(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda0;->run()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/main/MainActivityListHostFragment;)V @@ -26764,6 +26814,7 @@ HSPLorg/thoughtcrime/securesms/messagerequests/MessageRequestState$State;->()V HSPLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->(Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState$State;Z)V HSPLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->(Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState$State;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->isAccepted()Z HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/messages/IncomingMessageObserver;)V HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver$$ExternalSyntheticLambda0;->run()V @@ -27170,6 +27221,7 @@ HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->$r8$lambda$pNHvm3E5R2_hKbt HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->()V HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->(Ljava/lang/String;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->asGiven(Ljava/lang/String;)Lorg/thoughtcrime/securesms/profiles/ProfileName; +HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->fromParts(Ljava/lang/String;Ljava/lang/String;)Lorg/thoughtcrime/securesms/profiles/ProfileName; HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->getFamilyName()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->getGivenName()Ljava/lang/String; @@ -27270,7 +27322,6 @@ HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess$HostConfig;->getC HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess$HostConfig;->getHost()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess$WhenMappings;->()V HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->()V -HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->buildGConfiguration(Ljava/util/List;)Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->getConfiguration()Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->getConfiguration(Ljava/lang/String;)Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; HSPLorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess;->getUncensoredConfiguration()Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; @@ -27288,6 +27339,7 @@ HSPLorg/thoughtcrime/securesms/reactions/ReactionsConversationView;->init(Landro HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->()V +HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->contentsMatch(Ljava/lang/Object;Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4;->()V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda5;->()V @@ -27302,7 +27354,6 @@ HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$new$2(Lorg/thou HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->observable()Lio/reactivex/rxjava3/core/Observable; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->observeForever(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->refresh()Lorg/thoughtcrime/securesms/recipients/LiveRecipient; -HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->refresh(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->resolve()Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->set(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipientCache$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;)V @@ -27372,7 +27423,6 @@ HSPLorg/thoughtcrime/securesms/recipients/Recipient$messageRingtone$2;->(L HSPLorg/thoughtcrime/securesms/recipients/Recipient$shouldHideStory$1;->()V HSPLorg/thoughtcrime/securesms/recipients/Recipient$shouldHideStory$1;->()V HSPLorg/thoughtcrime/securesms/recipients/Recipient;->()V -HSPLorg/thoughtcrime/securesms/recipients/Recipient;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZLorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/util/List;Lj$/util/Optional;ZZZJLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Landroid/net/Uri;Landroid/net/Uri;ILorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Landroid/net/Uri;Ljava/lang/String;Landroid/net/Uri;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;JLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/recipients/Recipient;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZLorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/util/List;Lj$/util/Optional;ZZZJLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Landroid/net/Uri;Landroid/net/Uri;ILorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Landroid/net/Uri;Ljava/lang/String;Landroid/net/Uri;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;JLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/recipients/Recipient;->access$getTAG$cp()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->equals(Ljava/lang/Object;)Z @@ -27408,6 +27458,7 @@ HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getShouldShowE164()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getShowVerified()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getSmallFallbackContactPhotoDrawable(Landroid/content/Context;ZLorg/thoughtcrime/securesms/recipients/Recipient$FallbackPhotoProvider;I)Landroid/graphics/drawable/Drawable; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getWallpaper()Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper; +HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hasSameContent(Lorg/thoughtcrime/securesms/recipients/Recipient;)Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hashCode()I HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isActiveGroup()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isBlocked()Z @@ -27447,7 +27498,6 @@ HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->()V HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forId$default(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZILjava/lang/Object;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forId(Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)Lorg/thoughtcrime/securesms/recipients/Recipient; -HSPLorg/thoughtcrime/securesms/recipients/RecipientCreator;->forIndividual(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/RecipientRecord;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientId$1;->()V HSPLorg/thoughtcrime/securesms/recipients/RecipientId$Serializer;->()V HSPLorg/thoughtcrime/securesms/recipients/RecipientId$Serializer;->(Lorg/thoughtcrime/securesms/recipients/RecipientId$Serializer-IA;)V @@ -27576,6 +27626,11 @@ HSPLorg/thoughtcrime/securesms/service/KeyCachingService;->isLocked(Landroid/con HSPLorg/thoughtcrime/securesms/service/KeyCachingService;->onAppForegrounded(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/service/LocalBackupListener;->()V HSPLorg/thoughtcrime/securesms/service/LocalBackupListener;->schedule(Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/service/MessageBackupListener$Companion;->()V +HSPLorg/thoughtcrime/securesms/service/MessageBackupListener$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/service/MessageBackupListener$Companion;->schedule(Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/service/MessageBackupListener;->()V +HSPLorg/thoughtcrime/securesms/service/MessageBackupListener;->schedule(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/service/PendingRetryReceiptManager;->()V HSPLorg/thoughtcrime/securesms/service/PendingRetryReceiptManager;->(Landroid/app/Application;)V HSPLorg/thoughtcrime/securesms/service/PendingRetryReceiptManager;->getNextClosestEvent()Ljava/lang/Object; @@ -27631,6 +27686,11 @@ HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->()V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->(Landroid/app/Application;)V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->enable()V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->registerActivity(Landroidx/appcompat/app/AppCompatActivity;)V +HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$1;->()V +HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack;->(Ljava/lang/String;Ljava/lang/String;)V +HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack;->getPackId()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks;->()V +HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks;->contains(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/stickers/StickerRemoteUriLoader$Factory;->()V HSPLorg/thoughtcrime/securesms/stickers/StickerSearchRepository$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/stickers/StickerSearchRepository;Lorg/thoughtcrime/securesms/stickers/StickerSearchRepository$Callback;)V HSPLorg/thoughtcrime/securesms/stickers/StickerSearchRepository$$ExternalSyntheticLambda0;->run()V @@ -27919,11 +27979,13 @@ HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->groupLimits()Lorg/thoughtcrim HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->init()V HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->instantVideoPlayback()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->internalUser()Z +HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->libSignalWebSocketEnabled()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->mapToJson(Ljava/util/Map;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->messageBackups()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->okHttpAutomaticRetry()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->parseStoredConfig(Ljava/lang/String;)Ljava/util/Map; HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->refreshIfNecessary()V -HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->registrationV2()Z +HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->restoreAfterRegistration()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->retryReceipts()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->retryRespondMaxAge()J HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->triggerFlagChangeListeners(Ljava/util/Map;)V @@ -28545,9 +28607,10 @@ HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getPreKey HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getSecureValueRecoveryV2(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2; HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getSenderCertificate()[B HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V -HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda2;->()V +HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda0;->()V HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Z)V HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->retrieveProfile(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lorg/signal/core/util/concurrent/ListenableFuture; +HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->retrieveStickerManifest([B[B)Lorg/whispersystems/signalservice/api/messages/SignalServiceStickerManifest; HSPLorg/whispersystems/signalservice/api/SignalWebSocket$$ExternalSyntheticLambda0;->(Lio/reactivex/rxjava3/subjects/BehaviorSubject;)V HSPLorg/whispersystems/signalservice/api/SignalWebSocket$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->()V @@ -28684,7 +28747,6 @@ HSPLorg/whispersystems/signalservice/api/push/ServiceId$PNI;->equals(Ljava/lang/ HSPLorg/whispersystems/signalservice/api/push/ServiceId$PNI;->parseOrThrow(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/push/ServiceId$PNI; HSPLorg/whispersystems/signalservice/api/push/ServiceId$PNI;->toString()Ljava/lang/String; HSPLorg/whispersystems/signalservice/api/push/ServiceId;->()V -HSPLorg/whispersystems/signalservice/api/push/ServiceId;->(Lorg/signal/libsignal/protocol/ServiceId;)V HSPLorg/whispersystems/signalservice/api/push/ServiceId;->(Lorg/signal/libsignal/protocol/ServiceId;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/whispersystems/signalservice/api/push/ServiceId;->fromLibSignal(Lorg/signal/libsignal/protocol/ServiceId;)Lorg/whispersystems/signalservice/api/push/ServiceId; HSPLorg/whispersystems/signalservice/api/push/ServiceId;->isUnknown()Z @@ -28815,6 +28877,7 @@ HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createCdn HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createConnectionClient(Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)Lokhttp3/OkHttpClient; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createConnectionHolders([Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)[Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createServiceConnectionHolders([Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)[Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder; +HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->downloadFromCdn(Ljava/io/OutputStream;JILjava/util/Map;Ljava/lang/String;JLorg/whispersystems/signalservice/api/messages/SignalServiceAttachment$ProgressListener;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAuthorizationHeader(Lorg/whispersystems/signalservice/api/util/CredentialsProvider;)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAvailablePreKeys(Lorg/whispersystems/signalservice/api/push/ServiceIdType;)Lorg/whispersystems/signalservice/internal/push/OneTimePreKeyCounts; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getDevices()Ljava/util/List; @@ -28825,6 +28888,7 @@ HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->jsonReque HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;Z)Lokhttp3/Response; +HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->retrieveStickerManifest([B)[B HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->retrieveVersionedProfile(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;Lj$/util/Optional;Ljava/util/Locale;)Lorg/signal/core/util/concurrent/ListenableFuture; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->submitServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lj$/util/Optional;)Lorg/signal/core/util/concurrent/ListenableFuture; @@ -28844,6 +28908,9 @@ HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->ch HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->createFor(Lorg/whispersystems/signalservice/api/push/TrustStore;)[Ljavax/net/ssl/TrustManager; HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->createFor([Ljavax/net/ssl/TrustManager;)[Ljavax/net/ssl/TrustManager; HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->getAcceptedIssuers()[Ljava/security/cert/X509Certificate; +HSPLorg/whispersystems/signalservice/internal/util/Hex;->()V +HSPLorg/whispersystems/signalservice/internal/util/Hex;->appendHexChar(Ljava/lang/StringBuffer;I)V +HSPLorg/whispersystems/signalservice/internal/util/Hex;->toStringCondensed([B)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->()V HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->fromJson(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->toJson(Ljava/lang/Object;)Ljava/lang/String; @@ -28862,20 +28929,20 @@ HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Bu HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->(Ljava/lang/Class;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper;)V HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->(Ljava/lang/Class;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper-IA;)V HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->extend(Ljava/lang/Class;)Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->()V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->(Ljava/lang/String;Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lj$/util/Optional;Ljava/lang/String;Lorg/whispersystems/signalservice/api/websocket/HealthMonitor;Ljava/lang/String;Z)V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->(Ljava/lang/String;Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lj$/util/Optional;Ljava/lang/String;Lorg/whispersystems/signalservice/api/websocket/HealthMonitor;Z)V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->cleanupAfterShutdown()V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->connect()Lio/reactivex/rxjava3/core/Observable; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->createTlsSocketFactory(Lorg/whispersystems/signalservice/api/push/TrustStore;)Lorg/signal/libsignal/protocol/util/Pair; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->disconnect()V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->elapsedTime(J)J -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->getConnectionInfo()Lorg/signal/libsignal/protocol/util/Pair; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->isDead()Z -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->log(Ljava/lang/String;)V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->onFailure(Lokhttp3/WebSocket;Ljava/lang/Throwable;Lokhttp3/Response;)V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->readRequest(J)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->warn(Ljava/lang/String;Ljava/lang/Throwable;)V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->()V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->(Ljava/lang/String;Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lj$/util/Optional;Ljava/lang/String;Lorg/whispersystems/signalservice/api/websocket/HealthMonitor;Ljava/lang/String;Z)V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->(Ljava/lang/String;Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lj$/util/Optional;Ljava/lang/String;Lorg/whispersystems/signalservice/api/websocket/HealthMonitor;Z)V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->cleanupAfterShutdown()V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->connect()Lio/reactivex/rxjava3/core/Observable; +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->createTlsSocketFactory(Lorg/whispersystems/signalservice/api/push/TrustStore;)Lorg/signal/libsignal/protocol/util/Pair; +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->disconnect()V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->elapsedTime(J)J +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->getConnectionInfo()Lorg/signal/libsignal/protocol/util/Pair; +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->isDead()Z +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->log(Ljava/lang/String;)V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->onFailure(Lokhttp3/WebSocket;Ljava/lang/Throwable;Lokhttp3/Response;)V +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->readRequest(J)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage; +HSPLorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection;->warn(Ljava/lang/String;Ljava/lang/Throwable;)V HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->()V HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->build()Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage; HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->headers(Ljava/util/List;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; @@ -31439,7 +31506,6 @@ Lcom/fasterxml/jackson/databind/ser/Serializers$Base; Lcom/fasterxml/jackson/databind/ser/Serializers; Lcom/fasterxml/jackson/databind/ser/impl/FailingSerializer; Lcom/fasterxml/jackson/databind/ser/impl/IndexedListSerializer; -Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Double; Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Empty; Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$SerializerAndMapResult; Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Single; @@ -32374,7 +32440,6 @@ Lio/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription; Lio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription; Lio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper; Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList$NonThrowingPredicate; -Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList; Lio/reactivex/rxjava3/internal/util/ArrayListSupplier; Lio/reactivex/rxjava3/internal/util/AtomicThrowable; Lio/reactivex/rxjava3/internal/util/BackpressureHelper; @@ -34432,6 +34497,7 @@ Lorg/signal/glide/Log$Provider; Lorg/signal/glide/SignalGlideCodecs; Lorg/signal/glide/apng/decode/APNGDecoder; Lorg/signal/glide/common/decode/FrameSeqDecoder; +Lorg/signal/libsignal/attest/AttestationFailedException; Lorg/signal/libsignal/internal/FilterExceptions$ThrowingNativeIntOperation; Lorg/signal/libsignal/internal/FilterExceptions$ThrowingNativeLongOperation; Lorg/signal/libsignal/internal/FilterExceptions$ThrowingNativeOperation; @@ -34441,13 +34507,19 @@ Lorg/signal/libsignal/internal/Native; Lorg/signal/libsignal/internal/NativeHandleGuard$Owner; Lorg/signal/libsignal/internal/NativeHandleGuard$SimpleOwner; Lorg/signal/libsignal/internal/NativeHandleGuard; +Lorg/signal/libsignal/net/CdsiInvalidTokenException; Lorg/signal/libsignal/net/CdsiLookupResponse$Entry; Lorg/signal/libsignal/net/CdsiLookupResponse; +Lorg/signal/libsignal/net/CdsiProtocolException; Lorg/signal/libsignal/net/ChatService$DebugInfo; Lorg/signal/libsignal/net/ChatService$Response; Lorg/signal/libsignal/net/ChatService$ResponseAndDebugInfo; Lorg/signal/libsignal/net/ChatService; +Lorg/signal/libsignal/net/ChatServiceException; +Lorg/signal/libsignal/net/ChatServiceInactiveException; Lorg/signal/libsignal/net/Network$Environment; +Lorg/signal/libsignal/net/NetworkException; +Lorg/signal/libsignal/net/RetryLaterException; Lorg/signal/libsignal/protocol/IdentityKey; Lorg/signal/libsignal/protocol/IdentityKeyPair; Lorg/signal/libsignal/protocol/InvalidKeyException; @@ -34496,6 +34568,10 @@ Lorg/signal/libsignal/protocol/util/ByteUtil; Lorg/signal/libsignal/protocol/util/KeyHelper; Lorg/signal/libsignal/protocol/util/Medium; Lorg/signal/libsignal/protocol/util/Pair; +Lorg/signal/libsignal/sgxsession/SgxCommunicationFailureException; +Lorg/signal/libsignal/svr/DataMissingException; +Lorg/signal/libsignal/svr/RestoreFailedException; +Lorg/signal/libsignal/svr/SvrException; Lorg/signal/libsignal/usernames/BaseUsernameException; Lorg/signal/libsignal/zkgroup/InvalidInputException; Lorg/signal/libsignal/zkgroup/ServerPublicParams$$ExternalSyntheticLambda0; @@ -34513,11 +34589,13 @@ Lorg/signal/libsignal/zkgroup/profiles/ProfileKey; Lorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion; Lorg/signal/libsignal/zkgroup/receipts/ClientZkReceiptOperations; Lorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda1; +Lorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2; Lorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda3; Lorg/signal/paging/BufferedPagingController; Lorg/signal/paging/CompressedList; Lorg/signal/paging/DataStatus; Lorg/signal/paging/DataStream; +Lorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0; Lorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda2; Lorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda3; Lorg/signal/paging/FixedSizePagingController; @@ -35006,6 +35084,7 @@ Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$controller$1; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$data$1; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$safetyNumberRepository$2; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel; +Lorg/thoughtcrime/securesms/contacts/paged/collections/ContactSearchIterator; Lorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery; Lorg/thoughtcrime/securesms/contactshare/Contact; Lorg/thoughtcrime/securesms/contactshare/ContactUtil; @@ -35028,6 +35107,8 @@ Lorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLamb Lorg/thoughtcrime/securesms/conversation/ConversationItem$1; Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$ClickListener; +Lorg/thoughtcrime/securesms/conversation/ConversationItem$DoubleTapEditTouchListener$1; +Lorg/thoughtcrime/securesms/conversation/ConversationItem$DoubleTapEditTouchListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$GiftMessageViewCallback; Lorg/thoughtcrime/securesms/conversation/ConversationItem$LinkPreviewClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$PassthroughClickListener; @@ -35349,6 +35430,7 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$storyRingState Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel; Lorg/thoughtcrime/securesms/conversation/v2/DisabledInputView$inflater$2; Lorg/thoughtcrime/securesms/conversation/v2/DisabledInputView; +Lorg/thoughtcrime/securesms/conversation/v2/DoubleTapEditEducationSheet$Callback; Lorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState; Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState; Lorg/thoughtcrime/securesms/conversation/v2/MessageRequestViewModel; @@ -35361,6 +35443,7 @@ Lorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource$Companion; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource$threadRecipient$2; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource; +Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationMessageElement; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationUpdate; @@ -35420,12 +35503,14 @@ Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyView Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda2; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4; -Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda7; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda5; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda8; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$PassthroughClickListener; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$ReactionMeasureListener; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$bodyBubbleDrawable$1; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$footerDrawable$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$gestureDetector$1; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$replyDelegate$1; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$senderDrawable$1; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder; @@ -35635,6 +35720,7 @@ Lorg/thoughtcrime/securesms/database/DatabaseMonitor; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda10; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda11; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17; +Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda22; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda23; @@ -35642,6 +35728,8 @@ Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda24 Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda28; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda29; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30; +Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32; +Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda40; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda4; @@ -35669,6 +35757,7 @@ Lorg/thoughtcrime/securesms/database/GroupReceiptTable; Lorg/thoughtcrime/securesms/database/GroupTable$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$MembershipTable$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$MembershipTable; +Lorg/thoughtcrime/securesms/database/GroupTable$Reader; Lorg/thoughtcrime/securesms/database/GroupTable$ShowAsStoryState$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$ShowAsStoryState; Lorg/thoughtcrime/securesms/database/GroupTable; @@ -35681,6 +35770,7 @@ Lorg/thoughtcrime/securesms/database/JobDatabase$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/database/JobDatabase$Companion; Lorg/thoughtcrime/securesms/database/JobDatabase$deleteJobs$1; Lorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2; +Lorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2; Lorg/thoughtcrime/securesms/database/JobDatabase; Lorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/database/KeyValueDatabase$1; @@ -35856,6 +35946,7 @@ Lorg/thoughtcrime/securesms/database/SqlCipherDeletingErrorHandler; Lorg/thoughtcrime/securesms/database/SqlCipherErrorHandler$Companion; Lorg/thoughtcrime/securesms/database/SqlCipherErrorHandler; Lorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader; +Lorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader; Lorg/thoughtcrime/securesms/database/StickerTable; Lorg/thoughtcrime/securesms/database/StorySendTable$Companion; Lorg/thoughtcrime/securesms/database/StorySendTable; @@ -35970,6 +36061,7 @@ Lorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding; Lorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding; Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$Provider; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider$$ExternalSyntheticLambda0; @@ -36135,7 +36227,6 @@ Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda19; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda20; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda22; -Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda23; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda6; @@ -36147,7 +36238,6 @@ Lorg/thoughtcrime/securesms/jobmanager/JobInstantiator; Lorg/thoughtcrime/securesms/jobmanager/JobLogger; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda10; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda11; -Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda16; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda17; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda19; Lorg/thoughtcrime/securesms/jobmanager/JobManager$$ExternalSyntheticLambda1; @@ -36412,6 +36502,7 @@ Lorg/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob$Factory; Lorg/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob$Factory; Lorg/thoughtcrime/securesms/jobs/StickerDownloadJob$Factory; Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory; +Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob; Lorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob$Factory; Lorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob; Lorg/thoughtcrime/securesms/jobs/StorageForcePushJob$Factory; @@ -36869,6 +36960,8 @@ Lorg/thoughtcrime/securesms/service/ExpiringStoriesManager$Companion; Lorg/thoughtcrime/securesms/service/ExpiringStoriesManager; Lorg/thoughtcrime/securesms/service/KeyCachingService; Lorg/thoughtcrime/securesms/service/LocalBackupListener; +Lorg/thoughtcrime/securesms/service/MessageBackupListener$Companion; +Lorg/thoughtcrime/securesms/service/MessageBackupListener; Lorg/thoughtcrime/securesms/service/PendingRetryReceiptManager; Lorg/thoughtcrime/securesms/service/PersistentAlarmManagerListener; Lorg/thoughtcrime/securesms/service/RotateSenderCertificateListener; @@ -36894,6 +36987,9 @@ Lorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId; Lorg/thoughtcrime/securesms/shakereport/ShakeToReport; Lorg/thoughtcrime/securesms/sms/GroupV2UpdateMessageUtil; Lorg/thoughtcrime/securesms/sms/MessageSender$MessageSentEvent; +Lorg/thoughtcrime/securesms/stickers/BlessedPacks$1; +Lorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack; +Lorg/thoughtcrime/securesms/stickers/BlessedPacks; Lorg/thoughtcrime/securesms/stickers/StickerEventListener; Lorg/thoughtcrime/securesms/stickers/StickerLocator; Lorg/thoughtcrime/securesms/stickers/StickerPackInstallEvent; @@ -37159,7 +37255,7 @@ Lorg/whispersystems/signalservice/api/SignalServiceAccountDataStore; Lorg/whispersystems/signalservice/api/SignalServiceAccountManager; Lorg/whispersystems/signalservice/api/SignalServiceDataStore; Lorg/whispersystems/signalservice/api/SignalServiceKyberPreKeyStore; -Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda2; +Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda0; Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; Lorg/whispersystems/signalservice/api/SignalServicePreKeyStore; Lorg/whispersystems/signalservice/api/SignalServiceSenderKeyStore; @@ -37202,6 +37298,7 @@ Lorg/whispersystems/signalservice/api/push/ServiceId; Lorg/whispersystems/signalservice/api/push/ServiceIdType; Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; Lorg/whispersystems/signalservice/api/push/TrustStore; +Lorg/whispersystems/signalservice/api/push/exceptions/AuthorizationFailedException; Lorg/whispersystems/signalservice/api/push/exceptions/ConflictException; Lorg/whispersystems/signalservice/api/push/exceptions/ContactManifestMismatchException; Lorg/whispersystems/signalservice/api/push/exceptions/MalformedResponseException; @@ -37263,6 +37360,7 @@ Lorg/whispersystems/signalservice/internal/push/exceptions/GroupPatchNotAccepted Lorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil; Lorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager$1; Lorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager; +Lorg/whispersystems/signalservice/internal/util/Hex; Lorg/whispersystems/signalservice/internal/util/JsonUtil; Lorg/whispersystems/signalservice/internal/util/Util; Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder; @@ -37271,6 +37369,7 @@ Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Build Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper; Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper; Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper; +Lorg/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection; Lorg/whispersystems/signalservice/internal/websocket/ResponseMapper; Lorg/whispersystems/signalservice/internal/websocket/WebSocketConnection; Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; @@ -37430,6 +37529,7 @@ PLandroidx/core/os/HandlerCompat;->postDelayed(Landroid/os/Handler;Ljava/lang/Ru PLandroidx/core/view/NestedScrollingChildHelper;->stopNestedScroll()V PLandroidx/core/view/ViewCompat$Api16Impl;->hasTransientState(Landroid/view/View;)Z PLandroidx/core/view/ViewCompat;->hasTransientState(Landroid/view/View;)Z +PLandroidx/core/view/ViewGroupKt$descendants$1;->(Landroid/view/ViewGroup;Lkotlin/coroutines/Continuation;)V PLandroidx/core/view/ViewGroupKt$descendants$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; PLandroidx/core/view/ViewGroupKt;->getDescendants(Landroid/view/ViewGroup;)Lkotlin/sequences/Sequence; PLandroidx/core/view/ViewKt$allViews$1;->(Landroid/view/View;Lkotlin/coroutines/Continuation;)V @@ -37503,6 +37603,7 @@ PLandroidx/fragment/app/FragmentState;->writeToParcel(Landroid/os/Parcel;I)V PLandroidx/fragment/app/FragmentStateManager;->destroy()V PLandroidx/fragment/app/FragmentStateManager;->destroyFragmentView()V PLandroidx/fragment/app/FragmentStateManager;->detach()V +PLandroidx/fragment/app/FragmentStateManager;->saveState()Landroid/os/Bundle; PLandroidx/fragment/app/FragmentStateManager;->saveViewState()V PLandroidx/fragment/app/FragmentStateManager;->stop()V PLandroidx/fragment/app/FragmentStore;->getAllSavedState()Ljava/util/HashMap; @@ -37645,9 +37746,8 @@ PLandroidx/profileinstaller/ProfileVerifier;->setCompilationStatus(IZZ)Landroidx PLandroidx/profileinstaller/ProfileVerifier;->writeProfileVerification(Landroid/content/Context;Z)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus; PLandroidx/recyclerview/widget/AdapterListUpdateCallback;->onChanged(IILjava/lang/Object;)V PLandroidx/recyclerview/widget/AsyncDifferConfig;->getDiffCallback()Landroidx/recyclerview/widget/DiffUtil$ItemCallback; -PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areContentsTheSame(II)Z +PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areItemsTheSame(II)Z PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->getChangePayload(II)Ljava/lang/Object; -PLandroidx/recyclerview/widget/BatchingListUpdateCallback;->onChanged(IILjava/lang/Object;)V PLandroidx/recyclerview/widget/ChildHelper;->removeViewAt(I)V PLandroidx/recyclerview/widget/ConcatAdapter;->onDetachedFromRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/ConcatAdapter;->onViewDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V @@ -37664,6 +37764,7 @@ PLandroidx/recyclerview/widget/DiffUtil$Snake;->diagonalSize()I PLandroidx/recyclerview/widget/DiffUtil$Snake;->hasAdditionOrRemoval()Z PLandroidx/recyclerview/widget/DiffUtil$Snake;->toDiagonal()Landroidx/recyclerview/widget/DiffUtil$Diagonal; PLandroidx/recyclerview/widget/DiffUtil;->backward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; +PLandroidx/recyclerview/widget/DiffUtil;->forward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; PLandroidx/recyclerview/widget/GapWorker$LayoutPrefetchRegistryImpl;->lastPrefetchIncludedPosition(I)Z PLandroidx/recyclerview/widget/GapWorker;->remove(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/ItemTouchHelper;->endRecoverAnimation(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Z)V @@ -37678,7 +37779,6 @@ PLandroidx/recyclerview/widget/LinearLayoutManager;->getChildClosestToEnd()Landr PLandroidx/recyclerview/widget/LinearLayoutManager;->getChildClosestToStart()Landroid/view/View; PLandroidx/recyclerview/widget/LinearLayoutManager;->onDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$Recycler;)V PLandroidx/recyclerview/widget/LinearLayoutManager;->onSaveInstanceState()Landroid/os/Parcelable; -PLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedStart(Landroid/view/View;)I PLandroidx/recyclerview/widget/OrientationHelper;->getTotalSpaceChange()I PLandroidx/recyclerview/widget/RecyclerView$5;->removeViewAt(I)V PLandroidx/recyclerview/widget/RecyclerView$6;->markViewHoldersUpdated(IILjava/lang/Object;)V @@ -37686,12 +37786,11 @@ PLandroidx/recyclerview/widget/RecyclerView$Adapter;->notifyDataSetChanged()V PLandroidx/recyclerview/widget/RecyclerView$Adapter;->onDetachedFromRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/RecyclerView$Adapter;->onViewDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLandroidx/recyclerview/widget/RecyclerView$AdapterDataObservable;->notifyChanged()V +PLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->buildAdapterChangeFlagsForAnimations(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)I PLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->canReuseUpdatedViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Z PLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->canReuseUpdatedViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Ljava/util/List;)Z PLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->recordPreLayoutInformation(Landroidx/recyclerview/widget/RecyclerView$State;Landroidx/recyclerview/widget/RecyclerView$ViewHolder;ILjava/util/List;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->dispatchDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$Recycler;)V -PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedLeft(Landroid/view/View;)I -PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getLeftDecorationWidth(Landroid/view/View;)I PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->onDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->onDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$Recycler;)V PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->onItemsUpdated(Landroidx/recyclerview/widget/RecyclerView;II)V @@ -37734,13 +37833,13 @@ PLandroidx/recyclerview/widget/RecyclerView;->stopNestedScroll()V PLandroidx/recyclerview/widget/RecyclerViewAccessibilityDelegate$ItemDelegate;->getAndRemoveOriginalDelegateForItem(Landroid/view/View;)Landroidx/core/view/AccessibilityDelegateCompat; PLandroidx/recyclerview/widget/ViewInfoStore$InfoRecord;->drainCache()V PLandroidx/recyclerview/widget/ViewInfoStore;->addToOldChangeHolders(JLandroidx/recyclerview/widget/RecyclerView$ViewHolder;)V -PLandroidx/recyclerview/widget/ViewInfoStore;->isDisappearing(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Z PLandroidx/recyclerview/widget/ViewInfoStore;->onDetach()V PLandroidx/recyclerview/widget/ViewInfoStore;->popFromPostLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; PLandroidx/recyclerview/widget/ViewInfoStore;->popFromPreLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; PLandroidx/recyclerview/widget/ViewInfoStore;->removeViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLandroidx/savedstate/Recreator$SavedStateProvider;->(Landroidx/savedstate/SavedStateRegistry;)V PLandroidx/savedstate/Recreator$SavedStateProvider;->add(Ljava/lang/String;)V +PLandroidx/savedstate/SavedStateRegistry;->performSave(Landroid/os/Bundle;)V PLandroidx/savedstate/SavedStateRegistry;->runOnNextRecreation(Ljava/lang/Class;)V PLandroidx/savedstate/SavedStateRegistryController;->performSave(Landroid/os/Bundle;)V PLcom/airbnb/lottie/LottieAnimationView$SavedState$1;->()V @@ -37753,7 +37852,6 @@ PLcom/airbnb/lottie/LottieDrawable;->getProgress()F PLcom/airbnb/lottie/LottieDrawable;->getRepeatCount()I PLcom/airbnb/lottie/LottieDrawable;->getRepeatMode()I PLcom/airbnb/lottie/LottieDrawable;->isAnimatingOrWillAnimateOnVisible()Z -PLcom/airbnb/lottie/animation/keyframe/ValueCallbackKeyframeAnimation;->setProgress(F)V PLcom/bumptech/glide/Glide;->unregisterRequestManager(Lcom/bumptech/glide/RequestManager;)V PLcom/bumptech/glide/RequestManager;->onDestroy()V PLcom/bumptech/glide/load/Options;->equals(Ljava/lang/Object;)Z @@ -37824,7 +37922,6 @@ PLio/reactivex/rxjava3/core/Maybe;->observeOn(Lio/reactivex/rxjava3/core/Schedul PLio/reactivex/rxjava3/core/Observable;->distinctUntilChanged(Lio/reactivex/rxjava3/functions/BiPredicate;)Lio/reactivex/rxjava3/core/Observable; PLio/reactivex/rxjava3/core/Scheduler$PeriodicDirectTask;->dispose()V PLio/reactivex/rxjava3/disposables/CompositeDisposable;->clear()V -PLio/reactivex/rxjava3/internal/disposables/CancellableDisposable;->dispose()V PLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->complete(Lio/reactivex/rxjava3/core/MaybeObserver;)V PLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->dispose()V PLio/reactivex/rxjava3/internal/disposables/SequentialDisposable;->dispose()V @@ -37849,8 +37946,6 @@ PLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSu PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefCountSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount;->cancel(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefConnection;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount;->timeout(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefConnection;)V -PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->removeFirst()V -PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->dispose()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscriber;->dispose()V @@ -37879,6 +37974,7 @@ PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten$FlatMapMaybeObserve PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten;->(Lio/reactivex/rxjava3/core/MaybeSource;Lio/reactivex/rxjava3/functions/Function;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten;->subscribeActual(Lio/reactivex/rxjava3/core/MaybeObserver;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable;->(Ljava/util/concurrent/Callable;)V +PLio/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable;->subscribeActual(Lio/reactivex/rxjava3/core/MaybeObserver;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn$ObserveOnMaybeObserver;->(Lio/reactivex/rxjava3/core/MaybeObserver;Lio/reactivex/rxjava3/core/Scheduler;)V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn$ObserveOnMaybeObserver;->onComplete()V PLio/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn$ObserveOnMaybeObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -37919,7 +38015,6 @@ PLio/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest$Lat PLio/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest$LatestCoordinator;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap$ConcatMapDelayErrorObserver$DelayErrorInnerObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap$ConcatMapDelayErrorObserver;->dispose()V -PLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEach$DoOnEachObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver$InnerObserver;->(Lio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver;)V PLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver$InnerObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -37941,7 +38036,6 @@ PLio/reactivex/rxjava3/internal/operators/observable/ObservableMap$MapObserver;- PLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->clear()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->isEmpty()Z PLio/reactivex/rxjava3/internal/operators/observable/ObservableRefCount;->timeout(Lio/reactivex/rxjava3/internal/operators/observable/ObservableRefCount$RefConnection;)V -PLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$Node;)V PLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$ReplayObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay;->reset()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableSampleTimed$SampleTimedNoLast;->complete()V @@ -37955,16 +38049,18 @@ PLio/reactivex/rxjava3/internal/operators/observable/ObservableSkip$SkipObserver PLio/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap$SwitchMapObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap$SwitchMapObserver;->disposeInner()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed$DebounceTimedObserver;->dispose()V -PLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->isEmpty()Z -PLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; PLio/reactivex/rxjava3/internal/queue/SpscArrayQueue;->clear()V PLio/reactivex/rxjava3/internal/queue/SpscArrayQueue;->isEmpty()Z +PLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->cancelFuture(Ljava/util/concurrent/Future;)V PLio/reactivex/rxjava3/internal/schedulers/DisposeOnCancel;->cancel(Z)Z PLio/reactivex/rxjava3/internal/schedulers/ScheduledRunnable;->dispose()V PLio/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->dispose()V +PLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->(I)V +PLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->add(Ljava/lang/Object;)V +PLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->forEachWhile(Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList$NonThrowingPredicate;)V PLio/reactivex/rxjava3/internal/util/AtomicThrowable;->terminate()Ljava/lang/Throwable; PLio/reactivex/rxjava3/internal/util/AtomicThrowable;->tryTerminateAndReport()V PLio/reactivex/rxjava3/internal/util/ExceptionHelper;->terminate(Ljava/util/concurrent/atomic/AtomicReference;)Ljava/lang/Throwable; @@ -37981,6 +38077,7 @@ PLio/reactivex/rxjava3/observers/SerializedObserver;->onComplete()V PLio/reactivex/rxjava3/processors/BehaviorProcessor$BehaviorSubscription;->cancel()V PLio/reactivex/rxjava3/processors/BehaviorProcessor;->remove(Lio/reactivex/rxjava3/processors/BehaviorProcessor$BehaviorSubscription;)V PLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->cancel()V +PLio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;->onNext(Ljava/lang/Object;)V PLio/reactivex/rxjava3/processors/PublishProcessor;->remove(Lio/reactivex/rxjava3/processors/PublishProcessor$PublishSubscription;)V PLio/reactivex/rxjava3/subjects/BehaviorSubject;->onComplete()V PLio/reactivex/rxjava3/subjects/BehaviorSubject;->terminate(Ljava/lang/Object;)[Lio/reactivex/rxjava3/subjects/BehaviorSubject$BehaviorDisposable; @@ -37988,7 +38085,6 @@ PLio/reactivex/rxjava3/subjects/PublishSubject$PublishDisposable;->dispose()V PLio/reactivex/rxjava3/subjects/PublishSubject;->remove(Lio/reactivex/rxjava3/subjects/PublishSubject$PublishDisposable;)V PLio/reactivex/rxjava3/subjects/SerializedSubject;->test(Ljava/lang/Object;)Z PLj$/util/DesugarCollections;->a()Ljava/lang/reflect/Constructor; -PLj$/util/concurrent/g;->a(Ljava/lang/Object;I)Lj$/util/concurrent/l; PLj$/util/d;->a(Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set; PLj$/util/d;->keySet()Ljava/util/Set; PLkotlin/collections/CollectionsKt;->intersect(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set; @@ -37997,6 +38093,7 @@ PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->filterInPlace$Collect PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->retainAll(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->retainAll(Ljava/util/Collection;Ljava/lang/Iterable;)Z PLkotlin/collections/CollectionsKt___CollectionsKt;->intersect(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set; +PLkotlin/sequences/SequenceBuilderIterator;->yieldAll(Ljava/util/Iterator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; PLkotlin/sequences/SequenceScope;->yieldAll(Lkotlin/sequences/Sequence;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; PLkotlin/sequences/SequencesKt;->sequence(Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; PLkotlin/sequences/SequencesKt__SequenceBuilderKt$sequence$$inlined$Sequence$1;->(Lkotlin/jvm/functions/Function2;)V @@ -38068,24 +38165,12 @@ PLorg/signal/core/util/concurrent/MaybeCompat;->()V PLorg/signal/core/util/concurrent/MaybeCompat;->()V PLorg/signal/core/util/concurrent/MaybeCompat;->fromCallable$lambda$0(Lkotlin/jvm/functions/Function0;Lio/reactivex/rxjava3/core/MaybeEmitter;)V PLorg/signal/core/util/concurrent/MaybeCompat;->fromCallable(Lkotlin/jvm/functions/Function0;)Lio/reactivex/rxjava3/core/Maybe; -PLorg/signal/core/util/concurrent/SignalExecutors;->$r8$lambda$0Q0afsv1raKIrq3aP-SuMcT2Ad0(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V PLorg/signal/core/util/logging/Log$Logger;->i(Ljava/lang/String;Ljava/lang/String;)V PLorg/signal/core/util/logging/Log$Logger;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V PLorg/signal/core/util/logging/Log;->internal()Lorg/signal/core/util/logging/Log$Logger; PLorg/signal/libsignal/protocol/IdentityKey;->equals(Ljava/lang/Object;)Z PLorg/signal/libsignal/protocol/IdentityKey;->getPublicKey()Lorg/signal/libsignal/protocol/ecc/ECPublicKey; -PLorg/signal/libsignal/protocol/ecc/ECPublicKey;->equals(Ljava/lang/Object;)Z -PLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V -PLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->run()V -PLorg/signal/paging/BufferedPagingController;->$r8$lambda$GxlLAxjfERBgyqmyvxteAPWaQkA(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V -PLorg/signal/paging/BufferedPagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V -PLorg/signal/paging/BufferedPagingController;->onDataItemChanged(Ljava/lang/Object;)V -PLorg/signal/paging/DataStatus;->mark(I)V -PLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V -PLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->run()V -PLorg/signal/paging/FixedSizePagingController;->$r8$lambda$2jZFFAhs3dG0IThMmzJQSvWvcd0(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V -PLorg/signal/paging/FixedSizePagingController;->onDataItemChanged(Ljava/lang/Object;)V -PLorg/signal/paging/ProxyPagingController;->onDataItemChanged(Ljava/lang/Object;)V +PLorg/signal/paging/FixedSizePagingController;->buildDataNeededLog(ILjava/lang/String;)Ljava/lang/String; PLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda60;->isInternal()Z PLorg/thoughtcrime/securesms/LoggingFragment;->onDestroy()V PLorg/thoughtcrime/securesms/LoggingFragment;->onStop()V @@ -38597,7 +38682,6 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->$r8$lambda PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->$r8$lambda$KdCOHPNqejWN1AhOnsjSsYWIQ1E(Lorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;Lorg/thoughtcrime/securesms/database/model/GroupRecord;)Lj$/util/Optional; PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->$r8$lambda$jYghkNuRsI_xLxRgZRxsCeMcFTc(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState; PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getReminder(Lorg/thoughtcrime/securesms/database/model/GroupRecord;)Lio/reactivex/rxjava3/core/Maybe; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->getRequestReviewState$lambda$15(Lorg/thoughtcrime/securesms/database/model/GroupRecord;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState; PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->markLastSeen$lambda$28(J)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->markLastSeen(J)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->setLastVisibleMessageTimestamp$lambda$5(JJ)V @@ -38617,12 +38701,9 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$17;->onNext(L PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$4$$ExternalSyntheticLambda4;->cancel()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$4;->$r8$lambda$DeiphTJKC3SI_gAZ1faGZoiWX80(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$4;->apply$lambda$4$lambda$3(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)Lio/reactivex/rxjava3/core/SingleSource; PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$1;->test(Lj$/util/Optional;)Z PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$groupMemberServiceIds$1;->test(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;->getGroupMemberServiceIds()Lio/reactivex/rxjava3/core/Observable; @@ -38664,12 +38745,6 @@ PLorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate$Compa PLorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V PLorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate;->()V PLorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate;->(Landroidx/fragment/app/Fragment;Lorg/thoughtcrime/securesms/audio/AudioRecorder;Lorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate$SessionCallback;)V -PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel; -PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V -PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V -PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->getThreadHeader()Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey; -PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;->()V PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areContentsTheSame(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areItemsTheSame(Ljava/lang/Object;)Z @@ -38678,6 +38753,7 @@ PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->getChangePaylo PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->getChangePayload(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)Z @@ -38686,6 +38762,10 @@ PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areItemsTheSame PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->getChangePayload(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->getHasCapacity()Z +PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$2;->apply(Lkotlin/Unit;Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState; +PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$3;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)V PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->getState()Lio/reactivex/rxjava3/core/Flowable; PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->onCleared()V @@ -38693,17 +38773,17 @@ PLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onDestr PLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onStop()V PLorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->onSaveInstanceState()Landroid/os/Parcelable; -PLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->equals(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/database/CallTable$markAllCallEventsWithPeerBeforeTimestampRead$latestCallAsOfTimestamp$1;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;J)V +PLorg/thoughtcrime/securesms/database/CallTable$markAllCallEventsWithPeerBeforeTimestampRead$latestCallAsOfTimestamp$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/database/CallTable$markAllCallEventsWithPeerBeforeTimestampRead$latestCallAsOfTimestamp$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)Lorg/thoughtcrime/securesms/database/CallTable$Call; +PLorg/thoughtcrime/securesms/database/CallTable;->markAllCallEventsWithPeerBeforeTimestampRead(Lorg/thoughtcrime/securesms/recipients/RecipientId;J)Lorg/thoughtcrime/securesms/database/CallTable$Call; PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13;->run()V -PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->run()V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda26;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda26;->run()V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda6;->run()V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$2v1Sb-6JCd9xjyyzfbH5tKTRWy8(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V -PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$6H_TtixOHSa7Tr30medlqcHry2c(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$8PzBCQMLi_6Y7FOR98cRbpXw-Xk(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$ZjxWKgbWA1SSTmnWoVneQana_Lk(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyVerboseConversationListeners$20(J)V @@ -38711,9 +38791,7 @@ PLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerSchedule PLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$unregisterObserver$18(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyVerboseConversationListeners(Ljava/util/Set;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerScheduledMessageObserver(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -PLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterMapped(Ljava/util/Map;Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V -PLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyVerboseConversationListeners(Ljava/util/Set;)V PLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->(Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V PLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->addIfNotNull(Lorg/thoughtcrime/securesms/database/DraftTable$Draft;)V @@ -38729,13 +38807,14 @@ PLorg/thoughtcrime/securesms/database/MessageTable;->markExpireStarted(Ljava/uti PLorg/thoughtcrime/securesms/database/MessageTable;->setMessagesReadSince(JJ)Ljava/util/List; PLorg/thoughtcrime/securesms/database/MessageTable;->setReactionsSeen(JJ)V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda1;->cancel()V -PLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->onChanged()V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver;->$r8$lambda$6u1bbd117Cl1h38MfeI7BgZPo1A(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver;->databaseFlowable$lambda$1$lambda$0(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V +PLorg/thoughtcrime/securesms/database/SignalDatabase;->calls()Lorg/thoughtcrime/securesms/database/CallTable; PLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->(Ljava/util/Map;Lorg/thoughtcrime/securesms/database/ThreadTable;Ljava/util/List;ZLkotlin/jvm/internal/Ref$BooleanRef;)V PLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V PLorg/thoughtcrime/securesms/database/ThreadTable;->createQuery(Ljava/lang/String;J)Ljava/lang/String; +PLorg/thoughtcrime/securesms/database/ThreadTable;->getRecipientIdsForThreadIds(Ljava/util/Collection;)Ljava/util/List; PLorg/thoughtcrime/securesms/database/ThreadTable;->getThreadRecord(Ljava/lang/Long;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord; PLorg/thoughtcrime/securesms/database/ThreadTable;->setLastSeen(J)V PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(JZJ)Ljava/util/List; @@ -38743,10 +38822,6 @@ PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Ljava/util/Map; PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;ZJ)Ljava/util/List; PLorg/thoughtcrime/securesms/database/ThreadTable;->update(JZZ)Z PLorg/thoughtcrime/securesms/database/identity/IdentityRecordList;->equals(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->equals(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda3;->cancel()V -PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->$r8$lambda$_YM1i9V93JIKhbRirbAeb_98VJw(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3$lambda$2(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isForcedUnread()Z PLorg/thoughtcrime/securesms/databinding/ConversationSearchNavBinding;->getRoot()Lorg/thoughtcrime/securesms/components/ConversationSearchBottomBar; PLorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;->getRoot()Lorg/thoughtcrime/securesms/components/InputAwareConstraintLayout; @@ -38796,11 +38871,17 @@ PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->onCleared()V PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->setSavedLinkPreviewState(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V PLorg/thoughtcrime/securesms/logsubmit/LogSectionNotifications$$ExternalSyntheticApiModelOutline2;->m(Landroid/app/NotificationChannel;)Z PLorg/thoughtcrime/securesms/main/MainActivityListHostFragment;->onDestroyView()V -PLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/messagerequests/MessageRequestState;->getState()Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState$State; PLorg/thoughtcrime/securesms/mms/AttachmentManager;->isAttachmentPresent()Z +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver$$ExternalSyntheticLambda10;->()V +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver$$ExternalSyntheticLambda10;->test(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver$$ExternalSyntheticLambda11;->()V +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver$$ExternalSyntheticLambda11;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->$r8$lambda$b_EADC0vZU7XQFn_9g4yupsI8u4(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;)Z PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->()V +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->lambda$processCallEvents$6(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;)Z PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->process(Ljava/util/List;)V +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->processCallEvents(Ljava/util/List;J)V PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->()V PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->cancelAllMessageNotifications(Landroid/content/Context;Ljava/util/Set;)V PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->cancelLegacy(Landroid/content/Context;I)V @@ -38846,7 +38927,6 @@ PLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;- PLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->isDisplayContact()Z PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda0;->run()V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda2;->onChanged(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->contentsMatch(Ljava/lang/Object;Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda6;->run()V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/Recipient;)V From bdcf2431e7f0fd7fe3dc4cda32fa6d1c182a116f Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 29 Apr 2024 21:03:05 -0400 Subject: [PATCH 101/113] Bump version to 7.6.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 eb6c31e56d..8eb4f436e5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1413 -val canonicalVersionName = "7.5.2" +val canonicalVersionCode = 1414 +val canonicalVersionName = "7.6.0" val postFixSize = 100 val abiPostFix: Map = mapOf( From cfaf40e60547fd26784ed9a4870b0f9db13f682f Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 30 Apr 2024 10:34:26 -0400 Subject: [PATCH 102/113] Fix KeyValueDataSet tests. --- .../keyvalue/KeyValueDataSetTest.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/app/src/test/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSetTest.java b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSetTest.java index b3e72ce1ee..fe51dc564d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSetTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSetTest.java @@ -121,14 +121,6 @@ public void getInteger_positive() { assertEquals(Integer.class, subject.getType("key")); } - @Test(expected = IllegalArgumentException.class) - public void getInteger_negative() { - KeyValueDataSet subject = new KeyValueDataSet(); - subject.putInteger("key", 1); - - subject.getLong("key", 0); - } - @Test public void getInteger_default() { KeyValueDataSet subject = new KeyValueDataSet(); @@ -144,17 +136,30 @@ public void getLong_positive() { assertEquals(Long.class, subject.getType("key")); } - @Test(expected = IllegalArgumentException.class) - public void getLong_negative() { + @Test + public void getLong_default() { KeyValueDataSet subject = new KeyValueDataSet(); - subject.putLong("key", 1L); + assertEquals(1, subject.getLong("key", 1)); + } - subject.getInteger("key", 0); + @Test + public void getInteger_storedAsLong() { + KeyValueDataSet subject = new KeyValueDataSet(); + subject.putLong("key", 1); + assertEquals(1, subject.getInteger("key", 1)); + } + + @Test(expected = ArithmeticException.class) + public void getInteger_storedAsLongAndTooLargeForInt() { + KeyValueDataSet subject = new KeyValueDataSet(); + subject.putLong("key", Long.MAX_VALUE); + subject.getInteger("key", 1); } @Test - public void getLong_default() { + public void getLong_storedAsInt() { KeyValueDataSet subject = new KeyValueDataSet(); + subject.putInteger("key", 1); assertEquals(1, subject.getLong("key", 1)); } From e4d6f9240f04410c5ca47105f78a479394aa7dd5 Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Tue, 30 Apr 2024 14:44:00 -0400 Subject: [PATCH 103/113] Fix double tap layout warning. --- .../securesms/BindableConversationItem.java | 5 +++++ .../conversation/ConversationItem.java | 22 +++++++++---------- .../conversation/v2/ConversationAdapterV2.kt | 17 ++++++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java index 1a914b5fb3..92f02e6336 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms; import android.net.Uri; +import android.view.GestureDetector; import android.view.View; import androidx.annotation.NonNull; @@ -58,6 +59,10 @@ void bind(@NonNull LifecycleOwner lifecycleOwner, void setEventListener(@Nullable EventListener listener); + default void setGestureDetector(@Nullable GestureDetector gestureDetector) { + // Intentionally Blank. + } + default void setParentScrolling(boolean isParentScrolling) { // Intentionally Blank. } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index da423db299..41bc0447cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -237,6 +237,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private Stub giftViewStub; private Stub paymentViewStub; private @Nullable EventListener eventListener; + private @Nullable GestureDetector gestureDetector; private int defaultBubbleColor; private int defaultBubbleColorForWallpaper; @@ -499,6 +500,11 @@ public void setEventListener(@Nullable EventListener eventListener) { this.eventListener = eventListener; } + @Override + public void setGestureDetector(GestureDetector gestureDetector) { + this.gestureDetector = gestureDetector; + } + public boolean disallowSwipe(float downX, float downY) { if (!hasAudio(messageRecord)) return false; @@ -2442,20 +2448,12 @@ public void onClick(final View view) { } private class DoubleTapEditTouchListener implements View.OnTouchListener { - private final GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onDoubleTap(MotionEvent e) { - if (eventListener != null && batchSelected.isEmpty()) { - eventListener.onItemDoubleClick(getMultiselectPartForLatestTouch()); - return true; - } - return false; - } - }); - @Override public boolean onTouch(View v, MotionEvent event) { - return gestureDetector.onTouchEvent(event); + if (gestureDetector != null && batchSelected.isEmpty()) { + return gestureDetector.onTouchEvent(event); + } + return false; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt index c36368c4eb..a2e2e62c4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt @@ -6,6 +6,9 @@ package org.thoughtcrime.securesms.conversation.v2 import android.text.TextUtils +import android.view.GestureDetector +import android.view.GestureDetector.SimpleOnGestureListener +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import androidx.core.text.HtmlCompat @@ -382,8 +385,22 @@ class ConversationAdapterV2( } private inner class OutgoingMediaViewHolder(itemView: View) : ConversationViewHolder(itemView) { + val gestureDetector = GestureDetector( + context, + object : SimpleOnGestureListener() { + override fun onDoubleTap(e: MotionEvent): Boolean { + if (clickListener != null) { + clickListener.onItemDoubleClick(getMultiselectPartForLatestTouch()) + return true + } + return false + } + } + ) + override fun bind(model: OutgoingMedia) { bindable.setEventListener(clickListener) + bindable.setGestureDetector(gestureDetector) if (bindPayloadsIfAvailable()) { return From e4ab795c624538efbb55e238ad9a85cbd7a7c216 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 30 Apr 2024 15:04:38 -0400 Subject: [PATCH 104/113] Fix stream reading error. --- .../IncrementalMacAdditionalValidationsInputStream.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt index a4ebd6f446..4e55b1ff50 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/IncrementalMacAdditionalValidationsInputStream.kt @@ -11,6 +11,7 @@ import java.io.FilterInputStream import java.io.InputStream import java.security.MessageDigest import javax.crypto.Mac +import kotlin.math.max /** * This is meant as a helper stream to go along with [org.signal.libsignal.protocol.incrementalmac.IncrementalMacInputStream]. @@ -68,7 +69,10 @@ class IncrementalMacAdditionalValidationsInputStream( // Even though we're reading into the MAC, many of the bytes read in this method call could be non-MAC bytes, so we need to copy // those over, while excluding the bytes that are part of the MAC. - mac.update(buffer, offset, bytesRead - bytesOfMacRead) + val bytesOfNonMacRead = max(0, bytesRead - bytesOfMacRead) + if (bytesOfNonMacRead > 0) { + mac.update(buffer, offset, bytesOfNonMacRead) + } } else { mac.update(buffer, offset, bytesRead) } From 8f3e62245f826bcacd4e765d97c8976e924029da Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 30 Apr 2024 15:20:11 -0400 Subject: [PATCH 105/113] Fix some issues where views were accessed after being destroyed. --- .../securesms/conversation/v2/ConversationFragment.kt | 8 +++----- .../thoughtcrime/securesms/mediasend/CameraXFragment.java | 4 ++++ 2 files changed, 7 insertions(+), 5 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 044582a34e..829c9459c0 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 @@ -3098,7 +3098,7 @@ class ConversationFragment : } override fun onHide() { - if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) || activity == null || activity?.isFinishing == true) { return } @@ -3107,10 +3107,8 @@ class ConversationFragment : getVoiceNoteMediaController().resumePlayback(selectedConversationModel.audioUri, messageRecord.getId()) } - if (activity != null) { - WindowUtil.setLightStatusBarFromTheme(requireActivity()) - WindowUtil.setLightNavigationBarFromTheme(requireActivity()) - } + WindowUtil.setLightStatusBarFromTheme(requireActivity()) + WindowUtil.setLightNavigationBarFromTheme(requireActivity()) clearFocusedItem() diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java index 25be8d873b..078a0f04da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java @@ -561,6 +561,10 @@ private Unit initializeFlipButton(@NonNull View flipButton, @NonNull CameraXFlas return Unit.INSTANCE; } + if (!getLifecycle().getCurrentState().isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED)) { + return Unit.INSTANCE; + } + getViewLifecycleOwner().getLifecycle().addObserver(cameraScreenBrightnessController); if (cameraController.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) && cameraController.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA)) { flipButton.setVisibility(View.VISIBLE); From 5d15eef61d3f03cd164a3f1ded0b018c77ee9ac7 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 30 Apr 2024 16:04:14 -0400 Subject: [PATCH 106/113] Improve translations with pluralized string resources. --- .../app/privacy/PrivacySettingsFragment.kt | 2 +- ...CallParticipantsListUpdatePopupWindow.java | 9 +++--- .../spoofing/ReviewCardDialogFragment.java | 10 +++---- app/src/main/res/values/strings.xml | 30 +++++++++++++++---- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsFragment.kt index 51cb639ade..3aba9d95c4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsFragment.kt @@ -131,7 +131,7 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac clickPref( title = DSLSettingsText.from(R.string.PrivacySettingsFragment__blocked), - summary = DSLSettingsText.from(getString(R.string.PrivacySettingsFragment__d_contacts, state.blockedCount)), + summary = DSLSettingsText.from(resources.getQuantityString(R.plurals.PrivacySettingsFragment__d_contacts, state.blockedCount, state.blockedCount)), onClick = { Navigation.findNavController(requireView()) .safeNavigate(R.id.action_privacySettingsFragment_to_blockedUsersActivity) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsListUpdatePopupWindow.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsListUpdatePopupWindow.java index 7dfd77463a..fdc387cc3d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsListUpdatePopupWindow.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsListUpdatePopupWindow.java @@ -11,6 +11,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.PluralsRes; import androidx.annotation.StringRes; import org.thoughtcrime.securesms.R; @@ -141,7 +142,7 @@ private void setDescriptionForRecipients(@NonNull Set { adapter.submitList(cards); - description.setText(getString(getDescriptionResId(), cards.size())); + description.setText(getResources().getQuantityString(getDescriptionResId(), cards.size(), cards.size())); }); viewModel.getReviewEvents().observe(getViewLifecycleOwner(), this::onReviewEvent); @@ -105,7 +105,7 @@ private void initializeViewModel() throws BadGroupIdException { viewModel = new ViewModelProvider(this, factory).get(ReviewCardViewModel.class); } - private @StringRes int getDescriptionResId() { + private @PluralsRes int getDescriptionResId() { return requireArguments().getInt(EXTRA_DESCRIPTION_RES_ID); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 77299d3f76..84512896ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4227,8 +4227,16 @@ Review members Review request - %1$d group members have the same name, review the members below and choose to take action. - If you\'re not sure who the request is from, review the contacts below and take action. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + No other groups in common. No groups in common. @@ -4259,12 +4267,20 @@ %1$s joined %1$s and %2$s joined %1$s, %2$s and %3$s joined - %1$s, %2$s and %3$d others joined + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s left %1$s and %2$s left %1$s, %2$s and %3$s left - %1$s, %2$s and %3$d others left + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + You You (on another device) @@ -4634,7 +4650,11 @@ Blocked - %1$d contacts + + + %1$d contact + %1$d contacts + Messaging Disappearing messages App security From 8c81e4773787bc2126df5c7a6d7c712fca694d4f Mon Sep 17 00:00:00 2001 From: mtang-signal Date: Tue, 30 Apr 2024 16:29:33 -0400 Subject: [PATCH 107/113] Expand double tap touch area. --- .../conversation/v2/ConversationAdapterV2.kt | 28 ++++++++++--------- .../V2ConversationItemTextOnlyViewHolder.kt | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt index a2e2e62c4d..94cec13b4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt @@ -385,19 +385,6 @@ class ConversationAdapterV2( } private inner class OutgoingMediaViewHolder(itemView: View) : ConversationViewHolder(itemView) { - val gestureDetector = GestureDetector( - context, - object : SimpleOnGestureListener() { - override fun onDoubleTap(e: MotionEvent): Boolean { - if (clickListener != null) { - clickListener.onItemDoubleClick(getMultiselectPartForLatestTouch()) - return true - } - return false - } - } - ) - override fun bind(model: OutgoingMedia) { bindable.setEventListener(clickListener) bindable.setGestureDetector(gestureDetector) @@ -486,6 +473,19 @@ class ConversationAdapterV2( val bindable: BindableConversationItem get() = itemView as BindableConversationItem + val gestureDetector: GestureDetector = GestureDetector( + context, + object : SimpleOnGestureListener() { + override fun onDoubleTap(e: MotionEvent): Boolean { + if (clickListener != null && selectedItems.isEmpty()) { + clickListener.onItemDoubleClick(getMultiselectPartForLatestTouch()) + return true + } + return false + } + } + ) + override val root: ViewGroup = bindable.root protected val previousMessage: Optional @@ -512,6 +512,8 @@ class ConversationAdapterV2( ) true } + + itemView.setOnTouchListener { _, event: MotionEvent -> gestureDetector.onTouchEvent(event) } } fun bindPayloadsIfAvailable(): Boolean { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt index 998ab89aa0..654f828637 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt @@ -154,7 +154,7 @@ open class V2ConversationItemTextOnlyViewHolder>( ) } - binding.body.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) } + binding.root.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) } binding.root.setOnClickListener { onBubbleClicked() } binding.root.setOnLongClickListener { conversationContext.clickListener.onItemLongClick(binding.root, getMultiselectPartForLatestTouch()) From ced4ece5b8997378d367db32344e8d7b2f32cb9a Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 30 Apr 2024 16:41:02 -0400 Subject: [PATCH 108/113] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 89 ++++++++++- app/src/main/res/values-ar/strings.xml | 109 ++++++++++++- app/src/main/res/values-az/strings.xml | 89 ++++++++++- app/src/main/res/values-bg/strings.xml | 89 ++++++++++- app/src/main/res/values-bn/strings.xml | 89 ++++++++++- app/src/main/res/values-bs/strings.xml | 99 +++++++++++- app/src/main/res/values-ca/strings.xml | 89 ++++++++++- app/src/main/res/values-cs/strings.xml | 99 +++++++++++- app/src/main/res/values-da/strings.xml | 89 ++++++++++- app/src/main/res/values-de/strings.xml | 93 ++++++++++-- app/src/main/res/values-el/strings.xml | 89 ++++++++++- app/src/main/res/values-es/strings.xml | 89 ++++++++++- app/src/main/res/values-et/strings.xml | 89 ++++++++++- app/src/main/res/values-eu/strings.xml | 89 ++++++++++- app/src/main/res/values-fa/strings.xml | 89 ++++++++++- app/src/main/res/values-fi/strings.xml | 89 ++++++++++- app/src/main/res/values-fr/strings.xml | 169 +++++++++++++++------ app/src/main/res/values-ga/strings.xml | 104 ++++++++++++- app/src/main/res/values-gl/strings.xml | 89 ++++++++++- app/src/main/res/values-gu/strings.xml | 89 ++++++++++- app/src/main/res/values-hi/strings.xml | 89 ++++++++++- app/src/main/res/values-hr/strings.xml | 99 +++++++++++- app/src/main/res/values-hu/strings.xml | 89 ++++++++++- app/src/main/res/values-in/strings.xml | 84 +++++++++- app/src/main/res/values-it/strings.xml | 89 ++++++++++- app/src/main/res/values-iw/strings.xml | 99 +++++++++++- app/src/main/res/values-ja/strings.xml | 84 +++++++++- app/src/main/res/values-ka/strings.xml | 89 ++++++++++- app/src/main/res/values-kk/strings.xml | 89 ++++++++++- app/src/main/res/values-km/strings.xml | 84 +++++++++- app/src/main/res/values-kn/strings.xml | 89 ++++++++++- app/src/main/res/values-ko/strings.xml | 84 +++++++++- app/src/main/res/values-ky/strings.xml | 84 +++++++++- app/src/main/res/values-lt/strings.xml | 99 +++++++++++- app/src/main/res/values-lv/strings.xml | 94 +++++++++++- app/src/main/res/values-mk/strings.xml | 89 ++++++++++- app/src/main/res/values-ml/strings.xml | 89 ++++++++++- app/src/main/res/values-mr/strings.xml | 89 ++++++++++- app/src/main/res/values-ms/strings.xml | 84 +++++++++- app/src/main/res/values-my/strings.xml | 84 +++++++++- app/src/main/res/values-nb/strings.xml | 89 ++++++++++- app/src/main/res/values-nl/strings.xml | 89 ++++++++++- app/src/main/res/values-pa/strings.xml | 89 ++++++++++- app/src/main/res/values-pl/strings.xml | 99 +++++++++++- app/src/main/res/values-pt-rBR/strings.xml | 89 ++++++++++- app/src/main/res/values-pt/strings.xml | 89 ++++++++++- app/src/main/res/values-ro/strings.xml | 94 +++++++++++- app/src/main/res/values-ru/strings.xml | 99 +++++++++++- app/src/main/res/values-sk/strings.xml | 99 +++++++++++- app/src/main/res/values-sl/strings.xml | 99 +++++++++++- app/src/main/res/values-sq/strings.xml | 89 ++++++++++- app/src/main/res/values-sr/strings.xml | 89 ++++++++++- app/src/main/res/values-sv/strings.xml | 89 ++++++++++- app/src/main/res/values-sw/strings.xml | 89 ++++++++++- app/src/main/res/values-ta/strings.xml | 89 ++++++++++- app/src/main/res/values-te/strings.xml | 89 ++++++++++- app/src/main/res/values-th/strings.xml | 84 +++++++++- app/src/main/res/values-tl/strings.xml | 89 ++++++++++- app/src/main/res/values-tr/strings.xml | 89 ++++++++++- app/src/main/res/values-ug/strings.xml | 84 +++++++++- app/src/main/res/values-uk/strings.xml | 117 ++++++++++++-- app/src/main/res/values-ur/strings.xml | 89 ++++++++++- app/src/main/res/values-vi/strings.xml | 84 +++++++++- app/src/main/res/values-yue/strings.xml | 84 +++++++++- app/src/main/res/values-zh-rCN/strings.xml | 84 +++++++++- app/src/main/res/values-zh-rHK/strings.xml | 84 +++++++++- app/src/main/res/values-zh-rTW/strings.xml | 84 +++++++++- app/static-ips.gradle.kts | 2 +- 68 files changed, 5621 insertions(+), 521 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 586df2fc44..b4c8a7f9f3 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -89,6 +89,16 @@ Signal benodig die Berging-toestemming om foto\'s, videos of klank aan te heg, maar dit is permanent geweier. Gaan asseblief na die programinstellingsmenu, kies \"Toestemmings\" en aktiveer \"Berging\". Signal benodig toestemming vir Kontakte om kontakinligting aan te heg, maar dit is permanent geweier. Gaan asseblief na die programinstellingsmenu, kies \"Toestemmings\" en aktiveer \"Kontakte\". Signal benodig toestemming vir Plek om \'n plek aan te heg, maar dit is permanent geweier. Gaan asseblief na die programinstellingsmenu, kies \"Toestemmings\" en aktiveer \"Plek\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s het nie Betalings geaktiveer nie @@ -386,8 +396,16 @@ Jou aansluitingsversoek is na die groepadmin gestuur. Jy sal laat weet word wanneer hulle aksie geneem het. Kanselleer versoek - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal benodig toestemming vir die Mikrofoon om klankboodskappe te stuur, maar dit is permanent geweier. Gaan asseblief na die programinstellings, kies \"Toestemmings\" en aktiveer \"Mikrofoon\". + Signal benodig toestemming vir die Mikrofoon en Kamera om %1$s te kan bel, maar dit is permanent geweier. Gaan asseblief na die programinstellings, kies \"Toestemmings\" en skakel \"Mikrofoon\" en \"Kamera\" aan. Laat Signal toegang tot die kamera om foto\'s en video op te neem. Signal het die Kamera-toestemming nodig om foto\'s of video\'s te neem, maar dit is permanent geweier. Gaan asseblief na die programinstellings, kies \"Toestemmings\" en aktiveer \"Kamera\". @@ -411,7 +429,13 @@ Jy sal hierdie groep verlaat, en dit sal van al jou toestelle geskrap word. Skrap Skrap en verlaat - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Sluit aan @@ -3434,6 +3458,14 @@ Stuur foto\'s, video\'s en lêers van jou toestel af. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Sekuriteitsopstelling @@ -3688,6 +3720,14 @@ Klets Uitsending + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nuwe groep Instellings @@ -4184,8 +4224,16 @@ Hersien lede Hersien versoek - %1$d groeplede het dieselfde naam, hersien die lede hier onder en kies ’n aksie om te neem. - Indien jy onseker is oor die sender van die versoek, kan jy die kontakte hier onder nagaan en aksie neem. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Geen ander groepe in gemeen. Geen groepe in gemeen nie. @@ -4216,12 +4264,20 @@ %1$s het aangesluit %1$s en %2$s het aangesluit %1$s, %2$s en %3$s het aangesluit - %1$s, %2$s en nog %3$d het aangesluit + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s het verlaat %1$s en %2$s het verlaat %1$s, %2$s en %3$s het verlaat - %1$s, %2$s en nog %3$d het verlaat + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Jy Jy (op ’n ander toestel) @@ -4591,7 +4647,11 @@ Versper - %1$d kontakte + + + %1$d contact + %1$d contacts + Boodskappe Verdwynboodskappe Toepassingveiligheid @@ -6748,5 +6808,20 @@ Laai tans rugsteundata af… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 8b2336cae3..32651349d9 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -89,6 +89,16 @@ يحتاج سيجنال إلى إذن الوصول إلى التخزين من أجل إرفاق الصور والفيديوهات والملفّات الصوتيّة، ولكن الإذن لم يُمنح بشكل دائم. الّرجاء زيارة قائمة إعدادات التطبيق، واختيار \"الأذونات\"، ومن ثمّ تفعيل \"سعة التخزين\". يحتاج سيجنال إلى إذن الوصول لِ جهات الاتصال من أجل إرفاق بيانات جهة اتصال، لكن الإذن لم يُمنح بشكل دائم. الّرجاء زيارة قائمة إعدادات التطبيق، واختيار \"الأذونات\"، ومن ثم تفعيل \"جهات الاتصال\". يحتاج سيجنال إلى إذن الوصول إلى الموقع من أجل إرفاق الموقع ولكن تم إيقاف الإذن على نحو دائم. يرجى زيارة قائمة إعدادات التطبيق ثم اختيار \"الأذونات\" وتفعيل \"الموقع\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + لم يُفعّل %1$s عمليات الدفع @@ -398,8 +408,16 @@ تم إرسال طلبك للانضمام إلى المشرفين على المجموعة. سيتم تنبيهك عند اتخاذهم الإجراء المناسب لهم. إلغاء الطلب - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. يحتاج سيجنال إلى إذن الوصول إلى الميكروفون لإرسال الرسائل الصوتية، ولكن تم إيقاف الإذن على نحو دائم. الرّجاء الاطلاع على إعدادات التطبيق ثم اختيار \"الأذونات\" وتفعيل \"الميكروفون\". + يحتاج سيجنال إلى إذنَي الوصول إلى الميكروفون والكاميرا من أجل الاتصال بـ%1$s ولكن تم إيقافهما على نحو دائم. الرجاء الاطلاع على إعدادات التطبيق ثم اختيار \"الأذونات\" وتفعيل \"الميكروفون\" و\"الكاميرا\". لالتقاط الصور والفيديوهات، الرجاء السماح لسيجنال بالوصول إلي الكاميرا. يحتاج سيجنال إلى إذن الوصول إلى الكاميرا من أجل التقاط صور وفيديو، ولكن تم إيقاف الإذن على نحو دائم. الرجاء الاطلاع على إعدادات التطبيق ثم اختيار \"الأذونات\"، ثم تفعيل \"الكاميرا\". @@ -423,7 +441,13 @@ سَتغادر هذه المجموعة، ثم ستُحذَف من كل أجهزتك. حذف حذف ثم المغادرة - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. انضم @@ -3822,6 +3846,14 @@ أرسل صورًا وفيديوهات وملفات من جهازك. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + إعداد الأمان @@ -4084,6 +4116,14 @@ دردشة بث + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + مجموعة جديدة الإعدادات @@ -4608,8 +4648,24 @@ مراجعة الأعضاء مُراجعة الطلب - %1$d من أعضاء المجموعة يحملون نفس الاسم، يُرجى مراجعة اﻷعضاء أسفله ثم القيام باﻹجراء المناسب. - إذا التبس عليك مصدر الطلب، يُرجى مراجعة جهات الاتصال أدناه والقيام بالإجراء المناسب. + + + %1$d group members have the same name, review the members below and choose to take action. + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + لا وجود لمجموعات أخرى مشتركة. لا وجود لمجموعات مشتركة. @@ -4648,12 +4704,28 @@ انضم %1$s انضم %1$s و %2$s انضم %1$s، %2$s و%3$s - انضم %1$s، %2$s و%3$d آخرون + + + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + غادر العضو %1$s غادر %1$s و %2$s غادر %1$s، %2$s و%3$s - غادر %1$s، %2$s و%3$d آخرون + + + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + أنت أنت (في جهاز آخر) @@ -5031,7 +5103,15 @@ محظورة - %1$d جهة اتصال + + + %1$d contacts + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + %1$d contacts + التراسُل الرسائل المختفية أمان التطبيق @@ -7332,5 +7412,20 @@ يَجري تنزيل البيانات الاحتياطية… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index f5d0a0f225..957d600d21 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -89,6 +89,16 @@ Signal, foto, video və ya səs əlavə etmək üçün Anbara müraciət icazəsi tələb edir, ancaq bu icazə birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələr menyusunda \"İcazələr\"i seçib \"Anbar\"ı fəallaşdırın. Signal, əlaqə məlumatlarını əlavə etmək üçün Əlaqələrə müraciət icazəsi tələb edir, ancaq bu icazə birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələr menyusunda \"İcazələr\"i seçib \"Əlaqələr\"i fəallaşdırın. Signal, yerləşmə məlumatlarını əlavə etmək üçün Yerləşməyə müraciət icazəsi tələb edir, ancaq bu icazə birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələr menyusunda \"İcazələr\"i seçib \"Yerləşmə\"ni fəallaşdırın. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s aktiv Ödənişlərə malik deyil @@ -386,8 +396,16 @@ Qoşulma tələbiniz qrup admininə göndərildi. Tələbiniz cavablandırılanda bildiriş alacaqsınız. Tələbdən imtina - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal, səsli mesaj göndərmək üçün Mikrofon icazəsini tələb edir, ancaq bu icazə birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələrində \"İcazələr\"i seçib \"Mikrofon\"u fəallaşdırın. + Signal-ın, %1$s əlaqəsinə zəng etmək üçün Mikrofon və Kamera icazələrinə ehtiyacı var, ancaq bu icazələr birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələrində \"İcazələr\"i seçib \"Mikrofon\" və \"Kamera\"nı fəallaşdırın. Foto və video çəkmək üçün, Signal-ın kameraya müraciətinə icazə verin. Signal-ın, foto və ya video çəkmək üçün Kamera icazəsinə ehtiyacı var, ancaq bu icazə birdəfəlik rədd edilib. Zəhmət olmasa tətbiq tənzimləmələrində \"İcazələr\"i seçib \"Kamera\"nı fəallaşdırın. @@ -411,7 +429,13 @@ Bu qrupu tərk edəcəksiniz və bütün cihazlarınızdan silinəcək. Sil Sil və tərk et - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Qoşul @@ -3434,6 +3458,14 @@ Cihazınızdan şəkillər, videolar və fayllar göndərin. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Təhlükəsizlik quraşdırması @@ -3688,6 +3720,14 @@ Çat Yayım + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Yeni qrup Tənzimləmələr @@ -4184,8 +4224,16 @@ Üzvləri nəzərdən keçir Tələbi nəzərdən keçir - %1$d qrup üzvü eyni ada sahibdir, aşağıda nəzərdən keçirərək seçim edə bilərsiniz. - Tələbin kimdən gəldiyinə əmin deyilsinizsə, aşağıdakı əlaqələri nəzərdən keçirib seçim edin. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Başqa ortaq qrup yoxdur. Heç bir ortaq qrup yoxdur. @@ -4216,12 +4264,20 @@ %1$s qoşuldu %1$s və %2$s qoşuldu %1$s, %2$s və %3$s qoşuldu - %1$s, %2$s və digər %3$d nəfər qoşuldu + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s nəfər qaldı %1$s və %2$s nəfər qaldı %1$s, %2$s və %3$s nəfər qaldı - %1$s, %2$s və digər %3$d nəfər qaldı + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Siz Siz (başqa cihazda) @@ -4591,7 +4647,11 @@ Bloklandı - %1$d əlaqə + + + %1$d contact + %1$d contacts + Mesajlaşma Yox olan mesajlar Tətbiq təhlükəsizliyi @@ -6748,5 +6808,20 @@ Ehtiyat nüsxə verilənləri endirilir… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index c21954633e..a67ac4d3ff 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -89,6 +89,16 @@ Signal се нуждае от достъп до вградения диск, за да може да прикачва снимки, видеа или аудио, но той му е отказан. Моля, отидете в настройки в менюто и изберете \"Разрешения\" и \"Дискове\". Signal се нуждае от достъп до контактите Ви, за да може да прикачва информация за тях, но той му е отказан. Моля, отидете на настройки в менюто и изберете \"Разрешения\" и \"Контакти\". Signal се нуждае от достъп до местоположението Ви, за да може да го прикачва, но той му е отказан. Моля, отидете на настройки в менюто и изберете \"Разрешения\" и \"Местоположение\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s няма активирани Плащания @@ -386,8 +396,16 @@ Заявката ви за присъединяване към групата е изпратена до администратора ѝ. Ще бъдете уведомени, когата те вземат решение. Отказване на заявката - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal се нуждае от достъп до микрофона Ви, за да може да изпраща аудио съобщения, но той му е отказан. Моля, отидете в настройки в менюто и изберете \"Разрешения\" и \"Микрофон\". + Signal се нуждае от достъп до микрофона и камерта Ви, за да може да се обади на %1$s, но той му е отказан. Моля, отидете на настройки в менюто и изберете \"Разрешения\", \"Микрофон\" и \"Камера\". За да прави снимки и видеа, Signal се нуждае от достъп до камерта Ви. Signal се нуждае от достъп до камерта Ви, за да може да прави снимки или видеа, но той му е отказан. Моля, отидете в настройки в менюто и изберете \"Разрешения\" и \"Камера\". @@ -411,7 +429,13 @@ Ще напуснете тази група и тя ще бъде изтрита от всичките ви устройства. Изтриване Изтриване и напускане - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Присъедини се @@ -3434,6 +3458,14 @@ Изпращайте снимки, видеоклипове и файлове от вашето устройство. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Настройка на сигурността @@ -3688,6 +3720,14 @@ Чат Предаване + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Нова група Настройки @@ -4184,8 +4224,16 @@ Преглед на членовете Преглед на заявката - %1$d членовете на групата имат същото име, прегледайте членовете по-долу и изберете действия. - Ако не сте сигурни от кого е искането, прегледайте контактите по-долу и предприемете действие. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Няма други общи групи. Няма общи групи. @@ -4216,12 +4264,20 @@ %1$s се присъедини %1$s и %2$s се присъединиха %1$s, %2$s и %3$s се присъединиха - %1$s, %2$s и %3$d други се присъединиха + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s напусна %1$s и %2$s напуснаха %1$s, %2$s и %3$s напуснаха - %1$s, %2$s и %3$d други напуснаха + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Вие Тебе (на друго устройство) @@ -4591,7 +4647,11 @@ Блокиран - %1$d контакта + + + %1$d contact + %1$d contacts + Съобщения Изчезващи съобщения Сигурност @@ -6748,5 +6808,20 @@ Изтегляне на данни от резервно копие… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index b190b6874b..ac52b9ade1 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -89,6 +89,16 @@ ছবি, ভিডিও অথবা অডিও সংযুক্ত করার জন্য Signal এর স্টোরেজ ব্যাবহারের অনুমতির প্রয়োজন কিন্তু এর উপর স্থায়ী নিষেধাজ্ঞা জারি করা হয়েছে। দয়া করে আ্যপ সেটিংস-এ যান, \"অনুমতি\" নির্বাচন করুন এবং \"স্টোরেজ\" সক্ষম করুন। পরিচিতিসমূহের তথ্য সংযুক্ত করার জন্য Signal এর পরিচিতিসমূহ ব্যাবহারের অনুমতির প্রয়োজন কিন্তু এর উপর স্থায়ী নিষেধাজ্ঞা জারি করা হয়েছে। দয়া করে আ্যপ সেটিংসে যান, \"অনুমতি\" নির্বাচন করুন এবং \"পরিচিতিসমূহ\" সক্ষম করুন। স্থান সংযুক্ত করার জন্য Signal এর স্থান ব্যাবহারের অনুমতির প্রয়োজন কিন্তু এর উপর স্থায়ী নিষেধাজ্ঞা জারি করা হয়েছে। দয়া করে আ্যপ সেটিংসে যান, \"অনুমতি\" নির্বাচন করুন এবং \"স্থান\" সক্ষম করুন। + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s পেমেন্ট সক্রিয় করেননি @@ -386,8 +396,16 @@ আপনার গ্রুপে ঢোকার অনুরোধ গ্রুপের অ্যাডমিনদের কাছে পাঠানো হয়েছে। তারা কোনো পদক্ষেপ নিলে আপনাকে জানানো হবে। অনুরোধ বাতিল করুন - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. অডিও বার্তাগুলি প্রেরণের জন্য Signal এর মাইক্রোফোনের অনুমতি প্রয়োজন, কিন্তু এর উপর স্থায়ীভাবে নিষেধাজ্ঞা আরোপ করা হয়েছে। দয়া করে অ্যাপ্লিকেশন সেটিংসে যান, \"অনুমতিগুলি\" নির্বাচন করুন এবং \"মাইক্রোফোন\" সক্ষম করুন| + %1$s কে ফোন করতে Signal এর মাইক্রোফোন এবং ক্যামেরা ব্যাবহারের অনুমতির প্রয়োজন, কিন্তু এর উপর স্থায়ীভাবে নিষেধাজ্ঞা আরোপ করা হয়েছে। দয়া করে এপ সেটিংসে যান, \"অনুমতি\" নির্বাচন করুন এবং \"মাইক্রোফোন\" ও \"ক্যামেরা\" এর অনুমতি সক্ষম করুন। ছবি ও ভিডিও তুলতে Signal কে ক্যামেরা ব্যাবহারের অনুমতি দিন। ছবি অথবা ভিডিও তুলতে Signal এর ক্যামেরা ব্যাবহারের অনুমতির প্রয়োজন, কিন্তু এর উপর স্থায়ীভাবে নিষেধাজ্ঞা আরোপ করা হয়েছে। দয়া করে অ্যাপ সেটিংসে যান, \"অনুমতি\" নির্বাচন করুন এবং \"ক্যামেরা\" এর অনুমতি সক্ষম করুন। @@ -411,7 +429,13 @@ আপনি এই গ্রুপ ছেড়ে চলে যাবেন এবং এটি আপনার সকল ডিভাইস থেকে মুছে ফেলা হবে। মুছে ফেলুন মুছুন এবং ছেড়ে যান - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. যোগদান করুন @@ -3434,6 +3458,14 @@ আপনার ডিভাইস থেকে ছবি, ভিডিও ও ফাইল পাঠান। + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + নিরাপত্তা সেটআপ @@ -3688,6 +3720,14 @@ চ্যাট ব্রডকাস্ট + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + নতুন গ্রুপ সেটিংস @@ -4184,8 +4224,16 @@ সদস্যের পর্যালোচনা করুন অনুরোধ পর্যালোচনা করুন - %1$d জন গ্রুপ সদস্যদের একই নাম রয়েছে, নীচের সদস্যদের পর্যালোচনা করুন এবং পদক্ষেপ নিন। - আপনি যদি নিশ্চিত না হন কার থেকে অনুরোধ এসেছে, আগে কন্টাক্টটি পরিদর্শন সিদ্ধান্ত নিন + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + একই রকমের আর কোন গ্রুপ নেই একই রকমের কোন গ্রুপ নেই @@ -4216,12 +4264,20 @@ %1$s যোগ দিয়েছেন %1$s এবং %2$s যোগ দিয়েছে %1$s, %2$s এবং %3$s যোগ দিয়েছে - %1$s, %2$s, এবং অন্যান্য %3$dজন যোগ দিয়েছে + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s চলে গেছে %1$s এবং %2$s চলে গেছে %1$s, %2$s এবং%3$s চলে গেছে - %1$s, %2$s এবং অন্যান্য %3$d জন চলে গেছে + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + আপনি আপনি (অন্য ডিভাইসে আছেন) @@ -4591,7 +4647,11 @@ ব্লক করা হয়েছে - %1$d পরিচিতিসমূহ + + + %1$d contact + %1$d contacts + বাদানুবাদ অদৃশ্য বার্তা অ্যাপ এর নিরাপত্তা @@ -6748,5 +6808,20 @@ ব্যাকআপ ডেটা ডাউনলোড করা হচ্ছে… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index b3165cd961..e938380dc8 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -89,6 +89,16 @@ Signalu je potrebno dopuštenje da pristupi memoriji uređaja kako bi mogao priložiti slike, video i audiozapise, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavku \"Prostor za pohranu\". Signalu je potrebno dopuštenje da pristupi kontaktima kako bi mogao priložiti informacije o njima, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavku \"Kontakti\". Signalu je potrebno dopuštenje da sazna lokaciju uređaja, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavku \"Lokacija\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s nije aktivirao/la Plaćanja @@ -392,8 +402,16 @@ Vaš zahtjev za pristupanje poslan je administratoru grupe. Bit ćete obaviješteni kad ga on/a razmotri. Poništi zahtjev - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signalu je potrebno dopuštenje da pristupi mikrofonu kako bi mogao slati zvučne poruke, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavku \"Mikrofon\". + Signalu je potrebno dopuštenje da pristupi mikrofonu i kameri kako bi mogao nazvati %1$s, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavke \"Mikrofon\" i \"Kamera\". Da biste slikali i snimali, dozvolite Signalu da pristupi kameri. Signalu je potrebno dopuštenje da pristupi kameri kako bi mogao slikati i snimati, ali je ono trajno uskraćeno. Molimo nastavite do postavki aplikacije, odaberite \"Dozvole\" i aktivirajte stavku \"Kamera\". @@ -417,7 +435,13 @@ Napustit ćete ovu grupu i ona će biti izbrisana sa svih Vaših uređaja. Izbriši Izbriši i napusti - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Pristupi @@ -3628,6 +3652,14 @@ Šaljite fotografije, video zapise i fajlove sa svog uređaja. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Postavljanje sigurnosti @@ -3886,6 +3918,14 @@ Chat Emitovanje + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nova grupa Podešavanja @@ -4396,8 +4436,20 @@ Pregledajte članove Pregledajte zahtjev - %1$d člana grupe imaju isto ime. Provjerite članove ispod i odlučite kako s njima postupiti. - Ako niste sigurni od koga potječe zahtjev, provjerite kontakte ispod i odlučite kako postupiti. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nema drugih zajedničkih grupa. Nema zajedničkih grupa. @@ -4432,12 +4484,24 @@ %1$s se pridružio/la %1$s i %2$s su se pridružili %1$s, %2$s i %3$s su se pridružili - %1$s, %2$s i %3$d drugih su se pridružili + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s je otišao/la %1$s i %2$s su otišli %1$s, %2$s i %3$s su otišli - %1$s, %2$s i %3$d drugih su otišli + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Vi Vi (na drugom uređaju) @@ -4811,7 +4875,13 @@ Blokirano - %1$d kontakata + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Poruke Nestajuće poruke Sigurnost aplikacije @@ -7040,5 +7110,20 @@ Preuzimanje sigurnosne kopije podataka… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index c2d350e9c9..a5bbce3ee8 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -89,6 +89,16 @@ El Signal necessita el permís de l\'emmagatzematge per tal d\'adjuntar fotografies, vídeos o àudio, però s\'ha denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi l\'emmagatzematge. El Signal necessita el permís de l\'aplicació dels contactes per tal d\'adjuntar-ne informació, però s\'ha denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi els contactes. El Signal necessita el permís de la ubicació per tal d\'adjuntar-ne informació, però s\'ha denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi la ubicació. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s no ha activat Pagaments @@ -386,8 +396,16 @@ La sol·licitud per afegir-vos-hi s\'ha enviat a l\'administrador/a del grup. Rebreu una notificació quan la resolguin. Cancel·la la sol·licitud - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. El Signal necessita el permís del micròfon per tal d\'enviar missatges d\'àudio, però s\'ha denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi el micròfon. + El Signal necessita el permís del micròfon i de la càmera per tal de trucar a %1$s, però s\'han denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi el micròfon i la càmera. Per captar fotografies i vídeos, permeteu que el Signal tingui accés a la càmera. El Signal necessita el permís de la càmera per tal de fer fotografies i vídeos, però s\'ha denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i activeu-hi la càmera. @@ -411,7 +429,13 @@ Abandonaràs aquest grup i s\'esborrarà de tots els dispositius. Esborra Esborra i surt - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Afegeix-m\'hi @@ -3434,6 +3458,14 @@ Envia fotos, vídeos i arxius des del teu dispositiu. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Configuració de seguretat @@ -3688,6 +3720,14 @@ Xat Difusió + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Grup nou Configuració @@ -4184,8 +4224,16 @@ Reviseu els membres Reviseu la sol·licitud - %1$d membres del grup tenen el mateix nom. Reviseu els membres que es mostren a continuació. - Si no esteu segur de qui prové la sol·licitud, reviseu els contactes que hi ha a continuació. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Cap altre grup en comú Cap grup en comú @@ -4216,12 +4264,20 @@ %1$s s\'hi ha afegit. %1$s i %2$s s\'hi han afegit. %1$s, %2$s i %3$s s\'hi han afegit. - %1$s, %2$s i %3$d més s\'hi han afegit. + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s n\'ha sortit. %1$s i %2$s n\'han sortit. %1$s, %2$s i %3$s n\'han sortit. - %1$s, %2$s i %3$d més n\'han sortit. + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Vós Vós (en un altre dispositiu) @@ -4591,7 +4647,11 @@ Bloquejat - %1$d contactes + + + %1$d contact + %1$d contacts + Missatges Missatges efímers Seguretat de l\'aplicació @@ -6748,5 +6808,20 @@ S\'està descarregant la còpia de seguretat… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index def7046e55..41320f5890 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -89,6 +89,16 @@ Signal potřebuje oprávnění pro přístup k úložišti, aby mohl připojovat fotky, videa nebo audio, ale toto oprávnění je nyní zakázáno. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Úložiště\". Signal potřebuje oprávnění pro přístup ke kontaktům, aby mohl připojit informace o kontaktu, ale toto oprávnění je nyní zakázáno. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Kontakty\". Signal potřebuje oprávnění pro přístup k poloze, aby mohl připojit informace o poloze, ale toto oprávnění je nyní zakázáno. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Poloha\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s nemá aktivované Platby @@ -392,8 +402,16 @@ Váš požadavek na připojení ke skupině byl zaslán správci. Budete upozorněni, jakmile zareaguje. Zrušit požadavek - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal potřebuje oprávnění pro přístup k mikrofonu, aby mohl poslat audio zprávu, ale toto oprávnění je nyní zakázáno. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Mikrofon\". + Signal potřebuje oprávnění pro přístup k mikrofonu a fotoaparátu, abyste mohli volat %1$s, ale tato oprávnění jsou nyní zakázána. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Mikrofon\" a \"Fotoaparát\". Pro pořizování fotografií nebo videa potřebuje Signal přístup k fotoaparátu. Signal potřebuje oprávnění pro přístup k fotoaparátu, aby mohl pořizovat fotografie nebo video, ale toto oprávnění je nyní zakázáno. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Fotoaparát\". @@ -417,7 +435,13 @@ Tuto skupinu opustíte a bude odstraněna ze všech vašich zařízení. Odstranit Odstranit a opustit - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Připojit @@ -3628,6 +3652,14 @@ Posílejte fotky, videa a soubory přímo ze svého zařízení. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Nastavení zabezpečení @@ -3886,6 +3918,14 @@ Chatovat Rozeslat + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nová skupina Nastavení @@ -4396,8 +4436,20 @@ Zkontrolovat členy Zkontrolovat žádost - %1$d členů skupiny má stejné jméno, prověřte níže uvedené členy a zvolte akci. - Pokud si nejste jistí, od koho je požadavek, tak prověřte kontakty a proveďte akci. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Žádné další společné skupiny. Žádné společné skupiny. @@ -4432,12 +4484,24 @@ %1$s se připojil %1$s a %2$s se připojili %1$s, %2$s a %3$s se připojili. - %1$s, %2$s, %3$d a další se připojili + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s odešel %1$s a %2$s odešli %1$s, %2$s a %3$s odešli - %1$s, %2$s ,%3$d a další odešli + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Vy Vy (na jiném zařízení) @@ -4811,7 +4875,13 @@ Zablokováno - %1$d kontakty + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Odesílání zpráv Mizející zprávy Zabezpečení aplikace @@ -7040,5 +7110,20 @@ Stahování záložních dat… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 4851f21d91..4113112dfa 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -89,6 +89,16 @@ Signal beder om tilladelse til at tilgå din lagerplads, for at kunne vedhæfte billeder, videoer eller lydfiler, hvilket er blevet nægtet. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Lagerplads\". Signal beder om tilladelse til at tilgå dine kontakter, for at kunne vedhæfte kontaktinformation, hvilket er blevet nægtet. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Kontakter\". Signal beder om tilladelse til at tilgå din placering, for at kunne vedhæfte placeringer, hvilket er blevet nægtet. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Placering\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s har ikke aktiveret betalinger @@ -386,8 +396,16 @@ Din anmodning om at blive medlem er sendt til gruppeadministratoren. Du får besked, når den er behandlet. Annullér anmodning - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal beder om tilladelse til at tilgå mikrofonen for at kunne sende lydfiler, hvilket er blevet nægtet. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Mikrofon\". + Signal beder om tilladelse til at tilgå mikrofon og kamera, for at kunne ringe til %1$s, men er permanent blevet afvist. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Mikrofon\" og \"Kamera\". Giv Signal tilladelse til at tilgå dit kamera for at tage billeder og optage video. Signal beder om tilladelse til at tilgå dit kamera, for at kunne tage billeder eller optage video, hvilket er blevet nægtet. Gå venligst til appindstillinger, vælg \"Tilladelser\" og tilvælg \"Kamera\". @@ -411,7 +429,13 @@ Du forlader gruppen, og den vil blive slettet fra alle dine enheder. Slet Slet og forlad - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Deltag @@ -3434,6 +3458,14 @@ Send billeder, videoer og filer fra din enhed. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Sikkerhedsindstillinger @@ -3688,6 +3720,14 @@ Chat Transmission + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Ny gruppe Indstillinger @@ -4184,8 +4224,16 @@ Gennemgå medlemmer Gennemgå anmodning - %1$d medlemmer af gruppen har det samme navn, gennemgå medlemmer herunder og vælg en handling. - Hvis du ikke er sikker på hvem forespørgslen er fra, gennemgå kontakterne herunder og vælg en handling. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Ingen andre grupper til fælles Ingen grupper til fælles @@ -4216,12 +4264,20 @@ %1$s sluttede sig til opkaldet %1$s og %2$s sluttede sig til opkaldet %1$s, %2$s og %3$s sluttede sig til opkaldet - %1$s, %2$s og %3$d andre sluttede sig til opkaldet + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s forlod opkaldet %1$s og %2$s forlod opkaldet %1$s, %2$s og %3$s forlod opkaldet - %1$s, %2$s og %3$d andre forlod opkaldet + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Dig Dig (på en anden enhed) @@ -4591,7 +4647,11 @@ Blokeret - %1$d kontakter + + + %1$d contact + %1$d contacts + Beskeder Forsvindende beskeder App-sikkerhed @@ -6748,5 +6808,20 @@ Downloader sikkerhedskopidata… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b35be66d38..1075d4acc3 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -89,6 +89,16 @@ Signal benötigt die Berechtigung »Speicher« für das Anhängen von Fotos, Videos oder Audiodateien, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Speicher«. Signal benötigt die Berechtigung »Kontakte« für das Anhängen von Kontaktinformationen, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Kontakte«. Signal benötigt die Berechtigung »Standort« für das Anhängen von Standorten, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Standort«. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s hat Zahlungen nicht aktiviert @@ -386,8 +396,16 @@ Deine Beitrittsanfrage wurde an den Gruppen-Admin gesendet. Du wirst über die Entscheidung benachrichtigt. Anfrage abbrechen - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal benötigt die Berechtigung »Mikrofon« für das Senden von Sprachnachrichten, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Mikrofon«. + Signal benötigt die Berechtigungen »Mikrofon« und »Kamera«, um %1$s anzurufen, diese wurden jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Mikrofon« und »Kamera«. Zum Aufnehmen von Fotos und Videos erlaube Signal Zugriff auf deine Kamera. Signal benötigt die Berechtigung »Kamera« für die Aufnahme von Fotos oder Videos, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Kamera«. @@ -411,7 +429,13 @@ Du wirst diese Gruppe verlassen und sie wird von all deinen Geräten gelöscht werden. Löschen Löschen und verlassen - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Beitreten @@ -3227,7 +3251,7 @@ Sprache Signal-Nachrichten und ‑Anrufe Erweiterte PIN-Einstellungen - Kostenlos und verschlüsselt mit Signal-Nutzern Nachrichten austauschen und telefonieren + Kostenlos und verschlüsselt mit Signal-Kontakten Nachrichten austauschen und telefonieren Diagnoseprotokoll übermitteln Dein Konto löschen Kompatibilität WLAN-Telefonie @@ -3434,6 +3458,14 @@ Sende Fotos, Videos und Dateien von deinem Gerät. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Sicherheitseinstellungen @@ -3688,6 +3720,14 @@ Chat Broadcast + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Neue Gruppe Einstellungen @@ -4184,8 +4224,16 @@ Mitglieder prüfen Anfrage prüfen - %1$d Gruppenmitglieder haben denselben Namen. Prüfe die folgenden Mitglieder und handle entsprechend. - Falls du dir über die Herkunft der Anfrage unsicher bist, prüfe die folgenden Kontakte und handle entsprechend. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Keine anderen gemeinsamen Gruppen. Keine gemeinsamen Gruppen. @@ -4216,12 +4264,20 @@ %1$s ist beigetreten %1$s und %2$s sind beigetreten %1$s, %2$s und %3$s sind beigetreten - %1$s, %2$s und %3$d weitere Personen sind beigetreten + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ist ausgetreten %1$s und %2$s sind ausgetreten %1$s, %2$s und %3$s sind ausgetreten - %1$s, %2$s und %3$d weitere Personen sind ausgetreten + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Du Du (auf einem anderen Gerät) @@ -4591,7 +4647,11 @@ Blockiert - %1$d Nutzer + + + %1$d contact + %1$d contacts + Nachrichtenübermittlung Verschwindende Nachrichten App-Sicherheit @@ -5436,7 +5496,7 @@ Betrag - Vielen Dank, dass du Signal unterstützt. Deine Spende trägt dazu bei, die Entwicklung von Open-Source-Datenschutztechnologien voranzutreiben, die die freie Meinungsäußerung schützen und eine sichere globale Kommunikation für Millionen von Menschen auf der ganzen Welt ermöglichen. Falls du deinen Wohnsitz in den USA hast, bewahre bitte diese Bescheinigung für deine Steuerunterlagen auf. Die Signal Technology Foundation ist eine steuerbefreite gemeinnützige Organisation in den USA gemäß Abschnitt 501c3 des Internal Revenue Code. Unsere Federal Tax ID lautet 82-4506840. + Vielen Dank, dass du Signal unterstützt. Deine Spende trägt dazu bei, die Entwicklung von Open-Source-Datenschutztechnologien voranzutreiben, die die freie Meinungsäußerung schützen und eine sichere globale Kommunikation für Millionen von Menschen auf der ganzen Welt ermöglichen. Falls du deinen Wohnsitz in den USA hast, bewahre bitte diese Bescheinigung für deine Steuerunterlagen auf. Die Signal Stiftung (Signal Technology Foundation) ist eine steuerbefreite gemeinnützige Organisation in den USA gemäß Abschnitt 501c3 des Internal Revenue Code. Unsere Federal Tax ID lautet 82-4506840. %1$s - %2$s @@ -6748,5 +6808,20 @@ Sicherungsdaten werden heruntergeladen… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 48bec2e4ac..5ee0f7900c 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -89,6 +89,16 @@ Το Signal χρειάζεται τα δικαιώματα Αποθηκευτικού Χώρου για την επισύναψη φωτογραφιών, βίντεο ή κομματιών ήχου, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επέλεξε τα «Δικαιώματα», και ενεργοποίησε τον «Αποθηκευτικό Χώρο». Το Signal χρειάζεται τα δικαιώματα Επαφών για την επισύναψη πληροφοριών επαφών, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επέλεξε τα «Δικαιώματα», και ενεργοποίησε τις «Επαφές». Το Signal χρειάζεται τα δικαιώματα Τοποθεσίας για την επισύναψη τοποθεσιών, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επέλεξε τα «Δικαιώματα», και ενεργοποίησε την «Τοποθεσία». + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + Ο χρήστης %1$s δεν έχει ενεργοποιήσει τις πληρωμές @@ -386,8 +396,16 @@ Το αίτημά σου για να μπεις στην ομάδα στάλθηκε στον διαχειριστή/τρια της ομάδας. Θα ενημερωθείς όταν λάβει απόφαση. Ακύρωση αιτήματος - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Το Signal χρειάζεται τα δικαιώματα μικροφώνου για την αποστολή μηνυμάτων ήχου, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επίλεξε τα «Δικαιώματα», και ενεργοποίησε το «Μικρόφωνο». + Το Signal χρειάζεται τα δικαιώματα μικροφώνου και κάμερας για να καλέσεις τον/την %1$s, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επέλεξε τα «Δικαιώματα», και ενεργοποίησε το «Μικρόφωνο» και «Κάμερα». Για να τραβήξεις φωτογραφίες και βίντεο, δώσε στο Signal πρόσβαση στην κάμερα. Το Signal χρειάζεται τα δικαιώματα κάμερας για τη λήψη φωτογραφίων και βίντεο, αλλά αυτά δεν έχουν δοθεί μόνιμα. Πήγαινε στις ρυθμίσεις εφαρμογών, επέλεξε τα «Δικαιώματα», και ενεργοποίησε την «Κάμερα». @@ -411,7 +429,13 @@ Θα αποχωρήσεις από αυτή την ομάδα και θα διαγραφεί από όλες τις συσκευές σου. Διαγραφή Διαγραφή και αποχώρηση - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Μπες στην κλήση @@ -3434,6 +3458,14 @@ Στείλε φωτογραφίες, βίντεο και αρχεία από τη συσκευή σου. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Ρύθμιση ασφάλειας @@ -3688,6 +3720,14 @@ Συνομιλία Αποστολή σε όλους/ες + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Νέα ομάδα Ρυθμίσεις @@ -4184,8 +4224,16 @@ Ανασκόπηση μελών Εξέταση αιτήματος - %1$d μέλη της ομάδας έχουν το ίδιο όνομα, εξέτασε τα παρακάτω μέλη, και διάλεξε τί ενέργεια να κάνεις. - Αν δεν είσαι σίγουρος/η από ποιόν είναι το αίτημα, εξέτασε τις παρακάτω επαφές, και κάνε κάποια ενέργεια. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Καμμία άλλη κοινή ομάδα Καμμία κοινή ομάδα. @@ -4216,12 +4264,20 @@ Ο/Η %1$s μπήκε Οι %1$s και %2$s μπήκαν Οι %1$s, %2$s και %3$s μπήκαν - Οι %1$s, %2$s και %3$d ακόμα μπήκαν + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + Ο/Η %1$s έφυγε Οι %1$s και %2$s έφυγαν Οι %1$s, %2$s και %3$s έφυγαν - Οι %1$s, %2$s και %3$d ακόμα έφυγαν + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Εσύ Εσύ (από άλλη συσκευή) @@ -4591,7 +4647,11 @@ Λίστα αποκλεισμού - %1$d επαφές + + + %1$d contact + %1$d contacts + Συνομιλίες Μηνύματα που εξαφανίζονται Ασφάλεια εφαρμογής @@ -6748,5 +6808,20 @@ Λήψη δεδομένων αντιγράφου ασφαλείας… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 24270fa9d0..f1afcc8039 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -89,6 +89,16 @@ Signal necesita acceso al almacenamiento de tu teléfono para adjuntar fotos, vídeos o audio. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Almacenamiento». Signal necesita acceso a los contactos en tu teléfono para adjuntar información de personas en tus chats. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Contactos». Signal necesita acceso a tu posición para adjuntar la información en tus chats. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Ubicación». + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s no ha activado Pagos @@ -386,8 +396,16 @@ Se ha enviado tu solicitud para unirte al grupo al admin. Te llegará una notificación con su respuesta. Cancelar solicitud - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal necesita acceso al micrófono para enviar notas de voz. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Micrófono». + Signal necesita acceso al micrófono y cámara para llamar a %1$s. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Micrófono» y «Cámara». Para hacer fotos y vídeos, permite el acceso de Signal a la cámara. Signal necesita acceso a la cámara para tomar fotos o vídeos. Por favor, ve a la aplicación «Ajustes», selecciona Signal en el menú «Aplicaciones y notificaciones» y en «Permisos» activa «Cámara». @@ -411,7 +429,13 @@ Abandonarás este grupo, que se eliminará de todos tus dispositivos. Eliminar Eliminar y abandonar - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Unirse @@ -3434,6 +3458,14 @@ Envía fotos, vídeos y archivos desde tu dispositivo. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Configuración de seguridad @@ -3688,6 +3720,14 @@ Chat Transmisión + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nuevo grupo Ajustes @@ -4184,8 +4224,16 @@ Revisar participantes Revisar solicitud - %1$d participantes usan el mismo nombre, revisa la lista de participantes debajo y selecciona la opción correcta. - Si no sabes quién ha enviado esta solicitud, revisa la lista de personas debajo para tomar la decisión correcta. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + No hay otros grupos en común. No hay grupos en común. @@ -4216,12 +4264,20 @@ %1$s se ha unido %1$s y %2$s se han unido %1$s, %2$s y %3$s se han unido - %1$s, %2$s y %3$d más se han unido + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s se ha ido %1$s y %2$s se han ido %1$s, %2$s y %3$s se han ido - %1$s, %2$s y %3$d más se han ido + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Tú (en otro dispositivo) @@ -4591,7 +4647,11 @@ Personas bloqueadas - %1$d contactos + + + %1$d contact + %1$d contacts + Mensajería Desaparición de mensajes Seguridad de la aplicación @@ -6748,5 +6808,20 @@ Descargando copia de seguridad… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 83bc6b5bb7..507ab60ae9 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -89,6 +89,16 @@ Signal vajab fotode, videote või audiofailide manustamiseks ligipääsu salvestusmeediale, kuid see on püsivalt keelatud. Palun ava rakenduse seadete menüü, vali \"Õigused\" ja luba \"Salvestusmeedia\". Signal vajab kontaktide manustamiseks ligipääsu kontaktidele, kuid see on püsivalt keelatud. Palun ava rakenduse sätete menüü, vali \"Õigused\" ja luba \"Kontaktid\". Signal vajab asukoha manustamiseks ligipääsu seadme asukohale, kuid see on püsivalt keelatud. Palun ava rakenduse sätete menüü, vali \"Õigused\" ja luba \"Asukoht\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ei ole makseid aktiveerinud @@ -386,8 +396,16 @@ Sinu taotlus grupiga liitumiseks on grupi administraatoritele saadetud. Sulle antakse teada, kui nad vastavad. Tühista taotlus - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal vajab audiosõnumite saatmiseks ligipääsu seadme mikrofonile, kuid see on püsivalt keelatud. Palun ava rakenduse sätete menüü, vali \"Õigused\" ja luba \"Mikrofon\". + Signal vajab kasutajale %1$s helistamiseks ligipääsu kaamerale ja mikrofonile, kuid need on püsivalt keelatud. Palun ava rakenduse sätete menüü, vali \"Õigused\" ja luba \"Mikrofon\" ning \"Kaamera\". Luba Signalile ligipääs kaamerale fotode ja videote salvestamiseks. Signal vajab ligipääsu kaamerale, et salvestada fotosid ja videosid, kuid see on püsivalt keelatud. Palun ava rakenduse sätete menüü, vali \"Õigused\" ja luba \"Kaamera\". @@ -411,7 +429,13 @@ Sa lahkud sellest grupist ja see kustutatakse kõigist sinu seadmetest. Kustuta Kustuta ja lahku - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Liitu @@ -3434,6 +3458,14 @@ Saada oma seadmest fotosid, videoid ja faile. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Turvasätted @@ -3688,6 +3720,14 @@ Vestlus Kanna üle + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Uus grupp Seaded @@ -4184,8 +4224,16 @@ Vaata liikmed üle Vaata taotlus üle - %1$d grupi liiget on sama nimega, kontrolli allpool liikmeid ja vali, mida teha. - Kui sa pole kindel, kellelt soov on, siis vaata kontaktid allpool üle ja vali tegevus. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Muid ühised gruppe pole. Ühised grupid puuduvad @@ -4216,12 +4264,20 @@ %1$s liitus %1$s ja %2$s liitusid %1$s, %2$s ja %3$s liitusid - %1$s, %2$s ja %3$d muud liitusid + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s lahkus %1$s ja %2$s lahkusid %1$s, %2$s ja %3$s lahkusid - %1$s, %2$s ja %3$d teist lahkusid + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Sina Sina (teisest seadmest) @@ -4591,7 +4647,11 @@ Blokeeritud - %1$d kontakti + + + %1$d contact + %1$d contacts + Sõnumside Kaduvad sõnumid Rakenduse turvalisus @@ -6748,5 +6808,20 @@ Varukoopia andmete allalaadimine … + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 88a90dd83c..d31d9e1b56 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -89,6 +89,16 @@ Signal aplikazioak Gordailurako baimena behar du argazkiak, bideoak edo audioak bidaltzeko, baina ukatu egin diozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Gordailua\". Signal aplikazioak Kontaktuetarako baimena behar du kontaktuak atzitu ahal izateko, baina ukatu egin diozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Kontaktuak\". Signal aplikazioak Kokapenerako baimena behar du kokalekuak bidali ahal izateko baina ukatu egin diozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Kokalekua\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s(e)k ez ditu aktibatu ordainketak @@ -386,8 +396,16 @@ Taldean sartzeko zure eskaera taldeko administratzaileari bidali zaio. Jakinaraziko zaizu erabaki bat hartzen duenean. Bertan behera utzi eskaera - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signalek Mikrofonorako baimena behar du audio mezuak bidaltzeko, baina ukatu egin diozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Mikrofonoa\" + Signal aplikazioak Mikronofoa eta Kamera baimenak behar ditu %1$skontaktuari deitzeko, baina ukatu egin dizkiozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Mikrofonoa\" eta \"Kamera\" Argazkiak eta bideoak grabatzeko, eman baimena Signali kamera erabiltzeko. Signal aplikazioak Kamera baimena behar du argazkiak eta bideoak egiteko, baina ukatu egin diozu. Joan aplikazioaren ezarpenetara, aukeratu \"Baimenak\" eta aktibatu \"Kamera\". @@ -411,7 +429,13 @@ Talde honetatik irtengo zara, eta gailu guztietatik ezabatuko da. Ezabatu Ezabatu eta irten - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Batu @@ -3434,6 +3458,14 @@ Bidali argazkiak, bideoak eta fitxategiak zure gailutik. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Segurtasun-konfigurazioa @@ -3688,6 +3720,14 @@ Txata Difusioa + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Talde berria Ezarpenak @@ -4184,8 +4224,16 @@ Berrikusi kideak Berrikusi eskaera - Taldean %1$d kide izen bera dute; egiaztatu azpian agertzen diren kideak eta ekintza bat hautatu. - Ziur ez bazaude eskaera nork bidali duen, egiaztatu azpian agertzen diren kontaktuak eta ekintza bat hautatu. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Ez dago komuna den beste talderik. Ez dago komuna den talderik. @@ -4216,12 +4264,20 @@ %1$s batu da %1$s eta %2$s batu dira %1$s, %2$s eta %3$s batu dira - %1$s, %2$s eta beste %3$d batu dira + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s joan da %1$s eta %2$s joan dira %1$s, %2$s eta %3$s joan dira - %1$s, %2$s eta beste %3$d joan dira + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Zu Zu (beste gailu batean) @@ -4591,7 +4647,11 @@ Blokeatuta - %1$dkontaktu + + + %1$d contact + %1$d contacts + Mezularitza Mezuen desagerpena App segurtasuna @@ -6748,5 +6808,20 @@ Babeskopietako datuak deskargatzen… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 29eb38755e..66c64c8792 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -89,6 +89,16 @@ سیگنال برای پیوست کردن فایل‌های صوتی و تصویری به مجوز ذخیره‌سازی نیاز دارد، اما این مجوز به صورت دائمی رد شده است. لطفاً به منوی تنظیمات برنامه رفته، «مجوزها» را انتخاب کرده، و «ذخیره‌سازی» را فعال کنید. سیگنال برای پیوست کردن اطلاعات مخاطب به مجوز مخاطبین نیاز دارد، اما این مجوز به صورت دائمی رد شده است. لطفاً به منوی تنظیمات برنامه رفته، «مجوزها» را انتخاب کرده، و «مخاطبین» را فعال کنید. سیگنال برای پیوست کردن موقعیت مکانی به مجوز مکان نیاز دارد، اما این مجوز به صورت دائمی رد شده است. لطفاً به منوی تنظیمات برنامه رفته، «مجوزها» را انتخاب کرده، و «مکان» را فعال کنید. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s پرداخت‌ها را فعال نکرده است @@ -386,8 +396,16 @@ درخواست شما برای پیوستن به مدیر گروه ارسال شد. هنگامی که اقدامی انجام دهند مطلع خواهید شد. لغو درخواست - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. سیگنال برای ارسال پیام های صوتی نیاز به مجوز میکروفون دارد، ولی این اجازه به صورت دائم رد شده است. لطفاً به تنظیمات برنامه رفته، «مجوزها» را انتخاب کنید و «میکروفون» را فعال کنید. + سیگنال نیاز به مجوزها‌ی دوربین و میکروفون دارد تا بتواند با %1$s تماس بگیرد، اما دسترسی به آن‌ها به صورت دائم رد شده است. لطفاً به قسمت تنظیمات برنامه رفته، «مجوزها» را انتخاب کرده، «دوربین» و «میکروفون» را فعال کنید. برای ضبط عکس و ویدئو، به سیگنال اجازه دهید به دوربین شما دسترسی داشته باشد. برای گرفتن عکس یا ضبط ویدئو سیگنال نیاز به مجوز دوربین دارد، اما دسترسی به آن رد شده است. لطفاً به تنظیمات برنامه رفته، «مجوزها» را انتخاب کرده، «دوربین» را فعال کنید. @@ -411,7 +429,13 @@ شما این گروه را ترک خواهید کرد، و گروه از روی تمام دستگاه‌های شما پاک خواهد شد. پاک کردن پاک کردن و ترک گروه - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. پیوستن @@ -3434,6 +3458,14 @@ از دستگاه‌تان عکس، ویدیو و فایل ارسال کنید. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + راه‌اندازی امنیتی @@ -3688,6 +3720,14 @@ گفتگو پخش + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + گروه جدید تنظیمات @@ -4184,8 +4224,16 @@ بازبینی اعضا درخواست بازبینی - %1$d عضو گروه نام یکسان دارند، اعضا را در پایین مرور کنید و اقدام لازم را انجام دهید. - اگر مطمئن نیستید که فرستندهٔ درخواست چه کسی است، مخاطبین را در پایین مرور کنید و اقدام لازم را انجام دهید. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + هیچ گروه مشترک دیگری وجود ندارد. هیچ گروه مشترکی وجود ندارد. @@ -4216,12 +4264,20 @@ %1$s به تماس پیوست %1$s و %2$s پیوستند %1$s، %2$s و %3$s پیوستند - %1$s، %2$s و %3$d نفر دیگر پیوستند + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ترک کرد %1$s و %2$s ترک کردند %1$s، %2$s و %3$s ترک کردند - %1$s، %2$s و %3$d نفر دیگر ترک کردند + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + شما شما (روی دستگاه دیگر) @@ -4591,7 +4647,11 @@ مسدودشده - %1$d مخاطبین + + + %1$d contact + %1$d contacts + ‌پیام‌رسانی پیام‌های ناپدید شونده امنیت برنامه @@ -6748,5 +6808,20 @@ در حال بارگیری داده‌های پشتیبان… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 50291a8735..371181a6c0 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -89,6 +89,16 @@ Signal tarvitsee luvan käyttää laitteesi tallennustilaa kuvien, videoiden tai äänitiedostojen liittämistä varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Tallennustila\". Signal tarvitsee luvan käyttää laitteesi yhteystietoja oman yhteystietolistansa käsittelyä varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Yhteystiedot\". Signal tarvitsee luvan käyttää sijaintitietoja sijainnin liittämistä varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Sijainti\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ei ole aktivoinut Signal-maksuja @@ -386,8 +396,16 @@ Liittymispyyntösi on lähetetty ryhmän ylläpitäjälle. Saat ilmoituksen, kun he tekevät päätöksen. Peruuta pyyntö - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal tarvitsee luvan käyttää mikrofonia äänitiedostojen lähettämistä varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Mikrofoni\". + Signal tarvitsee luvan käyttää laitteesi mikrofonia ja kameraa yhteystiedolle %1$s soittamista varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Mikrofoni\" ja \"Kamera\". Kuvien ja videoiden ottaminen edellyttää, että annat Signalille kameran käyttöoikeuden. Signal tarvitsee luvan käyttää laitteesi kameraa kuvien ja videoiden ottamista varten, mutta tämä käyttöoikeus on pysyvästi evätty Signalilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Kamera\". @@ -411,7 +429,13 @@ Poistut tästä ryhmästä ja se poistetaan kaikilta laitteiltasi. Poista Poista ja poistu ryhmästä - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Liity @@ -3434,6 +3458,14 @@ Lähetä valokuvia, videoita ja tiedostoja laitteestasi. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Suojausasetukset @@ -3688,6 +3720,14 @@ Keskustelu Lähetä + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Uusi ryhmä Asetukset @@ -4184,8 +4224,16 @@ Tarkista jäsenet Tarkastuspyyntö - Ryhmän %1$d jäsenistä on käyttäjiä, joilla on sama nimi. Tarkista jäsenet alapuolelta ja korjaa ongelma. - Jos et ole varma lähettäjän henkilöllisyydestä, tarkista henkilötiedot alta. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Ei muita yhteisiä ryhmiä. Ei yhteisiä ryhmiä. @@ -4216,12 +4264,20 @@ %1$s liittyi %1$s ja %2$s liittyivät %1$s, %2$s ja %3$s liittyivät - %1$s, %2$s ja %3$d muuta liittyivät + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s poistui %1$s ja %2$s poistuivat %1$s, %2$s ja %3$s poistuivat - %1$s, %2$s ja %3$d muuta poistuivat + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Sinä Sinä (toisella laitteella) @@ -4591,7 +4647,11 @@ Estetty - %1$d yhteystietoa + + + %1$d contact + %1$d contacts + Viestintä Katoavat viestit Turvallisuus @@ -6748,5 +6808,20 @@ Ladataan varmuuskopion tietoja… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 8542b14486..4ea093f081 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -89,6 +89,16 @@ Pour joindre des photos, des vidéos ou de l’audio, Signal doit accéder au stockage de l’appareil, mais vous lui en avez interdit l’accès. Ouvrez l’application « Paramètres » de votre appareil, puis touchez Applications > Signal > Autorisations > Stockage. Pour joindre les coordonnées d’un contact, Signal doit accéder à l’application Contacts mais vous lui en avez interdit l’accès. Ouvrez l’application « Paramètres » de votre appareil, puis touchez Applications > Signal > Autorisations > Contacts > Autoriser. Pour joindre une position géographique, Signal doit accéder à l’application Position mais vous lui en avez interdit l’accès. Ouvrez l’application « Paramètres » de votre appareil, puis touchez Applications > Signal > Autorisations > Position et activer l’autorisation appropriée. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s n’a pas activé les paiements @@ -386,8 +396,16 @@ Votre demande de vous joindre au groupe a été envoyée à son administrateur. Vous serez averti de sa décision. Annuler la demande - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Pour envoyer des messages audio, Signal doit être autorisé à accéder à l’application Microphone, mais vous lui en avez interdit l’accès. Pour l’autoriser à y accéder, ouvrez l’application « Paramètres » de votre appareil, appuyez sur « Applications », puis sélectionnez « Autorisations » et activez « Microphone ». + Signal a besoin des autorisations Microphone et Appareil photo pour appeler %1$s, mais elles ont été refusées définitivement. Veuillez accéder aux paramètres de l’appli, sélectionner Autorisations et activer Microphone et Appareil photo. Pour prendre des photos et des vidéos, autorisez l’accès de Signal à l’appareil photo. Signal a besoin de l’autorisation Appareil photo afin de prendre des photos ou des vidéos, mais elle a été refusée définitivement. Veuillez accéder aux paramètres de l’appli, sélectionner Autorisations et activer Appareil photo. @@ -411,7 +429,13 @@ Vous quitterez ce groupe et il sera supprimé de tous vos appareils. Supprimer Supprimer et quitter - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Me joindre @@ -498,7 +522,7 @@ Cette personne porte le même nom qu’un autre contact. Nous contacter Confirmer - Pas maintenant + Plus tard Votre numéro de sécurité avec %1$s a changé Votre numéro de sécurité avec %1$s a changé, car cette personne a probablement réinstallé Signal ou changé d’appareil. Touchez Confirmer pour valider le nouveau numéro de sécurité. Cette action est facultative. @@ -678,7 +702,7 @@ Restaurer d’une sauvegarde ? - Restaurez vos messages et médias d’une sauvegarde locale. Si vous ne les restaurez pas maintenant, vous ne pourrez pas le faire plus tard. + Restaurez vos messages et médias depuis une sauvegarde locale. Si vous ne les restaurez pas maintenant, vous ne pourrez pas le faire plus tard. Restaurez à partir de l’icône de sauvegarde Choisir une sauvegarde En savoir plus @@ -688,7 +712,7 @@ La restauration est terminée. Pour continuer à utiliser les sauvegardes, veuillez choisir un dossier. Les nouvelles sauvegardes seront enregistrées dans cet emplacement. Sélectionner un dossier - Pas maintenant + Plus tard Sauvegarde non trouvée. @@ -796,7 +820,7 @@ Appareil sans nom - A été relié le %1$s + Associé le %1$s Dernière activité %1$s Aujourd’hui @@ -1318,7 +1342,7 @@ Activer les notifications ? Ne manquez jamais un message de vos contacts ni de vos groupes. Activer - Pas maintenant + Plus tard Message multimédia @@ -1645,7 +1669,7 @@ 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. Ce groupe hérité ne peut plus être utilisé, car il comporte trop de membres. Le nombre maximal de membres d’un groupe est %1$d. Poursuivre votre conversation avec %1$s et partager vos nom et photo avec cette personne ? - Vous joindre à ce groupe et partager votre nom et votre photo avec ses membres ? Ils ne sauront pas que vous avez vu leurs messages tant que vous n’aurez pas accepté. + Rejoindre ce groupe et partager votre nom et votre photo avec ses membres ? Aucune confirmation de lecture n’est envoyée tant que vous n’avrez pas accepté. Vous joindre à ce groupe et partager votre nom et votre photo avec ses membres ? Vous ne verrez pas leurs messages tant que vous n’aurez pas accepté. Vous joindre à ce groupe ? Ses membres ne sauront pas que vous avez lu leurs messages tant que vous n’aurez pas accepté. Débloquer ce groupe et partager vos nom et photo avec ses membres ? Vous ne recevrez aucun message tant que vous ne l’aurez pas débloqué. @@ -1694,7 +1718,7 @@ Aucun appareil n’a été trouvé. Erreur réseau. Le code QR est invalide - Désolé, trop d’appareils sont déjà reliés. Essayez d’en supprimer + Vous avez associé un trop grand nombre d’appareils. Veuillez en supprimer quelques-uns. Désolé, ce n’est pas un code QR valide de liaison d’appareil. Associer un appareil Signal ? Il semble que vous tentiez d’associer un appareil Signal via un lecteur tiers. Pour votre sécurité, veuillez rescanner le code à partir de Signal. @@ -1808,7 +1832,7 @@ Erreur du réseau. Le numéro n’est pas inscrit. Le numéro que vous avez composé ne prend pas en charge les communications vocales sécurisées. - Compris + J’ai compris @@ -1828,7 +1852,7 @@ Ouvrir les paramètres - Pas maintenant + Plus tard Accepter %1$d demande ? @@ -2558,7 +2582,7 @@ Les noms d’utilisateur sont associés à des codes QR uniques et des liens que vous pouvez envoyer à vos amis afin de démarrer rapidement une conversation. - Pas maintenant + Plus tard Configurer le nom d’utilisateur @@ -2598,9 +2622,9 @@ Pour répondre à l’appel vidéo, veuillez autoriser Signal à accéder à votre micro et votre appareil photo. Signal exige les autorisations Microphone et Appareil photo afin d’effectuer et de recevoir des appels, mais elles ont été refusées définitivement. Veuillez accéder au menu des paramètres des applis, sélectionner « Autorisations » et activer « Microphone » et « Appareil photo ». - L’appel a été pris sur un appareil relié. - L’appel a été refusé sur un appareil relié. - Occupé sur un appareil relié. + Vous avez répondu à cet appel sur un appareil associé. + Vous avez refusé cet appel via un appareil associé. + Occupé sur un appareil associé. L’icône « Changer de caméra » se trouve désormais ici. Touchez la vidéo pour l’essayer. @@ -2786,7 +2810,7 @@ Les bulles sont une fonctionnalité d’Android que vous pouvez désactiver pour les conversations de Signal. - Pas maintenant + Plus tard Désactiver @@ -2808,11 +2832,11 @@ Activer l’activité d’arrière-plan Tout semble en ordre maintenant ! Pour recevoir les notifications d’appel, appuyez ici et activez l’option « Afficher les notifications ». - Pour recevoir les notifications d\'appel, touchez ici, activez les notifications et assurez-vous que le son et les fenêtres surgissantes sont activées. + 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, touchez ici et activez l’activité d’arrière-plan dans la section « Batterie/Pile » des paramètres. Paramètres Pour recevoir les notifications d’appel, appuyez sur Réglages et activez l’option « Afficher les notifications ». - Pour recevoir les notifications d\'appel, touchez Paramètres, activez les notifications et assurez-vous que le son et les fenêtres surgissantes sont activées. + Pour recevoir les notifications d\'appel, touchez Paramètres, activez les notifications et vérifiez que le son et les pop-ups sont bien activés. Pour recevoir les notifications d’appel, touchez Paramètres et activez l’activité d’arrière-plan dans la section « Batterie/Pile » des paramètres. @@ -2827,7 +2851,7 @@ Associer l’appareil - Aucun appareil n’est relié + Aucun appareil associé Associer un nouvel appareil @@ -2978,7 +3002,7 @@ - Pour vérifier le chiffrement de bout en bout avec %1$s, comparez les chiffres ci-dessus avec leur appareil. Vous pouvez aussi scanner le code sur leur appareil. + Pour vérifier le chiffrement de bout en bout de vos communications avec %1$s, comparez les chiffres ci-dessus avec ceux de son appareil. Vous pouvez aussi scanner le code QR qui s’affiche sur son appareil. Touchez pour lire Correspondance effective Échec de la confirmation du numéro de sécurité. @@ -3398,7 +3422,7 @@ Activer - Pas maintenant + Plus tard Mise à jour requise @@ -3410,7 +3434,7 @@ - Pas maintenant + Plus tard Suivant @@ -3434,6 +3458,14 @@ Envoyez des photos, vidéos et fichiers depuis votre appareil. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Paramètres de sécurité @@ -3444,7 +3476,7 @@ Activer le verrouillage des paiements - Pas maintenant + Plus tard Passer cette étape ? @@ -3645,7 +3677,7 @@ Nouvelle conversation Ouvrir l’appareil photo - Aucune conversation pour l’instant.\nCommencez en envoyant un message à un ami. + Aucune conversation pour le moment.\nPour commencer, envoyez un message à un ami. @@ -3688,6 +3720,14 @@ Conversation Diffusion + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nouveau groupe Paramètres @@ -3856,7 +3896,7 @@ Connexion… Une autorisation est requise Poursuivre - Pas maintenant + Plus tard Migration de la base de données de Signal Phrase de passe de la sauvegarde Les sauvegardes sont enregistrées dans l\'espace de stockage externe et chiffrées avec la phrase de passe ci-dessous. Vous devez avoir cette phrase de passe afin de restaurer la sauvegarde. @@ -3879,7 +3919,7 @@ Confirmation… %1$d messages jusqu’à présent… Restaurer de la sauvegarde ? - Restaurez vos messages et médias d’une sauvegarde locale. Si vous ne les restaurez pas maintenant, vous ne pourrez pas le faire plus tard. + Restaurez vos messages et médias depuis une sauvegarde locale. Si vous ne les restaurez pas maintenant, vous ne pourrez pas le faire plus tard. Taille de la sauvegarde : %1$s Estampille temporelle de la sauvegarde : %1$s Activer les sauvegardes locales ? @@ -3921,7 +3961,7 @@ Numéro de téléphone - Choisissez qui peut voir votre numéro de téléphone et qui peut vous contacter sur Signal avec celui-ci. + Choisissez qui peut voir votre numéro de téléphone et qui peut l’utiliser pour vous rechercher sur Signal. Qui peut voir mon numéro de téléphone @@ -3999,7 +4039,7 @@ Transférez votre compte et vos messages d’un ancien appareil Android. Vous devez avoir accès à votre ancien appareil. Vous avez besoin d’accéder à votre ancien appareil. Restaurer depuis une sauvegarde - Restaurez vos messages d’une sauvegarde locale. Si vous ne les restaurez pas maintenant, vous ne pourrez pas le faire plus tard. + Restaurez vos messages depuis une sauvegarde locale. Si vous ne les restaurez pas maintenant, vous ne pourrez pas le faire plus tard. Plus d’options @@ -4184,8 +4224,16 @@ Examiner les membres Examiner la demande - %1$d membres du groupe portent le même nom. Examinez-les ci-dessous et choisissez l’action appropriée. - Si vous n’êtes pas certain de qui la demande provient, examinez les contacts ci-dessous et agissez en conséquence. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Aucun autre groupe en commun. Aucun groupe en commun. @@ -4216,12 +4264,20 @@ %1$s s’est joint %1$s et %2$s se sont joints %1$s, %2$s et %3$s se sont joints - %1$s, %2$s et %3$d se sont joints + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s a quitté l’appel %1$s et %2$s ont quitté l’appel %1$s, %2$s et %3$s ont quitté l’appel - %1$s, %2$s et %3$d autres ont quitté l’appel + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Vous Vous (sur un autre appareil) @@ -4464,7 +4520,7 @@ Compte Les rappels deviennent moins fréquents au fil du temps - Exige votre code PIN Signal pour réenregistrer votre numéro de téléphone sur Signal + Exiger le code PIN Signal pour réenregistrer votre numéro de téléphone sur Signal Changer de numéro de téléphone Données du compte @@ -4591,7 +4647,11 @@ Bloqué - %1$d contacts + + + %1$d contact + %1$d contacts + Messagerie Messages éphémères Sécurité de l’appli @@ -4696,7 +4756,7 @@ Faire un don - Pas maintenant + Plus tard Personnaliser les réactions @@ -4709,14 +4769,14 @@ Ajouter une photo de profil Choisissez une apparence et une couleur ou personnalisez vos initiales. - Pas maintenant + Plus tard Ajouter une photo Devenir donateur mensuel Signal est alimenté par des gens comme vous. Faites un don et recevez un macaron. - Pas maintenant + Plus tard Faire un don @@ -5022,7 +5082,7 @@ 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. - Pas maintenant + Plus tard Confirmer Mettre à jour l’abonnement Votre abonnement a été annulé. @@ -5127,7 +5187,7 @@ 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 - Pas maintenant + 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. @@ -5806,7 +5866,7 @@ Échanger - Pas maintenant + Plus tard Récupération du macaron… @@ -5820,7 +5880,7 @@ Faire un don mensuel - Pas maintenant + Plus tard Ne partager qu\'avec @@ -5893,7 +5953,7 @@ Balayez vers la droite pour quitter - Compris + J’ai compris Ouvrir le menu contextuel @@ -6018,7 +6078,7 @@ Sauvegarder les conversations - Pas maintenant + Plus tard Pour réactiver les sauvegardes : @@ -6205,7 +6265,7 @@ Réessayer - Pas maintenant + Plus tard Don effectué @@ -6411,7 +6471,7 @@ Aucun appel. - Commencez en appelant un ami. + Pour commencer, appelez un ami. Les liens d’appel que vous avez créés ne fonctionneront plus. @@ -6643,7 +6703,7 @@ Activer - Pas maintenant + Plus tard Activer les notifications en plein écran @@ -6660,7 +6720,7 @@ Renouveler l’abonnement - Pas maintenant + Plus tard @@ -6748,5 +6808,20 @@ Téléchargement des données sauvegardées… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 7deef29c90..4846c4e956 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -89,6 +89,16 @@ Tá gá ag Signal le cead stóras chun griangraif a thógáil, ach ní ceadaítear é go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Stóras\". Tá gá ag Signal le cead teagmhálaí chun eolas teagmhálaí a cheangail, ach ní ceadaítear é go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Teagmhálaí\". Tá gá ag Signal le cead áite i gcomhair do áit a cheangail, ach ní ceadaítear é go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Áit\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + Níor ghníomhachtaigh %1$s Íocaíochtaí @@ -395,8 +405,16 @@ Seoladh d\'iarratas chun a bheith mar bhall chuig riarthóir na baicle. Seolfar fógra chugat nuair a dhéanfaidh sé beart faoi d\'iarratas. Cealaigh an tIarratas - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Tá gá ag Signal le cead micreafóin chun teachtaireachtaí fuaime a sheoladh, ach ní ceadaítear é go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Micreafón\". + Tá gá ag Signal le cead micreafóin agus cheamara chun %1$s a ghlaoigh, ach ní ceadaítear iad go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Micreafón\" agus \"Ceamara\". Ceadaigh Signal do cheamara a úsáid chun grianghraif a thógáil nó físéan a dhéanamh. Tá gá ag Signal le cead cheamara chun griangraif a thógáil nó físéan a dhéanamh, ach ní ceadaítear é go deo. Lean ar aghaidh, le do thoil, go socruithe aipe, roghnaigh \"Ceadanna\", agus cumasaigh \"Ceamara\". @@ -420,7 +438,13 @@ Imeoidh tú as an ngrúpa seo, agus scriosfar ó gach gléas leat é. Scrios Scrios agus imigh as - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Téigh le @@ -3725,6 +3749,14 @@ Seol grianghraif, físeáin agus comhaid ó do ghléas. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Socrú slándála @@ -3985,6 +4017,14 @@ Déan comhrá Craoladh + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Baicle nua Socruithe @@ -4502,8 +4542,22 @@ Review Members Review Request - Tá an t-ainm céanna ag an líon seo ball den ghrúpa: %1$d, athbhreithnigh na baill thíos agus déan gníomh. - Mura bhfuil tú cinnte faoin duine a sheol an t-iarratas, déan athbhreithniú ar na teagmhálaithe thíos agus déan gníomh. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + No other groups in common. Níl aon ghrúpaí i bpáirt. @@ -4540,12 +4594,26 @@ Chuaigh %1$s isteach ann Chuaigh %1$s agus %2$s isteach ann Chuaigh %1$s, %2$s agus %3$s isteach ann - Chuaigh %1$s, %2$s agus %3$d eile isteach ann + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + D\'fhág %1$s D\'fhág %1$s agus %2$s D\'imigh %1$s, %2$s agus %3$s as - D\'imigh %1$s, %2$s agus %3$d eile as + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Tusa Tú (ar ghléas eile) @@ -4921,7 +4989,14 @@ Bac curtha - Líon teagmhálaithe: %1$d + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + %1$d contacts + Ag seoladh teachtaireachtaí Teachtaireachtaí a imíonn as amharc App Security @@ -7186,5 +7261,20 @@ Sonraí cúltaca á n-íoslódáil… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index bb1b4864be..1969032bb3 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -89,6 +89,16 @@ Signal require permiso de almacenamento para poder anexar fotografías, vídeos ou ficheiros de audio, pero este denegouse de forma permanente. Vai aos axustes da aplicación, selecciona «Permisos» e activa «Almacenamento». Signal require permiso para poder anexar información dos contactos, pero este denegouse de forma permanente. Vai a configuración da aplicación, selecciona «Permisos» e activa «Contactos». Signal require permiso para poder anexar a localización, pero este denegouse de forma permanente. Vai a configuración da aplicación, selecciona «Permisos» e activa «Localización». + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s non activou os Pagamentos @@ -386,8 +396,16 @@ Enviouse a solicitude á administración do grupo. Recibirás unha notificación cando a resolvan. Cancelar solicitude - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal require permiso para poder enviar mensaxes de son, pero este denegouse de forma permanente. Vai a configuración da aplicación, selecciona «Permisos» e activa «Micrófono». + Signal necesita permisos para acceder ao micrófono e á cámara para poder chamar a %1$s, pero denegáronse de forma permanente. Vai a configuración da aplicación, selecciona «Permisos» e activa «Micrófono» e «Cámara». Para tirar fotografías e facer vídeos, Signal necesita acceder á cámara. Signal necesita permiso para acceder á cámara e poder tirar fotografías, pero denegouse de forma permanente. Vai a configuración da aplicación, selecciona «Permisos» e activa «Cámara». @@ -411,7 +429,13 @@ Abandonarás este grupo e eliminarase de todos os teus dispositivos. Eliminar Eliminar e saír - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Unirse @@ -3434,6 +3458,14 @@ Envía fotos, vídeos e arquivos dende o teu dispositivo. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Configuración de seguranza @@ -3688,6 +3720,14 @@ Conversa Difusión + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Novo grupo Axustes @@ -4184,8 +4224,16 @@ Revisar membros Revisar solicitude - %1$d membros do grupo teñen o mesmo nome; revísaos e elixe unha acción. - Se non estás seguro de quen é a solicitude, revisa os contactos e elixe unha acción. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Ningún outro grupo en común. Ningún grupo en común. @@ -4216,12 +4264,20 @@ %1$suniuse %1$s e %2$s uníronse %1$s, %2$s e %3$s uníronse - %1$s, %2$s e outros %3$d uníronse + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s saíu %1$s e %2$s saíron %1$s, %2$s e %3$s saíron - %1$s, %2$s e outros %3$d saíron + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Ti Ti (noutro dispositivo) @@ -4591,7 +4647,11 @@ Bloqueado - %1$d contactos + + + %1$d contact + %1$d contacts + Mensaxería Desaparición das mensaxes Seguranza da app @@ -6748,5 +6808,20 @@ Descargando copia de seguranza… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 0bf29cd4d0..e273105b1b 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -89,6 +89,16 @@ ફોટા, વિડિયો અથવા ઓડિયો ને જોડવા માટે Signal ને સ્ટોરેજ પરવાનગીની જરૂર હોય છે, પરંતુ તે કાયમી ધોરણે નામંજૂર કરવામાં આવી છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ મેનૂ પર ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"સ્ટોરેજ\" સક્ષમ કરો. સંપર્ક માહિતીને જોડવા માટે Signal ને સંપર્કોની પરવાનગીની જરૂર હોય છે, પરંતુ તે કાયમી ધોરણે નામંજૂર કરવામાં આવી છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ મેનૂ પર ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"સંપર્કો\" સક્ષમ કરો. કોઈ સ્થળ જોડવા માટે Signal ને સ્થાનની પરવાનગીની જરૂર હોય છે, પરંતુ તે કાયમી ધોરણે નામંજૂર કરવામાં આવી છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ મેનૂ પર ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"સ્થાન\" સક્ષમ કરો. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$sએ પેમેન્ટ સક્રિય કરેલ નથી @@ -386,8 +396,16 @@ તમારી જૂથમાં જોડાવવાની વિનંતી એડમિનને મોકલવામાં આવી છે. જ્યારે તેઓ મંજૂર કરશે ત્યારે તમને જાણ કરવામાં આવશે. વિનંતી રદ કરો - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ઓડિયો મેસેજ મોકલવા માટે Signal ને માઇક્રોફોન પરવાનગીની આવશ્યકતા છે, પરંતુ તે કાયમી ધોરણે નામંજૂર કરવામાં આવી છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"માઇક્રોફોન\" સક્ષમ કરો. + કૉલ કરવા માટે Signal ને માઇક્રોફોન અને કેમેરાની પરવાનગીની જરૂર છે %1$s, પરંતુ તેઓને કાયમી નામંજૂર કરવામાં આવ્યા છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"માઇક્રોફોન\" અને \"કેમેરો\" સક્ષમ કરો. ફોટા અને વિડિયો મેળવવા માટે, કેમેરામાં Signal એક્સેસની મંજૂરી આપો. ફોટા અથવા વિડિયો લેવા માટે Signal ને કૅમેરાની પરવાનગીની જરૂર હોય છે, પરંતુ તે કાયમી ધોરણે નામંજૂર કરવામાં આવી છે. કૃપા કરીને એપ્લિકેશન સેટિંગ્સ ચાલુ રાખો, \"પરવાનગી\" પસંદ કરો અને \"કૅમેરા\" સક્ષમ કરો. @@ -411,7 +429,13 @@ તમે આ ગ્રુપને છોડી દો, અને તે તમારા બધા ડિવાઇસમાંથી ડિલીટ કરવામાં આવશે. ડિલીટ કરો ડિલીટ કરો અને છોડો - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. જોડાઓ @@ -3434,6 +3458,14 @@ તમારા ડિવાઇસમાંથી ફોટા, વીડિયો અને ફાઇલો મોકલો. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + સુરક્ષા સેટઅપ @@ -3688,6 +3720,14 @@ ચેટ બ્રોડકાસ્ટ + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + નવું ગ્રુપ સેટિંગ્સ @@ -4184,8 +4224,16 @@ સભ્યોને રિવ્યૂ કરો વિનંતીને રિવ્યૂ કરો - %1$d ગ્રુપના સભ્યોનું નામ એક જ છે, નીચેના સભ્યોની સમીક્ષા કરો અથવા કરવાનું કાર્યવાહી પસંદ કરો. - જો તમને ખાતરી ન હોય કે વિનંતી કોની છે, તો નીચેના સંપર્કોની સમીક્ષા કરો અને પગલાં લો. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + અન્ય કોઈ ગ્રુપ સામાન્ય નથી. કોમનમાં કોઈ ગ્રુપ નથી @@ -4216,12 +4264,20 @@ %1$s જોડાયા %1$s અને %2$s જોડાયા %1$s, %2$s અને %3$s જોડાયા - %1$s, %2$s અને %3$d અન્ય જોડાયા + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s છોડ્યું %1$s અને %2$s છોડ્યું %1$s, %2$s અને %3$s છોડ્યું - %1$s, %2$s અને %3$d અન્યએ છોડ્યું + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + તમે તમે (બીજા ડિવાઇસ પર) @@ -4591,7 +4647,11 @@ બ્લૉક કર્યા - %1$d સંપર્કો + + + %1$d contact + %1$d contacts + મેસેજ કરી રહ્યા છીએ અદૃશ્ય થઈ રહેલા મેસેજ એપ્લિકેશનની સુરક્ષા @@ -6748,5 +6808,20 @@ બેકઅપ ડેટા ડાઉનલોડ કરી રહ્યાં છીએ… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index b452a2deb8..df30d77393 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -89,6 +89,16 @@ फ़ोटो, वीडियो या ऑडियो संलग्न करने के लिए Signal को संग्रहण अनुमति की आवश्यकता होती है, लेकिन इसे स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स मेनू पर जारी रखें, \"अनुमतियां\" चुनें, और \"संग्रहण\" सक्षम करें। संपर्क जानकारी संलग्न करने के लिए Signal को संपर्क अनुमति की आवश्यकता होती है, लेकिन इसे स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स मेनू पर जारी रखें, \"अनुमतियां\" चुनें, और \"संपर्क\" सक्षम करें। किसी स्थान को संलग्न करने के लिए Signal को स्थान अनुमति की आवश्यकता होती है, लेकिन इसे स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स मेनू पर जारी रखें, \"अनुमतियां\" चुनें, और \"स्थान\" सक्षम करें। + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ने भुगतान सक्रीय नहीं किए हैं @@ -386,8 +396,16 @@ समूह से जुड़ने का आपका अनुरोध समूह संचालक को भेज दिया गया है। जब वे जवाब देंगे तो आपको सूचित किया जाएगा। निवेदन रद्द करें - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ऑडियो मेसेज को भेजने के लिए Signal को माइक्रोफ़ोन अनुमति की आवश्यकता होती है, लेकिन इसे स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स जारी रखें, \"अनुमतियां\" चुनें, और \"माइक्रोफ़ोन\" सक्षम करें। + %1$s को कॉल करने के लिए Signal को माइक्रोफ़ोन और कैमरा अनुमतियों की आवश्यकता होती है, लेकिन उन्हें स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स जारी रखें, \"अनुमतियां\" चुनें, और \"माइक्रोफ़ोन\" और \"कैमरा\" सक्षम करें। फोटो और वीडियो कैप्चर करने के लिए, कैमरे को Signal पहुंच की अनुमति दें। Signal को फ़ोटो या वीडियो लेने के लिए कैमरा अनुमति की आवश्यकता होती है, लेकिन इसे स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स जारी रखें, \"अनुमतियां\" चुनें, और \"कैमरा\" सक्षम करें। @@ -411,7 +429,13 @@ आप इस ग्रूप को छोङ देंगे और यह ग्रूप आपके सभी डिवाइस से डिलीट कर दिया जाएगा। डिलीट करें डिलीट करें और छोड़ दें - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. जुड़ें @@ -3434,6 +3458,14 @@ अपनी डिवाइस से फोटो, वीडियो और फाइलें भेजें। + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + सुरक्षा सेटअप @@ -3688,6 +3720,14 @@ चैट प्रसारण + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + नया समूह सेटिंग्स @@ -4184,8 +4224,16 @@ सदस्यों की समीक्षा करें निवेदन की समीक्षा करें - %1$d समूह सदस्यों के एक ही नाम हैं, नीचे दिए गए सदस्यों की समीक्षा करें और कार्रवाई का चुनाव करें। - अगर आप इस निवेदन को भेजने वाले के बारे में निश्चित नहीं हों, तो निचे दिए गए संपर्कों की समीक्षा करें और कार्रवाई करें। + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + कोई दूसरा साझा समूह नहीं कोई साझा समूह नहीं @@ -4216,12 +4264,20 @@ %1$s समूह में शामिल हुए %1$s और %2$s समूह में शामिल हुए %1$s, %2$s %3$s और समूह में शामिल हुए - %1$s, %2$s और %3$d अन्य उपयोगकर्ता शामिल हुए + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s समूह छोड़ गए %1$sऔर %2$s समूह छोड़ गए %1$s, %2$s और %3$s समूह छोड़ गए - %1$s, %2$s और %3$d अन्यउपयोगकर्ता समूह छोड़ गए + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + आप आप (दूसरे डिवाइस से) @@ -4591,7 +4647,11 @@ ब्लॉक किया गया - %1$d संपर्क + + + %1$d contact + %1$d contacts + संदेश संवाद गायब होने वाले मेसेज ऐप सुरक्षा @@ -6748,5 +6808,20 @@ बैकअप डाटा डाउनलोड हो रहा है… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 82930971f0..67218e15f6 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -89,6 +89,16 @@ Signal zahtijeva dopuštenje za prostor za pohranu kako bi priložio fotografije, videozapise ili zvuk, ali je trajno odbijen. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Prostor za pohranu\". Signal zahtijeva dopuštenje za kontakte kako bi priložio podatke o kontaktu, ali je trajno odbijen. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Kontakti\". Signal zahtijeva dopuštenje za lokaciju kako bi priložio lokaciju, ali je trajno odbijen. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Lokacija\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + Korisnik %1$s nije omogućio Plaćanja @@ -392,8 +402,16 @@ Vaš zahtjev za pridruživanje poslan je administratoru grupe. Biti će te obaviješteni kada nešto poduzmu. Poništi zahtjev - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal zahtijeva dopuštenje za mikrofon za slanje zvučnih poruka, ali je trajno odbijen. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Mikrofon\". + Signal zahtijeva dopuštenja za mikrofon i kameru kako bi nazvao %1$s, ali ona su trajno odbijena. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Mikrofon\" i \"Kamera\". Za snimanje slika i video zapisa, omogućite Signalu pristup vašoj kameri. Signal zahtijeva dopuštenje za kameru kako bi mogao fotografirati ili snimati videe, ali je trajno odbijen. Otvorite postavke aplikacije, odaberite \"Dozvole\" i omogućite \"Kamera\". @@ -417,7 +435,13 @@ Napustit ćete ovu grupu i ona će biti izbrisana sa svih vaših uređaja. Izbriši Izbriši i napusti grupu - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Pridruži se @@ -3628,6 +3652,14 @@ Šaljite fotografije, videozapise i datoteke sa svoga uređaja. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Sigurnosne postavke @@ -3886,6 +3918,14 @@ Razgovor Emitiranje + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nova grupa Postavke @@ -4396,8 +4436,20 @@ Pregled članova Pregled zahtjeva - %1$d člana/ova grupe imaju isto ime, pregledajte članove u nastavku i odaberite poduzeti radnju. - Ako niste sigurni od koga je zahtjev, pregledajte kontakte u nastavku i poduzmite radnje. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nema drugih zajedničkih grupa. Nema zajedničkih grupa. @@ -4432,12 +4484,24 @@ %1$s se pridružio/la %1$s i %2$s se pridružio/la %1$s, %2$s i %3$s se pridružio/la - %1$s, %2$s i %3$d osoba su se pridružili + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s je napustio/la %1$s i %2$s je napustio/la %1$s, %2$s i %3$s je napustio/la - %1$s, %2$s i %3$d osoba je napustilo + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Vi Vi (na drugom uređaju) @@ -4811,7 +4875,13 @@ Blokirani - %1$d kontakata + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Razmjena poruka Poruke koje nestaju Sigurnost aplikacije @@ -7040,5 +7110,20 @@ Preuzimanje sigurnosne kopije podataka… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index e5aef7089f..481437839b 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -89,6 +89,16 @@ A Signalnak szüksége van a Tárhely engedélyre, hogy fotókat, videókat vagy hangokat csatolhasson, de ez jelenleg meg van tagadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd a \"Tárhely\"-t. A Signalnak szüksége van a Névjegyek engedélyre, hogy névjegy információt csatolhasson, de ez jelenleg meg van tagadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd a \"Névjegyek\"-et. A Signalnak szüksége van a Helyadatok engedélyre, hogy helyzetet csatolhasson, de ez jelenleg meg van tagadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd a \"Helyadatok\"-at. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s nem aktiválta a Kifizetéseket @@ -386,8 +396,16 @@ A csatlakozási kérésed el lett küldve a csoportadmin részére. Értesítést fogsz kapni, amint reagálnak rá. Kérés törlése - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. A Signalnak szüksége van a Mikrofon engedélyre, hogy hangüzeneteket küldhessen, de ez jelenleg nincs megadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd az \"Mikrofon\"-t. + A Signalnak szüksége van Mikrofon és Kamera engedélyekre %1$s felhívásához, de ez jelenleg nincs megadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd a \"Mikrofon\"-t és a \"Kamera\"-t. Fotók és videók készítéséhez engedélyezd a Signalnak a kamerához való hozzáférést! A Signalnak szüksége van a Kamera engedélyre, hogy fotót vagy videót készíthessen, de ez jelenleg nincs megadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd a \"Kamera\"-t. @@ -411,7 +429,13 @@ Ha kilépsz a csoportból, akkor a csoport a többi társított eszközödön is törölve lesz. Törlés Törlés és kilépés - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Csatlakozás @@ -3434,6 +3458,14 @@ Fényképeket, videókat és fájlokat is küldhetsz az eszközödről. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Biztonsági beállítás @@ -3688,6 +3720,14 @@ Csevegés Adás + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Új csoport Beállítások @@ -4184,8 +4224,16 @@ Tagok áttekintése Kérés áttekintése - %1$d csoporttagnak ugyanaz a neve. Tekintsd át a listájukat, és dönts a továbbiakról! - Ha nem vagy benne biztos, hogy kitől is érkezett a kérés, akkor tekintsd át az alábbi kontaktokat döntés előtt! + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nincs egyéb közös csoportotok. Nincs közös csoportotok. @@ -4216,12 +4264,20 @@ %1$s csatlakozott %1$s és %2$s csatlakozott %1$s, %2$s és %3$s csatlakozott - %1$s, %2$s és %3$d másik személy csatlakozott + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s kilépett %1$s és %2$s kilépett %1$s, %2$s és %3$s kilépett - %1$s, %2$s és %3$d másik személy kilépett + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Te Te (másik eszközön) @@ -4591,7 +4647,11 @@ Letiltva - %1$d kontakt + + + %1$d contact + %1$d contacts + Üzenetküldés Eltűnő üzenetek App biztonság @@ -6748,5 +6808,20 @@ Biztonsági mentés adatainak letöltése… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 9c4c46425e..e9f641eaf6 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -89,6 +89,16 @@ Signal memerlukan izin Penyimpanan untuk melampirkan foto, video, atau audio, tetapi saat ini izin ditolak secara permanen. Harap lanjutkan ke menu pengaturan aplikasi, Pilih \"Izin\", dan aktifkan \"Penyimpanan\". Signal memerlukan izin Kontak untuk melampirkan informasi kontak, tetapi saat ini izin ditolak secara permanen. Harap melanjutkan ke menu pengaturan aplikasi, Pilih \"Izin\", dan aktifkan \"Kontak\". Signal memerlukan izin Lokasi untuk melampirkan lokasi, tetapi saat ini izin ditolak secara permanen. Harap melanjutkan ke menu pengaturan aplikasi, Pilih \"Izin\", dan aktifkan \"Lokasi\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s belum mengaktifkan Pembayaran @@ -383,8 +393,16 @@ Permintaan Anda untuk bergabung telah terkirim kepada admin grup. Anda akan diberitahu saat mereka meresponnya. Batalkan Permintaan - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal memerlukan izin Mikrofon untuk mengirim pesan audio, tetapi saat ini izin ditolak secara permanen. Mohon lanjutkan ke pengaturan aplikasi, pilih \"Izin\" dan aktifkan \"Mikrofon\". + Signal memerlukan izin Mikrofon dan Kamera untuk memanggil %1$s, tetapi saat ini izin ditolak secara permanen. Mohon lanjutkan ke pengaturan aplikasi, pilih \"Izin\" dan aktifkan \"Mikrofon\" serta \"Kamera\". Untuk mengambil foto dan video, izinkan Signal mengakses kamera. Signal memerlukan izin Kamera untuk mengambil foto dan video, tetapi saat ini izin ditolak secara permanen. Mohon lanjutkan ke pengaturan aplikasi, pilih \"Izin\" dan aktifkan \"Kamera\". @@ -408,7 +426,13 @@ Anda akan keluar dari grup ini, dan grup akan dihapus dari semua perangkat Anda. Hapus Hapus dan keluar - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Bergabung @@ -3337,6 +3361,14 @@ Kirim foto, video, dan file dari perangkat Anda. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Pengaturan keamanan @@ -3589,6 +3621,14 @@ Obrolan Siaran + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Grup baru Pengaturan @@ -4078,8 +4118,14 @@ Tinjau anggota Tinjau permintaan - %1$danggota grup memiliki nama yang sama, tinjau anggota-anggota di bawah dan pilih tindakan. - Jika anda tidak yakin dari mana permintaannya berasal, tinjau kontak-kontak dibawah dan ambil tindakan. + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + Tidak ada grup lain yang sama. Tidak ada grup yang sama. @@ -4108,12 +4154,18 @@ %1$s bergabung %1$s dan %2$s telah bergabung %1$s,%2$s, dan %3$s telah bergabung - %1$s, %2$s, %3$d dan lainnya telah bergabung + + + %1$s, %2$s and %3$d others joined + %1$s telah keluar %1$s dan %2$s telah keluar %1$s, %2$s dan %3$s keluar - %1$s, %2$s dan %3$d lainnya keluar + + + %1$s, %2$s and %3$d others left + Anda Anda (di perangkat lain) @@ -4481,7 +4533,10 @@ Diblokir - %1$d kontak + + + %1$d contacts + Olah pesan Penghilangan pesan Keamanan aplikasi @@ -6602,5 +6657,20 @@ Mengunduh data cadangan … + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 14d50efdf6..35ad023b0f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -89,6 +89,16 @@ Signal richiede l\'autorizzazione all\'accesso della memoria per allegare foto, video o audio, ma questa è stata negata in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Archiviazione\". Signal richiede l\'autorizzazione alla lettura dei contatti per allegare le informazioni di contatto, ma questa è stata negata in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Contatti\". Signal richiede l\'autorizzazione alla geolocalizzazione per allegare una posizione, ma questa è stata negata in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Posizione\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s non ha attivato la funzione per i pagamenti @@ -386,8 +396,16 @@ La tua richiesta di unirti è stata inviata agli amministratori del gruppo. Riceverai una notifica quando interverranno. Annulla richiesta - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal richiede l\'autorizzazione all\'uso del microfono per inviare messaggi audio, ma è stata negata in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Microfono\". + Signal richiede le autorizzazioni all\'uso del microfono e della fotocamera per chiamare %1$s, ma sono state negate in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Microfono\" e \"Fotocamera\". Per poter catturare foto e video, permetti a Signal di accedere alla fotocamera del tuo dispositivo Signal richiede l\'autorizzazione all\'uso della fotocamera per scattare foto o registrare video, ma è stata negata in modo permanente. Si prega di aprire il menu delle impostazioni dell\'app, selezionare \"Autorizzazioni\" e abilitare \"Fotocamera\". @@ -411,7 +429,13 @@ Quando abbandonerai il gruppo, verrà eliminato da tutti i tuoi dispositivi. Elimina Elimina e abbandona - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Unisciti @@ -3434,6 +3458,14 @@ Invia foto, video e file dal tuo dispositivo. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Impostazioni di sicurezza @@ -3688,6 +3720,14 @@ Chat Broadcast + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nuovo gruppo Impostazioni @@ -4184,8 +4224,16 @@ Controlla membri Controlla richieste - %1$d membri del gruppo hanno lo stesso nome, controlla i membri di seguito e scegli di agire. - Se non sei sicuro della provenienza della richiesta, controlla i contatti di seguito e agisci. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nessun altro gruppo in comune. Nessun gruppo in comune. @@ -4216,12 +4264,20 @@ %1$s si è unito %1$s e %2$s si sono uniti %1$s, %2$s e %3$s si sono uniti - %1$s, %2$s e altri %3$d si sono uniti + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ha abbandonato %1$s e %2$s hanno abbandonato %1$s, %2$s e %3$s hanno abbandonato - %1$s, %2$s e altri %3$d hanno abbandonato + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Tu Tu (su un altro dispositivo) @@ -4591,7 +4647,11 @@ Bloccati - %1$d contatti + + + %1$d contact + %1$d contacts + Messaggistica Messaggi a scomparsa Sicurezza app @@ -6748,5 +6808,20 @@ Download dati di backup… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index d8da804517..fd20afc8c9 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -89,6 +89,16 @@ Signal דורש את הרשאת האחסון על מנת לצרף תמונות, סרטונים, או שמע, אבל היא נדחתה לצמיתות. אנא המשך אל תפריט הגדרות היישום, בחר \"הרשאות\" ואפשר את \"אחסון\". Signal דורש הרשאת אנשי קשר על מנת לצרף מידע איש קשר, אבל היא נדחתה לצמיתות. אנא המשך אל תפריט הגדרות היישום, בחר \"הרשאות\" ואפשר את \"אנשי קשר\". Signal דורש הרשאת מיקום על מנת לצרף מיקום, אבל היא נדחתה לצמיתות. אנא המשך אל תפריט הגדרות היישום, בחר \"הרשאות\" ואפשר את \"מיקום\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s לא הפעיל/ה תשלומים @@ -392,8 +402,16 @@ בקשתך להצטרף נשלחה אל מנהלן הקבוצה. תיודע כאשר הוא מחליט. בטל בקשה - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal דורש את הרשאת המיקרופון על מנת לשלוח הודעות שמע, אבל היא נדחתה לצמיתות. אנא המשך אל הגדרות היישום, בחר \"הרשאות\" ואפשר את \"מיקרופון\". + Signal צריך את ההרשאות של המיקרופון והמצלמה על מנת לחייג אל %1$s, אבל הן נדחו לצמיתות. אנא המשך אל הגדרות היישום, בחר \"הרשאות\" ואפשר את \"מיקרופון\" ואת \"מצלמה\". כדי ללכוד תצלומים וסרטונים, התר אל Signal גישה אל המצלמה. Signal צריך את הרשאת המצלמה כדי לצלם תצלומים או להקליט סרטונים, אבל היא נדחתה לצמיתות. אנא המשך אל הגדרות היישום, בחר \"הרשאות\" ואפשר את \"מצלמה\". @@ -417,7 +435,13 @@ אתם תעזבו קבוצה זו, והיא תימחק מכל המכשירים שלכם. מחיקה מחיקה ועזיבה - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. הצטרף @@ -3628,6 +3652,14 @@ לשלוח תמונות, סרטונים וקבצים מהמכשיר שלך. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + הגדרות אבטחה @@ -3886,6 +3918,14 @@ צ׳אט שידור + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + קבוצה חדשה הגדרות @@ -4396,8 +4436,20 @@ סקירת חברי קבוצה סקירת בקשה - אל %1$d חברי קבוצה יש אותו שם, סקור את חברי הקבוצה למטה ובחר לנקוט בפעולה. - אם אינך בטוח ממי הבקשה, סקור את חברי הקבוצה למטה ונקוט בפעולה. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + אין קבוצות אחרות במשותף. אין קבוצות במשותף. @@ -4432,12 +4484,24 @@ %1$s הצטרף/ה %1$s וגם %2$s הצטרפו %1$s, %2$s וגם %3$s הצטרפו - %1$s, %2$s ועוד %3$d אחרים הצטרפו + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s עזב/ה %1$s וגם %2$s עזבו %1$s, %2$s וגם %3$s עזבו - %1$s, %2$s ועוד %3$d אחרים עזבו + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + את/ה את/ה (במכשיר אחר) @@ -4811,7 +4875,13 @@ חסום/ה - %1$d אנשי קשר + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + תכתובת הודעות נעלמות אבטחת יישום @@ -7040,5 +7110,20 @@ מורידים נתוני גיבוי… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a1197533b7..69ee27de62 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -89,6 +89,16 @@ 写真や動画、音声データを添付するには、Signalにストレージへのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アクセス許可」で「ストレージ」を有効にしてください。 連絡先情報を添付するには、Signalに連絡先へのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アプリの権限」で「連絡先」を有効にしてください。 位置情報を添付するには、Signalに位置情報へのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アプリの権限」で「位置情報」を有効にしてください。 + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s さんは決済機能を有効にしていません @@ -383,8 +393,16 @@ あなたの参加申請はグループ管理者に送信されました。応答があると通知されます。 申請をキャンセル - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. 音声メッセージを添付するには、Signalにマイクへのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アプリの権限」で「マイク」を有効にしてください。 + %1$s と通話するには、Signalにマイクとカメラへのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アプリの権限」で「マイク」と「カメラ」を有効にしてください。 写真や動画を撮るには、Signalにカメラへのアクセスを許可してください。 写真や動画を撮るには、Signalにカメラへのアクセス許可が必要ですが、無効になっています。アプリ設定メニューの「アプリの権限」で「カメラ」を有効にしてください。 @@ -408,7 +426,13 @@ このグループから抜けて、すべての端末から消去します。 消去する 消去して抜ける - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. 参加する @@ -3337,6 +3361,14 @@ 端末から写真、動画、ファイルを送信します。 + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + セキュリティ設定 @@ -3589,6 +3621,14 @@ チャット 一斉送信 + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + 新規グループ 設定 @@ -4078,8 +4118,14 @@ メンバーの確認 申請の確認 - %1$d人のグループメンバーが同じ名前です。以下のメンバーを確認して、対応してください。 - この申請者が誰であるか不確かな場合、以下の連絡先を確認して対応してください。 + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + 他の共通のグループはありません 共通のグループはありません @@ -4108,12 +4154,18 @@ %1$s が参加しました %1$s と %2$s が参加しました %1$s, %2$s および %3$s が参加しました - %1$s, %2$s ほか%3$d名が参加しました + + + %1$s, %2$s and %3$d others joined + %1$s が退出しました %1$s と %2$s が退出しました %1$s, %2$s および %3$s が退出しました - %1$s, %2$s ほか%3$d名が退出しました + + + %1$s, %2$s and %3$d others left + あなた あなた (別端末) @@ -4481,7 +4533,10 @@ ブロック済 - %1$d人 + + + %1$d contacts + メッセージ送受信 消えるメッセージ アプリのセキュリティ @@ -6602,5 +6657,20 @@ バックアップデータをダウンロードしています… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index d979da4a54..3cca73096e 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -89,6 +89,16 @@ სურათების, ვიდეოების ან აუდიოს მისამაგრებლად Signal-ს მეხსიერებაზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ გადახვიდე აპის პარამეტრების მენიუში, აირჩიე \"ნებართვები\" და გააქტიურე \"მეხსიერება\". საკონტაქტო ინფორმაციის მისაბმელად Signal-ს კონტაქტებზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ გადახვიდე აპის პარამეტრების მენიუში, აირჩიე \"ნებართვები\" და გააქტიურე \"კონტაქტები\". ადგილმდებარეობის საჩვენებლად Signal-ს ადგილმდებარეობაზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ, გადახვიდე აპის პარამეტრების მენიუში, აირჩიე \"ნებართვები\" და გააქტიურე \"ადგილმდებარეობა\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s-ს ტრანზაქციები არ გაუაქტიურებია @@ -386,8 +396,16 @@ შენი გაწევრიანების მოთხოვნა იქნა გაგზავნილი ჯგუფის ადმინთან. შენ მიიღებ შეტყობინებას, როდესაც ისინი რამეს მოიმოქმედებენ. მოთხოვნის გაუქმება - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. აუდიო შეტყობინებების გადასაგზავნად Signal-ს მიკროფონზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ, გადახვიდე აპის პარამეტრების მენიუში, აირჩიო \"უფლებები\" და ჩართო \"მიკროფონი\". + %1$s-თან დასარეკად Signal-ს მიკროფონსა და კამერაზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ, გადახვიდე აპის პარამეტრების მენიუში, აირჩიო \"უფლებები\" და ჩართოთ \"მიკროფონი\" და \"კამერა\". სურათების და ვიდეოების გადასაღებად, მიეცი Signal-ს წვდომა კამერაზე. სურათების და ვიდეოების გადასაღებად Signal-ს კამერაზე წვდომის ნებართვა სჭირდება, მაგრამ ის სამუდამოდ იქნა უარყოფილი. გთხოვთ, გადახვიდე აპის პარამეტრების მენიუში, აირჩიო \"უფლებები\" და ჩართოთ \"კამერა\". @@ -411,7 +429,13 @@ ამ ჯგუფს დატოვებ და ის შენი ყველა მოწყობილობიდან წაიშლება. წაშლა წაშლა და დატოვება - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. შემოუერთდი @@ -3434,6 +3458,14 @@ გაგზავნე ფოტოები, ვიდეოები და ფაილები შენი მოწყობილობიდან. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + უსაფრთხოების პარამეტრების დაყენება @@ -3688,6 +3720,14 @@ ჩატი მაუწყებლობა + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + ახალი ჯგუფი Settings @@ -4184,8 +4224,16 @@ წევრების განხილვა მოთხოვნის განხილვა - ჯგუფის %1$d წევრს აქვს იგივე სახელი, გადახედე ქვემოთ მოცემულ წევრებს და აირჩიე მოქმედება. - თუ არ ხარ დარწმუნებული, ვისგან არის მოთხოვნა, გადახედე ქვემოთ მოცემულ კონტაქტებს და მიიღე ზომები. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + არცერთი სხვა საერთო ჯგუფი. არცერთი საერთო ჯგუფი. @@ -4216,12 +4264,20 @@ %1$s შემოგიერთდათ %1$s და %2$s შემოგიერთდნენ %1$s, %2$s და %3$s შემოგიერთდნენ - %1$s, %2$s და %3$d სხვები შემოგიერთდნენ + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s გავიდა %1$s და %2$s გავიდნენ %1$s, %2$s და %3$s გავიდნენ - %1$s, %2$s და %3$d სხვები გავიდნენ + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + შენ შენ (სხვა მოწყობილობით) @@ -4591,7 +4647,11 @@ დაბლოკილია - %1$d კონტაქტი + + + %1$d contact + %1$d contacts + მიმოწერა გაქრობადი შეტყობინებები აპის უსაფრთხოება @@ -6748,5 +6808,20 @@ მიმდინარეობს სარეზერვო მონაცემების გადმოწერა… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 7632fab0d8..fa148bc700 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -89,6 +89,16 @@ Фотосурет, видео немесе аудионы тіркеу үшін Signal қолданбасына құрылғы жады ашық болу керек, бірақ параметрлерде оған кіруге рұқсат берілмеген. Қолданба параметрлері мәзіріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Жад\" функциясын қосыңыз. Контакт туралы ақпаратты тіркеу үшін Signal қолданбасына Контактілер ашық болу керек, бірақ параметрлерде оған кіруге рұқсат берілмеген. Қолданба параметрлері мәзіріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Контактілер\" функциясын қосыңыз. Геолокацияны тіркеу үшін Signal қолданбасына Геолокация ашық болу керек, бірақ параметрлерде оған кіруге рұқсат берілмеген. Қолданба параметрлері мәзіріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Геолокация\" функциясын қосыңыз. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s Төлемдерді белсендірмеді @@ -386,8 +396,16 @@ Топқа қосылу туралы өтінішіңіз топ әкімшісіне жіберілді. Олар шара қолданғанда, сізге хабарландыру келеді. Өтініштен бас тарту - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Аудио хаттар жіберу үшін Signal-ға микрофон ашық болу керек, бірақ параметрлерде микрофонды пайдалануға рұқсат берілмеген. Қолданба параметрлеріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Микрофон\" параметрін қосыңыз. + %1$s қоңырау шалу үшін Signal қолданбасына микрофон мен камераны пайдалануға рұқсат керек, бірақ параметрлерде оларды пайдалануға рұқсат берілмеген. Қолданба параметрлеріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Микрофон\" және \"Камера\" параметрлерін қосыңыз. Фотосуреттер мен видео түсіру үшін Signal қолданбасына камера ашық болу керек. Фотосурет немесе видео түсіру үшін Signal қолданбасына камера ашық болу керек, бірақ параметрлерде оны пайдалануға рұқсат берілмеген. Қолданба параметрлеріне кіріп, \"Рұқсаттар\" бөлімін таңдаңыз да, \"Камера\" параметрін қосыңыз. @@ -411,7 +429,13 @@ Сіз бұл топтан шығып кетесіз және ол сіздің барлық құрылғыларыңыздан жойылады. Жою Жойып, шығып кету - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Қосылу @@ -3434,6 +3458,14 @@ Құрылғыңыздан фотосуреттер, видеолар және файлдар жіберіңіз. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Қауіпсіздік параметрлерін реттеу @@ -3688,6 +3720,14 @@ Чат Трансляция + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Жаңа топ Параметрлер @@ -4184,8 +4224,16 @@ Топ мүшелерін қарап шығу Өтінішті қарап шығу - %1$d топ мүшесінің аттары бірдей, төмендегі топ мүшелерін қарап шығып, тиісті шара қолданыңыз. - Бұл өтініштің кімнен келгенін біле алмасаңыз, төмендегі контактілерді қарап шығып, тиісті шара қолданыңыз. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Басқа ортақ топтар жоқ. Ортақ топтар жоқ. @@ -4216,12 +4264,20 @@ %1$s қосылды %1$s және %2$s қосылды %1$s, %2$s және %3$s қосылды - %1$s, %2$s және тағы %3$d адам қосылды + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s шығып кетті %1$s және %2$s шығып кетті %1$s, %2$s және %3$s шығып кетті - %1$s, %2$s және тағы %3$d адам шығып кетті + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Сіз Сіз (басқа құрылғыда) @@ -4591,7 +4647,11 @@ Блокталған - %1$d контакт + + + %1$d contact + %1$d contacts + Хат алмасу Disappearing messages Қолданба қауіпсіздігі @@ -6748,5 +6808,20 @@ Резервтік дерек жүктеп алынуда… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index ebeb0a2bc4..1f43613392 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -89,6 +89,16 @@ Signal ទាមទារសិទ្ធិប្រើប្រាស់អង្គរក្សាទុកដើម្បីភ្ជាប់រូបថត វីដេអូ ឬសំឡេង ប៉ុន្តែវាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅការកំណត់របស់ប្រព័ន្ធ ជ្រើសរើស \"អនុញ្ញាត\" និងបើក \"អង្គរក្សាទុក\" ។ Signal ទាមទារសិទ្ធិប្រើប្រាស់បញ្ជីទំនាក់ទំនងដើម្បីភ្ជាប់ព័ត៌មានបញ្ជីទំនាក់ទំនង ប៉ុន្តែវាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅការកំណត់របស់ប្រព័ន្ធ ជ្រើសរើស \"អនុញ្ញាត\" និងបើក \"បញ្ជីទំនាក់ទំនង\" ។ Signalត្រូវការសិទ្ធិប្រើប្រាស់ទីតាំងដើម្បីភ្ជាប់ទីតាំង ប៉ុន្តែវាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅការកំណត់ជ្រើសរើស \"អនុញ្ញាត\" និងបើក \"ទីតាំង\" ។ + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s មិនទាន់បានបើកដំណើរការការបង់ប្រាក់ @@ -383,8 +393,16 @@ ការស្នើសុំរបស់អ្នកដើម្បីចូលក្រុម ត្រូវបានផ្ញើទៅកាន់អ្នកគ្រប់គ្រងក្រុម។ អ្នកនឹងទទួលដំណឹង នៅពេលគេមានសកម្មភាព។ បោះបង់ការស្នើសុំ - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal សុំសិទ្ធិប្រើប្រាស់ម៉ៃក្រូហ្វូន ដើម្បីផ្ញើសារជាសំឡេង, ប៉ុន្តែ វាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅកាន់ ការកំណត់កម្មវិធី, ជ្រើសរើស \"ការអនុញ្ញាត\", និងបើក \"ប្រដាប់ស្រូបសំឡេង\"។ + Signalត្រូវការសិទ្ធិប្រើប្រាស់ម៉ៃក្រូហ្វូន និងកាមេរ៉ាដើម្បីហៅទៅកាន់ %1$s, ប៉ុន្តែពួកវាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅការកំណត់ជ្រើសរើស \"អនុញ្ញាត\" ហើយបើក \"ម៉ៃក្រូហ្វូន\" និង \"កាមេរ៉ា\"។ ដើម្បីថតរូបភាព និងវីដេអូ, សូមអនុញ្ញាត Signal ចូលប្រើប្រាស់កាមេរ៉ា។ Signalត្រូវការសិទ្ធិប្រើប្រាស់កាមេរ៉ាដើម្បីថតរូប ឬវីដេអូ ប៉ុន្តែវាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅការកំណត់ជ្រើសរើស \"អនុញ្ញាត\" ហើយបើក \"កាមេរ៉ា\"។ @@ -408,7 +426,13 @@ អ្នកនឹងចាកចេញពីក្រុមនេះ ហើយវានឹងត្រូវបានលុបចេញពីឧបករណ៍របស់អ្នកទាំងអស់។ លុប លុប និងចាកចេញ - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. ចូលរួម @@ -3337,6 +3361,14 @@ ផ្ញើរូបថត វីដេអូ និងឯកសារពីឧបករណ៍របស់អ្នក។ + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + ការរៀបចំប្រព័ន្ធសុវត្ថិភាព @@ -3589,6 +3621,14 @@ ការជជែក ផ្សព្វផ្សាយ + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + ក្រុមថ្មី ការកំណត់ @@ -4078,8 +4118,14 @@ ពិនិត្យមើលសមាជិក ពិនិត្យមើលសំណើ - សមាជិកក្រុមចំនួន %1$dមានឈ្មោះដូចគ្នា សូមពិនិត្យសមាជិកក្រុមទាំងអស់ខាងក្រោម ហើយចាត់វិធានការ - ប្រសិនបើអ្នកមិនច្បាស់ថាសំណើរនេះមកពីណាទេ សូមពិនិត្យលេខទំនាក់ទំនងនៅខាងក្រោមហើយចាត់វិធានការ + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + មិនមានក្រុមដទៃទៀតរួមគ្នាទេ មិនមានក្រុមរួមគ្នាទេ @@ -4108,12 +4154,18 @@ %1$s បានចូលរួម %1$s និង %2$s បានចូលរួម %1$s %2$s និង %3$s បានចូលរួម - %1$s %2$s និង %3$d បានចូលរួម + + + %1$s, %2$s and %3$d others joined + %1$s នៅសល់ %1$s និង %2$s នៅសល់ %1$s %2$s និង %3$s នៅសល់ - %1$s %2$s និង %3$d ផ្សេងទៀតដែលនៅសល់ + + + %1$s, %2$s and %3$d others left + អ្នក អ្នក (នៅលើឧបករណ៍មួយផ្សេងទៀត) @@ -4481,7 +4533,10 @@ បានទប់ស្កាត់ - %1$d លេខទំនាក់ទំនង + + + %1$d contacts + ការផ្ញើសារ សារដែលបាត់ទៅវិញ សន្តិសុខកម្មវិធី @@ -6602,5 +6657,20 @@ កំពុងទាញយកទិន្នន័យបម្រុងទុក… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index a0db312de2..599e7d2f16 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -89,6 +89,16 @@ ಫೋಟೊಗಳು, ವೀಡಿಯೋಗಳು ಅಥವಾ ಆಡಿಯೋಗಳನ್ನು ಲಗತ್ತಿಸಲು Signal ಗೆ ಸ್ಟೋರೇಜ್ ಅನುಮತಿಯ ಅಗತ್ಯವಿರುತ್ತದೆ, ಆದರೆ ಅದನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್ಸ್ ಮೆನುಗೆ ಮುಂದುವರಿಯಿರಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆ ಮಾಡಿ ಮತ್ತು \"ಸ್ಟೋರೇಜ್\" ಸಕ್ರಿಯಗೊಳಿಸಿ. ಸಂಪರ್ಕ ಮಾಹಿತಿಯನ್ನು ಲಗತ್ತಿಸಲು Signal ಸಂಪರ್ಕಗಳ ಅನುಮತಿ ಅಗತ್ಯವಿರುತ್ತದೆ, ಆದರೆ ಅದನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಅಪ್ಲಿಕೇಶನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಮೆನುಗೆ ಮುಂದುವರಿಯಿರಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆಮಾಡಿ ಮತ್ತು \"ಸಂಪರ್ಕಗಳು\" ಸಕ್ರಿಯಗೊಳಿಸಿ. ಸ್ಥಳ ಮಾಹಿತಿಯನ್ನು ಲಗತ್ತಿಸಲು Signal ಗೆ ಸ್ಥಳ ಅನುಮತಿಯ ಅಗತ್ಯವಿರುತ್ತದೆ, ಆದರೆ ಅದನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಮೆನುಗೆ ಮುಂದುವರಿಯಿರಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆಮಾಡಿ ಮತ್ತು \"ಸ್ಥಳ\" ಸಕ್ರಿಯಗೊಳಿಸಿ. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ಅವರು ಪಾವತಿಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿಲ್ಲ @@ -386,8 +396,16 @@ ಗುಂಪು ಸೇರಲು ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ನಿರ್ವಾಹಕರಿಗೆ ಕಳುಹಿಸಲಾಗಿದೆ. ಅವರು ಕ್ರಮ ಕೈಗೊಂಡಾಗ ನಿಮಗೆ ಸೂಚಿಸಲಾಗುತ್ತದೆ. ಕೋರಿಕೆಯನ್ನು ರದ್ದುಮಾಡಿ - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ಆಡಿಯೊ ಸಂದೇಶ ಕಳುಹಿಸುವ ಸಲುವಾಗಿ Signal ಗೆ ಮೈಕ್ರೊಫೋನ್ ಅನುಮತಿ ಅಗತ್ಯವಿದೆ, ಆದರೆ ಅದನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಅಪ್ಲಿಕೇಶನ್ ಸಂಯೋಜನೆಗೆ ಮುಂದುವರಿಸಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆ ಮಾಡಿ, ಮತ್ತು \"ಮೈಕ್ರೊಫೋನ್\" ಸಕ್ರಿಯಗೊಳಿಸಿ. + %1$s ಗೆ ಕರೆ ಮಾಡಲು Signal ಗೆ ಮೈಕ್ರೊಫೋನ್ ಹಾಗೂ ಕ್ಯಾಮರಾ ಅನುಮತಿಗಳು ಅಗತ್ಯವಿರುತ್ತವೆ, ಆದರೆ ಅವುಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ ಗಳಿಗೆ ಮುಂದುವರಿಯಿರಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆ ಮಾಡಿ, ಮತ್ತು \"ಮೈಕ್ರೊಫೋನ್\" ಮತ್ತು \"ಕ್ಯಾಮೆರಾ\" ಸಕ್ರಿಯಗೊಳಿಸಿ. ಫೋಟೋಗಳನ್ನು ಮತ್ತು ವಿಡಿಯೊ ಸೆರೆಹಿಡಿಯಲು, Signal ಗೆ ಕ್ಯಾಮರಾ ಪ್ರವೇಶ ಅನುಮತಿಸಿ. ಫೋಟೊಗಳು ಅಥವಾ ವೀಡಿಯೊಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು Signal ಗೆ ಕ್ಯಾಮೆರಾ ಅನುಮತಿಯ ಅಗತ್ಯವಿರುತ್ತದೆ, ಆದರೆ ಇದನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರಾಕರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಮುಂದುವರಿಯಿರಿ, \"ಅನುಮತಿಗಳು\" ಆಯ್ಕೆ ಮಾಡಿ ಮತ್ತು \"ಕ್ಯಾಮರಾ\" ಸಕ್ರಿಯಗೊಳಿಸಿ. @@ -411,7 +429,13 @@ ನೀವು ಈ ಗ್ರೂಪ್ ಅನ್ನು ತೊರೆಯುತ್ತೀರಿ ಮತ್ತು ಇದನ್ನು ನಿಮ್ಮ ಎಲ್ಲ ಸಾಧನಗಳಿಂದಲೂ ಅಳಿಸಲಾಗುವುದು. ಅಳಿಸಿ ಅಳಿಸಿ ಮತ್ತು ತೊರೆಯಿರಿ - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. ಸೇರು @@ -3434,6 +3458,14 @@ ನಿಮ್ಮ ಸಾಧನದಿಂದ ಫೊಟೋಗಳು, ವೀಡಿಯೊಗಳು ಮತ್ತು ಫೈಲ್‌ಗಳನ್ನು ಕಳುಹಿಸಿ. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + ಭದ್ರತಾ ಸೆಟ್ ಅಪ್ @@ -3688,6 +3720,14 @@ ಚಾಟ್ ಪ್ರಸಾರ ಮಾಡಿ + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + ಹೊಸ ಗುಂಪು ಸೆಟ್ಟಿಂಗ್‌ಗಳು @@ -4184,8 +4224,16 @@ ಸದಸ್ಯರನ್ನು ಪರಿಶೀಲಿಸಿ ವಿನಂತಿಯನ್ನು ಪರಿಶೀಲಿಸಿ - %1$d ಗುಂಪು ಸದಸ್ಯರು ಒಂದೇ ಹೆಸರನ್ನು ಹೊಂದಿದ್ದಾರೆ, ಕೆಳಗಿನ ಸದಸ್ಯರನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಕ್ರಮ ತೆಗೆದುಕೊಳ್ಳಿರಿ. - ವಿನಂತಿ ಯಾರೆಂದು ನಿಮಗೆ ಖಚಿತವಿಲ್ಲದಿದ್ದರೆ, ಕೆಳಗಿನ ಸಂಪರ್ಕಗಳನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಕ್ರಮ ತೆಗೆದುಕೊಳ್ಳಿರಿ. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + ಬೇರೆ ಯಾವುದೇ ಗುಂಪುಗಳು ಸಾಮಾನ್ಯವಾಗಿಲ್ಲ. ಯಾವುದೇ ಒಂದೇ ಗುಂಪುಗಳಲ್ಲಿಲ್ಲ. @@ -4216,12 +4264,20 @@ %1$s ಸೇರಿಕೊಂಡಿದ್ದಾರೆ %1$s ಹಾಗು %2$s ಸೇರಿಕೊಂಡಿದ್ದಾರೆ %1$s, %2$s ಹಾಗು %3$s ಸೇರಿಕೊಂಡಿದ್ದಾರೆ - %1$s, %2$s ಹಾಗು %3$d ಇನ್ನಿತರು ಸೇರಿಕೊಂಡಿದ್ದಾರೆ + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ಉಳಿದಿದ್ದಾರೆ %1$s ಹಾಗು %2$s ಉಳಿದಿದ್ದಾರೆ %1$s, %2$s ಹಾಗು %3$s ಉಳಿದಿದ್ದಾರೆ - %1$s, %2$s ಹಾಗು %3$d ಇನ್ನಿತರು ಉಳಿದಿದ್ದಾರೆ + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + ನೀವು ನೀವು (ಮತ್ತೊಂದು ಸಾಧನದಲ್ಲಿ) @@ -4591,7 +4647,11 @@ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ - %1$d ಸಂಪರ್ಕಗಳು + + + %1$d contact + %1$d contacts + ಸಂದೇಶ ಕಳುಹಿಸುವಿಕೆ ಕಣ್ಮರೆಯಾಗುವ ಸಂದೇಶಗಳು ಆಪ್ ಸುರಕ್ಷತೆ @@ -6748,5 +6808,20 @@ ಬ್ಯಾಕಪ್ ಡೇಟಾವನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d5963cff24..2b550c61c4 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -89,6 +89,16 @@ Signal에서 사진, 동영상 또는 오디오를 첨부하려면 저장 공간 접근 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'저장 공간\' 항목을 허용해 주세요. Signal에서 연락처 정보를 첨부하려면 연락처 접근 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'연락처\' 항목을 허용해 주세요. Signal에서 위치 정보를 첨부하려면 위치 접근 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'위치\' 항목을 허용해 주세요. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s 님이 결제를 아직 활성화하지 않았습니다. @@ -383,8 +393,16 @@ 참가 요청이 그룹 관리자에게 전송되었습니다. 요청이 처리되면 알림을 받게 됩니다. 요청 취소하기 - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal에서 오디오 메시지를 보내려면 마이크 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'마이크\' 항목을 허용해 주세요. + Signal에서 %1$s에게 전화하려면 마이크와 카메라 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'마이크\'와 \'카메라\' 항목을 허용해 주세요. Signal에서 사진과 동영상을 찍으려면 카메라 권한이 필요합니다. Signal에서 사진이나 동영상을 찍으려면 카메라 권한이 필요하지만 현재 거부되어 있습니다. 앱 설정 메뉴에서 \'권한\'을 선택한 후 \'카메라\' 항목을 허용해 주세요. @@ -408,7 +426,13 @@ 당신은 그룹을 떠날 것이며, 당신의 모든 기기에서 삭제될 겁니다. 삭제 삭제 후 나가기 - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. 참가 @@ -3337,6 +3361,14 @@ 기기에서 사진, 동영상, 파일을 전송하세요. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + 보안 설정 @@ -3589,6 +3621,14 @@ 대화 방송 + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + 새 그룹 설정 @@ -4078,8 +4118,14 @@ 구성원 검토 검토 요청 - %1$d명의 그룹 멤버가 같은 이름을 가지고 있습니다. 아래 멤버를 검토한 후 조치를 취하세요. - 요청의 출처가 확실하지 않은 경우 아래 연락처를 검토하고 조치를 취하세요. + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + 공통된 다른 그룹이 없습니다. 공통된 그룹이 없습니다. @@ -4108,12 +4154,18 @@ %1$s 가입함 %1$s 및 %2$s 가입함 %1$s, %2$s 및 %3$s 가입함 - %1$s, %2$s 및 %3$d명이 가입함 + + + %1$s, %2$s and %3$d others joined + %1$s 남음 %1$s 및 %2$s 남음 %1$s, %2$s 및 %3$s 남음 - %1$s, %2$s 및 %3$d명 남음 + + + %1$s, %2$s and %3$d others left + 나(다른 기기에서) @@ -4481,7 +4533,10 @@ 차단함 - 연락처 %1$d개 + + + %1$d contacts + 메시징 사라지는 메시지 애플리케이션 보안 @@ -6602,5 +6657,20 @@ 백업 데이터를 다운로드하는 중… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 8c62dc3d11..5c5e46949e 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -89,6 +89,16 @@ Signal колдонмосуна сүрөттөрдү, видеолорду же аудиолорду тиркөө үчүн Сактагычка кирүүгө уруксат беришиңиз керек, бирок сиз андан баш тарткансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Сактагыч\" дегенди иштетиңиз. Signal колдонмосуна байланыштын маалыматын тиркөө үчүн Байланыштарды колдонууга уруксат беришиңиз керек, бирок сиз андан баш тарткансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Байланыштар\" дегенди иштетиңиз. Signal колдонмосуна турган жериңизди тиркөө үчүн Жүргөн жерди аныктоо функциясын колдонууга уруксат беришиңиз керек, бирок сиз андан баш тарткансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Жүргөн жерди аныктоо\" дегенди иштетиңиз. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s төлөмдөр кызматын иштете элек @@ -383,8 +393,16 @@ Кошулуу өтүнүчүңүз топтун администраторуна жөнөтүлдү. Анын чечими тууралуу билдирме аласыз. Өтүнүчтү жокко чыгаруу - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal колдонмосуна аудио билдирүүлөрдү жөнөтүү үчүн микрофонду колдонууга уруксат беришиңиз керек, бирок сиз андан баш тарткансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Микрофон\" дегенди иштетиңиз. + Signal колдонмосуна %1$s чалуу үчүн микрофон менен камераны колдонууга уруксат беришиңиз керек, бирок сиз андан баш тарткансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Микрофон\" жана \"Камера\" дегенди иштетиңиз. Сүрөт же видео тартуу үчүн Signal колдонмосуна камераны колдонууга уруксат беришиңиз керек. Signal колдонмосуна сүрөт же видео тартуу үчүн камераны колдонууга уруксат беришиңиз керек, бирок сиз аны четке каккансыз. Колдонмонун параметрлерине кирүү үчүн \"Улантуу\" дегенди басып, \"Уруксаттар\" дегенди тандап, \"Камера\" дегенди иштетиңиз. @@ -408,7 +426,13 @@ Бул топтон чыгасыз жана ал бардык түзмөктөрүңүздөн өчүрүлөт. Өчүрүү Өчүрүп, топтон чыгам - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Кошулуу @@ -3337,6 +3361,14 @@ Түзмөгүңүздөн сүрөттөрдү, видеолорду жана файлдарды жөнөтөсүз. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Коопсуздук параметрлери @@ -3589,6 +3621,14 @@ Маек Берүү + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Жаңы топ Тууралоо @@ -4078,8 +4118,14 @@ Мүчөлөрдү карап чыгуу Өтүнүчтү карап чыгуу - %1$d топ мүчөсүнүн аты бирдей, төмөндөгү мүчөлөрдү карап чыгып, чара көрүүнү тандаңыз. - Эгер бул өтүнүч кимден экенин билбесеңиз, төмөнкү мүчөлөрдү карап чыгып, чара көрүңүз. + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + Башка орток топтор жок. Орток топтор жок. @@ -4108,12 +4154,18 @@ %1$s кошулду %1$s жана %2$s кошулушту %1$s, %2$s жана %3$s кошулушту - %1$s, %2$s жана %3$d башкалар кошулушту + + + %1$s, %2$s and %3$d others joined + %1$s чыкты %1$s жана %2$s чыгышты %1$s, %2$s жана %3$s чыгышты - %1$s, %2$s жана %3$d башкалар чыгышты + + + %1$s, %2$s and %3$d others left + Сиз Сиз (башка түзмөктө) @@ -4481,7 +4533,10 @@ Бөгөттөлдү - %1$d байланыш + + + %1$d contacts + Билдирүү алмашуу Жоголуп кетүүчү билдирүүлөр Колдонмонун коопсуздугу @@ -6602,5 +6657,20 @@ Камдык көчүрмөлөр жүктөлүп алынууда… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 6164ee8f4f..589890674d 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -89,6 +89,16 @@ Norint pridėti nuotraukas, vaizdo įrašus ar garsą, Signal reikia saugyklos leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymų meniu, pasirinkite „Leidimai“ ir įjunkite „Saugyklą“. Norint pridėti kontaktinę informaciją, Signal reikia adresatų leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymų meniu, pasirinkite „Leidimai“ ir įjunkite „Adresatus“. Norint pridėti vietą, Signal reikia vietos leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymų meniu, pasirinkite „Leidimai“ ir įjunkite „Vietą“. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s neįjungė Mokėjimų @@ -392,8 +402,16 @@ Jūsų prašymas prisijungti prie grupės buvo išsiųstas grupės administratoriui. Jums bus pranešta, kai jis atliks tam tikrus veiksmus. Panaikinti prašymą - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Norint siųsti garso žinutes, Signal reikia mikrofono leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymus, pasirinkite „Leidimai“ ir įjunkite „Mikrofoną“. + Norint skambinti %1$s, programai Signal reikia mikrofono ir kameros leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymus, pasirinkite „Leidimai“ ir įjunkite „Mikrofoną“ ir „Kamerą“. Norėdami fotografuoti ir filmuoti vaizdo įrašus, leiskite Signal prieigą prie kameros. Norint fotografuoti, Signal reikia kameros leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymų meniu, pasirinkite „Leidimai“ ir įjunkite „Kamerą“. @@ -417,7 +435,13 @@ Tu išeisi iš šios grupės ir ji bus ištrinta iš visų tavo įrenginių. Ištrinti Ištrinti ir išeiti - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Prisijungti @@ -3628,6 +3652,14 @@ Siųsk nuotraukas, vaizdo įrašus ir failus iš savo įrenginio. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Saugumo konfigūracija @@ -3886,6 +3918,14 @@ Pokalbis Transliavimas + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nauja grupė Nustatymai @@ -4396,8 +4436,20 @@ Peržiūrėti narius Peržiūrėti užklausą - %1$d grupės nariai(-ių) turi tokį patį vardą, peržiūrėkite žemiau esančius narius ir pasirinkite norėdami imtis veiksmų. - Jei nesate tikri, nuo ko yra gautas prašymas, peržiūrėkite žemiau esančius adresatus ir imkitės veiksmų. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nėra kitų bendrų grupių. Nėra bendrų grupių. @@ -4432,12 +4484,24 @@ Prisijungė %1$s Prisijungė %1$s ir %2$s Prisijungė %1$s, %2$s ir %3$s - Prisijungė %1$s, %2$s dar %3$d žmonės(-ių) + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s išėjo %1$s ir %2$s išėjo %1$s, %2$s ir %3$s išėjo - %1$s, %2$s ir dar %3$d žmonės(-ių) išėjo + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Jūs Jūs (kitame įrenginyje) @@ -4811,7 +4875,13 @@ Užblokuotas - %1$d adresatų + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Susirašinėjimas Išnykstančios žinutės Programėlės saugumas @@ -7040,5 +7110,20 @@ Atsiunčiami atsarginės kopijos duomenys… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 3bec7b447f..fe2748c1d2 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -89,6 +89,16 @@ Lai pievienoto fotogrāfijas, videoierakstus un audioierakstus, Signal nepieciešama atļauja pieejai pie Noliktavas, bet tā ir liegta. Dodieties uz lietotnes iestatījumu izvēlni, izvēlieties \"Atļaujas\" un iespējojiet opciju \"Krātuve\". Signal nepieciešama atļauja pieejai pie Kontaktiem, bet tā ir liegta. Dodieties uz lietotnes iestatījumiem, izvēlieties \"Atļaujas\" un iespējojiet \"Kontakti\". Signal nepieciešama atļauja pieejai pie atrašanās vietas pakalpojumiem, bet tā ir liegta. Dodieties uz lietotnes iestatījumiem, izvēlieties \"Atļaujas\" un iespējojiet \"Atrašanās vieta\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + Lietotājs %1$s nav aktivizējis maksājumus @@ -389,8 +399,16 @@ Jūsu pieprasījums pievienoties ir nosūtīts grupas administratoriem. Jums tiks paziņots, kad viņi veiks kādu darbību. Atcelt pieprasījumu - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Lai nosūtītu audio ziņas, Signal nepieciešama atļauja pieejai mikrofonam, bet tā ir liegta. Dodieties uz lietotnes iestatījumiem, izvēlieties \"Atļaujas\" un iespējojiet \"Mikrofons\". + Lai zvanītu %1$s, Signal nepieciešama atļauja piekļuvei mikrofonam un kamerai, bet tā ir liegta. Dodieties uz lietotnes iestatījumiem, izvēlieties \"Atļaujas\" un iespējojiet \"Mikrofons\" un \"Kamera\". Lai uzņemtu fotogrāfijas un video, ļaujiet Signal piekļūt kamerai. Lai uzņemtu fotogrāfijas vai video, Signal nepieciešama atļauja piekļūt kamerai, bet tā ir liegta. Dodieties uz lietotnes iestatījumiem, izvēlieties \"Atļaujas\" un iespējojiet \"Kamera\". @@ -414,7 +432,13 @@ Jūs pametīsiet šo grupu, un tā tiks izdzēsta no visām jūsu ierīcēm. Dzēst Dzēst un pamest - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Pievienoties @@ -3531,6 +3555,14 @@ Sūtiet fotoattēlus, videoklipus un failus no savas ierīces. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Drošības iestatījumi @@ -3787,6 +3819,14 @@ Saruna Pārraide + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Jauna grupa Iestatījumi @@ -4290,8 +4330,18 @@ Pārskatīt dalībniekus Pārskatīt pieprasījumu - %1$d grupas dalībniekiem ir vienāds vārds; pārskatiet tālāk norādītos dalībniekus un izvēlieties rīkoties. - Ja nezināt, no kā ir pieprasījums, pārskatiet tālāk norādītās kontaktpersonas un rīkojieties. + + + %1$d group members have the same name, review the members below and choose to take action. + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nav citu kopīgu grupu. Nav kopīgu grupu. @@ -4324,12 +4374,22 @@ %1$s pievienojās %1$s un %2$s pievienojās %1$s, %2$s un %3$s pievienojās - %1$s, %2$s un %3$d citi pievienojās + + + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s izstājās %1$s un %2$s izstājās %1$s, %2$s un %3$s izstājās - %1$s, %2$s un %3$d citi izstājās + + + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Jūs Jūs (citā ierīcē) @@ -4701,7 +4761,12 @@ Bloķēti - %1$d kontaktpersonas + + + %1$d contacts + %1$d contact + %1$d contacts + Ziņapmaiņa Gaistošās ziņas Lietotnes aizsardzība @@ -6894,5 +6959,20 @@ Lejupielādē rezerves kopijas datus… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 9e7c3210e0..8b50d38d73 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -89,6 +89,16 @@ Signal има потреба од дозвола до складот за да можете да прикачите слики, видеа или аудио. Оваа дозвола е трајно одбиена. Ве молиме продолжете до менито за поставувања, изберете „Дозволи“, и вклучете „Склад“ Signal има потреба од дозвола до контактите за да можете да прикачите информации за контакти. Оваа дозвола е трајно одбиена. Ве молиме продолжете до менито за поставување на апликациите, изберете „Дозволи“ и вклучете \"Контакти\". Signal има потреба од дозвола за локација за да може да прикачи локација. Оваа дозвола е трајно одбиена. Ве молиме продолжете до менито за поставувања, изберете „Дозволи“, и вклучете „Локација“. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s нема активирано плаќања @@ -386,8 +396,16 @@ Вашето барање за да се приклучите е испратено до администраторот на групата. Ќе бидете известени штом преземат нешто околу тоа. Откажи барање - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal има потреба од дозвола до микрофонот за да може да испраќа аудио пораки. Оваа дозвола е трајно одбиена. Ве молиме продолжете до менито за поставувања, изберете „Дозволи“ и вклучете „Микрофон“. + Signal има потреба од дозвола до микрофонот и камерата за да може да воспостави повик со %1$s. Овие дозволи се трајно одбиени. Ве молиме продолжете до менито за поставувањата, изберете „Дозволи“ и вклучете „Микрофон“ и „Камера“. За да снимате фотографии и видеа, дозволете му на Signal пристап до камерата. Signal има потреба од дозвола до камерата за да слика и снима видео. Оваа дозвола е трајно одбиена. Ве молиме продолжете до менито за поставувања, изберете „Дозволи“ и вклучете „Камера”. @@ -411,7 +429,13 @@ Ќе ја напуштите оваа група и таа ќе биде избришана од сите ваши уреди. Избриши Избриши и напушти - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Приклучи сѐ @@ -3434,6 +3458,14 @@ Испраќајте слики, видеа или датотеки од вашиот уред. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Поставувања за безбедност @@ -3688,6 +3720,14 @@ Разговор Емитување + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Нова група Поставувања @@ -4184,8 +4224,16 @@ Прегледајте ги членовите Прегледајте го барањето - %1$d членови на групата го имаат истото име, прегледајте ги членовите подолу и решете што сакате да направите. - Ако не сте сигурни од кого е барањето, прегледајте ги контактите подолу и решете што сакате да направите. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Немате други заеднички групи. Немате заеднички групи. @@ -4216,12 +4264,20 @@ %1$s се приклучи %1$s и %2$s се приклучија %1$s, %2$s и %3$s се приклучија - %1$s, %2$s и %3$d други се приклучија + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s напушти %1$s и %2$s напуштија %1$s, %2$s и %3$s напуштија - %1$s, %2$s и %3$d други напуштија + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Вие Вие (од друг уред) @@ -4591,7 +4647,11 @@ Барањето е блокирано - %1$d контакти + + + %1$d contact + %1$d contacts + Пораки Исчезнувачки пораки Безбедност на апликацијата @@ -6748,5 +6808,20 @@ Се преземаат податоците од резервната копија… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 75e4337663..bca8ab9d3c 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -89,6 +89,16 @@ ചിത്രങ്ങളോ ദൃശ്യങ്ങളോ ഓഡിയോയോ അറ്റാച്ച് ചെയ്യുന്നതിന് Signal-ന് സ്റ്റോറേജ് അനുമതി ആവശ്യമാണ്. പക്ഷേ ഇത് ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങൾ മെനുവിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"സ്റ്റോറേജ്\" പ്രവർത്തനക്ഷമമാക്കുക. കോൺ‌ടാക്റ്റ് വിവരങ്ങൾ‌ അറ്റാച്ച് ചെയ്യുന്നതിന് Signal-ന് കോൺ‌ടാക്റ്റുകളുടെ അനുമതി ആവശ്യമാണ്, പക്ഷേ ഇത് ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങൾ മെനുവിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"കോൺടാക്റ്റുകൾ\" പ്രവർത്തനക്ഷമമാക്കുക. ഒരു ലൊക്കേഷൻ അറ്റാച്ച് ചെയ്യുന്നതിന് Signal-ന് ലൊക്കേഷൻ അനുമതി ആവശ്യമാണ്, പക്ഷേ ഇത് ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങൾ മെനുവിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"ലൊക്കേഷൻ\" പ്രവർത്തനക്ഷമമാക്കുക. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s എന്നത് പേയ്മെന്റുകൾ ആക്‌ടിവേറ്റ് ചെയ്‌തിട്ടില്ല @@ -386,8 +396,16 @@ ഗ്രൂപ്പില്‍ ചേരുന്നതിനുള്ള നിങ്ങളുടെ അഭ്യർത്ഥന അഡ്മിന് അയച്ചു. അവർ നടപടിയെടുക്കുമ്പോൾ നിങ്ങളെ അറിയിക്കും. അഭ്യർത്ഥന റദ്ദാക്കുക - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ഓഡിയോ സന്ദേശങ്ങൾ അയയ്‌ക്കാൻ Signal-ന് മൈക്രോഫോൺ അനുമതി ആവശ്യമാണ്, പക്ഷേ ഇത് ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങളിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"മൈക്രോഫോൺ\" പ്രവർത്തനക്ഷമമാക്കുക. + %1$s എന്നയാളെ വിളിക്കുന്നതിന് Signal-ന് മൈക്രോഫോൺ, ക്യാമറ അനുമതികൾ ആവശ്യമാണ്, പക്ഷേ അവ ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങളിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"മൈക്രോഫോൺ\", \"ക്യാമറ\" എന്നിവ പ്രവർത്തനക്ഷമമാക്കുക. ഫോട്ടോകളും വീഡിയോയും എടുക്കാൻ ക്യാമറയിലേക്ക് Signal-ന് ആക്സസ് അനുവദിക്കുക. ഫോട്ടോകളോ വീഡിയോയോ എടുക്കാൻ Signal-ന് ക്യാമറാ അനുമതി ആവശ്യമാണ്, പക്ഷേ ഇത് ശാശ്വതമായി നിരസിച്ചിരിക്കുകയാണ്. ആപ്പ് ക്രമീകരണങ്ങളിൽ ചെന്ന്, \"അനുമതികൾ\" തിരഞ്ഞെടുത്ത് \"ക്യാമറ\" പ്രവർത്തനക്ഷമമാക്കുക. @@ -411,7 +429,13 @@ നിങ്ങൾ ഈ ഗ്രൂപ്പിൽ നിന്ന് പുറത്തുകടക്കും, ഇത് നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിൽ നിന്നും ഇല്ലാതാക്കുകയും ചെയ്യും. ഇല്ലാതാക്കൂ ഇല്ലാതാക്കിയ ശേഷം പുറത്തുകടക്കുക - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. ചേരുക @@ -3434,6 +3458,14 @@ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് ഫോട്ടോകളോ വീഡിയോകളോ ഫയലുകളോ അയയ്ക്കുക. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + സുരക്ഷാ സജ്ജീകരണം @@ -3688,6 +3720,14 @@ ചാറ്റ് ബ്രോഡ്കാസ്റ്റ് + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + പുതിയ ഗ്രൂപ്പ് ക്രമീകരണങ്ങൾ @@ -4184,8 +4224,16 @@ അംഗങ്ങളെ അവലോകനം ചെയ്യുക അവലോകന അഭ്യർത്ഥന - %1$d ഗ്രൂപ്പ് അംഗങ്ങൾക്ക് സമാന പേരുണ്ട്, ചുവടെയുള്ള അംഗങ്ങളെ അവലോകനം ചെയ്ത് നടപടിയെടുക്കാൻ തിരഞ്ഞെടുക്കുക. - അഭ്യർത്ഥന ആരാണ് നിങ്ങൾക്ക് അയച്ചതെന്ന് ഉറപ്പില്ലെങ്കിൽ, ചുവടെയുള്ള കോൺടാക്റ്റുകൾ അവലോകനം ചെയ്ത് നടപടിയെടുക്കുക. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + സാമാന്യമായി മറ്റ് ഗ്രൂപ്പുകളൊന്നുമില്ല. സാമാന്യമായി ഗ്രൂപ്പുകളൊന്നുമില്ല. @@ -4216,12 +4264,20 @@ %1$s ചേർന്നു %1$s-ഉം %2$s-ഉം ചേർന്നു %1$s-ഉം, %2$s-ഉം %3$s-ഉം ചേർന്നിട്ടുണ്ട് - %1$s-ഉം, %2$s-ഉം %3$d-ഉം മറ്റുള്ളവരും ചേർന്നിട്ടുണ്ട് + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ഇറങ്ങി പോയിട്ടുണ്ട് %1$s-ഉം %2$s-ഉം ഇറങ്ങി പോയിട്ടുണ്ട് %1$s-ഉം, %2$s-ഉം %3$s-ഉം ഇറങ്ങി പോയിട്ടുണ്ട് - %1$s-ഉം, %2$s-ഉം %3$d-ഉം മറ്റുള്ളവരും ഇറങ്ങി പോയിട്ടുണ്ട് + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + നിങ്ങൾ നിങ്ങൾ (മറ്റൊരു ഉപകരണത്തിൽ) @@ -4591,7 +4647,11 @@ ബ്ലോക്ക് ചെയ്തു - %1$d കോൺ‌ടാക്റ്റുകൾ + + + %1$d contact + %1$d contacts + സന്ദേശ വിനിമയം അപ്രത്യക്ഷമാകുന്ന സന്ദേശങ്ങൾ ആപ്പ് സുരക്ഷ @@ -6748,5 +6808,20 @@ ബാക്കപ്പ് ഡാറ്റ ഡൗൺലോഡ് ചെയ്യുന്നു… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index e40de4509a..67ff047803 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -89,6 +89,16 @@ फोटो, व्हिडिओ, किंवा ऑडिओ, संलग्न करण्यासाठी Signal ला संचयन परवानगीची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"संचयन\" सक्षम करा. संपर्क माहिती संलग्न करण्यासाठी Signal ला संपर्क परवानगीची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"संपर्क\" सक्षम करा. स्थान संलग्न करण्यासाठी Signal ला स्थान परवानगीची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"स्थान\" सक्षम करा. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ने पेमेंट्स सक्रिय केलेले नाहीत @@ -386,8 +396,16 @@ आपली सामील होण्याची विनंती गट प्रशासकाकडे पाठविली गेली आहे. जेव्हा ते कारवाई करतील तेव्हा आपल्याला सूचित केले जाईल. विनंती रद्द करा - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ऑडिओ संदेश पाठवण्यासाठी Signal ला मायक्रोफोन परवानगीची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"मायक्रोफोन\" सक्षम करा. + %1$s ला कॉल करण्यासाठी Signal ला मायक्रोफोन आणि कॅमेरा परवानग्यांची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"मायक्रोफोन\" आणि \"कॅमेरा\" सक्षम करा. फोटो आणि व्हिडिओ कॅप्चर करण्यासाठी, Signal ला कॅमेरा अॅक्सेसची अनुमती द्या. फोटो किंवा व्हिडिओ काढण्यासाठी Signal ला कॅमेरा परवानगीची आवश्यकता असते, पण ती कायमची नाकारली गेली आहे. कृपया अॅप सेटिंग मेनू मध्ये सुरू ठेवा, \"परवानग्या\" निवडा, आणि \"कॅमेरा\" सक्षम करा. @@ -411,7 +429,13 @@ आपण हा ग्रुप सोडाल , आणि तो आपल्या सर्व डिव्हाइसेस वरून हटविला जाईल. हटवा ग्रुप हटवा आणि सोडून द्या - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. सामील व्हा @@ -3434,6 +3458,14 @@ फोटो, व्हिडीओ आणि फाईल आपल्या डिव्हाइसवरून पाठवा. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + सुरक्षितता सेटअप @@ -3688,6 +3720,14 @@ चॅट प्रक्षेपण + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + नवीन गट सेटिंग @@ -4184,8 +4224,16 @@ सदस्यांचे पुनरावलोकन करा विनंतीचे पुनरावलोकन करा - %1$d गट सदस्यांचे नाव सारखे आहेत, खाली सदस्यांचे पुनरावलोकन करा आणि काय करायचे ते निवडा. - विनंती कोणाकडून आहे याची आपल्याला खात्री नसल्यास, खाली असलेल्या संपर्कांचे पुनरावलोकन करा आणि कारवाई करा. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + इतर कुठलेही गट समाईक नाहीत कुठलेही गट समाईक नाहीत. @@ -4216,12 +4264,20 @@ %1$s सामील झाले %1$s आणि %2$s सामील झाले %1$s, %2$s आणि %3$s सामील झाले - %1$s, %2$s आणि %3$d इतर व्यक्ती सामील झाले + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s सोडून गेले %1$s आणि %2$s सोडून गेले %1$s, %2$s आणि %3$s सोडून गेले - %1$s, %2$s आणि %3$d इतर व्यक्ती सोडून गेले + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + आपण आपण (दुसऱ्या डिव्हाईसवर) @@ -4591,7 +4647,11 @@ अवरोधित केले - %1$d संपर्क + + + %1$d contact + %1$d contacts + संदेशन हरवणारे संदेश अॅप सुरक्षा @@ -6748,5 +6808,20 @@ बॅकअप डेटा डाऊनलोड करत आहे… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index ba2dc71d51..8d3faf53be 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -89,6 +89,16 @@ Signal memerlukan kebenaran Storan untuk melampirkan foto, video atau audio, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Storan\". Signal memerlukan kebenaran Kenalan untuk melampirkan maklumat hubungan, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Kenalan\". Signal memerlukan kebenaran Lokasi untuk melampirkan lokasi, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Lokasi\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s belum mengaktifkan Pembayaran @@ -383,8 +393,16 @@ Permintaan anda untuk join telah dihantar kepada admin group. Anda akan diberitahu sekiranya mereka telah mengambil tindakan. Batalkan Permintaan - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal memerlukan kebenaran Mikrofon untuk menghantar mesej audio, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Mikrofon\". + Signal memerlukan kebenaran Mikrofon dan Kamera untuk memanggil %1$s, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Mikrofon\" dan \"Kamera\". Untuk menangkap foto dan video, benarkan Signal untuk mengakses kamera anda. Signal memerlukan kebenaran Kamera untuk menangkap foto dan video, tetapi telah ditolak secara kekal. Sila terus ke menu tetapan aplikasi, pilih \"Kebenaran\", dan dayakan \"Kamera\". @@ -408,7 +426,13 @@ Anda akan meninggalkan kumpulan ini, dan ia akan dipadam daripada semua peranti anda. Padam Padam dan tinggalkan - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Sertai @@ -3337,6 +3361,14 @@ Hantar foto, video dan fail daripada peranti anda. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Persediaan keselamatan @@ -3589,6 +3621,14 @@ Sembang Siaran + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Kumpulan baru Tetapan @@ -4078,8 +4118,14 @@ Semak ahli Semak permintaan - %1$d ahli kumpulan mempunyai nama yang sama, semak ahli di bawah dan pilih untuk mengambil tindakan. - Jika anda tidak pasti tentang penghantar permintaan, semak kenalan di bawah dan ambil tindakan. + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + Tidak ada kumpulan lain yang sama. Tidak ada kumpulan yang sama. @@ -4108,12 +4154,18 @@ %1$s telah menyertai kumpulan %1$s dan %2$s telah menyertai kumpulan %1$s, %2$s dan %3$s telah menyertai kumpulan - %1$s, %2$s dan %3$d orang yang lain telah menyertai kumpulan + + + %1$s, %2$s and %3$d others joined + %1$s telah meninggalkan kumpulan %1$s dan %2$s telah meninggalkan kumpulan %1$s, %2$s dan %3$s telah meninggalkan kumpulan - %1$s, %2$s dan %3$d orang yang lain telah meninggalkan kumpulan + + + %1$s, %2$s and %3$d others left + Anda Anda (pada peranti lain) @@ -4481,7 +4533,10 @@ Disekat - %1$d kenalan + + + %1$d contacts + Pemesejan Mesej hilang Keselamatan aplikasi @@ -6602,5 +6657,20 @@ Memuat turun data sandaran… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 45034981ca..a3aacbf6de 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -89,6 +89,16 @@ ဓာတ်ပုံ၊ ဗီဒီယို သို့မဟုတ် အသံ ပို့ရန်အတွက် သိုလှောင်မှုခွင့်ပြုချက်ကို Signal မှ ရရန် လိုအပ်သည်။ သို့သော် အမြဲတမ်းအတွက် ခွင့်မပြုပါဟု ရွေးထားပြီး ဖြစ်နေသဖြင့် အက်ပ် စက်တင်မီနူးသို့ သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ သိုလှောင်မှုကို ခွင့်ပြုပေးပါ။ အဆက်အသွယ်များ၏ အချက်အလက်များကို ပို့နိုင်ရန်အတွက် အဆက်အသွယ်များ ခွင့်ပြုချက်ကို Signal မှ ရရန်လိုအပ်သည်။ သို့သော် အမြဲတမ်းအတွက် ခွင့်မပြုပါဟုရွေးထားပြီး ဖြစ်နေသဖြင့် အက်ပ် စက်တင်မီနူးသို့သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ အဆက်အသွယ်များကို ခွင့်ပြုပေးပါ။ မိမိ၏ တည်နေရာကို ပို့နိုင်ရန် တည်နေရာ ခွင့်ပြုချက်ကို Signal မှ ရရန်လိုအပ်သည်။ သို့သော် အမြဲတမ်းအတွက် ခွင့်မပြုပါဟု ရွေးထားပြီး ဖြစ်နေသဖြင့် အက်ပ် စက်တင်မီနူးသို့သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ တည်နေရာကို ခွင့်ပြုပေးပါ။ + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s သည် ငွေပေးချေမှုများကို သက်ဝင်လုပ်ဆောင်ထားခြင်း မရှိသေးပါ @@ -383,8 +393,16 @@ သင်၏အဖွဲ့ဝင်ရန် တောင်းဆိုမှုကို အဖွဲ့အက်မင်ထံသို့ ပို့ပြီးပါပြီ။ သူတို့ဆောင်ရွက်ချက်ကို အကြောင်းပြန်ပေးပါမည်။ တောင်းဆိုမှုကိုပယ်ဖျက်သည်။ - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. အသံဖိုင်များပို့နိုင်ရန် Signalမှ မိုက်ခရိုဖုန်းအား အသုံးပြုခွင့်ရရန် လိုအပ်သည်။ သို့သော် လုံးဝခွင့်မပြုပါ ဟုရွေးထားပြီး ဖြစ်နေသဖြင့် အပ်ပလီကေးရှင်း အပြင်အဆင်သို့ သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ မိုက်ခရိုဖုန်းကို အသုံးပြုနိုင်အောင် ပြုလုပ်ပါ။ + %1$sဖုန်းခေါ်ဆိုမှုပြုနိုင်ရန် Signalမှ မိုက်ခရိုဖုန်း နှင့် ကင်မရာအား အသုံးပြုခွင့်ရရန်လိုအပ်သည်။ သို့သော် အမြဲတမ်းတွက် ခွင့်မပြုပါ ဟုရွေးထားပြီး ဖြစ်နေသဖြင့် အပ်ပလီကေးရှင်း အပြင်အဆင်သို့ သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ မိုက်ခရိုဖုန်း နှင့် ကင်မရာကို အသုံးပြုနိုင်အောင် ပြုလုပ်ပါ။ ဓါတ်ပုံနှင့် ဗီဒီယိုရိုက်နိုင်ရန် Signal မှ မိမိကင်မရာကို အသုံးပြုခွင့်ပေးပါ။ ဓါတ်ပုံနှင့် ဗီဒီယိုရိုက်နိုင်ရန် Signal မှ မိမိကင်မရာကို အသုံးပြုခွင့်ပေးထားရန်လိုသည်။ သို့သော် လုံးဝခွင့်မပြုပါ ဟုရွေးထားပြီး ဖြစ်နေသဖြင့် အပ်ပလီကေးရှင်း အပြင်အဆင်သို့ သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ ကင်မရာကို အသုံးပြုနိုင်အောင် ပြုလုပ်ပါ။ @@ -408,7 +426,13 @@ သင်သည် ဤအဖွဲ့မှ ထွက်မည်ဖြစ်ပြီး ယင်းအဖွဲ့ကို သင်၏ အခြားစက်များအားလုံးမှလည်း ဖျက်သွားပါမည်။ ဖျက်ရန် ဖျက်ပြီး ထွက်ရန် - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. ပူးပေါင်း @@ -3337,6 +3361,14 @@ သင့်စက်မှ ဓာတ်ပုံများ၊ ဗီဒီယိုများနှင့် ဖိုင်များကို ပေးပို့လိုက်ပါ။ + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + လုံခြုံရေး သတ်မှတ်ချိန်ညှိမှု @@ -3589,6 +3621,14 @@ ချက်(တ်) ထုတ်လွှင့်ပါ + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + အဖွဲ့သစ် ဆက်တင် @@ -4078,8 +4118,14 @@ မန်ဘာများကို ပြန်လည်သုံးသပ်ရန် ပြန်လည်သုံးသပ်မှု တောင်းဆိုချက် - အဖွဲ့ဝင် %1$d ဦးသည် အမည်တူနေပါသည်၊ အောက်ပါအဖွဲ့ဝင်များကို စစ်ဆေးပါ သို့မဟုတ် လုပ်ဆောင်ချက်တစ်ခုလုပ်ရန် ရွေးချယ်ပါ။ - မည်သူမှ တောင်းဆိုသည်ကို မသေချာပါက အောက်ပါ အဆက်အသွယ်များကို ပြန်လည်စစ်ဆေးပြီး လုပ်ဆောင်ချက်တစ်ခု ရွေးပါ။ + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + အခြား တူညီသော အဖွဲ့များမရှိပါ အခြား တူညီသော အဖွဲ့များမရှိပါ @@ -4108,12 +4154,18 @@ အဖွဲ့ထဲ %1$s ၀င်ရောက်လာသည်။ %1$s နှင့် %2$s တို့သည် အဖွဲ့ထဲသို့ ဝင်လာပါသည် %1$s, %2$s နှင့် %3$s တို့သည် အဖွဲ့ထဲသို့ ဝင်လာပါသည် - %1$s, %2$s နှင့် အခြား %3$d ဦးသည် အဖွဲ့ထဲသို့ ဝင်လာပါသည် + + + %1$s, %2$s and %3$d others joined + %1$s အဖွဲ့မှ ထွက်သွားသည် %1$s နှင့် %2$s တို့သည် အဖွဲ့မှ ထွက်သွားသည် %1$s, %2$s နှင့် %3$s တို့သည် အဖွဲ့မှ ထွက်သွားသည် - %1$s, %2$s နှင့် အခြား %3$d ဦးသည် အဖွဲ့မှ ထွက်သွားသည် + + + %1$s, %2$s and %3$d others left + သင် သင် (အခြားစက်ကိရိယာပေါ်တွင်) @@ -4481,7 +4533,10 @@ ဘလော့ခ်ထားပြီး - အဆက်အသွယ် %1$d ခု + + + %1$d contacts + မက်ဆေ့ချ်ပေးပို့ခြင်း ပျောက်ကွယ်မည့် မက်ဆေ့ချ်များ အက်ပ် လုံခြုံရေး @@ -6602,5 +6657,20 @@ ဘက်ခ်အပ် ဒေတာများကို ဒေါင်းလုဒ်လုပ်နေသည်… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index e450f67258..a8e277cf55 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -89,6 +89,16 @@ Signal krever tillatelse fra systemet for å kunne legge til bilder, videoer eller lyd, men du har valgt å avslå dette permanent. Gå til «Apper»-menyen på systemet og slå på tillatelsen «Lagring». Signal krever tillatelse fra systemet for å kunne legge til kontaktinformasjon, men du har valgt å avslå dette permanent. Gå til «Apper»-menyen på systemet og slå på tillatelsen «Kontakter». Signal krever tillatelse fra systemet for å kunne legge til posisjonsdata, men du har valgt å avslå dette permanent. Gå til «Apper»-menyen på systemet og slå på tillatelsen «Posisjon». + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s har ikke aktivert betalinger @@ -386,8 +396,16 @@ Din forespørsel om å bli med har blitt sendt til gruppeadministratoren. Du vil bli varslet når de tar aksjon. Avbryt forespørsel - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal krever tillatelse fra systemet for å kunne bruke mikrofonen, men du har valgt å avslå dette permanent. Gå til «Apper»-menyen på systemet og slå på tillatelsen «Mikrofon». + Signal krever tillatelser fra systemet for å kunne ringe %1$s, men du har valgt å avslå minst én av disse permanent. Gå til «Apper»-menyen på systemet og slå på tillatelser for «Mikrofon» og «Kamera». Du må gi Signal «Kamera»-tillatelse på systemet for å kunne filme og ta bilder. Signal krever tillatelse fra systemet for å kunne ta bilder eller filme, men du har valgt å avslå dette permanent. Gå til «Apper»-menyen på systemet og slå på tillatelsen «Kamera». @@ -411,7 +429,13 @@ Du forlater denne gruppen, og den blir slettet fra alle enhetene dine. Slett Slett og forlat - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Bli med @@ -3434,6 +3458,14 @@ Send bilder, videoer og filer fra enheten din. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Sikkerhetsoppsett @@ -3688,6 +3720,14 @@ Samtale Kringkast + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Ny gruppe Innstillinger @@ -4184,8 +4224,16 @@ Gå gjennom medlemslisten Se gjennom forespørsel - %1$d gruppemedlemmer har det samme navnet, gå gjennom medlemmene under og velg handling. - Hvis du ikke er sikker på hvem forespørselen er fra, se gjennom kontaktene nedenfor og velg handling. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Ingen andre grupper til felles. Ingen grupper til felles. @@ -4216,12 +4264,20 @@ %1$s ble med %1$s og %2$s ble med %1$s, %2$s og %3$s ble med - %1$s, %2$s og %3$d andre ble med + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s forlot %1$s og %2$s forlot %1$s, %2$s og %3$s forlot - %1$s, %2$s og %3$d andre forlot + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Deg Deg (på en annen enhet) @@ -4591,7 +4647,11 @@ Blokkert - %1$d kontakter + + + %1$d contact + %1$d contacts + Meldinger Tidsavgrensede meldinger App-sikkerhet @@ -6748,5 +6808,20 @@ Laster ned sikkerhetskopi … + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index cd0a3c5af2..45fdf72cff 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -89,6 +89,16 @@ Signal heeft toegang tot de externe opslagruimte nodig om afbeeldingen, video\'s of audio te kunnen verzenden, maar deze toestemming is pertinent geweigerd. Ga naar de instellingen, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. Signal heeft toegang tot de contacten nodig om contactinformatie in Signal weer te geven, maar deze toestemming is pertinent geweigerd. Ga naar de instellingen, tik op ‘Toestemmingen’ en schakel ‘Contacten’ in. Signal heeft toegang tot de locatie nodig om locaties te kunnen verzenden aan je gesprekspartners, maar deze toestemming is pertinent geweigerd. Ga naar de instellingen, tik op ‘Toestemmingen’ en schakel ‘Locatie’ in. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s heeft Betalingen niet geactiveerd @@ -386,8 +396,16 @@ Je verzoek om lid te worden van de groep is doorgestuurd naar een beheerder. Je krijgt een melding zodra deze een besluit heeft genomen. Verzoek annuleren - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal heeft toegang tot de microfoon nodig om spraakberichten te kunnen opnemen, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Microfoon’ in. + Signal heeft toegang tot de microfoon en de camera nodig om %1$s te kunnen bellen, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, 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 opnemen, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Camera’ in. @@ -411,7 +429,13 @@ Je zult deze groep verlaten en het gesprek zal op al je eigen apparaten worden verwijderd. Verwijderen Verwijderen en verlaten - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Deelnemen @@ -3434,6 +3458,14 @@ Verstuur foto\'s, video\'s en bestanden vanaf je apparaat. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Veiligheidsinstellingen @@ -3688,6 +3720,14 @@ Chatten Uitzenden + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nieuwe groep Instellingen @@ -4184,8 +4224,16 @@ Groepsleden vergelijken Personen vergelijken - %1$d groepsleden hebben dezelfde naam. Vergelijk deze groepsleden hieronder en onderneem actie indien je denkt dat een van hen zich probeert voor te doen als de ander. - Vergelijk, als je niet zeker bent van wie het gespreksverzoek afkomstig is, de personen hieronder en onderneem actie indien je denkt dat een van hen zich probeert voor te doen als de ander. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Geen andere gemeenschappelijke groepen Geen gemeenschappelijke groepen @@ -4216,12 +4264,20 @@ %1$s neemt nu deel aan deze oproep %1$s en %2$s nemen nu deel %1$s, %2$s en %3$s nemen nu deel - %1$s, %2$s en %3$d andere nemen nu deel + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s heeft de oproep verlaten %1$s en %2$s hebben de oproep verlaten %1$s, %2$s en %3$s hebben de oproep verlaten - %1$s, %2$s en %3$d andere hebben de oproep verlaten + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Jij Jij (vanaf een ander apparaat) @@ -4591,7 +4647,11 @@ Geblokkeerd - %1$d personen & groepen + + + %1$d contact + %1$d contacts + Berichten Verdwijnende berichten App-beveiliging @@ -6748,5 +6808,20 @@ Back-upgegevens aan het downloaden… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 7b8f4bbbeb..587e7ae1f3 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -89,6 +89,16 @@ Signal ਨੂੰ ਫ਼ੋਟੋਆਂ, ਵੀਡੀਓ ਜਾਂ ਆਡੀਓ ਜੋੜਨ ਲਈ ਸਟੋਰੇਜ ਦੀ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਸਨੂੰ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ਮੇਨੂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ ਅਤੇ \"ਸਟੋਰੇਜ\" ਨੂੰ ਸਮਰੱਥ ਕਰੋ। Signal ਨੂੰ ਸੰਪਰਕ ਜਾਣਕਾਰੀ ਜੋੜਨ ਲਈ ਸੰਪਰਕਾਂ ਲਈ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਸਨੂੰ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ਮੇਨੂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ ਅਤੇ \"ਸੰਪਰਕ\" ਨੂੰ ਸਮਰੱਥ ਕਰੋ। Signal ਨੂੰ ਕੋਈ ਟਿਕਾਣਾ ਜੋੜਨ ਲਈ ਟਿਕਾਣੇ ਲਈ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਸਨੂੰ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ਮੇਨੂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ ਅਤੇ \"ਟਿਕਾਣਾ\" ਸਮਰੱਥ ਕਰੋ। + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ਨੇ ਭੁਗਤਾਨ ਫੀਚਰ ਨੂੰ ਐਕਟੀਵੇਟ ਨਹੀਂ ਕੀਤਾ ਹੈ @@ -386,8 +396,16 @@ ਤੁਹਾਡੀ ਸ਼ਾਮਲ ਹੋਣ ਦੀ ਬੇਨਤੀ ਗਰੁੱਪ ਪਰਸ਼ਾਸ਼ਕ ਨੂੰ ਭੇਜ ਦਿੱਤੀ ਗਈ ਹੈ। ਜਦੋਂ ਉਹ ਕਾਰਵਾਈ ਕਰਨਗੇ ਤਾਂ ਤੂਹਾਨੂੰ ਸੂਚਿਤ ਕੀਤਾ ਜਾਵੇਗਾ। ਬੇਨਤੀ ਨੂੰ ਰੱਦ ਕਰੋ - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ਆਡੀਓ ਸੁਨੇਹੇ ਭੇਜਣ ਲਈ Signal ਨੂੰ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਸਨੂੰ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ| ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ, ਅਤੇ \"ਮਾਈਕ੍ਰੋਫ਼ੋਨ\" ਨੂੰ ਸਮਰੱਥ ਕਰੋ। + %1$s ਨੂੰ ਕਾਲ ਕਰਨ ਲਈ Signal ਨੂੰ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਅਤੇ ਕੈਮਰਾ ਇਜਾਜ਼ਤਾਂ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਹਨਾਂ ਲਈ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ | ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ, ਅਤੇ \"ਮਾਈਕ੍ਰੋਫ਼ੋਨ\" ਅਤੇ \"ਕੈਮਰਾ\" ਨੂੰ ਸਮਰੱਥ ਕਰੋ। ਵੀਡੀਓ ਅਤੇ ਫ਼ੋਟੋਆਂ ਖਿੱਚਣ ਲਈ Signal ਨੂੰ ਕੈਮਰੇ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ। ਵੀਡੀਓ ਅਤੇ ਫੋਟੋਆਂ ਖਿੱਚਣ ਲਈ Signal ਨੂੰ ਕੈਮਰੇ ਦੀ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ, ਪਰ ਇਸ ਨੂੰ ਸਥਾਈ ਤੌਰ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਸੈਟਿੰਗਾਂ ’ਤੇ ਜਾਰੀ ਰੱਖੋ, \"ਇਜਾਜ਼ਤਾਂ\" ਚੁਣੋ, ਅਤੇ \"ਕੈਮਰਾ\" ਨੂੰ ਸਮਰੱਥ ਕਰੋ। @@ -411,7 +429,13 @@ ਤੁਸੀਂ ਇਸ ਗਰੁੱਪ ਨੂੰ ਛੱਡ ਦਿਓਗੇ, ਅਤੇ ਇਸ ਨੂੰ ਤੁਹਾਡੇ ਸਾਰੇ ਡਿਵਾਈਸਾਂ ਵਿੱਚੋਂ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਮਿਟਾਓ ਹਟਾਓ ਅਤੇ ਛੱਡੋ - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. ਸ਼ਾਮਲ ਹੋਵੋ @@ -3434,6 +3458,14 @@ ਆਪਣੇ ਡਿਵਾਈਸ ਤੋਂ ਫ਼ੋਟੋਆਂ, ਵੀਡੀਓ ਅਤੇ ਫ਼ਾਈਲਾਂ ਭੇਜੋ। + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + ਸੁਰੱਖਿਆ ਸੈੱਟਅੱਪ @@ -3688,6 +3720,14 @@ ਚੈਟ ਬ੍ਰੌਡਕਾਸਟ + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + ਨਵਾਂ ਸਮੂਹ ਸੈਟਿੰਗਾਂ @@ -4184,8 +4224,16 @@ ਮੈਂਬਰਾਂ ਦੀ ਸਮੀਖਿਆ ਕਰੋ ਬੇਨਤੀ ਦੀ ਸਮੀਖਿਆ ਕਰੋ - %1$d ਗਰੁੱਪ ਮੈਂਬਰਾਂ ਦੇ ਨਾਂ ਇੱਕੋ ਜਿਹੇ ਹਨ, ਹੇਠਲੇ ਮੈਂਬਰਾਂ ਦੀ ਸਮੀਖਿਆ ਕਰੋ ਅਤੇ ਕਾਰਵਾਈ ਦੀ ਚੋਣ ਕਰੋ। - ਜੇ ਤੁਹਾਨੂੰ ਇਹ ਪੱਕਾ ਨਹੀਂ ਪਤਾ ਹੈ ਕਿ ਬੇਨਤੀ ਕਿਸ ਤੋਂ ਆਈ ਹੈ, ਤਾਂ ਹੇਠਲੇ ਸੰਪਰਕਾਂ ਦੀ ਸਮੀਖਿਆ ਕਰੋ ਅਤੇ ਕਾਰਵਾਈ ਕਰੋ। + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + ਕੋਈ ਹੋਰ ਗਰੁੱਪ ਸਾਂਝੇ ਨਹੀਂ। ਕੋਈ ਗਰੁੱਪ ਸਾਂਝੇ ਨਹੀਂ। @@ -4216,12 +4264,20 @@ %1$s ਸ਼ਾਮਲ ਹੋਏ %1$s ਅਤੇ %2$s ਸ਼ਾਮਲ ਹੋਏ %1$s, %2$s ਅਤੇ %3$s ਸ਼ਾਮਲ ਹੋਏ - %1$s, %2$s ਅਤੇ %3$d ਹੋਰ ਸ਼ਾਮਲ ਹੋਏ + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ਨੇ ਛੱਡਿਆ %1$s ਅਤੇ %2$s ਨੇ ਛੱਡਿਆ %1$s, %2$s ਅਤੇ %3$s ਨੇ ਛੱਡਿਆ - %1$s, %2$s ਅਤੇ %3$d ਹੋਰਾਂ ਨੇ ਛੱਡਿਆ + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + ਤੁਸੀਂ ਤੁਸੀਂ (ਹੋਰ ਡਿਵਾਈਸ ਉੱਤੇ) @@ -4591,7 +4647,11 @@ ਪਾਬੰਦੀ ਲਗਾਈ ਗਈ - %1$d ਸੰਪਰਕ + + + %1$d contact + %1$d contacts + ਸੁਨੇਹੇ ਲੈਣ-ਦੇਣ ਅਲੋਪ ਹੋਣ ਵਾਲੇ ਸੁਨੇਹੇ ਐਪ ਦੀ ਸੁਰੱਖਿਆ @@ -6748,5 +6808,20 @@ ਬੈਕਅੱਪ ਡਾਟਾ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index fdfd3c33fc..f95713c7bf 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -89,6 +89,16 @@ Signal wymaga pozwolenia na przechowywanie w celu dołączania zdjęć, filmów lub dźwięków, ale zostało one na stałe odrzucone. Przejdź do menu ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Przechowywanie\". Signal wymaga pozwolenia na dostęp do kontaktów w celu dołączenia informacji o kontaktach, ale zostało one na stałe odrzucone. Przejdź do menu ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Kontakty\". Signal wymaga pozwolenia na dostęp do lokalizacji w celu załączenia lokalizacji w wiadomościach, ale zostało one na stałe odrzucone. Przejdź do ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Lokalizacja\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s nie aktywował(a) płatności @@ -392,8 +402,16 @@ Twoja prośba o przyjęcie do grupy została wysłana do administratora. Zostaniesz powiadomiony(a), gdy podejmie decyzję. Anuluj prośbę - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal wymaga pozwolenia na dostęp do mikrofonu w celu umożliwienia wysyłania wiadomości głosowych, ale zostało one na stałe odrzucone. Przejdź do ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Mikrofon\". + Signal wymaga pozwolenia na dostęp do mikrofonu i aparatu, aby zadzwonić do %1$s, ale zostały one na stałe odrzucone. Przejdź do ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Aparat\" oraz \"Mikrofon\". Aby móc robić zdjęcia i nagrywać wideo, zezwól Signal na dostęp do aparatu. Signal wymaga pozwolenia na dostęp do aparatu w celu umożliwienia robienia zdjęć i nagrywania filmów, ale zostało one na stałe odrzucone. Przejdź do ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Aparat\". @@ -417,7 +435,13 @@ Opuścisz tę grupę i zostanie ona usunięta ze wszystkich Twoich urządzeń. Usuń Usuń i opuść - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Dołącz @@ -3628,6 +3652,14 @@ Wysyłaj zdjęcia, wideo i pliki ze swojego urządzenia. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Konfiguracja bezpieczeństwa @@ -3886,6 +3918,14 @@ Czat Nadawanie + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nowa grupa Ustawienia @@ -4396,8 +4436,20 @@ Przejrzyj członków Przejrzyj prośby - %1$d członków grupy ma takie samo imię, przejrzyj poniższych członków i podejmij działania. - Jeśli nie masz pewności, od kogo pochodzi ta prośba, przejrzyj poniższe kontakty i podejmij działania. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Brak innych, wspólnych grup. Brak wspólnych grup. @@ -4432,12 +4484,24 @@ %1$s dołączył(a) %1$s i %2$s dołączyli(ły) %1$s, %2$s i %3$s dołączyli(ły) - %1$s, %2$s i %3$d innych dołączyło + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s opuścił(a) rozmowę %1$s i %2$s opuścii(ły) rozmowę %1$s, %2$s i %3$s opuścili(ły) rozmowę - %1$s, %2$s i %3$d innych opuściło rozmowę + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Ty Ty (na innym urządzeniu) @@ -4811,7 +4875,13 @@ Zablokowano - %1$d kontakty(ów) + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Wiadomości Znikające wiadomości Bezpieczeństwo aplikacji @@ -7040,5 +7110,20 @@ Pobieranie danych kopii zapasowej… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index e4e5718c71..a845bd29a5 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -89,6 +89,16 @@ O Signal precisa da permissão Armazenamento para anexar fotos, vídeos ou áudio, mas ela foi permanentemente negada. Vá até o menu de configurações de aplicativos, selecione \"Permissões\" e habilite \"Armazenamento\". O Signal precisa da permissão Contatos para anexar informações de contato, mas ela foi permanentemente negada. Vá até o menu de configurações de aplicativos, selecione \"Permissões\" e habilite \"Contatos\". O Signal precisa da permissão Localização para anexar uma localização, mas ela foi permanentemente negada. Vá até o menu de configurações de aplicativos, selecione \"Permissões\" e habilite \"Localização\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s não ativou Pagamentos @@ -386,8 +396,16 @@ Seu pedido para participar foi enviado ao administrador do grupo. Você será notificado quando a decisão for tomada. Cancelar pedido - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. O Signal precisa da permissão Microfone para enviar mensagens de áudio, mas ela foi permanentemente negada. Favor ir no menu de configurações de aplicativos, selecionar \"Permissões\", e habilitar \"Microfone\". + O Signal precisa das permissões Microfone e Câmera para chamar %1$s, mas elas foram permanentemente negadas. Favor ir no menu de configurações de aplicativos, selecionar \"Permissões\", e habilitar \"Microfone\" e \"Câmera\". Para tirar fotos e fazer vídeos, permita ao Signal acessar a câmera. O Signal precisa da permissão Câmera para tirar fotos e fazer vídeos, mas ela foi permanentemente negada. Favor ir no menu de configurações de aplicativos, selecionar \"Permissões\", e habilitar \"Câmera\". @@ -411,7 +429,13 @@ Você deixará este grupo, que será apagada de todos os seus dispositivos. Apagar Apagar e sair - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Entrar @@ -3434,6 +3458,14 @@ Envie fotos, vídeos e arquivos do seu dispositivo. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Configuração de segurança @@ -3688,6 +3720,14 @@ Chat Transmissão + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Novo grupo Configurações @@ -4184,8 +4224,16 @@ Revisar os membros Analisar o pedido - %1$d membros do grupo têm o mesmo nome. Revise os membros abaixo e decida o que fazer. - Se você não tiver certeza de quem é o pedido, revise os contatos abaixo e tome as providências. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nenhum outro grupo em comum. Não há grupos em comum. @@ -4216,12 +4264,20 @@ %1$s entrou %1$s e %2$s entraram %1$s, %2$s e %3$s entraram - %1$s, %2$s e %3$d outras pessoas entraram + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s saiu %1$s e %2$s saíram %1$s, %2$s e %3$s saíram - %1$s, %2$s e %3$d outras pessoas saíram + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Você Você (em outro aparelho) @@ -4591,7 +4647,11 @@ Bloqueados - %1$d contatos + + + %1$d contact + %1$d contacts + Mensagens Mensagens efêmeras Segurança do app @@ -6748,5 +6808,20 @@ Baixando dados de backup… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index db82da488a..79dbc6b0e9 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -89,6 +89,16 @@ O Signal requer permissão de acesso ao armazenamento de forma a poder anexar fotografias, vídeo e áudio, mas esta foi negada permanentemente. Por favor, aceda ao menu de definições das aplicações do seu telemóvel, selecione a aplicação Signal, e em \"Permissões\" ative \"Armazenamento\" ou \"Memória\". O Signal requer permissão de acesso aos contactos para anexar os dados do contacto, mas esta foi negada permanentemente. Por favor, aceda às definições das aplicações no menu do telemóvel, selecione a aplicação Signal e em \"Permissões\" ative \"Contactos\". O Signal requer permissão de acesso à localização para poder anexar uma localização, mas esta foi negada permanentemente. Por favor aceda às definições das aplicações no menu do telemóvel, selecione a aplicação Signal e nas \"Permissões\" ative a \"Localização\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s não ativou os Pagamentos. @@ -386,8 +396,16 @@ O seu pedido para entrar foi enviado ao administrador do grupo. Será notificado quando ele tomar uma decisão. Cancelar pedido - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. O Signal requer permissão de acesso ao microfone para enviar mensagens áudio, mas esta foi negada permanentemente. Por favor, aceda às definições das aplicações do telemóvel, selecione a aplicação Signal e nas \"Permissões\" ative \"Microfone\". + O Signal requer permissão de acesso ao microfone e câmara, para poder ligar a %1$s, mas esta foi negada permanentemente. Por favor, aceda às definições das aplicações do telemóvel, selecione a aplicação Signal e nas \"Permissões\" ative o \"Microfone\" e a \"Câmara\". Para tirar fotografias e filmar vídeos, dê permissões ao Signal para acesso à câmara. O Signal requer permissão de acesso à câmara para tirar fotografias ou vídeos, mas esta foi negada permanentemente. Por favor, aceda às definições das aplicações do telemóvel, seleccione a aplicação Signal e nas \"Permissões\" ative a \"Câmara\". @@ -411,7 +429,13 @@ Irá abandonar este grupo, e ele será eliminado de todos os seus dispositivos. Eliminar Eliminar e abandonar - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Entrar @@ -3434,6 +3458,14 @@ Envie fotos, vídeos e ficheiros a partir do seu dispositivo. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Configuração de segurança @@ -3688,6 +3720,14 @@ Chat Emissão + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Novo grupo Definições @@ -4184,8 +4224,16 @@ Rever membros Rever pedido - %1$d membros do grupo têm o mesmo nome, reveja os membros abaixo e escolha que ação tomar. - Se não tiver a certeza de quem é a solicitação, analise os contactos abaixo e tome uma atitude. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Sem outros grupos em comum Sem grupos em comum. @@ -4216,12 +4264,20 @@ %1$s juntou-se %1$s e %2$s juntaram-se %1$s, %2$s e %3$s juntaram-se - %1$s, %2$s e outros %3$d juntaram-se + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s restante(s) %1$s e %2$s restante(s) %1$s, %2$s e %3$s restante(s) - %1$s, %2$s e outros %3$d restantes + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Você Você (noutro dispositivo) @@ -4591,7 +4647,11 @@ Bloqueados - %1$d contactos + + + %1$d contact + %1$d contacts + Mensagens Destruição de mensagens Segurança da aplicação @@ -6748,5 +6808,20 @@ A descarregar dados da cópia de segurança… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 541784253e..c1ffbc9a7c 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -89,6 +89,16 @@ Signal are nevoie de permisiunea pentru spațiul de stocare pentru a putea atașa poze, videoclipuri sau audio, dar i-a fost refuzat accesul permanent. Te rugăm continuă în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \"Spațiu de stocare\". Signal are nevoie de permisiunea pentru Contacte pentru a putea atașa informații despre contacte, dar i-a fost refuzat accesul permanent. Te rugăm continuă în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \"Contacte\". Signal are nevoie de permisiunea pentru Locație pentru a putea atașa o locație dar i-a fost refuzat accesul permanent. Te rugăm continuă în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \'Locație\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s nu a activat Plățile @@ -389,8 +399,16 @@ Solicitarea ta de a te alătura grupului a fost trimisă administratorului. Vei fi notificat când acesta ia o decizie. Anulează Cerere - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal are nevoie de permisiunea pentru Microfon pentru a putea trimite mesaje audio dar i-a fost refuzat accesul permanent. Te rugăm să continui în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \"Microfon\". + Signal are nevoie de permisiunile pentru Microfon și Cameră pentru a putea apela pe %1$s, dar i-a fost refuzat accesul permanent. Te rugăm continuă în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \"Microfon\" și \"Camera\". Pentru a captura poze sau filme, permite aplicației Signal să acceseze camera. Signal are nevoie de permisiunea pentru Cameră pentru a captura poze sau filme dar i-a fost refuzat accesul permanent. Te rugăm continuă în meniul de setări al aplicației, selectează \"Permisiuni\" și activează \"Camera\". @@ -414,7 +432,13 @@ Vei părăsi acest grup și va fi șters de pe toate dispozitivele tale. Șterge Șterge și părăsește - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Alătură-te @@ -3531,6 +3555,14 @@ Trimite fotografii, videoclipuri și fișiere de pe dispozitivul tău. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Configurarea securității @@ -3787,6 +3819,14 @@ Conversație Difuzare + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Grup nou Setări @@ -4290,8 +4330,18 @@ Revizuire membri Revizuire solicitare - %1$d membri ai grupului au același nume, revizuiește membrii de mai jos și ia măsuri. - Dacă nu ești sigur de la cine provine solicitarea, consultă persoanele de contact de mai jos și ia măsuri. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Nu există alte grupuri în comun. Niciun grup în comun. @@ -4324,12 +4374,22 @@ %1$s s-a alăturat %1$s și %2$s s-au alăturat %1$s, %2$s și %3$s s-au alăturat - %1$s, %2$s și %3$d alții s-au alăturat + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s a ieșit %1$s și %2$s au ieșit %1$s, %2$s și %3$s au ieșit - %1$s, %2$s și %3$d alții au ieșit + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Tu Tu (pe alt dispozitiv) @@ -4701,7 +4761,12 @@ Blocat - %1$d contacte + + + %1$d contact + %1$d contacts + %1$d contacts + Mesagerie Dispariție mesaje Securitate aplicație @@ -6894,5 +6959,20 @@ Se descarcă datele din copia de rezervă… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index b1bef2dfdf..cb6b96b2cf 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -89,6 +89,16 @@ Signal требуется разрешение на доступ к хранилищу для прикрепления фото, видео или аудио, но оно было вами отклонено. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Хранилище». Signal требуется разрешение на доступ к контактам для прикрепления контактной информации, но оно было вами отклонено. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Контакты». Signal требуется разрешение на доступ к местоположению для прикрепления местоположения, но оно было вами отклонено. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Местоположение». + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + Пользователь %1$s не активировал платежи @@ -392,8 +402,16 @@ Ваш запрос на присоединение был отправлен администратору группы. Вы будете уведомлены, когда он примет решение. Отменить запрос - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal требуется разрешение на доступ к микрофону для отправки аудиосообщений, но оно было вами отклонено. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Микрофон». + Signal требуются разрешения на доступ к микрофону и камере, чтобы позвонить %1$s, но они были вами отклонены. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Микрофон» и «Камера». Для создания фото и видео разрешите Signal доступ к камере. Signal требуется разрешение на доступ к камере, чтобы снимать фото или видео, но оно было вами отклонено. Нажмите «Продолжить», чтобы перейти в настройки приложения, откройте «Разрешения» и включите «Камера». @@ -417,7 +435,13 @@ Вы покинете эту группу, и она будет удалена со всех ваших устройств. Удалить Удалить и покинуть - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Войти @@ -3628,6 +3652,14 @@ Отправляйте фото, видео и файлы со своего устройства. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Настройки безопасности @@ -3886,6 +3918,14 @@ Чат Вещание + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Новая группа Настройки @@ -4396,8 +4436,20 @@ Проверить участников Проверить запрос - %1$d участников группы имеют одинаковое имя. Просмотрите участников ниже и, если необходимо, примите меры. - Если вы не уверены, от кого этот запрос, просмотрите участников ниже и примите меры. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Нет других общих групп. Нет общих групп. @@ -4432,12 +4484,24 @@ %1$s присоединился(-лась) %1$s и %2$s присоединились %1$s, %2$s и %3$s присоединились - %1$s, %2$s и ещё %3$d присоединились + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s покинул(-а) звонок %1$s и %2$s покинули звонок %1$s, %2$s и %3$s покинули звонок - %1$s, %2$s и ещё %3$d покинули звонок + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Вы Вы (на другом устройстве) @@ -4811,7 +4875,13 @@ Заблокированные - %1$d контактов + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Общение Исчезающие сообщения Безопасность приложения @@ -7040,5 +7110,20 @@ Скачиваем резервную копию… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index f184d1b320..34eb6b8a03 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -89,6 +89,16 @@ Signal potrebuje prístup k úložisku aby k správam mohol pridať obrázkové, video a zvukové prílohy, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Úložisko\". Signal potrebuje prístup ku kontaktom aby k správam mohol pripojiť informácie o kontakte, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Kontakty\". Signal potrebuje prístup k polohe aby k správam mohol pripojiť informácie o polohe, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Poloha\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + Používateľ %1$s neaktivoval platby @@ -392,8 +402,16 @@ Vaša žiadosť o členstvo bola odoslaná administrátorovi skupiny. Keď na ňu zareaguje, dostanete oznámenie. Zrušiť žiadosť - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal potrebuje prístup k mikrofónu aby mohol posielať zvukové správy, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Mikrofón\". + Signal potrebuje prístup k mikrofónu a fotoaparátu aby mohol zavolať %1$s, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Mikrofón\" a \"Fotoaparát\". Pre fotenie a nahrávanie videa potrebuje Signal prístup k fotoaparátu. Signal potrebuje prístup k fotoaparátu aby mohol vytvárať fotografie a video, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Fotoaparát\". @@ -417,7 +435,13 @@ Opustíte túto skupinu a vymažete ju zo všetkých vašich zariadení. Vymazať Vymazať a opustiť - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Pridať sa @@ -3628,6 +3652,14 @@ Posielajte fotografie, videá a súbory zo svojho zariadenia. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Nastavenie zabezpečenia @@ -3886,6 +3918,14 @@ Čet Poslať všetkým + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nová skupina Nastavenia @@ -4396,8 +4436,20 @@ Skontrolujte členov Overiť žiadosť - %1$d členovia skupiny majú rovnaké meno. Overte nižšie zobrazených členov a zvoľte ďalší krok. - Ak si nie ste istý/á, od koho je táto žiadosť, overte nižšie zobrazené kontakty a zvoľte úkon. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Žiadne ďalšie spoločné skupiny. Žiadne spoločné skupiny. @@ -4432,12 +4484,24 @@ %1$s sa pripojil(a) k hovoru %1$s a %2$s sa pripojili k hovoru %1$s, %2$s a %3$s sa pripojili k hovoru - %1$s, %2$s a %3$d ďalší sa pripojili k hovoru + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s opustil(a) hovor %1$s a %2$s opustili hovor %1$s, %2$s a %3$s opustili hovor - %1$s, %2$s a %3$d ďalší opusili hovor + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Vy Vy (na inom zariadení) @@ -4811,7 +4875,13 @@ Blokované - %1$d kontaktov + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Odosielanie správ Miznúce správy Zabezpečenie aplikácie @@ -7040,5 +7110,20 @@ Sťahujú sa zálohované údaje… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index f054560684..388a5a764a 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -89,6 +89,16 @@ Dostop do shrambe je bil trajno onemogočen. Aplikacija Signal potrebuje dovoljenje za dostop do sistemskega pomnilnika za pripenjanje fotografij, videa in zvoka k sporočilom. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenje pod postavko \"Shramba\". Dostop do stikov je bil trajno onemogočen. Aplikacija Signal potrebuje dovoljenje za dostop do stikov, kadar želite sporočilom pripeti podatke oseb iz svojega imenika. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenje pod postavko \"Stiki\". Dostop do lokacije je bil trajno onemogočen. Aplikacija Signal potrebuje dovoljenje za dostop do lokacije, kadar želite k sporočilom dodati podatek o svoji trenutni lokaciji. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenje pod postavko \"Lokacija\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ni aktiviral/-a plačil @@ -392,8 +402,16 @@ Vaša prošnja za pridružitev je bila posredovana skrbniku_ci skupine. Ob njegovem_njenem odzivu boste obveščeni. Preklic prošnje - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Dostop do mikrofona je bil trajno onemogočen. Aplikacija Signal potrebuje dovoljenje za dostop do mikrofona naprave za pošiljanje zvokovnih sporočil. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenje pod postavko \"Mikrofon\". + Dostop do mikrofona in kamere je bil trajno onemogočen. Za klic uporabnika_ce %1$spotrebuje Aplikacija Signal dovoljenje za dostop do mikrofona in kamere naprave. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenji pod postavkama \"Mikrofon\" in \"Kamera\". Za zajemanje videa in fotografij dovolite aplikaciji Signal dostop do kamere naprave. Dostop do kamere je bil trajno onemogočen. Aplikacija Signal potrebuje dovoljenje za dostop do kamere za zajemanje videa in fotografij. Prosimo, pojdite v meni Nastavitve aplikacij, izberite \"Dovoljenja\" in omogočite dovoljenje pod postavko \"Kamera\". @@ -417,7 +435,13 @@ Zapustili boste skupino in ta bo izbrisana na vseh vaših napravah. Izbriši Izbriši in zapusti - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Pridruži se @@ -3628,6 +3652,14 @@ Pošiljajte fotografije, videoposnetke in datoteke iz svoje naprave. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Nastavitev varnosti @@ -3886,6 +3918,14 @@ Klepet Oddaja + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Nova skupina Nastavitve @@ -4396,8 +4436,20 @@ Pregled članstva Pregled prošnje - %1$d članov_ic skupine ima enako ime. Preglejte člane_ice spodaj in ustrezno ukrepajte. - Če niste prepričani, čigava je prošnja, preglejte stike spodaj in ustrezno ukrepajte. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Ni drugih skupnih skupin. Ni skupnih skupin. @@ -4432,12 +4484,24 @@ Pridružil_a se je %1$s Pridružila_li sta se %1$s in %2$s Pridružili_le so se %1$s, %2$s in %3$s - Pridružila_li sta se %1$s, %2$s in še %3$d drugih + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s je odšel_la %1$s in %2$s sta odšla_li %1$s, %2$s in %3$s so odšli_le - %1$s, %2$s in še %3$d drugih je odšlo + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Vi Vi (na drugi napravi) @@ -4811,7 +4875,13 @@ Blokirano - %1$d stikov + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + Sporočanje Izginjajoča sporočila Varnost aplikacije @@ -7040,5 +7110,20 @@ Prenos varnostne kopije … + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index f011808e41..76772bc158 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -89,6 +89,16 @@ Signal kërkon lejen e Storage për të bashkëngjitur foto, video ose audio, por i është refuzuar përgjithmonë. Ju lutemi, vazhdoni te menuja e parametrave të aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Storage\". Signal kërkon lejen e Kontakteve për të bashkangjitur informacionin e kontaktit, por i është refuzuar përgjithmonë. Ju lutemi, vazhdoni te menuja e parametrave të aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Kontaktet\". Signal kërkon lejen e Vendndodhjes për të bashkangjitur një vendndodhje, por i është refuzuar përgjithmonë. Ju lutemi, vazhdoni te menuja e parametrave të aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Vendndodhja\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s nuk ka aktivizuar Pagesat @@ -386,8 +396,16 @@ Kërkesa juaj për t\'ju bashkuar grupit i është dërguar administratorit të grupit. Do të njoftoheni kur ata të ndërmarrin veprime. Anuloje kërkesën - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal kërkon lejen e aktivizimit të Mikrofonit për të dërguar mesazhe audio, por është i mbyllur përgjithmonë. Ju lutemi, vazhdoni te parametrat e aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Mikrofoni\". + Signal ka nevojë për lejet e aktivizimit të mikrofonit dhe të kamerës për të telefonuar%1$s, por janë mbyllur përgjithmonë. Ju lutemi, vazhdoni te parametrat e aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Mikrofoni\" dhe \"Kamera\". Për të bërë foto dhe video, lejo qasjen e Signal-it në kamerë. Signal ka nevojë për lejen e aktivizimit të kamerës për të bërë foto ose video, por është i mbyllur përgjithmonë. Ju lutemi, vazhdoni te parametrat e aplikacionit, zgjidhni \"Lejet\" dhe aktivizoni \"Kamera\". @@ -411,7 +429,13 @@ Do të largohesh nga ky grup dhe do të fshihet nga të gjitha pajisjet e tua. Fshije Fshije dhe dil - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Bashkohu @@ -3434,6 +3458,14 @@ Dërgo foto, video dhe skedarë nga pajisja jote. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Konfigurimi i sigurisë @@ -3688,6 +3720,14 @@ Biseda Transmetim + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Grup i ri Parametrat @@ -4184,8 +4224,16 @@ Shqyrtoni anëtarë Shqyrtoni kërkesë - %1$d anëtarë grupi kanë të njëjtin emër, shqyrtoni anëtarët më poshtë dhe zgjidhni kryerjen e një veprimi. - Nëse s\\’jeni i sigurt se prej kujt është kërkesa, shqyrtoni kontaktet më poshtë dhe kryeni një veprim. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + S\\’ka grupe të tjerë të përbashkët. S\\’ka grupe të përbashkët. @@ -4216,12 +4264,20 @@ Erdhi %1$s Erdhën %1$s dhe %2$s Erdhën %1$s, %2$s dhe %3$s - Erdhën %1$s, %2$s dhe %3$d të tjerë + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s iku %1$s dhe %2$s ikën %1$s, %2$s dhe %3$s ikën - %1$s, %2$s dhe %3$d të tjerë ikën + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Ju Ju (në një tjetër pajisje) @@ -4591,7 +4647,11 @@ E bllokuar - %1$d kontakte + + + %1$d contact + %1$d contacts + Shkëmbim Mesazhesh Zhdukje mesazhesh Siguri aplikacioni @@ -6748,5 +6808,20 @@ Po shkarkohen të dhënat rezervë… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 0dd5292b67..a7a3450488 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -89,6 +89,16 @@ Signal-у је потребна дозвола за приступ вашем меморијском простору да би могао да приложи слике, видео или аудио, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опцију „Меморијски простор“. Signal-у је потребна дозвола за приступ вашим контактима да би могао да приложи информације о контакту, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опцију „Контакти“. Signal-у је потребна дозвола за приступ вашој локацији да би могао да приложи локацију, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опцију „Локација“. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s није активирао/ла плаћања @@ -386,8 +396,16 @@ Ваш захтев за придруживање је послат администратору групе. Бићете обавештени када администратор предузме мере. Откажи захтев - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal-у је потребна дозвола за приступ микрофону да би могао да шаље аудио поруке, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опцију „Микрофон“. + Signal-у је потребна дозвола за приступ микрофону и камери да би могао да успостави позив са корисником %1$s, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опције „Микрофон“ и „Камера“. Да бисте могли да снимате фотографије и видео, дозволите Signal-у приступ камери. Signal-у је потребна дозвола за приступ камери да би могао да снима фотографије и видео, али му је она трајно одбијена. Идите у мени за подешавања апликације, изаберите „Дозволе“ и укључите опцију „Камера“. @@ -411,7 +429,13 @@ Напустићете групу и она ће бити избрисана на свим вашим уређајима. Избриши Избриши и напусти - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Придружите се @@ -3434,6 +3458,14 @@ Шаљите слике, видео и фајлове са свог уређаја. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Сигурносна подешавања @@ -3688,6 +3720,14 @@ Ћаскање Емитовање + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Нова група Подешавања @@ -4184,8 +4224,16 @@ Прегледајте чланове Прегледајте захтев - Њих %1$d у групи имају слична имена. Прегледајте те чланове у наставку и предузмите мере. - Ако нисте сигурни од кога потиче захтев, прегледајте контакте у наставку и предузмите мере. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Немате других заједничких група. Немате заједничких група. @@ -4216,12 +4264,20 @@ %1$s се придружио/ла %1$s и %2$s су се придружили %1$s, %2$s и %3$s су се придружили - %1$s, %2$s и још њих %3$d су се придружили + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s је напустио/ла групу %1$s и %2$s су напустили групу %1$s, %2$s и %3$s су напустили групу - %1$s, %2$s и још њих %3$d су напустили групу + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Ви Ви (на другом уређају) @@ -4591,7 +4647,11 @@ Корисник је блокиран - Контаката: %1$d + + + %1$d contact + %1$d contacts + Поруке Нестајуће поруке Безбедност апликације @@ -6748,5 +6808,20 @@ Преузимање података резервне копије… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 8f253585a4..acc2072bff 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -89,6 +89,16 @@ Signal behöver behörigheten Lagring för att bifoga bilder, video och ljud men den har avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Lagring\". Signal behöver behörigheten Kontakter för att bifoga kontaktinformation men den har avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Kontakter\". Signal behöver behörigheten Plats för att bifoga platser men den har avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Plats\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s har inte aktiverat betalningar @@ -386,8 +396,16 @@ Din förfrågan om att gå med har skickats till gruppadministratören. Du får ett meddelande när administratören vidtar åtgärder. Avbryt förfrågan - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal behöver behörigheten Mikrofon för att skicka ljudmeddelanden men den avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Mikrofon\". + Signal behöver behörigheterna Mikrofon och Kamera för att ringa%1$s men de har avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Mikrofon\" och Kamera\". För att fånga fotografier och video, tillåt Signal att tillgå kameran. Signal behöver behörigheten Kamera för att ta bilder och filma men den har avfärdats permanent. Fortsätt till inställningar för appar, välj \"Behörigheter\" och aktivera \"Kamera\". @@ -411,7 +429,13 @@ Du lämnar denna grupp och den tas bort från alla dina enheter. Ta bort Ta bort och lämna - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Gå med @@ -3434,6 +3458,14 @@ Skicka foton, videor och filer från din enhet. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Säkerhetsinställning @@ -3688,6 +3720,14 @@ Chatt Sändning + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Ny grupp Inställningar @@ -4184,8 +4224,16 @@ Granska medlemmar Granska förfrågan - %1$d gruppmedlemmar har samma namn, granska medlemmarna nedan och välj att vidta åtgärder. - Om du inte är säker på vem förfrågan kommer från, granska kontakterna nedan och vidta åtgärder. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Inga andra grupper gemensamt. Inga grupper gemensamt. @@ -4216,12 +4264,20 @@ %1$s gick med %1$s och %2$s gick med %1$s, %2$s och %3$s gick med - %1$s, %2$s och %3$d andra gick med + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s lämnade %1$s och %2$s lämnade %1$s, %2$s och %3$s lämnade - %1$s, %2$s och %3$d andra lämnade + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Du Du (på en annan enhet) @@ -4591,7 +4647,11 @@ Blockerade - %1$d kontakter + + + %1$d contact + %1$d contacts + Meddelanden Försvinnande meddelanden Appsäkerhet @@ -6748,5 +6808,20 @@ Laddar ner säkerhetskopieringsdata … + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index a0101ec885..045841e9ef 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -89,6 +89,16 @@ Signal inahitaji idhini ya Hifadhi ili kuunganisha picha, video, au sauti, lakini imekataliwa kabisa. Tafadhali endelea kwenye orodha ya mipangilio ya programu, chagua \"Ruhusa\", na uwezesha \"Hifadhi\". Signal inahitaji idhini ya Mawasiliano ili kuunganisha maelezo ya mawasiliano, lakini imekataliwa kabisa. Tafadhali endelea kwenye orodha ya mipangilio ya programu, chagua \"Ruhusa\", na uwezeshe \"Mawasiliano\". Signal inahitaji ruhusa ya Eneo ili kuunganisha mahali, lakini imekataliwa kabisa. Tafadhali endelea kwenye orodha ya mipangilio ya programu, chagua \"Ruhusa\", na uwezeshe \"Eneo\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s hajaamilisha Malipo @@ -386,8 +396,16 @@ Ombi lako la kujiunga limetumwa kwa msimamizi wa kikundi. Utaarifiwa atakapochukua hatua. Ghairi Ombi - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal inahitaji idhini ya Kipaza sauti ili kutuma ujumbe wa sauti, lakini imekataliwa kabisa. Tafadhali endelea kwenye mipangilio ya programu, chagua \"Ruhusa\", na uwezeshe \"Kipaza sauti\". + Signal inahitaji ruhusa ya Kipaza sauti na Kamera ili kupiga%1$s , lakini zimekataliwa kabisa. Tafadhali endelea kwenye mipangilio ya programu, chagua \"Ruhusa\", na uwezeshe \"Kipaza sauti\" na \"Kamera\". Ili kupiga picha na video, ruhusu ufikiaji wa Signal kwa kamera. Signal inahitaji kibali cha Kamera kuchukua picha au video, lakini imekataliwa kabisa. Tafadhali endelea kwenye mipangilio ya programu, chagua \"Ruhusa\", na uwezeshe \"Kamera\". @@ -411,7 +429,13 @@ Utaondoka kundi hili, na litafutwa kutoka kwa vifaa vyako vyote. Futa Futa na uondoke - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Jiunge @@ -3434,6 +3458,14 @@ Tuma picha, video na faili kutoka kwenye kifaa chako. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Mfumo wa usalama @@ -3688,6 +3720,14 @@ Gumzo Tangaza + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Kundi jipya Mipangilio @@ -4184,8 +4224,16 @@ Kagua wanachama Kagua ombi - Washiriki %1$d wa kikundi wana majina yanayofanana, wakague wanachama hapa chini na uchague kuchukua hatua. - Ikiwa hujui ombi hili limetoka kwa nani, kagua waasiliani walio hapa chini na uchukue hatua. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Hakuna vikundi vingine mnavyoshiriki pamoja. Hakuna vikundi mnavyoshiriki pamoja. @@ -4216,12 +4264,20 @@ %1$s amejiunga %1$s na %2$s amejiunga %1$s, %2$s na %3$s wamejiunga - %1$s, %2$s na wengine %3$d wamejiunga + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ameondoka %1$s na %2$s wameondoka %1$s, %2$s na %3$s wameondoka - %1$s, %2$s na wengine %3$d wameondoka + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Wewe Wewe (kwenye kifaa kingine) @@ -4591,7 +4647,11 @@ Zuiliwa - Waasiliani %1$d + + + %1$d contact + %1$d contacts + Kutuma ujumbe Jumbe zinazotoweka Usalama wa programu @@ -6748,5 +6808,20 @@ Inapakua data chelezo… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 7df938113e..12df3aa18d 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -89,6 +89,16 @@ புகைப்படங்கள், காணொளிகள் அல்லது ஆடியோவை இணைக்கவும் Signal க்கு சேமிப்பக அனுமதி தேவைப்படுகிறது, ஆனால் அது நிரந்தரமாக மறுக்கப்பட்டது. பயன்பாடு அமைப்புகள் மெனுவில் தொடரவும், \"அனுமதிகள்\" என்பதைத் தேர்ந்தெடுத்து \"சேமிப்பகத்தை\" இயக்கவும். தொடர்பு தகவலை இணைக்கவும் Signal க்கு தொடர்புகளின் அனுமதி தேவைப்படுகிறது, ஆனால் அது நிரந்தரமாக மறுக்கப்பட்டது. பயன்பாடு அமைப்புகள் மெனுவில் தொடரவும், \"அனுமதிகள்\" என்பதைத் தேர்ந்தெடுத்து, \"தொடர்புகளை\" இயக்கவும். இருப்பிடத்தை இணைக்கவும், Signal க்கு இருப்பிட அனுமதி தேவைப்படுகிறது, ஆனால் அது நிரந்தரமாக மறுக்கப்பட்டது. பயன்பாடு அமைப்புகள் மெனுவில் தொடரவும், \"அனுமதிகள்\" என்பதைத் தேர்ந்தெடுத்து, \"இருப்பிடம்\" ஐ இயக்கவும். + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s கட்டணங்களைச் செயல்படுத்தவில்லை @@ -386,8 +396,16 @@ குழுவில் சேர உங்கள் கோரிக்கை குழு நிர்வாகிக்கு அனுப்பப்பட்டுள்ளது. அவர்கள் நடவடிக்கை எடுக்கும்போது உங்களுக்கு அறிவிக்கப்படும். கோரிக்கையை ரத்துசெய் - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ஆடியோ செய்திகளை அனுப்ப Signal க்கு மைக்ரோஃபோன் அனுமதி தேவைப்படுகிறது, ஆனால் அது நிரந்தரமாக மறுக்கப்பட்டது. பயன்பாடு அமைப்புகளைத் தொடரவும், \"அனுமதிகள்\" என்பதைத் தேர்ந்தெடுத்து, \"மைக்ரோஃபோனை\" இயக்கவும். + Signal லை அழைக்க மைக்ரோஃபோன் மற்றும் கேமரா அனுமதிகள் தேவை%1$s, ஆனால் அவை நிரந்தரமாக மறுக்கப்பட்டுள்ளன. பயன்பாடு அமைப்புகளைத் தொடரவும், \"அனுமதி\" என்பதைத் தேர்ந்தெடுத்து, \"மைக்ரோஃபோன்\" மற்றும் \"கேமரா\" ஐ இயக்கவும். புகைப்படங்கள் மற்றும் காணொளி பிடிக்க, Signal-ஐ கேமராவை அணுக அனுமதிக்கவும். புகைப்படங்கள் அல்லது வீடியோ எடுக்க Signal-க்கு கேமரா அனுமதி தேவை, ஆனால் அது நிரந்தரமாக மறுக்கப்பட்டது. செயலியின் அமைப்புகளுக்கு சென்று, \"அனுமதிகள்\" என்பதைத் தேர்ந்தெடுத்து \"கேமரா\"-ஐ இயலச் செய்யவும். @@ -411,7 +429,13 @@ நீங்கள் இந்த குழுவை விட்டு வெளியேறும், மற்றும் இது எல்லாவற்றிலிருந்தும் நீக்கப்படும் உங்கள் சாதனங்கள். நீக்கு நீக்கி வெளியேறுக - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. சேரவும் @@ -3434,6 +3458,14 @@ உங்கள் டிவைஸில் இருந்து புகைப்படங்கள், வீடியோக்கள் மற்றும் கோப்புகளை அனுப்பலாம். + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + பாதுகாப்பு அமைப்பு @@ -3688,6 +3720,14 @@ சாட் ஒளிபரப்பு + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + புதிய குழு அமைப்புகள் @@ -4184,8 +4224,16 @@ மதிப்பாய்வு கோரிக்கை மதிப்பாய்வு கோரிக்கை - %1$dகுழு உறுப்பினர்களுக்கு ஒரே பெயர் உள்ளது, கீழே உள்ள உறுப்பினர்களை மதிப்பாய்வு செய்து நடவடிக்கை எடுக்க முடிவு செய்யுங்கள் - உங்களுக்கு யார் கோரிக்கையை அனுப்பினார்கள் என்பது உங்களுக்குத் தெரியாவிட்டால், கீழே உள்ள தொடர்புகளை மதிப்பாய்வு செய்து நடவடிக்கை எடுக்கவும். + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + பொதுவான குழுக்கள் இல்லை பொதுவான குழுக்கள் இல்லை @@ -4216,12 +4264,20 @@ %1$s சேர்ந்தார் %1$s மற்றும் %2$sசேர்ந்தார் %1$s, %2$s மற்றும் %3$s சேர்ந்தார் - %1$s, %2$s and %3$dஅதிக உறுப்பினர்கள் இணைந்தனர் + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s வெளியேறினர் %1$s மற்றும் %2$s வெளியேறினர் %1$s, %2$s மற்றும் %3$sவெளியேறினர் - %1$s, %2$s and மேலும்%3$d வெளியேறினர் + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + நீங்கள் நீங்கள் (மற்றொரு சாதனத்தில்) @@ -4591,7 +4647,11 @@ தடைசெய்யப்பட்டார் - %1$d contacts + + + %1$d contact + %1$d contacts + செய்தி அனுப்புதல் காணாமல் போகும் செய்திகள் பயன்பாட்டு பாதுகாப்பு @@ -6748,5 +6808,20 @@ காப்புப்பிரதி தரவைப் பதிவிறக்குகிறது… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 62307322d9..565cc28ae8 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -89,6 +89,16 @@ చిత్రాలు , వీడియోలు లేదా ఆడియోను అటాచ్ చేయడానికి Signal కు నిల్వ అనుమతి అవసరం, కానీ ఇది శాశ్వతంగా తిరస్కరించబడింది. దయచేసి అనువర్తనం సెట్టింగ్ల మెనుకు కొనసాగండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"నిల్వ\" ను ప్రారంభించండి. సంప్రదింపు సమాచారాన్ని అటాచ్ చేయడానికి Signal కాంటాక్ట్స్ అనుమతి అవసరం, కానీ అది శాశ్వతంగా తిరస్కరించబడింది. దయచేసి అనువర్తన సెట్టింగ్ల మెనుకి కొనసాగండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"పరిచయాలు\" ప్రారంభించండి. ఒక స్థానాన్ని అటాచ్ చేయడానికి Signalకి నగర అనుమతి అవసరం, కానీ ఇది శాశ్వతంగా తిరస్కరించబడింది. దయచేసి అనువర్తనం సెట్టింగ్ల మెనుకి కొనసాగండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"స్థానం\" ని ప్రారంభించండి. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s చెల్లింపులను యాక్టివేట్ చేయలేదు @@ -386,8 +396,16 @@ గ్రూప్‌లో చేరడానికి మీ అభ్యర్థన నిర్వాహకుడికి పంపబడింది. వారు చర్య తీసుకున్నప్పుడు మీకు తెలియజేయబడుతుంది. అభ్యర్ధన రద్దు చేయి - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ఆడియో సందేశాలను పంపడానికి Signalకు మైక్రోఫోన్ అనుమతి అవసరం, కానీ ఇది శాశ్వతంగా తిరస్కరించబడింది. దయచేసి అనువర్తనం సెట్టింగ్లకు కొనసాగించండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"మైక్రోఫోన్\" ని ప్రారంభించండి. + Signal కాల్ చేయడానికి మైక్రోఫోన్ మరియు కెమెరా అనుమతులు అవసరం %1$s,కానీ అవి శాశ్వతంగా తిరస్కరించబడ్డాయి. దయచేసి అనువర్తనం సెట్టింగ్లకు కొనసాగించండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"మైక్రోఫోన్\" మరియు \"కెమెరా\" ని ప్రారంభించండి. చిత్రాలు మరియు వీడియోలను సంగ్రహించడానికి, కెమెరాకి Signal ప్రాప్తిని అనుమతించండి. చిత్రాలను లేదా వీడియోను తీసుకోవడానికి Signal కు కెమెరా అనుమతి అవసరం, కానీ ఇది శాశ్వతంగా తిరస్కరించబడింది. దయచేసి అనువర్తనం సెట్టింగ్లకు కొనసాగించండి, \"అనుమతులు\" ఎంచుకోండి మరియు \"కెమెరా\" ని ప్రారంభించండి. @@ -411,7 +429,13 @@ మీరు ఈ గ్రూప్‌ను వదిలి వెళతారు మరియు ఇది మీ పరికరాలు అన్నింటి నుండి తొలగించబడుతుంది. తొలగించండి తొలగించండి మరియు వదిలివేయండి - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. చేరండి @@ -3434,6 +3458,14 @@ మీ పరికరం నుండి ఫోటోలు, వీడియోలు మరియు ఫైళ్ళను పంపండి. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + భద్రతా సెటప్ @@ -3688,6 +3720,14 @@ చాట్ ప్రసారం + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + కొత్త సమూహం అమరికలు @@ -4184,8 +4224,16 @@ సభ్యులను సమీక్షించండి అభ్యర్థనను సమీక్షించండి - %1$d మంది సమూహ సభ్యులకు ఒకే పేరు ఉంది, దిగువ సభ్యులను సమీక్షించండి మరియు చర్య తీసుకోవడానికి ఎంచుకోండి. - అభ్యర్థన ఎవరో మీకు తెలియకపోతే, దిగువ పరిచయాలను సమీక్షించి చర్య తీసుకోండి. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + ఉమ్మడిగా ఇతర సమూహాలు లేవు. సాధారణమైన సమూహాలు లేవు @@ -4216,12 +4264,20 @@ %1$s చేరారు %1$s మరియు %2$s చేరారు %1$s,%2$s మరియు %3$s చేరారు - %1$s, %2$s, మరో %3$d మంది చేరారు + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s వెళ్ళిపోయారు %1$s మరియు %2$s వెళ్ళిపోయారు %1$s, %2$s మరియు %3$s వెళ్ళిపోయారు - %1$s, %2$s మరియు %3$d ఇతరులు వెళ్ళిపోయారు + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + మీరు మీరు (మరొక పరికరంలో) @@ -4591,7 +4647,11 @@ బ్లాక్ చేయబడింది - %1$d కాంటాక్ట్‌లు + + + %1$d contact + %1$d contacts + సందేశం అదృశ్యమవుతున్న సందేశాలు యాప్ సెక్యూరిటీ @@ -6748,5 +6808,20 @@ బ్యాకప్ డేటాను డౌన్‌లోడ్ చేస్తోంది… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index e5eef8a7a4..cd241284f1 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -89,6 +89,16 @@ เพื่อที่จะแนบรูปภาพ วิดีโอ หรือเสียง Signal ต้องได้รับอนุญาตให้เข้าถึงที่เก็บข้อมูล แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ที่เก็บข้อมูล\" เพื่อที่จะแนบข้อมูลผู้ติดต่อ Signal ต้องได้รับอนุญาตให้เข้าถึงผู้ติดต่อ แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ผู้ติดต่อ\" เพื่อที่จะแนบข้อมูลตำแหน่งที่ตั้ง Signal ต้องได้รับอนุญาตให้เข้าถึงตำแหน่งที่ตั้ง แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ตำแหน่งที่ตั้ง\" + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s ยังไม่ได้เปิดใช้การชำระเงิน @@ -383,8 +393,16 @@ คำขอเข้ากลุ่มของคุณถูกส่งไปยังผู้ดูแลกลุ่มแล้ว คุณจะได้รับแจ้งเมื่อผู้ดูแลดำเนินการ ยกเลิกคำขอ - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. เพื่อจะส่งข้อความเสียง Signal ต้องได้รับอนุญาตให้เข้าถึงไมโครโฟน แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ไมโครโฟน\" + เพื่อที่จะโทรหา %1$s Signal ต้องได้รับอนุญาตให้เข้าถึงไมโครโฟนและกล้อง แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ไมโครโฟน\" และ \"กล้อง\" อนุญาต Signal ให้ใช้กล้องเพื่อถ่ายรูปและวิดีโอ เพื่อที่จะถ่ายรูปหรือวิดีโอ Signal ต้องได้รับอนุญาตให้เข้าถึงกล้อง แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"กล้อง\" @@ -408,7 +426,13 @@ คุณจะออกจากกลุ่มนี้และระบบจะลบกลุ่มออกจากอุปกรณ์ทุกเครื่องของคุณ ลบ ลบและออก - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. เข้าร่วม @@ -3337,6 +3361,14 @@ ส่งรูปภาพ วิดีโอ และไฟล์ต่างๆ จากอุปกรณ์ของคุณ + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + การตั้งค่าความปลอดภัย @@ -3589,6 +3621,14 @@ แชท เผยแพร่ + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + กลุ่มใหม่ การตั้งค่า @@ -4078,8 +4118,14 @@ พิจารณาสมาชิก พิจารณาคำขอ - สมาชิกในกลุ่ม %1$d คน มีชื่อเหมือนกัน พิจารณาสมาชิกด้านล่างแล้วเลือกตัดสินใจ - หากคุณไม่มั่นใจว่าคำขอมาจากใคร คุณสามารถพิจารณาได้จากรายชื่อผู้ติดต่อด้านล่างแล้วเลือกตัดสินใจ + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + ไม่มีกลุ่มอื่นร่วมกัน ไม่มีกลุ่มร่วมกัน @@ -4108,12 +4154,18 @@ %1$s ได้เข้าร่วมกลุ่ม %1$s และ %2$s ได้เข้าร่วมกลุ่ม %1$s %2$s และ %3$s ได้เข้าร่วมกลุ่ม - %1$s %2$s และอีก %3$d คน ได้เข้าร่วมกลุ่ม + + + %1$s, %2$s and %3$d others joined + %1$s ออกจากกลุ่ม %1$s และ %2$s ออกจากกลุ่ม %1$s %2$s และ %3$s ได้ออกจากกลุ่ม - %1$s %2$s และอีก %3$d คน ได้ออกจากกลุ่ม + + + %1$s, %2$s and %3$d others left + คุณ คุณ (อยู่บนอุปกรณ์เครื่องอื่น) @@ -4481,7 +4533,10 @@ ถูกบล็อก - %1$d ผู้ติดต่อ + + + %1$d contacts + การส่งข้อความ ข้อความที่ลบตัวเอง ความปลอดภัยของแอป @@ -6602,5 +6657,20 @@ กำลังดาวน์โหลดข้อมูลสำรอง… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 752d34ab87..50d6c01257 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -89,6 +89,16 @@ Kailangan ng Signal ng pahintulot sa Storage upang makapag-attach ng photos, videos, o audio, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Permissions\", at i-enable ang \"Storage\". Kailangan ng Signal ng pahintulot sa Contacts upang makapag-attach ng contact information, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Permissions\", at i-enable ang \"Contacts\". Kailangan ng Signal ng pahintulot sa Lokasyon upang makapag-attach ng lokasyon, ngunit permanente itong ipinagbabawal. Pumunta sa app settings menu, piliin ang \"Permissions\", at i-enable ang \"Location\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + Hindi pa na-activate ni %1$s ang Payments @@ -386,8 +396,16 @@ Ang iyong request to join ay na-send na sa group admin. You\'ll be notified when they take action. I-cancel ang Request - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Kailangan ng Signal ang pahintulot sa Mikropono upang makapagpadala ng mensaheng audio, ngunit permanente itong ipinagbabawal. Pumunta sa app settings, piliin ang \"Mga Pahintulot\", at i-enable ang \"Mikropono\". + Kailangan ng Signal ang pahintulot sa Mikropono at Camera upang matawagan si %1$s, ngunit ito ay permaneteng ipinagbabawal. Pumunta sa app settings, piliin ang \"Pahintulot\", at i-enable ang \"Mikropono\" at \"Camera\". Upang makakuha ng larawan at video, payagan ang Signal na gamitin ang camera. Kailangan ng Signal ang pahintulot sa Camera upang makakuha ng mga larawan at video, ngunit ito ay permaneteng ipinagbabawal. Pumunta sa app settings, piliin ang \"Pahintulot\", at i-enable ang \"Camera\". @@ -411,7 +429,13 @@ Aalis ka sa grupong ito, at ito\'y mabubura sa lahat ng iyong mga device. Burahin Burahin at umalis - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Mag-join @@ -3434,6 +3458,14 @@ Mag-send ng photos, videos, at files mula sa device mo. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Security setup @@ -3688,6 +3720,14 @@ Chat Broadcast + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Bagong Grupo Settings @@ -4184,8 +4224,16 @@ I-review ang Members I-review ang Request - %1$d group members ang may parehong pangalan, i-review ang members sa baba at piliing mag-take action. - Kung hindi ka sigurado kung kanino galing ang request na ito, i-review ang contacts sa baba and take action. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Walang other groups in common. Walang groups in common. @@ -4216,12 +4264,20 @@ %1$s joined %1$s and %2$s joined %1$s, %2$s, and %3$s joined - %1$s, %2$s, and %3$d others joined + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s left %1$s and %2$s left %1$s, %2$s, and %3$s left - %1$s, %2$s, and %3$d others left + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Ikaw You (sa ibang device) @@ -4591,7 +4647,11 @@ Naka-block - %1$d contacts + + + %1$d contact + %1$d contacts + Messaging Mga naglalahong mensahe App security @@ -6748,5 +6808,20 @@ Dina-download ang backup data… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index a5fbee3257..0bb3220f7f 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -89,6 +89,16 @@ Signal, fotoğraflar, videolar, veya ses ekleyebilmek için Depolama iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Depolama\"yı etkinleştirin. Signal, iletişim bilgilerini eklemek için Kişiler iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Kişiler\"i etkinleştirin. Signal, konum bilgisi eklemek için Konum iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Konum\"u etkinleştirin. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s, Ödemeleri etkinleştirmedi @@ -386,8 +396,16 @@ Katılma isteğiniz grup yöneticisine gönderildi. İsteğiniz yanıtlandığı zaman haberdar edileceksiniz. İsteği İptal Et - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal, sesli iletiler göndermek için Mikrofon iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Mikrofon\"u etkinleştirin. + Signal, %1$s ile arama yapmanız için Mikrofon ve Kamera iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, \"Mikrofon\" ve \"Kamera\"yı etkinleştirin. Fotoğraf veya video çekmek için, Signal\'in kameraya erişmesine izin verin. Signal, fotoğraf veya video çekmek için Kamera iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Kamera\"yı etkinleştirin. @@ -411,7 +429,13 @@ Bu gruptan ayrılacaksın ve grup tüm cihazlarından silinecek. Sil Sil ve ayrıl - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Katıl @@ -3434,6 +3458,14 @@ Cihazından fotoğraf, video ve dosya gönder. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Güvenlik kurulumu @@ -3688,6 +3720,14 @@ Sohbet Yayın + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Yeni grup Ayarlar @@ -4184,8 +4224,16 @@ Üyeleri incele İsteği incele - %1$d grup üyesi aynı ada sahip, aşağıdan inceleyerek tercih yapabilirsiniz. - İsteğin kimden geldiğinden emin değilseniz, aşağıdaki kişileri inceleyerek tercih yapabilirsiniz. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Başka ortak grup yok. Ortak grup yok. @@ -4216,12 +4264,20 @@ %1$s katıldı %1$s ve %2$s katıldı %1$s, %2$s ve %3$s katıldı - %1$s, %2$s ve %3$d diğer kişi katıldı + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s ayrıldı %1$s ve %2$s ayrıldı %1$s, %2$s ve %3$s ayrıldı - %1$s, %2$s ve %3$d diğer kişi ayrıldı + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + Siz Siz (başka bir cihazda) @@ -4591,7 +4647,11 @@ Engellendi - %1$d kişi + + + %1$d contact + %1$d contacts + Yazışma Kaybolan iletiler Uygulama güvenliği @@ -6748,5 +6808,20 @@ Yedekleme verileri indiriliyor… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index db3d8d30f5..37feb2999e 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -89,6 +89,16 @@ Signal رەسىم ، سىن ياكى ئۈن ھۆججەتلىرىنى قوشۇش ئۈچۈن ساقلىغۇچ ھوقۇقى تەلەپ قىلىدۇ، ئەمما ئۇ مەڭگۈلۈك رەت قىلىندى. ئەپ تەڭشىكى تىزىملىكىدىن، «ھوقۇقلار» نى تاللاپ ۋە «ساقلىغۇچ» نى قوزغىتىڭ. ئالاقىلىشىش ئۇچۇرلىرىنى چاپلاش ئۈچۈن Signal ئالاقەداشلار ھوقۇقى تەلەپ قىلىدۇ، ئەمما ئۇ مەڭگۈلۈك رەت قىلىندى. ئەپ تەڭشىكى تىزىملىكىدىن «ھوقۇقلار» نى تاللاپ، «ئالاقەداشلار» نى قوزغىتىڭ. Signal ئورۇن بەلگىلەش ئۈچۈن ئورۇن ھوقۇقى تەلەپ قىلىدۇ، ئەمما ئۇ مەڭگۈلۈك رەت قىلىندى. ئەپ تەڭشىكى تىزىملىكىگە بېرىڭ، «ھوقۇقلار» نى تاللاڭ ۋە «ئورۇن» نى قوزغىتىڭ. + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s پۇل تۆلەش ئىقتىدارىنى تېخى ئاكتىپلىمىغان @@ -383,8 +393,16 @@ قوشۇلۇش تەلىپىڭىز گۇرۇپپا باشقۇرغۇچىسىغا ئەۋەتىلدى. ئۇلار قارار قىلغاندا سىزگە ئۇقتۇرۇلىدۇ. ئىلتىماستىن ۋاز كەچ - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. ئۈنلىك ئۇچۇر يوللاش ئۈچۈن Signal مىكروفون ھوقۇقى تەلەپ قىلىدۇ، ئەمما ئۇ مەڭگۈلۈك رەت قىلىندى. ئەپ تەڭشىكى تىزىملىكىدىن «ھوقۇقلار» نى تاللاپ ۋە «مىكروفون» نى قوزغىتىڭ. + %1$s گە چاقىرىق قىلىش ئۈچۈن Signal مىكروفون ۋە كامېرا ھوقۇقىنى ئىشلىتىشى كېرەك، لېكىن ئۇ ھوقۇقلار مەڭگۈلۈك چەكلەنگەن. ئەپ تەڭشىكىگە كىرىپ، «ھوقۇقلار» نى تاللاپ، «مىكروفون» ۋە «كامېرا» نى قوزغىتىڭ. رەسىم ۋە سىنغا ئېلىش ئۈچۈن ، Signal نىڭ كامېرانى زىيارەت قىلىشىغا يول قويۇڭ. رەسىم تارتىش ياكى سىنغا ئېلىش ئۈچۈن Signal كامېرا ھوقۇقىغا موھتاج ، ئەمما ئۇ مەڭگۈلۈك رەت قىلىندى. ئەپ تەڭشەكلىرىدىن، «ھوقۇقلار» نى تاللاپ ۋە «كامېرا» نى قوزغىتىڭ. @@ -408,7 +426,13 @@ بۇ گۇرۇپپىدىن چېكىنىپ چىقىسىز، شۇنداقلا بۇ گۇرۇپپا بارلىق ئۈسكۈنىلىرىڭىزدىن ئۆچۈرۈلىدۇ. ئۆچۈر ئۆچۈر ۋە ئايرىل - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. قوشۇل @@ -3337,6 +3361,14 @@ ئۈسكۈنىڭىزدىن رەسىم ، سىن ۋە ھۆججەتلەرنى ئەۋەتىڭ. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + بىخەتەرلىك تەڭشىكى @@ -3589,6 +3621,14 @@ پاراڭ تارقىتىش + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + يېڭى گۇرۇپپا تەڭشەكلەر @@ -4078,8 +4118,14 @@ ئەزالارنى تەكشۈر ئىلتىماسنى تەكشۈر - %1$d گۇرۇپپا ئەزاسىنىڭ ئوخشاش ئىسمى بار ئىكەن. تۆۋەندىكىلەرنى كۆزدىن كەچۈرۈڭ، بىر مەشغۇلات تاللاڭ. - ئىلتىماسنىڭ نەدىن كەلگەنلىكىنى جەزملەشتۈرەلمىسىڭىز، ئاستىدىكى ئالاقەداشلارنى كۆزدىن كەچۈرۈڭ ۋە مەشغۇلات قىلىڭ. + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + باشقا ئورتاق گۇرۇپپا يوق ئورتاق گۇرۇپپا يوق. @@ -4108,12 +4154,18 @@ %1$s قېتىلدى %1$s بىلەن%2$s قېتىلدى %1$s، %2$s ۋە%3$s قېتىلدى - %1$s، %2$s ۋە%3$d باشقىلار قېتىلدى + + + %1$s, %2$s and %3$d others joined + %1$s ئايرىلدى %1$s ۋە%2$s ئايرىلدى %1$s، %2$s ۋە%3$s ئايرىلدى - %1$s، %2$s ۋە%3$d باشقىلار ئايرىلدى + + + %1$s, %2$s and %3$d others left + سىز سىز (يەنە بىر ئۈسكۈنىدە) @@ -4481,7 +4533,10 @@ چەكلەندى - %1$d ئالاقەداش + + + %1$d contacts + ئۇچۇرلىشىش غايىب ئۇچۇرلار ئەپ بىخەتەرلىكى @@ -6602,5 +6657,20 @@ زاپاس سانلىق مەلۇماتنى چۈشۈرۈۋاتىدۇ… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index eb87567613..0eba43839a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -89,6 +89,16 @@ Signal потребує дозволу \"Зберігання\" для прикріплення фото, відео та аудіо, але наразі доступу немає. Будь ласка, перейдіть до налаштувань додатку, оберіть \"Дозволи\", та увімкніть \"Зберігання\". Signal потребує дозволу \"Контакти\", щоб прикріпити контакт, але наразі доступу немає. Будь ласка, перейдіть до налаштувань додатку, оберіть \"Дозволи\", та увімкніть \"Контакти\". Signal потребує дозволу \"Геодані\", щоб прикріпити дані про розташування, але наразі доступу немає. Будь ласка, перейдіть до налаштувань додатку, оберіть \"Дозволи\", та увімкніть \"Геодані\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s не активував/-ла платежі @@ -392,8 +402,16 @@ Ваш запит на приєднання надіслано до адміністраторів групи. Ви отримаєте повідомлення, коли вони відреагують. Відкликати запит - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal потребує дозволу «Мікрофон», щоб відправляти аудіоповідомлення, але наразі доступу немає. Будь ласка, перейдіть до налаштувань застосунку, оберіть «Дозволи» та увімкніть «Мікрофон». + Signal потребує дозволів «Мікрофон» та «Камера», щоб подзвонити до %1$s, але наразі доступу немає. Будь ласка, перейдіть до налаштувань застосунку, оберіть «Дозволи» та увімкніть «Мікрофон» та «Камера». Щоб знімати фото і відео, надайте Signal доступ до камери. Signal потребує дозволу \"Камера\", щоб фотографувати або знімати відео, але наразі доступу немає. Будь ласка, перейдіть до налаштувань додатку, оберіть \"Дозволи\", та увімкніть \"Камера\". @@ -417,7 +435,13 @@ Ви покинете цю групу, та її буде видалено з усіх ваших пристроїв. Видалити Видалити і покинути - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Приєднатись @@ -3112,8 +3136,8 @@ Проблема з доставленням - Не вдалося доставити вам повідомлення, стікер, реакцію або сповіщення про прочитання від користувача %1$s. Користувач намагався надіслати це безпосередньо вам або в груповий чат. - Не вдалося доставити вам повідомлення, стікер, реакцію або сповіщення про прочитання від користувача %1$s. + Не вдалося доставити вам повідомлення, стікер, реакцію або відмітку про читання від користувача %1$s. Користувач намагався надіслати це безпосередньо вам або в груповий чат. + Не вдалося доставити вам повідомлення, стікер, реакцію або відмітку про читання від користувача %1$s. Ім\'я (обов\'язково) @@ -3423,10 +3447,10 @@ Режим сумісності \'WiFi Calling\' Увімкніть, якщо ваш пристрій використовує доставку SMS/MMS через WiFi (вмикайте лише якщо «WiFi Calling» увімкнен на вашому пристрої) Клавіатура в режимі «інкогніто» - Звіти про прочитання - Якщо звіти про прочитання вимкнено, ви не бачитимете ці звіти від інших. - Індикатори набору тексту - Якщо індикатори набору тексту вимкнено, ви не бачитимете їх від інших користувачів. + Відмітки про читання + Якщо вимкнути відмітки про читання, ви не будете бачити, чи ваші повідомлення прочитали. + Індикатор набору тексту + Якщо індикатор набору тексту вимкнено, його не буде видно, коли інший користувач набиратиме повідомлення. Запитати клавіатуру, щоб вимкнути персоналізоване навчання Це налаштування може не спрацювати, бо ваша клавіатура може його ігнорувати. @@ -3628,6 +3652,14 @@ Надсилайте фото, відео й інші файли зі свого пристрою. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Налаштування безпеки @@ -3886,6 +3918,14 @@ Чат Трансляція + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Нова група Налаштування @@ -4396,8 +4436,20 @@ Перегляд учасників Переглянути запит - %1$d учасники групи мають однакові імена, перегляньте учасників нижче та оберіть дію. - Якщо ви не впевнені, від кого надійшов запит, перегляньте контакти нижче та прийміть рішення. + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + Інших спільних груп немає. Немає спільних груп. @@ -4432,12 +4484,24 @@ Приєднався: %1$s %1$s та %2$s приєднались %1$s, %2$s і %3$s приєднались - %1$s, %2$s та %3$d інші приєднались + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + %1$s, %2$s and %3$d others joined + Залишив розмову: %1$s %1$s та %2$s покинули розмову %1$s,%2$s та %3$s покинули розмову - %1$s, %2$s та %3$d інші покинули розмову + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + %1$s, %2$s and %3$d others left + Ви Ви (на іншому пристрої) @@ -4811,8 +4875,14 @@ Заблоковані - %1$d контактів - Листування + + + %1$d contact + %1$d contacts + %1$d contacts + %1$d contacts + + Повідомлення Тимчасові повідомлення Безпека програми @@ -4837,8 +4907,8 @@ Показувати піктограму в деталях повідомлення, доставлених за допомогою прихованого відправника. - Якщо ввімкнути цю функцію, нові повідомлення, надіслані та отримані в нових розмовах, розпочатих вами, зникнуть, коли вони будуть прочитані. - Якщо увімкнути — нові надіслані та отримані повідомлення у цій розмові, що їх було прочитано, зникатимуть після перегляду. + Якщо ввімкнути цю функцію, нові повідомлення, надіслані та отримані в нових чатах, що ви почали, зникатимуть після перегляду. + Якщо ввімкнути цю функцію, нові повідомлення, надіслані та отримані в цьому чаті, зникатимуть після перегляду. Вимк. 4 тижні 1 тиждень @@ -7040,5 +7110,20 @@ Завантаження резервної копії… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 1a3fd6383b..f3a80c1d37 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -89,6 +89,16 @@ Signal کو آڈیو، ویڈیو، تصاویر وغیرہ منسلک کرنے کیلئے سٹوریج اجازت کی ضرورت ہوتی ہے، لیکن اس کی مستقل طور پر نفی کر دی گئی ہے ۔ برائے مہربانی ایپ ترتیبات کی فہرست میں جائیں، \"منظوری\" منتخب کریں، اور \"اسٹوریج\" فعال کریں۔ رابطوں کی معلومات لینے کیلئے Signal کو رابطوں کی منظوری کی ضرورت ہوتی ہے، لیکن اس کی ہمیشہ کیلئے نفی کردی گئی ہے۔ براہ کرم ایپ ترتیبات کی فہرست میں جاکر \"منظوری\" کا انتخاب کریں،اور \"رابطے\" فعال کریں۔ Signal کو مقام سے منسلک کرنے کیلئے مقام کی منظوری کی ضرورت ہے،لیکن اس کی مستقل طور پر نفی کردی گئی ہے۔ براہ کرم ایپ ترتیبات کی فہرست میں جائیں\"منظوری\" منتخب کریں، اور \"مقام\" فعال کریں۔ + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s نے پیمنٹس فعال نہیں کیں @@ -386,8 +396,16 @@ آپ کی شمولیت کی درخواست گروپ منتظم کو بھیج دی گئی ہے۔ جب وہ کارروائی کریں گے تو آپ کو مطلع کیا جائے گا۔ درخواست منسوخ - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal کو آڈیو پیغامات بھیجنے کے لیے مائکروفون کی اجازت کی ضرورت ہے، لیکن اس کی مستقل طور پر نفی کردی گئی ہے ۔ برائے مہربانی ایپ کی ترتیبات میں جائیں، \"منظوری\"، منتخب کریں، اور \"مائکروفون\" فعال کریں۔ + %1$s کال کیلئے Signal کو مائکروفون اور کیمرہ کی منظوری کی ضرورت ہوتی ہے، لیکن ان کی مستقل طور پر نفی کر دی گئی ہے ۔ برائے مہربانی ایپ کی ترتیبات میں جائیں، \"منظوری\" منتخب کریں، اور \"مائکروفون\" اور \"کیمرہ\"کو فعال کریں۔ تصویریں اور ویڈیو بنانے کے لیے ، Signal کو کیمرہ تک رسائی کی ضرورت ہے Signal کو ویڈیو اور تصویریں بنانے کے لیے کیمرہ کی اجازت کی ضرورت ہے، لیکن یہ مستقل طور پر تسلیم نہیں ہوں گے۔ برائے مہربانی ایپ کی ترتیبات میں جائیں، \"منظوری\" منتخب کریں۔ اور \"کیمرہ\" فعال کریں۔ @@ -411,7 +429,13 @@ آپ اس گروپ کو چھوڑ دیں گے ، اور یہ آپ کے سبھی devices سے حذف ہوجائے گا۔ حذف کریں حذف کریں اور چھوڑیں - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. شامل ہوں @@ -3434,6 +3458,14 @@ اپنی ڈیوائس سے تصاویر، ویڈیوز اور فائلز بھیجیں۔ + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + سکیورٹی سیٹ اپ @@ -3688,6 +3720,14 @@ چیٹ نشر کرنا + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + نیا گروپ ترتیبات @@ -4184,8 +4224,16 @@ ممبران کا جائزہ لیں نظرثانی کی درخواست - %1$dگروپ ممبران کا ایک ہی نام ہے ، ذیل ممبران کا جائزہ لیں اور کارروائی کرنے کا انتخاب کریں۔ - اگر آپ کو یقین نہیں ہے کہ درخواست کس کی ہے تو ، نیچے دیئے گئے رابطوں کا جائزہ لیں اور کارروائی کریں۔ + + + %1$d group member have the same name, review the member below and choose to take action. + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contact below and take action. + If you\'re not sure who the request is from, review the contacts below and take action. + کوئی دوسرا گروہ مشترکہ نہیں۔ مشترکہ طور پر کوئی گروپ نہیں۔ @@ -4216,12 +4264,20 @@ %1$sشامل ہوئے %1$sاور %2$s شامل ہو گئے %1$s,%2$sاور%3$s شامل ہو گئے - %1$s،%2$s اور%3$d دوسرے شامل ہوئے + + + %1$s, %2$s and %3$d other joined + %1$s, %2$s and %3$d others joined + %1$sچلے گئے %1$s اور %2$s چلے گئے %1$s،%2$s اور%3$s چلے گئے - %1$s،%2$s اور%3$d دوسرے چلے گئے + + + %1$s, %2$s and %3$d other left + %1$s, %2$s and %3$d others left + آپ آپ (دوسرے آلے پر) @@ -4591,7 +4647,11 @@ بلاک کر دیا گیا - %1$dرابطے + + + %1$d contact + %1$d contacts + پیغام رسانی پیغامات غائب ہو رہے ہیں ایپ سیکیورٹی @@ -6748,5 +6808,20 @@ بیک اپ ڈیٹا ڈاؤن لوڈ ہو رہا ہے… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 0efe449b07..4d4383588c 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -89,6 +89,16 @@ Signal cần quyền truy cập Bộ nhớ để đính kèm ảnh, video hoặc âm thanh, nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Bộ nhớ\". Signal cần quyền truy cập Danh bạ để đính kèm thông tin liên lạc nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Danh bạ\". Signal cần quyền truy cập Vị trí để đính kèm vị trí, nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Vị trí\". + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s chưa bật tính năng Thanh toán @@ -383,8 +393,16 @@ Yêu cầu tham gia nhóm của bạn đã được gửi tới quản trị viên nhóm. Bạn sẽ được thông báo khi họ đưa ra quyết định. Hủy yêu cầu - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal cần quyền truy cập Micro để gửi tin nhắn âm thanh, nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Micro\". + Signal cần quyền truy cập Micro và Máy ảnh để gọi %1$s, nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Micro\" và \"Máy ảnh\" Để chụp ảnh, hãy cho phép Signal truy cập máy ảnh. Signal cần quyền truy cập Máy ảnh để chụp ảnh và quay video, nhưng đã bị từ chối vĩnh viễn. Vui lòng mở cài đặt ứng dụng, chọn \"Quyền\" và bật \"Máy ảnh\". @@ -408,7 +426,13 @@ Bạn sẽ rời nhóm, và lịch sử trò chuyện sẽ bị xóa trên tất cả các thiết bị của bạn. Xóa Xóa và rời khỏi - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. Tham gia @@ -3337,6 +3361,14 @@ Gửi ảnh, video và tập tin từ thiết bị của bạn. + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + Thiết lập bảo mật @@ -3589,6 +3621,14 @@ Trò chuyện Phát rộng + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + Tạo nhóm mới Cài đặt @@ -4078,8 +4118,14 @@ Xem lại thành viên Xem lại yêu cầu - %1$d thành viên nhóm có cùng tên, hãy xem các thành viên dưới đây và chọn hành động. - Nếu bạn không chắc yêu cầu từ người nào, xem các liên hệ bên dưới và chọn hành động. + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + Không có nhóm chung nào khác. Không có nhóm chung. @@ -4108,12 +4154,18 @@ %1$s đã tham gia %1$s và %2$s đã tham gia %1$s, %2$s và %3$s đã tham gia - %1$s, %2$s và %3$d người khác đã tham gia + + + %1$s, %2$s and %3$d others joined + Còn lại %1$s Còn lại %1$s và %2$s Còn lại %1$s, %2$s và %3$s - Còn lại %1$s, %2$s và %3$d người khác + + + %1$s, %2$s and %3$d others left + Bạn Bạn (trên một thiết bị khác) @@ -4481,7 +4533,10 @@ Đã chặn - %1$d liên hệ + + + %1$d contacts + Nhắn tin Tin nhắn tự hủy Bảo mật ứng dụng @@ -6602,5 +6657,20 @@ Đang tải dữ liệu sao lưu… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index dbd67f6b14..ae76686ab1 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -89,6 +89,16 @@ Signal 要攞「儲存裝置」權限,先可以加入相片、影片或者聲音做附件,但權限已被永久拒絕。請到呢個 app 嘅設定功能表,揀選「權限」,然後啟用「儲存裝置」。 Signal 要攞「聯絡人」權限,先可以加入聯絡人資訊做附件,但權限已被永久拒絕。請到呢個 app 嘅設定功能表,揀選「權限」,然後啟用「聯絡人」。 Signal 要攞「位置」權限,先可以加入位置,但權限已被永久拒絕。請到呢個 app 嘅設定功能表,揀選「權限」,然後啟用「位置」。 + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s 未啟動付款 @@ -383,8 +393,16 @@ 已經話咗畀個谷嘅話事人知,您要求加入呢個谷。一有消息會通知您㗎喇。 取消請求 - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal 要攞「麥克風」權限,先可以傳送語音訊息,但權限已被永久拒絕。請到呢個 app 嘅應用程式設定,揀選「權限」,然後啟用「麥克風」。 + Signal 要攞「麥克風」同「相機」權限,先可以同 %1$s 通話,但權限已被永久拒絕。請到呢個 app 嘅應用程式設定,揀選「權限」,然後啟用「麥克風」同「相機」。 要影相或者拍片嘅話,請允許 Signal 存取您部機嘅相機。 Signal 要攞「相機」權限,先可以影相或拍片,但權限已被永久拒絕。請到呢個 app 嘅應用程式設定,揀選「權限」,然後啟用「相機」。 @@ -408,7 +426,13 @@ 你會退出呢個群組,而呢個群組亦都會喺你所有裝置上面刪除。 刪除 刪除,然後退出 - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. 摻埋我 @@ -3337,6 +3361,14 @@ 由你部機傳送相片、影片同檔案。 + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + 安全設定 @@ -3589,6 +3621,14 @@ 聊天 廣播 + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + 開個新谷 設定 @@ -4078,8 +4118,14 @@ 檢查吓成員名單 審查請求 - 個谷嘅 %1$d 位成員撞名,請批閱下列成員並視乎情況做要做嘅嘢。 - 假如您唔肯定係邊個發出請求,請批閱下列嘅聯絡人並去做要做嘅嘢。 + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + 無其他有緣相遇嘅谷。 無有緣相遇嘅谷。 @@ -4108,12 +4154,18 @@ %1$s 已加入 %1$s 同 %2$s 已加入 %1$s、%2$s 同 %3$s 已加入 - %1$s、%2$s 同另外 %3$d 人已加入 + + + %1$s, %2$s and %3$d others joined + %1$s 已退出 %1$s 同 %2$s 已退出 %1$s、%2$s 同 %3$s 已退出 - %1$s、%2$s 同另外 %3$d 人已退出 + + + %1$s, %2$s and %3$d others left + 您 (用另一部機) @@ -4481,7 +4533,10 @@ 封鎖咗 - %1$d 位仁兄 + + + %1$d contacts + 發訊息 過眼雲煙訊息 App 保安措施 @@ -6602,5 +6657,20 @@ 下載緊備份資料… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 122403ab9b..6e6c62d017 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -89,6 +89,16 @@ Signal 需“存储”权限,来发送图片、视频和音频,但该权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“存储”。 Signal 需“通讯录”权限,来发送通讯录信息,但是该权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“通讯录”。 Signal 需“位置”权限,来发送位置信息,但是该权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“位置”。 + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s还未激活付款 @@ -383,8 +393,16 @@ 您的入群申请已发送给群组管理员,您将在管理员作出决定后收到通知。 撤回申请 - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal 需“麦克风”权限,来发送音频,但该权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“麦克风”。 + Signal 需“麦克风”和“相机”权限,来呼叫 %1$s,但这些权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“麦克风”和“相机”。 要拍照或录像,请允许 Signal 访问相机。 Signal 需“相机”权限,来拍摄图片或视频,但该权限已永久禁用。请访问应用设置菜单,选择“权限”并启用“相机”。 @@ -408,7 +426,13 @@ 你将离开这个组,它将从你所有的设备中删除。 删除 删除并离开 - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. 加入 @@ -3337,6 +3361,14 @@ 用于从您的设备中发送照片、视频和文件。 + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + 安全设置 @@ -3589,6 +3621,14 @@ 聊天 广播 + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + 新建群组 设置 @@ -4078,8 +4118,14 @@ 查看成员 审查请求 - %1$d 位群组成员的名字相同,请审查以下成员并采取相应措施。 - 如果您不确定此请求的来源,请审查以下联系人并采取相应措施。 + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + 没有其它共同群组。 无共同群组。 @@ -4108,12 +4154,18 @@ %1$s已加入 %1$s与%2$s已加入 %1$s、%2$s和 %3$s已加入 - %1$s、%2$s 及其它 %3$d 位用户已加入 + + + %1$s, %2$s and %3$d others joined + %1$s 已离开 %1$s和%2$s已离开 %1$s、%2$s和%3$s已离开 - %1$s、%2$s 及其它 %3$d 位用户已离开 + + + %1$s, %2$s and %3$d others left + 您(其它设备) @@ -4481,7 +4533,10 @@ 已屏蔽 - %1$d 个联系人 + + + %1$d contacts + 消息传输 限时消息 应用安全 @@ -6602,5 +6657,20 @@ 正在下载备份数据… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 0fc6a20ba5..497cab3e08 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -89,6 +89,16 @@ Signal 需要儲存裝置權限才可附加照片、影片或音訊,但已被永久拒絕。請前往應用程式設定功能表,選擇 「權限」,然後啟用 「儲存裝置」。 Signal 需要聯絡人權限才可附加聯絡人資訊,但已被永久拒絕。請前往應用程式設定功能表,選擇「權限」,然後啟用 「聯絡人」。 Signal 需要位置權限才可附加位置,但已被永久拒絕。請前往應用程式設定功能表,選擇「權限」,然後啟用 「位置」。 + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s 尚未啟動付款 @@ -383,8 +393,16 @@ 您欲加入群組的請求已傳送至此群組的管理員。一經處理,將會通知您。 取消請求 - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal需要存取咪高峰來傳送語音訊息,但您已經永久拒絕。請前往應用程式設定功能表,按「權限」,然後開啟「咪高峰」。 + Signal需要存取咪高峰及相機來呼叫%1$s,但您已經永久拒絕。請前往應用程式設定功能表,按「權限」,然後開啟「咪高峰」和「相機」。 要拍照或錄影,請允許Signal存取相機。 Signal需要存取相機來拍照或錄影,但您已經永久拒絕。請前往應用程式設定功能表,按「權限」,然後開啟「相機」。 @@ -408,7 +426,13 @@ 您將退出此群組,並從您所有的裝置上刪除此群組。 刪除 刪除並退出 - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. 加入 @@ -3337,6 +3361,14 @@ 從你的裝置發送照片、影片和檔案。 + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + 安全設定 @@ -3589,6 +3621,14 @@ 聊天 廣播 + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + 建立新群組 設定 @@ -4078,8 +4118,14 @@ 審查成員 檢查要求 - %1$d 個群組成員的名稱相同,請檢閱下列成員並選擇要採取的動作。 - 若您不確定此請求來自誰人,請檢閱下列聯絡人並採取動作。 + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + 無其他共同群組。 無共同群組。 @@ -4108,12 +4154,18 @@ %1$s 已加入 %1$s 和 %2$s 已加入 %1$s、%2$s 和 %3$s 已加入 - %1$s、%2$s 和另 %3$d 人已加入 + + + %1$s, %2$s and %3$d others joined + %1$s 已退出 %1$s 和 %2$s 已退出 %1$s、%2$s 和 %3$s 已退出 - %1$s、%2$s 和另 %3$d 人已退出 + + + %1$s, %2$s and %3$d others left + 您 (在另一台裝置上) @@ -4481,7 +4533,10 @@ 已封鎖 - %1$d 個聯絡人 + + + %1$d contacts + 傳訊 限時訊息 應用程式安全性 @@ -6602,5 +6657,20 @@ 正在下載備份資料… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index cd7e383eb4..7b1966e0cf 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -89,6 +89,16 @@ Signal 需要儲存的權限,以存取照片、影片或聲音檔。但是現在被設定為永久拒絕存取。請到應用程式設定中,選擇「權限」及開啟「儲存」。 Signal 需要聯絡人的權限以存取聯絡人資訊,但是現在被設定為永久拒絕存取。請到應用程式設定中,選取「權限」,並啟用「聯絡人」的權限。 Signal 需要位置的權限以存取位置資訊,但是現在被設定為永久拒絕存取。請到應用程式設定中,選取「權限」,並啟用「位置」的權限。 + + + Allow access to your location + + To send your location: + + Allow Signal access to send your location. + + Signal needs location access to send your location. + %1$s 尚未啟動付款 @@ -383,8 +393,16 @@ 你的加入要求已傳送到群組管理員。 他們採取行動時會通知你。 取消要求 - + + Allow access to your microphone + + To send audio messages: + + To send voice messages, allow Signal access to your microphone. + + Signal needs microphone access to record a voice message. Signal 需要麥克風的權限來傳送語音訊息,但是現在系統設定為總是拒絕 Signal。請到 Signal 的應用程式設定中,點選「權限」,並啟用「麥克風」的權限。 + Signal 需要麥克風和相機的權限來打電話給 %1$s,但是現在系統設定為總是拒絕 Signal。請到 Signal 的應用程式設定中,點選「權限」,並啟用麥克風和相機的權限。 請授予 Signal 使用相機的權限,才能拍攝照片和影片 Signal 需要相機的權限來拍攝照片或是影片 ,但是現在系統設定為總是拒絕 Signal。請到 Signal 的應用程式設定中,點選「權限」,並啟用相機的權限。 @@ -408,7 +426,13 @@ 你將離開此群組,及它將從你所有的裝置上被刪除。 刪除 刪除並離開 - + + + To start a call: + + To start a call, allow Signal access to your microphone. + + Signal needs microphone access to start a call. 加入 @@ -3337,6 +3361,14 @@ 從你的裝置發送照片、影片和檔案。 + + + 1. Tap “Settings” below + + 2. %1$s Allow the permission + + Settings + 安全設定 @@ -3589,6 +3621,14 @@ 聊天 訊息廣播 + + + Double tap to edit + + Quickly tap twice on your messages to edit them. You can edit your messages up to 24hrs after they’ve been sent. + + Got it + 新增群組 設定 @@ -4078,8 +4118,14 @@ 審查成員 檢查要求 - %1$d 個群組成員的名字相同,請查看以下成員並選擇採取措施。 - 如果你不確定請求來自誰,請查看下面的聯絡人並採取措施。 + + + %1$d group members have the same name, review the members below and choose to take action. + + + + If you\'re not sure who the request is from, review the contacts below and take action. + 沒有其他共同的群組。 沒有共同的群組。 @@ -4108,12 +4154,18 @@ %1$s 已加入 %1$s 和 %2$s 已加入 %1$s, %2$s 和 %3$s已加入 - %1$s, %2$s 和 %3$d 及其他人已加入 + + + %1$s, %2$s and %3$d others joined + %1$s 離開了 %1$s 和 %2$s 已離開 %1$s, %2$s 和 %3$s 已離開 - %1$s, %2$s 和 %3$d 及其他人已離開 + + + %1$s, %2$s and %3$d others left + 你 (在其他的裝置上) @@ -4481,7 +4533,10 @@ 已封鎖 - %1$d 個聯絡人 + + + %1$d contacts + 傳訊 自動銷毀訊息 應用程式安全性 @@ -6602,5 +6657,20 @@ 正在下載備份資料… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + + diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index 44fdecc9ad..f40097e6c8 100644 --- a/app/static-ips.gradle.kts +++ b/app/static-ips.gradle.kts @@ -1,5 +1,5 @@ rootProject.extra["service_ips"] = """new String[]{"13.248.212.111","76.223.92.165"}""" -rootProject.extra["storage_ips"] = """new String[]{"142.251.40.243"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.251.40.115"}""" 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"}""" From 89d420cda8e9caf0f5e340205d5661b1e759129f Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 30 Apr 2024 16:42:44 -0400 Subject: [PATCH 109/113] Bump version to 7.6.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 8eb4f436e5..de94e746b3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1414 -val canonicalVersionName = "7.6.0" +val canonicalVersionCode = 1415 +val canonicalVersionName = "7.6.1" val postFixSize = 100 val abiPostFix: Map = mapOf( From 234b3967edc8e36e3a11a5c31d080af54e11c914 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Wed, 1 May 2024 10:42:23 -0400 Subject: [PATCH 110/113] Fix button crash in v1 PIN restore fragment. --- .../org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java index 011e76c8e1..41f3c5ef6d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java @@ -72,7 +72,7 @@ private void initViews(@NonNull View root) { RegistrationViewDelegate.setDebugLogSubmitMultiTapView(root.findViewById(R.id.pin_restore_pin_title)); pinEntry = root.findViewById(R.id.pin_restore_pin_input); - pinButton = root.findViewById(R.id.pin_restore_pin_continue); + pinButton = root.findViewById(R.id.pin_restore_pin_confirm); errorLabel = root.findViewById(R.id.pin_restore_pin_input_label); keyboardToggle = root.findViewById(R.id.pin_restore_keyboard_toggle); helpButton = root.findViewById(R.id.pin_restore_forgot_pin); From c3070f2913b934e6b3a4c5514e04b9667920b551 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 1 May 2024 16:33:33 -0400 Subject: [PATCH 111/113] Revert "Expand double tap touch area." This reverts commit 8c81e4773787bc2126df5c7a6d7c712fca694d4f. --- .../conversation/v2/ConversationAdapterV2.kt | 28 +++++++++---------- .../V2ConversationItemTextOnlyViewHolder.kt | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt index 94cec13b4d..a2e2e62c4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt @@ -385,6 +385,19 @@ class ConversationAdapterV2( } private inner class OutgoingMediaViewHolder(itemView: View) : ConversationViewHolder(itemView) { + val gestureDetector = GestureDetector( + context, + object : SimpleOnGestureListener() { + override fun onDoubleTap(e: MotionEvent): Boolean { + if (clickListener != null) { + clickListener.onItemDoubleClick(getMultiselectPartForLatestTouch()) + return true + } + return false + } + } + ) + override fun bind(model: OutgoingMedia) { bindable.setEventListener(clickListener) bindable.setGestureDetector(gestureDetector) @@ -473,19 +486,6 @@ class ConversationAdapterV2( val bindable: BindableConversationItem get() = itemView as BindableConversationItem - val gestureDetector: GestureDetector = GestureDetector( - context, - object : SimpleOnGestureListener() { - override fun onDoubleTap(e: MotionEvent): Boolean { - if (clickListener != null && selectedItems.isEmpty()) { - clickListener.onItemDoubleClick(getMultiselectPartForLatestTouch()) - return true - } - return false - } - } - ) - override val root: ViewGroup = bindable.root protected val previousMessage: Optional @@ -512,8 +512,6 @@ class ConversationAdapterV2( ) true } - - itemView.setOnTouchListener { _, event: MotionEvent -> gestureDetector.onTouchEvent(event) } } fun bindPayloadsIfAvailable(): Boolean { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt index 654f828637..998ab89aa0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt @@ -154,7 +154,7 @@ open class V2ConversationItemTextOnlyViewHolder>( ) } - binding.root.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) } + binding.body.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) } binding.root.setOnClickListener { onBubbleClicked() } binding.root.setOnLongClickListener { conversationContext.clickListener.onItemLongClick(binding.root, getMultiselectPartForLatestTouch()) From 76c04d8d6d6999e4f6468ca6244c1e5bb3241471 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 1 May 2024 16:42:03 -0400 Subject: [PATCH 112/113] Update translations and other static files. --- app/src/main/res/values-uk/strings.xml | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 0eba43839a..3e3377e104 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1154,7 +1154,7 @@ Сповіщати мене про згадування - Отримувати сповіщення, якщо вас згадали в чатах із вимкнутими сповіщеннями? + Отримувати сповіщення, коли вас згадують, навіть якщо сповіщення від чату вимкнено? Завжди сповіщати Не сповіщати @@ -2447,7 +2447,7 @@ %1$s приєднався до Signal! Тимчасові повідомлення вимкнено Таймер тимчасових повідомлень встановлено на %1$s - Код безпеки змінився + Змінився код безпеки Код безпеки для чату з %1$s змінився. Ви перевірили Номер безпеки є НЕперевіреним @@ -2989,13 +2989,13 @@ Вимкнути - Історія змін коду безпеки + Зміна коду безпеки Прийняти Усе одно зателефонувати Приєднатися до дзвінка Продовжити дзвінок Покинути дзвінок - Можливо, ці користувачі перевстановили застосунок або замінили пристрої. Перевірте коди безпеки для чатів з цими користувачами, щоб упевнитись у конфіденційності спілкування. + Можливо, ці люди перевстановили застосунок або замінили пристрої. Перевірте коди безпеки для чатів з ними, щоб упевнитись у конфіденційності спілкування. Детальніше Попереднє підтверджено @@ -3209,7 +3209,7 @@ Є деякі проблеми, що потребують вашої уваги. Надіслано Одержано - Зникає + Зникне через Через @@ -4731,11 +4731,11 @@ - Стандартне + Стандартна Швидше, менше даних - Великий + Висока Повільніше, більше даних @@ -4943,9 +4943,9 @@ Якість відправлених медіа Відправлення медіа високої якості споживатиме більше даних. - Великий + Висока - Стандартне + Стандартна Виклики @@ -5093,10 +5093,10 @@ Вимкнути сповіщення - Сповіщення увімкнені + Сповіщення ввімкнено Згадки - Завжди повідомляти - Не повідомляти + Завжди сповіщати + Не сповіщати Власні повідомлення @@ -6243,9 +6243,9 @@ Верифіковано - Історія змін коду безпеки + Зміна коду безпеки - Ці люди могли заново встановити Signal або змінити пристрої. Натисніть на одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. + Можливо, ці люди перевстановили Signal або замінили пристрій. Натисніть на користувача, щоб підтвердити новий код безпеки. Це необов\'язково. Перевірка коду безпеки @@ -6272,7 +6272,7 @@ Готово - Історія змін коду безпеки + Зміна коду безпеки %1$d одержувач міг заново встановити Signal або змінити пристрій. Натисніть на одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. From 333fa22c963da9b99467a1bbffc2ad8792cc883c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 1 May 2024 16:42:29 -0400 Subject: [PATCH 113/113] Bump version to 7.6.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 de94e746b3..75c09a1288 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1415 -val canonicalVersionName = "7.6.1" +val canonicalVersionCode = 1416 +val canonicalVersionName = "7.6.2" val postFixSize = 100 val abiPostFix: Map = mapOf(