mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-05-09 01:38:53 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6108bc56e6 | |||
| 33c661d5cd | |||
| fea5c36eee | |||
| baa16d1ac7 | |||
| 4baa7527f7 | |||
| 152bfb4c88 | |||
| 1b56620d9d | |||
| 91a39e7555 | |||
| b41f2862f4 | |||
| 0048b766fe | |||
| c12b39fe3a | |||
| ebe9a0e558 | |||
| 5f0d00d835 | |||
| 8220d54be0 | |||
| 79fce12aeb | |||
| 949eb54aee | |||
| 27a4996195 | |||
| 50524a1954 | |||
| f6d5365e83 | |||
| d0ab5f8027 | |||
| 79dfecca77 | |||
| 13f40ee9f4 | |||
| c7676cbaea | |||
| f7dbfb7cc6 |
+3
-2
@@ -57,8 +57,8 @@ ktlint {
|
||||
version = "0.43.2"
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 1119
|
||||
def canonicalVersionName = "5.48.3"
|
||||
def canonicalVersionCode = 1120
|
||||
def canonicalVersionName = "5.49.0"
|
||||
|
||||
def postFixSize = 100
|
||||
def abiPostFix = ['universal' : 0,
|
||||
@@ -470,6 +470,7 @@ dependencies {
|
||||
implementation project(':donations')
|
||||
implementation project(':contacts')
|
||||
implementation project(':qr')
|
||||
implementation project(':sms-exporter')
|
||||
|
||||
implementation libs.libsignal.android
|
||||
implementation libs.google.protobuf.javalite
|
||||
|
||||
@@ -670,6 +670,11 @@
|
||||
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".exporter.flow.SmsExportActivity"
|
||||
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<service android:enabled="true" android:name=".exporter.SignalSmsExportService" android:foregroundServiceType="dataSync" />
|
||||
<service android:enabled="true" android:name=".service.webrtc.WebRtcCallService" android:foregroundServiceType="camera|microphone"/>
|
||||
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
||||
<service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/>
|
||||
|
||||
+1
-2
@@ -104,7 +104,7 @@ class ChangeNumberRepository(private val accountManager: SignalServiceAccountMan
|
||||
@WorkerThread
|
||||
fun changeLocalNumber(e164: String, pni: PNI): Single<Unit> {
|
||||
val oldStorageId: ByteArray? = Recipient.self().storageServiceId
|
||||
SignalDatabase.recipients.updateSelfPhone(e164)
|
||||
SignalDatabase.recipients.updateSelfPhone(e164, pni)
|
||||
val newStorageId: ByteArray? = Recipient.self().storageServiceId
|
||||
|
||||
if (MessageDigest.isEqual(oldStorageId, newStorageId)) {
|
||||
@@ -117,7 +117,6 @@ class ChangeNumberRepository(private val accountManager: SignalServiceAccountMan
|
||||
}
|
||||
}
|
||||
|
||||
SignalDatabase.recipients.setPni(Recipient.self().id, pni)
|
||||
ApplicationDependencies.getRecipientCache().clear()
|
||||
|
||||
SignalStore.account().setE164(e164)
|
||||
|
||||
+56
@@ -1,16 +1,24 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.chats.sms
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.Navigation
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.thoughtcrime.securesms.R
|
||||
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.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.SmsUtil
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
@@ -22,6 +30,7 @@ private const val SMS_REQUEST_CODE: Short = 1234
|
||||
class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
||||
|
||||
private lateinit var viewModel: SmsSettingsViewModel
|
||||
private lateinit var smsExportLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
@@ -29,6 +38,12 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
||||
}
|
||||
|
||||
override fun bindAdapter(adapter: MappingAdapter) {
|
||||
smsExportLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
showSmsRemovalDialog()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel = ViewModelProvider(this)[SmsSettingsViewModel::class.java]
|
||||
|
||||
viewModel.state.observe(viewLifecycleOwner) {
|
||||
@@ -42,6 +57,32 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
||||
|
||||
private fun getConfiguration(state: SmsSettingsState): DSLConfiguration {
|
||||
return configure {
|
||||
when (state.smsExportState) {
|
||||
SmsSettingsState.SmsExportState.FETCHING -> Unit
|
||||
SmsSettingsState.SmsExportState.HAS_UNEXPORTED_MESSAGES -> {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__export_sms_messages),
|
||||
onClick = {
|
||||
smsExportLauncher.launch(SmsExportActivity.createIntent(requireContext()))
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
}
|
||||
SmsSettingsState.SmsExportState.ALL_MESSAGES_EXPORTED -> {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__remove_sms_messages),
|
||||
onClick = {
|
||||
showSmsRemovalDialog()
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
}
|
||||
SmsSettingsState.SmsExportState.NO_SMS_MESSAGES_IN_DATABASE -> Unit
|
||||
SmsSettingsState.SmsExportState.NOT_AVAILABLE -> Unit
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__use_as_default_sms_app),
|
||||
@@ -96,4 +137,19 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
||||
|
||||
startActivityForResult(intent, SMS_REQUEST_CODE.toInt())
|
||||
}
|
||||
|
||||
private fun showSmsRemovalDialog() {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.RemoveSmsMessagesDialogFragment__remove_sms_messages)
|
||||
.setMessage(R.string.RemoveSmsMessagesDialogFragment__you_have_changed)
|
||||
.setPositiveButton(R.string.RemoveSmsMessagesDialogFragment__keep_messages) { _, _ -> }
|
||||
.setNegativeButton(R.string.RemoveSmsMessagesDialogFragment__remove_messages) { _, _ ->
|
||||
SignalExecutors.BOUNDED.execute {
|
||||
SignalDatabase.sms.deleteExportedMessages()
|
||||
SignalDatabase.mms.deleteExportedMessages()
|
||||
}
|
||||
Snackbar.make(requireView(), R.string.SmsSettingsFragment__sms_messages_removed, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.chats.sms
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
|
||||
class SmsSettingsRepository {
|
||||
fun getSmsExportState(): Single<SmsSettingsState.SmsExportState> {
|
||||
if (!FeatureFlags.smsExporter()) {
|
||||
return Single.just(SmsSettingsState.SmsExportState.NOT_AVAILABLE)
|
||||
}
|
||||
|
||||
return Single.fromCallable {
|
||||
checkInsecureMessageCount() ?: checkUnexportedInsecureMessageCount()
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun checkInsecureMessageCount(): SmsSettingsState.SmsExportState? {
|
||||
val smsCount = SignalDatabase.sms.insecureMessageCount
|
||||
val mmsCount = SignalDatabase.mms.insecureMessageCount
|
||||
val totalSmsMmsCount = smsCount + mmsCount
|
||||
|
||||
return if (totalSmsMmsCount == 0) {
|
||||
SmsSettingsState.SmsExportState.NO_SMS_MESSAGES_IN_DATABASE
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun checkUnexportedInsecureMessageCount(): SmsSettingsState.SmsExportState {
|
||||
val unexportedSmsCount = SignalDatabase.sms.unexportedInsecureMessages.use { it.count }
|
||||
val unexportedMmsCount = SignalDatabase.mms.unexportedInsecureMessages.use { it.count }
|
||||
val totalUnexportedCount = unexportedSmsCount + unexportedMmsCount
|
||||
|
||||
return if (totalUnexportedCount > 0) {
|
||||
SmsSettingsState.SmsExportState.HAS_UNEXPORTED_MESSAGES
|
||||
} else {
|
||||
SmsSettingsState.SmsExportState.ALL_MESSAGES_EXPORTED
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -3,5 +3,14 @@ package org.thoughtcrime.securesms.components.settings.app.chats.sms
|
||||
data class SmsSettingsState(
|
||||
val useAsDefaultSmsApp: Boolean,
|
||||
val smsDeliveryReportsEnabled: Boolean,
|
||||
val wifiCallingCompatibilityEnabled: Boolean
|
||||
)
|
||||
val wifiCallingCompatibilityEnabled: Boolean,
|
||||
val smsExportState: SmsExportState = SmsExportState.FETCHING
|
||||
) {
|
||||
enum class SmsExportState {
|
||||
FETCHING,
|
||||
HAS_UNEXPORTED_MESSAGES,
|
||||
ALL_MESSAGES_EXPORTED,
|
||||
NO_SMS_MESSAGES_IN_DATABASE,
|
||||
NOT_AVAILABLE
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.components.settings.app.chats.sms
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
@@ -9,6 +11,9 @@ import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
||||
class SmsSettingsViewModel : ViewModel() {
|
||||
|
||||
private val repository = SmsSettingsRepository()
|
||||
|
||||
private val disposables = CompositeDisposable()
|
||||
private val store = Store(
|
||||
SmsSettingsState(
|
||||
useAsDefaultSmsApp = Util.isDefaultSmsProvider(ApplicationDependencies.getApplication()),
|
||||
@@ -19,6 +24,16 @@ class SmsSettingsViewModel : ViewModel() {
|
||||
|
||||
val state: LiveData<SmsSettingsState> = store.stateLiveData
|
||||
|
||||
init {
|
||||
disposables += repository.getSmsExportState().subscribe { state ->
|
||||
store.update { it.copy(smsExportState = state) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
disposables.clear()
|
||||
}
|
||||
|
||||
fun setSmsDeliveryReportsEnabled(enabled: Boolean) {
|
||||
store.update { it.copy(smsDeliveryReportsEnabled = enabled) }
|
||||
SignalStore.settings().isSmsDeliveryReportsEnabled = enabled
|
||||
|
||||
+2
-1
@@ -287,7 +287,8 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
requireContext(),
|
||||
StoryViewerArgs(
|
||||
recipientId = state.recipient.id,
|
||||
isInHiddenStoryMode = state.recipient.shouldHideStory()
|
||||
isInHiddenStoryMode = state.recipient.shouldHideStory(),
|
||||
isFromQuote = true
|
||||
)
|
||||
)
|
||||
StoryDialogs.displayStoryOrProfileImage(
|
||||
|
||||
+20
-22
@@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.util.SpanUtil
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import java.util.Objects
|
||||
|
||||
/**
|
||||
@@ -62,27 +61,26 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
||||
)
|
||||
|
||||
if (!recipient.isGroup) {
|
||||
if (recipient.isSelf) {
|
||||
val aci: String = SignalStore.account().aci?.toString() ?: "null"
|
||||
longClickPref(
|
||||
title = DSLSettingsText.from("ACI"),
|
||||
summary = DSLSettingsText.from(aci),
|
||||
onLongClick = { copyToClipboard(aci) }
|
||||
)
|
||||
val pni: String = SignalStore.account().pni?.toString() ?: "null"
|
||||
longClickPref(
|
||||
title = DSLSettingsText.from("PNI"),
|
||||
summary = DSLSettingsText.from(pni),
|
||||
onLongClick = { copyToClipboard(pni) }
|
||||
)
|
||||
} else {
|
||||
val serviceId: String = recipient.serviceId.map(ServiceId::toString).orElse("null")
|
||||
longClickPref(
|
||||
title = DSLSettingsText.from("ServiceId"),
|
||||
summary = DSLSettingsText.from(serviceId),
|
||||
onLongClick = { copyToClipboard(serviceId) }
|
||||
)
|
||||
}
|
||||
val e164: String = recipient.e164.orElse("null")
|
||||
longClickPref(
|
||||
title = DSLSettingsText.from("E164"),
|
||||
summary = DSLSettingsText.from(e164),
|
||||
onLongClick = { copyToClipboard(e164) }
|
||||
)
|
||||
|
||||
val serviceId: String = recipient.serviceId.map { it.toString() }.orElse("null")
|
||||
longClickPref(
|
||||
title = DSLSettingsText.from("ServiceId"),
|
||||
summary = DSLSettingsText.from(serviceId),
|
||||
onLongClick = { copyToClipboard(serviceId) }
|
||||
)
|
||||
|
||||
val pni: String = recipient.pni.map { it.toString() }.orElse("null")
|
||||
longClickPref(
|
||||
title = DSLSettingsText.from("PNI"),
|
||||
summary = DSLSettingsText.from(pni),
|
||||
onLongClick = { copyToClipboard(pni) }
|
||||
)
|
||||
}
|
||||
|
||||
if (state.groupId != null) {
|
||||
|
||||
@@ -195,8 +195,8 @@ object ContactSearchItems {
|
||||
|
||||
private fun presentPrivacyMode(privacyMode: DistributionListPrivacyMode): String {
|
||||
return when (privacyMode) {
|
||||
DistributionListPrivacyMode.ONLY_WITH -> context.getString(R.string.ChooseInitialMyStoryMembershipFragment__only_share_with)
|
||||
DistributionListPrivacyMode.ALL_EXCEPT -> context.getString(R.string.ChooseInitialMyStoryMembershipFragment__all_signal_connections_except)
|
||||
DistributionListPrivacyMode.ONLY_WITH -> context.getString(R.string.ContactSearchItems__only_share_with)
|
||||
DistributionListPrivacyMode.ALL_EXCEPT -> context.getString(R.string.ChooseInitialMyStoryMembershipFragment__all_except)
|
||||
DistributionListPrivacyMode.ALL -> context.getString(R.string.ChooseInitialMyStoryMembershipFragment__all_signal_connections)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,9 @@ object ContactDiscovery {
|
||||
descriptor = "refresh-all",
|
||||
refresh = {
|
||||
if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
ContactDiscoveryRefreshV2.refreshAll(context)
|
||||
ContactDiscoveryRefreshV2.refreshAll(context, useCompat = false)
|
||||
} else if (FeatureFlags.cdsV2Compat()) {
|
||||
ContactDiscoveryRefreshV2.refreshAll(context, useCompat = true)
|
||||
} else if (FeatureFlags.cdsV2LoadTesting()) {
|
||||
loadTestRefreshAll(context)
|
||||
} else {
|
||||
@@ -103,7 +105,9 @@ object ContactDiscovery {
|
||||
descriptor = "refresh-multiple",
|
||||
refresh = {
|
||||
if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
ContactDiscoveryRefreshV2.refresh(context, recipients)
|
||||
ContactDiscoveryRefreshV2.refresh(context, recipients, useCompat = false)
|
||||
} else if (FeatureFlags.cdsV2Compat()) {
|
||||
ContactDiscoveryRefreshV2.refresh(context, recipients, useCompat = true)
|
||||
} else if (FeatureFlags.cdsV2LoadTesting()) {
|
||||
loadTestRefresh(context, recipients)
|
||||
} else {
|
||||
@@ -124,7 +128,9 @@ object ContactDiscovery {
|
||||
descriptor = "refresh-single",
|
||||
refresh = {
|
||||
if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
ContactDiscoveryRefreshV2.refresh(context, listOf(recipient))
|
||||
ContactDiscoveryRefreshV2.refresh(context, listOf(recipient), useCompat = false)
|
||||
} else if (FeatureFlags.cdsV2Compat()) {
|
||||
ContactDiscoveryRefreshV2.refresh(context, listOf(recipient), useCompat = true)
|
||||
} else if (FeatureFlags.cdsV2LoadTesting()) {
|
||||
loadTestRefresh(context, listOf(recipient))
|
||||
} else {
|
||||
@@ -381,14 +387,14 @@ object ContactDiscovery {
|
||||
private fun loadTestRefreshAll(context: Context): RefreshResult {
|
||||
return loadTestOperation(
|
||||
{ ContactDiscoveryRefreshV1.refreshAll(context) },
|
||||
{ ContactDiscoveryRefreshV2.refreshAll(context, ignoreResults = true) }
|
||||
{ ContactDiscoveryRefreshV2.refreshAll(context, useCompat = false, ignoreResults = true) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadTestRefresh(context: Context, recipients: List<Recipient>): RefreshResult {
|
||||
return loadTestOperation(
|
||||
{ ContactDiscoveryRefreshV1.refresh(context, recipients) },
|
||||
{ ContactDiscoveryRefreshV2.refresh(context, recipients, ignoreResults = true) }
|
||||
{ ContactDiscoveryRefreshV2.refresh(context, recipients, useCompat = false, ignoreResults = true) }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -122,7 +122,7 @@ class ContactDiscoveryRefreshV1 {
|
||||
|
||||
if (result.getNumberRewrites().size() > 0) {
|
||||
Log.i(TAG, "[getDirectoryResult] Need to rewrite some numbers.");
|
||||
recipientDatabase.updatePhoneNumbers(result.getNumberRewrites());
|
||||
recipientDatabase.rewritePhoneNumbers(result.getNumberRewrites());
|
||||
}
|
||||
|
||||
Map<RecipientId, ACI> aciMap = recipientDatabase.bulkProcessCdsResult(result.getRegisteredNumbers());
|
||||
@@ -250,8 +250,8 @@ class ContactDiscoveryRefreshV1 {
|
||||
KeyStore iasKeyStore = getIasKeyStore(context);
|
||||
|
||||
try {
|
||||
Map<String, ACI> results = accountManager.getRegisteredUsers(iasKeyStore, sanitizedNumbers, BuildConfig.CDS_MRENCLAVE);
|
||||
FuzzyPhoneNumberHelper.OutputResult outputResult = FuzzyPhoneNumberHelper.generateOutput(results, inputResult);
|
||||
Map<String, ACI> results = accountManager.getRegisteredUsers(iasKeyStore, sanitizedNumbers, BuildConfig.CDS_MRENCLAVE);
|
||||
FuzzyPhoneNumberHelper.OutputResult<ACI> outputResult = FuzzyPhoneNumberHelper.generateOutput(results, inputResult);
|
||||
|
||||
return new ContactIntersection(outputResult.getNumbers(), outputResult.getRewrites(), ignoredNumbers);
|
||||
} catch (SignatureException | UnauthenticatedQuoteException | UnauthenticatedResponseException | Quote.InvalidQuoteFormatException | InvalidKeyException e) {
|
||||
|
||||
+159
-109
@@ -4,20 +4,25 @@ import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.signal.contacts.SystemContactsRepository
|
||||
import org.signal.core.util.Stopwatch
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||
import org.thoughtcrime.securesms.contacts.sync.FuzzyPhoneNumberHelper.InputResult
|
||||
import org.thoughtcrime.securesms.contacts.sync.FuzzyPhoneNumberHelper.OutputResult
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.CdsV2Result
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.ACI
|
||||
import org.whispersystems.signalservice.api.services.CdsiV2Service
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Performs the CDS refresh using the V2 interface (either CDSH or CDSI) that returns both PNIs and ACIs.
|
||||
@@ -39,145 +44,190 @@ object ContactDiscoveryRefreshV2 {
|
||||
@WorkerThread
|
||||
@Synchronized
|
||||
@JvmStatic
|
||||
fun refreshAll(context: Context, ignoreResults: Boolean = false): ContactDiscovery.RefreshResult {
|
||||
val stopwatch = Stopwatch("refresh-all")
|
||||
|
||||
val previousE164s: Set<String> = if (SignalStore.misc().cdsToken != null) {
|
||||
SignalDatabase.cds.getAllE164s()
|
||||
} else {
|
||||
Log.w(TAG, "No token set! Cannot provide previousE164s.")
|
||||
emptySet()
|
||||
}
|
||||
stopwatch.split("previous")
|
||||
|
||||
fun refreshAll(context: Context, useCompat: Boolean, ignoreResults: Boolean = false): ContactDiscovery.RefreshResult {
|
||||
val recipientE164s: Set<String> = SignalDatabase.recipients.getAllE164s().sanitize()
|
||||
val newRecipientE164s: Set<String> = recipientE164s - previousE164s
|
||||
stopwatch.split("recipient")
|
||||
|
||||
val systemE164s: Set<String> = SystemContactsRepository.getAllDisplayNumbers(context).toE164s(context).sanitize()
|
||||
val newSystemE164s: Set<String> = systemE164s - previousE164s
|
||||
stopwatch.split("system")
|
||||
|
||||
val newE164s: Set<String> = newRecipientE164s + newSystemE164s
|
||||
|
||||
val tokenToUse: ByteArray? = if (previousE164s.isNotEmpty()) {
|
||||
SignalStore.misc().cdsToken
|
||||
} else {
|
||||
if (SignalStore.misc().cdsToken != null) {
|
||||
Log.w(TAG, "We have a token, but our previousE164 list is empty! We cannot provide a token.")
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
val response: CdsiV2Service.Response = makeRequest(
|
||||
previousE164s = previousE164s,
|
||||
newE164s = newE164s,
|
||||
serviceIds = SignalDatabase.recipients.getAllServiceIdProfileKeyPairs(),
|
||||
token = tokenToUse,
|
||||
return refreshInternal(
|
||||
recipientE164s = recipientE164s,
|
||||
systemE164s = systemE164s,
|
||||
inputPreviousE164s = SignalDatabase.cds.getAllE164s(),
|
||||
saveToken = true,
|
||||
tag = "refresh-all"
|
||||
useCompat = useCompat,
|
||||
ignoreResults = ignoreResults
|
||||
)
|
||||
stopwatch.split("network")
|
||||
|
||||
SignalDatabase.cds.updateAfterCdsQuery(newE164s, recipientE164s + systemE164s)
|
||||
stopwatch.split("cds-db")
|
||||
|
||||
var registeredIds: Set<RecipientId> = emptySet()
|
||||
|
||||
if (ignoreResults) {
|
||||
Log.w(TAG, "[refresh-all] Ignoring CDSv2 results.")
|
||||
} else {
|
||||
registeredIds = SignalDatabase.recipients.bulkProcessCdsV2Result(
|
||||
response.results
|
||||
.mapValues { entry -> RecipientDatabase.CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) }
|
||||
)
|
||||
stopwatch.split("recipient-db")
|
||||
|
||||
SignalDatabase.recipients.bulkUpdatedRegisteredStatus(registeredIds.associateWith { null }, emptyList())
|
||||
stopwatch.split("update-registered")
|
||||
}
|
||||
|
||||
stopwatch.stop(TAG)
|
||||
Log.d(TAG, "[refresh-all] Used ${response.quotaUsedDebugOnly} units of our quota.")
|
||||
|
||||
return ContactDiscovery.RefreshResult(registeredIds, emptyMap())
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@WorkerThread
|
||||
@Synchronized
|
||||
@JvmStatic
|
||||
fun refresh(context: Context, inputRecipients: List<Recipient>, ignoreResults: Boolean = false): ContactDiscovery.RefreshResult {
|
||||
val stopwatch = Stopwatch("refresh-some")
|
||||
|
||||
val recipients = inputRecipients.map { it.resolve() }
|
||||
stopwatch.split("resolve")
|
||||
|
||||
val inputIds: Set<RecipientId> = recipients.map { it.id }.toSet()
|
||||
fun refresh(context: Context, inputRecipients: List<Recipient>, useCompat: Boolean, ignoreResults: Boolean = false): ContactDiscovery.RefreshResult {
|
||||
val recipients: List<Recipient> = inputRecipients.map { it.resolve() }
|
||||
val inputE164s: Set<String> = recipients.mapNotNull { it.e164.orElse(null) }.toSet()
|
||||
|
||||
if (inputE164s.size > MAXIMUM_ONE_OFF_REQUEST_SIZE) {
|
||||
return if (inputE164s.size > MAXIMUM_ONE_OFF_REQUEST_SIZE) {
|
||||
Log.i(TAG, "List of specific recipients to refresh is too large! (Size: ${recipients.size}). Doing a full refresh instead.")
|
||||
val fullResult: ContactDiscovery.RefreshResult = refreshAll(context, ignoreResults)
|
||||
|
||||
return ContactDiscovery.RefreshResult(
|
||||
val fullResult: ContactDiscovery.RefreshResult = refreshAll(context, ignoreResults)
|
||||
val inputIds: Set<RecipientId> = recipients.map { it.id }.toSet()
|
||||
|
||||
ContactDiscovery.RefreshResult(
|
||||
registeredIds = fullResult.registeredIds.intersect(inputIds),
|
||||
rewrites = fullResult.rewrites.filterKeys { inputE164s.contains(it) }
|
||||
)
|
||||
}
|
||||
|
||||
if (inputE164s.isEmpty()) {
|
||||
Log.w(TAG, "No numbers to refresh!")
|
||||
return ContactDiscovery.RefreshResult(emptySet(), emptyMap())
|
||||
} else {
|
||||
Log.i(TAG, "Doing a one-off request for ${inputE164s.size} recipients.")
|
||||
}
|
||||
|
||||
val response: CdsiV2Service.Response = makeRequest(
|
||||
previousE164s = emptySet(),
|
||||
newE164s = inputE164s,
|
||||
serviceIds = SignalDatabase.recipients.getAllServiceIdProfileKeyPairs(),
|
||||
token = null,
|
||||
saveToken = false,
|
||||
tag = "refresh-some"
|
||||
)
|
||||
stopwatch.split("network")
|
||||
|
||||
var registeredIds: Set<RecipientId> = emptySet()
|
||||
|
||||
if (ignoreResults) {
|
||||
Log.w(TAG, "[refresh-some] Ignoring CDSv2 results.")
|
||||
} else {
|
||||
registeredIds = SignalDatabase.recipients.bulkProcessCdsV2Result(
|
||||
response.results
|
||||
.mapValues { entry -> RecipientDatabase.CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) }
|
||||
refreshInternal(
|
||||
recipientE164s = inputE164s,
|
||||
systemE164s = inputE164s,
|
||||
inputPreviousE164s = emptySet(),
|
||||
saveToken = false,
|
||||
useCompat = useCompat,
|
||||
ignoreResults = ignoreResults
|
||||
)
|
||||
stopwatch.split("recipient-db")
|
||||
|
||||
SignalDatabase.recipients.bulkUpdatedRegisteredStatus(registeredIds.associateWith { null }, emptyList())
|
||||
stopwatch.split("update-registered")
|
||||
}
|
||||
|
||||
Log.d(TAG, "[refresh-some] Used ${response.quotaUsedDebugOnly} units of our quota.")
|
||||
stopwatch.stop(TAG)
|
||||
|
||||
return ContactDiscovery.RefreshResult(registeredIds, emptyMap())
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun makeRequest(previousE164s: Set<String>, newE164s: Set<String>, serviceIds: Map<ServiceId, ProfileKey>, token: ByteArray?, saveToken: Boolean, tag: String): CdsiV2Service.Response {
|
||||
return ApplicationDependencies.getSignalServiceAccountManager().getRegisteredUsersWithCdsi(
|
||||
private fun refreshInternal(
|
||||
recipientE164s: Set<String>,
|
||||
systemE164s: Set<String>,
|
||||
inputPreviousE164s: Set<String>,
|
||||
saveToken: Boolean,
|
||||
useCompat: Boolean,
|
||||
ignoreResults: Boolean
|
||||
): ContactDiscovery.RefreshResult {
|
||||
val stopwatch = Stopwatch("refreshInternal-${if (useCompat) "compat" else "v2"}")
|
||||
|
||||
val previousE164s: Set<String> = if (SignalStore.misc().cdsToken != null) inputPreviousE164s else emptySet()
|
||||
|
||||
val allE164s: Set<String> = recipientE164s + systemE164s
|
||||
val newRawE164s: Set<String> = allE164s - previousE164s
|
||||
val fuzzyInput: InputResult = FuzzyPhoneNumberHelper.generateInput(newRawE164s, recipientE164s)
|
||||
val newE164s: Set<String> = fuzzyInput.numbers
|
||||
|
||||
if (newE164s.isEmpty() && previousE164s.isEmpty()) {
|
||||
Log.w(TAG, "[refreshInternal] No data to send! Ignoring.")
|
||||
return ContactDiscovery.RefreshResult(emptySet(), emptyMap())
|
||||
}
|
||||
|
||||
val token: ByteArray? = if (previousE164s.isNotEmpty()) SignalStore.misc().cdsToken else null
|
||||
|
||||
stopwatch.split("preamble")
|
||||
|
||||
val response: CdsiV2Service.Response = ApplicationDependencies.getSignalServiceAccountManager().getRegisteredUsersWithCdsi(
|
||||
previousE164s,
|
||||
newE164s,
|
||||
serviceIds,
|
||||
SignalDatabase.recipients.getAllServiceIdProfileKeyPairs(),
|
||||
useCompat,
|
||||
Optional.ofNullable(token),
|
||||
BuildConfig.CDSI_MRENCLAVE
|
||||
) { tokenToSave ->
|
||||
if (saveToken) {
|
||||
SignalStore.misc().cdsToken = tokenToSave
|
||||
Log.d(TAG, "[$tag] Token saved!")
|
||||
Log.d(TAG, "Token saved!")
|
||||
} else {
|
||||
Log.d(TAG, "Ignoring token.")
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "[refreshInternal] Used ${response.quotaUsedDebugOnly} quota.")
|
||||
stopwatch.split("network")
|
||||
|
||||
SignalDatabase.cds.updateAfterCdsQuery(newE164s, allE164s + newE164s)
|
||||
stopwatch.split("cds-db")
|
||||
|
||||
val registeredIds: MutableSet<RecipientId> = mutableSetOf()
|
||||
val rewrites: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
if (ignoreResults) {
|
||||
Log.w(TAG, "[refreshInternal] Ignoring CDSv2 results.")
|
||||
} else {
|
||||
if (useCompat) {
|
||||
val transformed: Map<String, ACI?> = response.results.mapValues { entry -> entry.value.aci.orElse(null) }
|
||||
val fuzzyOutput: OutputResult<ACI> = FuzzyPhoneNumberHelper.generateOutput(transformed, fuzzyInput)
|
||||
|
||||
if (transformed.values.any { it == null }) {
|
||||
throw IOException("Unexpected null ACI!")
|
||||
}
|
||||
|
||||
SignalDatabase.recipients.rewritePhoneNumbers(fuzzyOutput.rewrites)
|
||||
stopwatch.split("rewrite-e164")
|
||||
|
||||
val aciMap: Map<RecipientId, ACI?> = SignalDatabase.recipients.bulkProcessCdsResult(fuzzyOutput.numbers)
|
||||
|
||||
registeredIds += aciMap.keys
|
||||
rewrites += fuzzyOutput.rewrites
|
||||
stopwatch.split("process-result")
|
||||
|
||||
val existingIds: Set<RecipientId> = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values)
|
||||
val inactiveIds: Set<RecipientId> = (existingIds - registeredIds).removeRegisteredButUnlisted()
|
||||
|
||||
SignalDatabase.recipients.bulkUpdatedRegisteredStatus(aciMap, inactiveIds)
|
||||
stopwatch.split("update-registered")
|
||||
} else {
|
||||
val transformed: Map<String, CdsV2Result> = response.results.mapValues { entry -> CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) }
|
||||
val fuzzyOutput: OutputResult<CdsV2Result> = FuzzyPhoneNumberHelper.generateOutput(transformed, fuzzyInput)
|
||||
|
||||
SignalDatabase.recipients.rewritePhoneNumbers(fuzzyOutput.rewrites)
|
||||
stopwatch.split("rewrite-e164")
|
||||
|
||||
val existingIds: Set<RecipientId> = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values)
|
||||
val inactiveIds: Set<RecipientId> = (existingIds - registeredIds).removeRegisteredButUnlisted()
|
||||
|
||||
registeredIds += SignalDatabase.recipients.bulkProcessCdsV2Result(fuzzyOutput.numbers)
|
||||
rewrites += fuzzyOutput.rewrites
|
||||
stopwatch.split("process-result")
|
||||
|
||||
SignalDatabase.recipients.bulkUpdatedRegisteredStatusV2(registeredIds, inactiveIds)
|
||||
stopwatch.split("update-registered")
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.stop(TAG)
|
||||
|
||||
return ContactDiscovery.RefreshResult(registeredIds, rewrites)
|
||||
}
|
||||
|
||||
private fun hasCommunicatedWith(recipient: Recipient): Boolean {
|
||||
val localAci = SignalStore.account().requireAci()
|
||||
return SignalDatabase.threads.hasThread(recipient.id) || (recipient.hasServiceId() && SignalDatabase.sessions.hasSessionFor(localAci, recipient.requireServiceId().toString()))
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun Set<RecipientId>.removeRegisteredButUnlisted(): Set<RecipientId> {
|
||||
val futures: List<Future<Pair<RecipientId, Boolean?>>> = Recipient.resolvedList(this)
|
||||
.filter { hasCommunicatedWith(it) }
|
||||
.map {
|
||||
SignalExecutors.UNBOUNDED.submit(
|
||||
Callable {
|
||||
try {
|
||||
it.id to ApplicationDependencies.getSignalServiceAccountManager().isIdentifierRegistered(it.requireServiceId())
|
||||
} catch (e: IOException) {
|
||||
it.id to null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val registeredIds: MutableSet<RecipientId> = mutableSetOf()
|
||||
val retryIds: MutableSet<RecipientId> = mutableSetOf()
|
||||
|
||||
for (future in futures) {
|
||||
val (id, registered) = future.get()
|
||||
if (registered == null) {
|
||||
retryIds += id
|
||||
registeredIds += id
|
||||
} else if (registered) {
|
||||
registeredIds += id
|
||||
}
|
||||
}
|
||||
|
||||
if (retryIds.isNotEmpty()) {
|
||||
Log.w(TAG, "Failed to determine registered status of ${retryIds.size} recipients. Assuming registered, but enqueuing profile jobs to check later.")
|
||||
RetrieveProfileJob.enqueue(retryIds)
|
||||
}
|
||||
|
||||
return this - registeredIds
|
||||
}
|
||||
|
||||
private fun Set<String>.toE164s(context: Context): Set<String> {
|
||||
|
||||
+7
-7
@@ -51,8 +51,8 @@ class FuzzyPhoneNumberHelper {
|
||||
* these results and our initial input set, we can decide if we need to rewrite which number we
|
||||
* have stored locally.
|
||||
*/
|
||||
static @NonNull OutputResult generateOutput(@NonNull Map<String, ACI> registeredNumbers, @NonNull InputResult inputResult) {
|
||||
Map<String, ACI> allNumbers = new HashMap<>(registeredNumbers);
|
||||
static @NonNull <E> OutputResult<E> generateOutput(@NonNull Map<String, E> registeredNumbers, @NonNull InputResult inputResult) {
|
||||
Map<String, E> allNumbers = new HashMap<>(registeredNumbers);
|
||||
Map<String, String> rewrites = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, String> entry : inputResult.getMapOfOriginalToVariant().entrySet()) {
|
||||
@@ -76,7 +76,7 @@ class FuzzyPhoneNumberHelper {
|
||||
}
|
||||
}
|
||||
|
||||
return new OutputResult(allNumbers, rewrites);
|
||||
return new OutputResult<>(allNumbers, rewrites);
|
||||
}
|
||||
|
||||
private interface FuzzyMatcher {
|
||||
@@ -170,16 +170,16 @@ class FuzzyPhoneNumberHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static class OutputResult {
|
||||
private final Map<String, ACI> numbers;
|
||||
public static class OutputResult<E> {
|
||||
private final Map<String, E> numbers;
|
||||
private final Map<String, String> rewrites;
|
||||
|
||||
private OutputResult(@NonNull Map<String, ACI> numbers, @NonNull Map<String, String> rewrites) {
|
||||
private OutputResult(@NonNull Map<String, E> numbers, @NonNull Map<String, String> rewrites) {
|
||||
this.numbers = numbers;
|
||||
this.rewrites = rewrites;
|
||||
}
|
||||
|
||||
public @NonNull Map<String, ACI> getNumbers() {
|
||||
public @NonNull Map<String, E> getNumbers() {
|
||||
return numbers;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -1250,6 +1250,7 @@ public class ConversationParentFragment extends Fragment
|
||||
startActivity(StoryViewerActivity.createIntent(
|
||||
requireContext(),
|
||||
new StoryViewerArgs.Builder(recipient.getId(), recipient.get().shouldHideStory())
|
||||
.isFromQuote(true)
|
||||
.build()));
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -89,7 +89,9 @@ class ConversationListSearchAdapter extends RecyclerView.Adapter<Conversation
|
||||
|
||||
@Override
|
||||
public long getHeaderId(int position) {
|
||||
if (getConversationResult(position) != null) {
|
||||
if (position < 0) {
|
||||
return StickyHeaderDecoration.StickyHeaderAdapter.NO_HEADER_ID;
|
||||
} else if (getConversationResult(position) != null) {
|
||||
return TYPE_CONVERSATIONS;
|
||||
} else if (getContactResult(position) != null) {
|
||||
return TYPE_CONTACTS;
|
||||
|
||||
+3
-4
@@ -126,10 +126,9 @@ public class SignalBaseIdentityKeyStore {
|
||||
}
|
||||
|
||||
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, IdentityKeyStore.Direction direction) {
|
||||
Recipient self = Recipient.self();
|
||||
|
||||
boolean isSelf = address.getName().equals(self.requireServiceId().toString()) ||
|
||||
address.getName().equals(self.requireE164());
|
||||
boolean isSelf = address.getName().equals(SignalStore.account().requireAci().toString()) ||
|
||||
address.getName().equals(SignalStore.account().requirePni().toString()) ||
|
||||
address.getName().equals(SignalStore.account().getE164());
|
||||
|
||||
if (isSelf) {
|
||||
return identityKey.equals(SignalStore.account().getAciIdentityKey().getPublicKey());
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.database.model.ParentStoryId;
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.StoryResult;
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.insights.InsightsConstants;
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||
@@ -94,6 +95,9 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||
public abstract boolean isSent(long messageId);
|
||||
public abstract List<MessageRecord> getProfileChangeDetailsRecords(long threadId, long afterTimestamp);
|
||||
public abstract Set<Long> getAllRateLimitedMessageIds();
|
||||
public abstract Cursor getUnexportedInsecureMessages();
|
||||
public abstract int getInsecureMessageCount();
|
||||
public abstract void deleteExportedMessages();
|
||||
|
||||
public abstract void markExpireStarted(long messageId);
|
||||
public abstract void markExpireStarted(long messageId, long startTime);
|
||||
@@ -353,6 +357,14 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||
return String.format(Locale.ENGLISH, "(%s OR %s) AND %s", isSent, isReceived, isSecure);
|
||||
}
|
||||
|
||||
protected String getInsecureMessageClause() {
|
||||
String isSent = "(" + getTypeField() + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_SENT_TYPE;
|
||||
String isReceived = "(" + getTypeField() + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_INBOX_TYPE;
|
||||
String isSecure = "(" + getTypeField() + " & " + (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT) + ")";
|
||||
|
||||
return String.format(Locale.ENGLISH, "(%s OR %s) AND NOT %s", isSent, isReceived, isSecure);
|
||||
}
|
||||
|
||||
public void setReactionsSeen(long threadId, long sinceTimestamp) {
|
||||
SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
@@ -803,6 +815,11 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||
@Deprecated
|
||||
MessageRecord getCurrent();
|
||||
|
||||
/**
|
||||
* Pulls the export state out of the query, if it is present.
|
||||
*/
|
||||
@NonNull MessageExportState getMessageExportStateForCurrentRecord();
|
||||
|
||||
/**
|
||||
* From the {@link Closeable} interface, removing the IOException requirement.
|
||||
*/
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.thoughtcrime.securesms.database.model.StoryType;
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||
@@ -189,7 +190,9 @@ public class MmsDatabase extends MessageDatabase {
|
||||
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1, " +
|
||||
MESSAGE_RANGES + " BLOB DEFAULT NULL, " +
|
||||
STORY_TYPE + " INTEGER DEFAULT 0, " +
|
||||
PARENT_STORY_ID + " INTEGER DEFAULT 0);";
|
||||
PARENT_STORY_ID + " INTEGER DEFAULT 0, " +
|
||||
EXPORT_STATE + " BLOB DEFAULT NULL, " +
|
||||
EXPORTED + " INTEGER DEFAULT 0);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
|
||||
@@ -1216,8 +1219,12 @@ public class MmsDatabase extends MessageDatabase {
|
||||
}
|
||||
|
||||
private Cursor rawQuery(@NonNull String where, @Nullable String[] arguments, boolean reverse, long limit) {
|
||||
return rawQuery(MMS_PROJECTION, where, arguments, reverse, limit);
|
||||
}
|
||||
|
||||
private Cursor rawQuery(@NonNull String[] projection, @NonNull String where, @Nullable String[] arguments, boolean reverse, long limit) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
String rawQueryString = "SELECT " + Util.join(MMS_PROJECTION, ",") +
|
||||
String rawQueryString = "SELECT " + Util.join(projection, ",") +
|
||||
" FROM " + MmsDatabase.TABLE_NAME + " LEFT OUTER JOIN " + AttachmentDatabase.TABLE_NAME +
|
||||
" ON (" + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " = " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ")" +
|
||||
" WHERE " + where + " GROUP BY " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID;
|
||||
@@ -2416,6 +2423,53 @@ public class MmsDatabase extends MessageDatabase {
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getUnexportedInsecureMessages() {
|
||||
return rawQuery(
|
||||
SqlUtil.appendArg(MMS_PROJECTION, EXPORT_STATE),
|
||||
getInsecureMessageClause() + " AND NOT " + EXPORTED,
|
||||
null,
|
||||
false,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInsecureMessageCount() {
|
||||
try (Cursor cursor = getWritableDatabase().query(TABLE_NAME, SqlUtil.COUNT, getInsecureMessageClause(), null, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteExportedMessages() {
|
||||
beginTransaction();
|
||||
try {
|
||||
List<Long> threadsToUpdate = new LinkedList<>();
|
||||
try (Cursor cursor = getReadableDatabase().query(TABLE_NAME, THREAD_ID_PROJECTION, EXPORTED + " = ?", SqlUtil.buildArgs(1), THREAD_ID, null, null, null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
threadsToUpdate.add(CursorUtil.requireLong(cursor, THREAD_ID));
|
||||
}
|
||||
}
|
||||
|
||||
getWritableDatabase().delete(TABLE_NAME, EXPORTED + " = ?", SqlUtil.buildArgs(1));
|
||||
|
||||
for (final long threadId : threadsToUpdate) {
|
||||
SignalDatabase.threads().update(threadId, false);
|
||||
}
|
||||
|
||||
SignalDatabase.attachments().deleteAbandonedAttachmentFiles();
|
||||
|
||||
setTransactionSuccessful();
|
||||
} finally {
|
||||
endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteThreads(@NonNull Set<Long> threadIds) {
|
||||
Log.d(TAG, "deleteThreads(count: " + threadIds.size() + ")");
|
||||
@@ -2664,6 +2718,25 @@ public class MmsDatabase extends MessageDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MessageExportState getMessageExportStateForCurrentRecord() {
|
||||
byte[] messageExportState = CursorUtil.requireBlob(cursor, MmsDatabase.EXPORT_STATE);
|
||||
if (messageExportState == null) {
|
||||
return MessageExportState.getDefaultInstance();
|
||||
}
|
||||
|
||||
try {
|
||||
return MessageExportState.parseFrom(messageExportState);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
return MessageExportState.getDefaultInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
if (cursor == null) return 0;
|
||||
else return cursor.getCount();
|
||||
}
|
||||
|
||||
private NotificationMmsMessageRecord getNotificationMmsMessageRecord(Cursor cursor) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
|
||||
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT));
|
||||
|
||||
@@ -29,6 +29,8 @@ public interface MmsSmsColumns {
|
||||
public static final String REMOTE_DELETED = "remote_deleted";
|
||||
public static final String SERVER_GUID = "server_guid";
|
||||
public static final String RECEIPT_TIMESTAMP = "receipt_timestamp";
|
||||
public static final String EXPORT_STATE = "export_state";
|
||||
public static final String EXPORTED = "exported";
|
||||
|
||||
/**
|
||||
* For storage efficiency, all types are stored within a single 64-bit integer column in the
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
@@ -24,21 +25,23 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import net.zetetic.database.sqlcipher.SQLiteQueryBuilder;
|
||||
|
||||
import org.signal.core.util.CursorUtil;
|
||||
import org.signal.core.util.SqlUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.util.Pair;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase.MessageUpdate;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId;
|
||||
import org.thoughtcrime.securesms.database.model.MessageId;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.notifications.v2.DefaultMessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.signal.core.util.CursorUtil;
|
||||
import org.signal.core.util.SqlUtil;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
|
||||
|
||||
import java.io.Closeable;
|
||||
@@ -50,6 +53,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.thoughtcrime.securesms.database.MmsSmsColumns.Types.GROUP_V2_LEAVE_BITS;
|
||||
@@ -613,6 +617,63 @@ public class MmsSmsDatabase extends Database {
|
||||
SignalDatabase.mms().updateViewedStories(syncMessageIds);
|
||||
}
|
||||
|
||||
private @NonNull MessageExportState getMessageExportState(@NonNull MessageId messageId) throws NoSuchMessageException {
|
||||
String table = messageId.isMms() ? MmsDatabase.TABLE_NAME : SmsDatabase.TABLE_NAME;
|
||||
String[] projection = SqlUtil.buildArgs(MmsSmsColumns.EXPORT_STATE);
|
||||
String[] args = SqlUtil.buildArgs(messageId.getId());
|
||||
|
||||
try (Cursor cursor = getReadableDatabase().query(table, projection, ID_WHERE, args, null, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
byte[] bytes = CursorUtil.requireBlob(cursor, MmsSmsColumns.EXPORT_STATE);
|
||||
if (bytes == null) {
|
||||
return MessageExportState.getDefaultInstance();
|
||||
} else {
|
||||
try {
|
||||
return MessageExportState.parseFrom(bytes);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
return MessageExportState.getDefaultInstance();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new NoSuchMessageException("The requested message does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMessageExportState(@NonNull MessageId messageId, @NonNull Function<MessageExportState, MessageExportState> transform) throws NoSuchMessageException {
|
||||
SQLiteDatabase database = getWritableDatabase();
|
||||
|
||||
database.beginTransaction();
|
||||
try {
|
||||
MessageExportState oldState = getMessageExportState(messageId);
|
||||
MessageExportState newState = transform.apply(oldState);
|
||||
|
||||
setMessageExportState(messageId, newState);
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public void markMessageExported(@NonNull MessageId messageId) {
|
||||
String table = messageId.isMms() ? MmsDatabase.TABLE_NAME : SmsDatabase.TABLE_NAME;
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
|
||||
contentValues.put(MmsSmsColumns.EXPORTED, 1);
|
||||
|
||||
getWritableDatabase().update(table, contentValues, ID_WHERE, SqlUtil.buildArgs(messageId.getId()));
|
||||
}
|
||||
|
||||
private void setMessageExportState(@NonNull MessageId messageId, @NonNull MessageExportState messageExportState) {
|
||||
String table = messageId.isMms() ? MmsDatabase.TABLE_NAME : SmsDatabase.TABLE_NAME;
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
|
||||
contentValues.put(MmsSmsColumns.EXPORT_STATE, messageExportState.toByteArray());
|
||||
|
||||
getWritableDatabase().update(table, contentValues, ID_WHERE, SqlUtil.buildArgs(messageId.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Unhandled ids
|
||||
*/
|
||||
|
||||
@@ -1141,22 +1141,22 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
Recipient.self().live().refresh()
|
||||
}
|
||||
|
||||
fun updatePhoneNumbers(mapping: Map<String?, String?>) {
|
||||
/**
|
||||
* Takes a mapping of old->new phone numbers and updates the table to match.
|
||||
* Intended to be used to handle changing number formats.
|
||||
*/
|
||||
fun rewritePhoneNumbers(mapping: Map<String, String>) {
|
||||
if (mapping.isEmpty()) return
|
||||
val db = writableDatabase
|
||||
|
||||
db.beginTransaction()
|
||||
try {
|
||||
val query = "$PHONE = ?"
|
||||
for ((key, value) in mapping) {
|
||||
val values = ContentValues().apply {
|
||||
put(PHONE, value)
|
||||
}
|
||||
db.updateWithOnConflict(TABLE_NAME, values, query, arrayOf(key), SQLiteDatabase.CONFLICT_IGNORE)
|
||||
Log.i(TAG, "Rewriting ${mapping.size} phone numbers.")
|
||||
|
||||
writableDatabase.withinTransaction {
|
||||
for ((originalE164, updatedE164) in mapping) {
|
||||
writableDatabase.update(TABLE_NAME)
|
||||
.values(PHONE to updatedE164)
|
||||
.where("$PHONE = ?", originalE164)
|
||||
.run(SQLiteDatabase.CONFLICT_IGNORE)
|
||||
}
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
db.endTransaction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2046,16 +2046,23 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the provided IDs together. The assumption here is that all of the IDs correspond to the local user and have been verified.
|
||||
*/
|
||||
fun linkIdsForSelf(aci: ACI, pni: PNI, e164: String) {
|
||||
getAndPossiblyMergePnp(serviceId = aci, pni = pni, e164 = e164, changeSelf = true, pniVerified = true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Does *not* handle clearing the recipient cache. It is assumed the caller handles this.
|
||||
*/
|
||||
fun updateSelfPhone(e164: String) {
|
||||
fun updateSelfPhone(e164: String, pni: PNI) {
|
||||
val db = writableDatabase
|
||||
|
||||
db.beginTransaction()
|
||||
try {
|
||||
val id = Recipient.self().id
|
||||
val newId = getAndPossiblyMerge(SignalStore.account().requireAci(), e164, changeSelf = true)
|
||||
val newId = getAndPossiblyMergePnp(serviceId = SignalStore.account().requireAci(), pni = pni, e164 = e164, pniVerified = true, changeSelf = true)
|
||||
|
||||
if (id == newId) {
|
||||
Log.i(TAG, "[updateSelfPhone] Phone updated for self")
|
||||
@@ -2068,7 +2075,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
.values(NEEDS_PNI_SIGNATURE to 0)
|
||||
.run()
|
||||
|
||||
SignalDatabase.pendingPniSignatureMessages.deleteAll()
|
||||
pendingPniSignatureMessages.deleteAll()
|
||||
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
@@ -2123,6 +2130,27 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
return results
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives you all of the recipientIds of possibly-registered users (i.e. REGISTERED or UNKNOWN) that can be found by the set of
|
||||
* provided E164s.
|
||||
*/
|
||||
fun getAllPossiblyRegisteredByE164(e164s: Set<String>): Set<RecipientId> {
|
||||
val results: MutableSet<RecipientId> = mutableSetOf()
|
||||
val queries: List<SqlUtil.Query> = SqlUtil.buildCollectionQuery(PHONE, e164s)
|
||||
|
||||
for (query in queries) {
|
||||
readableDatabase.query(TABLE_NAME, arrayOf(ID, REGISTERED), query.where, query.whereArgs, null, null, null).use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
if (RegisteredState.fromId(cursor.requireInt(REGISTERED)) != RegisteredState.NOT_REGISTERED) {
|
||||
results += RecipientId.from(cursor.requireLong(ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
fun setPni(id: RecipientId, pni: PNI) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
@@ -2288,6 +2316,32 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
return ids
|
||||
}
|
||||
|
||||
fun bulkUpdatedRegisteredStatusV2(registered: Set<RecipientId>, unregistered: Collection<RecipientId>) {
|
||||
writableDatabase.withinTransaction {
|
||||
val registeredValues = contentValuesOf(
|
||||
REGISTERED to RegisteredState.REGISTERED.id
|
||||
)
|
||||
|
||||
for (id in registered) {
|
||||
if (update(id, registeredValues)) {
|
||||
setStorageIdIfNotSet(id)
|
||||
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id)
|
||||
}
|
||||
}
|
||||
|
||||
val unregisteredValues = contentValuesOf(
|
||||
REGISTERED to RegisteredState.NOT_REGISTERED.id,
|
||||
STORAGE_SERVICE_ID to null
|
||||
)
|
||||
|
||||
for (id in unregistered) {
|
||||
if (update(id, unregisteredValues)) {
|
||||
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a tuple of (e164, pni, aci) and incorporates it into our database.
|
||||
* It is assumed that we are in a transaction.
|
||||
@@ -2295,7 +2349,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
* @return The [RecipientId] of the resulting recipient.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun processPnpTuple(e164: String?, pni: PNI?, aci: ACI?, pniVerified: Boolean, changeSelf: Boolean = false, pnpEnabled: Boolean = FeatureFlags.phoneNumberPrivacy()): ProcessPnpTupleResult {
|
||||
fun processPnpTuple(e164: String?, pni: PNI?, aci: ACI?, pniVerified: Boolean, changeSelf: Boolean = false): ProcessPnpTupleResult {
|
||||
val changeSet: PnpChangeSet = processPnpTupleToChangeSet(e164, pni, aci, pniVerified, changeSelf)
|
||||
|
||||
val affectedIds: MutableSet<RecipientId> = mutableSetOf()
|
||||
@@ -2321,7 +2375,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
}
|
||||
}
|
||||
|
||||
val finalId: RecipientId = writePnpChangeSetToDisk(changeSet, pnpEnabled, pni)
|
||||
val finalId: RecipientId = writePnpChangeSetToDisk(changeSet, pni)
|
||||
|
||||
return ProcessPnpTupleResult(
|
||||
finalId = finalId,
|
||||
@@ -2334,7 +2388,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun writePnpChangeSetToDisk(changeSet: PnpChangeSet, pnpEnabled: Boolean, inputPni: PNI?): RecipientId {
|
||||
fun writePnpChangeSetToDisk(changeSet: PnpChangeSet, inputPni: PNI?): RecipientId {
|
||||
for (operation in changeSet.operations) {
|
||||
@Exhaustive
|
||||
when (operation) {
|
||||
@@ -2392,18 +2446,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
.run()
|
||||
}
|
||||
is PnpOperation.Merge -> {
|
||||
val primary = getRecord(operation.primaryId)
|
||||
val secondary = getRecord(operation.secondaryId)
|
||||
|
||||
if (primary.serviceId != null && !primary.sidIsPni() && secondary.e164 != null) {
|
||||
merge(operation.primaryId, operation.secondaryId, inputPni)
|
||||
} else {
|
||||
if (!pnpEnabled) {
|
||||
throw AssertionError("This type of merge is not supported in production!")
|
||||
}
|
||||
|
||||
merge(operation.primaryId, operation.secondaryId, inputPni)
|
||||
}
|
||||
merge(operation.primaryId, operation.secondaryId, inputPni)
|
||||
}
|
||||
is PnpOperation.SessionSwitchoverInsert -> {
|
||||
// TODO [pnp]
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.android.mms.pdu_alt.NotificationInd;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import net.zetetic.database.sqlcipher.SQLiteStatement;
|
||||
|
||||
@@ -47,6 +48,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.StoryResult;
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
@@ -66,15 +68,16 @@ import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -131,7 +134,9 @@ public class SmsDatabase extends MessageDatabase {
|
||||
REMOTE_DELETED + " INTEGER DEFAULT 0, " +
|
||||
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||
SERVER_GUID + " TEXT DEFAULT NULL, " +
|
||||
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1);";
|
||||
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1, " +
|
||||
EXPORT_STATE + " BLOB DEFAULT NULL, " +
|
||||
EXPORTED + " INTEGER DEFAULT 0);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
|
||||
@@ -901,6 +906,51 @@ public class SmsDatabase extends MessageDatabase {
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getUnexportedInsecureMessages() {
|
||||
return queryMessages(
|
||||
SqlUtil.appendArg(MESSAGE_PROJECTION, EXPORT_STATE),
|
||||
getInsecureMessageClause() + " AND NOT " + EXPORTED,
|
||||
null,
|
||||
false,
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInsecureMessageCount() {
|
||||
try (Cursor cursor = getWritableDatabase().query(TABLE_NAME, SqlUtil.COUNT, getInsecureMessageClause(), null, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteExportedMessages() {
|
||||
beginTransaction();
|
||||
try {
|
||||
List<Long> threadsToUpdate = new LinkedList<>();
|
||||
try (Cursor cursor = getReadableDatabase().query(TABLE_NAME, THREAD_ID_PROJECTION, EXPORTED + " = ?", SqlUtil.buildArgs(1), THREAD_ID, null, null, null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
threadsToUpdate.add(CursorUtil.requireLong(cursor, THREAD_ID));
|
||||
}
|
||||
}
|
||||
|
||||
getWritableDatabase().delete(TABLE_NAME, EXPORTED + " = ?", SqlUtil.buildArgs(1));
|
||||
|
||||
for (final long threadId : threadsToUpdate) {
|
||||
SignalDatabase.threads().update(threadId, false);
|
||||
}
|
||||
|
||||
setTransactionSuccessful();
|
||||
} finally {
|
||||
endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MessageRecord> getProfileChangeDetailsRecords(long threadId, long afterTimestamp) {
|
||||
String where = THREAD_ID + " = ? AND " + DATE_RECEIVED + " >= ? AND " + TYPE + " = ?";
|
||||
@@ -1541,11 +1591,15 @@ public class SmsDatabase extends MessageDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor queryMessages(@NonNull String where, @NonNull String[] args, boolean reverse, long limit) {
|
||||
private Cursor queryMessages(@NonNull String where, @Nullable String[] args, boolean reverse, long limit) {
|
||||
return queryMessages(MESSAGE_PROJECTION, where, args, reverse, limit);
|
||||
}
|
||||
|
||||
private Cursor queryMessages(@NonNull String[] projection, @NonNull String where, @Nullable String[] args, boolean reverse, long limit) {
|
||||
SQLiteDatabase db = databaseHelper.getSignalReadableDatabase();
|
||||
|
||||
return db.query(TABLE_NAME,
|
||||
MESSAGE_PROJECTION,
|
||||
projection,
|
||||
where,
|
||||
args,
|
||||
null,
|
||||
@@ -1776,7 +1830,7 @@ public class SmsDatabase extends MessageDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Reader implements Closeable {
|
||||
public static class Reader implements MessageDatabase.Reader {
|
||||
|
||||
private final Cursor cursor;
|
||||
private final Context context;
|
||||
@@ -1798,6 +1852,20 @@ public class SmsDatabase extends MessageDatabase {
|
||||
else return cursor.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MessageExportState getMessageExportStateForCurrentRecord() {
|
||||
byte[] messageExportState = CursorUtil.requireBlob(cursor, SmsDatabase.EXPORT_STATE);
|
||||
if (messageExportState == null) {
|
||||
return MessageExportState.getDefaultInstance();
|
||||
}
|
||||
|
||||
try {
|
||||
return MessageExportState.parseFrom(messageExportState);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
return MessageExportState.getDefaultInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public SmsMessageRecord getCurrent() {
|
||||
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
|
||||
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.RECIPIENT_ID));
|
||||
@@ -1853,6 +1921,28 @@ public class SmsDatabase extends MessageDatabase {
|
||||
public void close() {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Iterator<MessageRecord> iterator() {
|
||||
return new ReaderIterator();
|
||||
}
|
||||
|
||||
private class ReaderIterator implements Iterator<MessageRecord> {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return cursor != null && cursor.getCount() != 0 && !cursor.isLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageRecord next() {
|
||||
MessageRecord record = getNext();
|
||||
if (record == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
+23
-2714
File diff suppressed because it is too large
Load Diff
+2711
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -7,7 +7,7 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
* Adding an urgent flag to message envelopes to help with notifications. Need to track flag in
|
||||
* MSL table so can be resent with the correct urgency.
|
||||
*/
|
||||
object UrgentMslFlagMigration : SignalDatabaseMigration {
|
||||
object V150_UrgentMslFlagMigration : SignalDatabaseMigration {
|
||||
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("ALTER TABLE msl_payload ADD COLUMN urgent INTEGER NOT NULL DEFAULT 1")
|
||||
}
|
||||
+2
-2
@@ -13,9 +13,9 @@ import org.thoughtcrime.securesms.util.Base64
|
||||
/**
|
||||
* Performs a check and ensures that MyStory exists at the correct distribution list id and correct distribution id.
|
||||
*/
|
||||
object MyStoryMigration : SignalDatabaseMigration {
|
||||
object V151_MyStoryMigration : SignalDatabaseMigration {
|
||||
|
||||
private val TAG = Log.tag(MyStoryMigration::class.java)
|
||||
private val TAG = Log.tag(V151_MyStoryMigration::class.java)
|
||||
|
||||
private const val TABLE_NAME = "distribution_list"
|
||||
private const val NAME = "name"
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.thoughtcrime.securesms.database.helpers.migration
|
||||
|
||||
import android.app.Application
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
|
||||
/**
|
||||
* Marks story recipients with a new group type constant.
|
||||
*/
|
||||
object V152_StoryGroupTypesMigration : SignalDatabaseMigration {
|
||||
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL(
|
||||
"""
|
||||
UPDATE recipient
|
||||
SET group_type = 4
|
||||
WHERE distribution_list_id IS NOT NULL
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package org.thoughtcrime.securesms.database.helpers.migration
|
||||
|
||||
import android.app.Application
|
||||
import android.database.Cursor
|
||||
import androidx.core.content.contentValuesOf
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
import org.signal.core.util.CursorUtil
|
||||
import org.signal.core.util.SqlUtil
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
|
||||
/**
|
||||
* Performs a check and ensures that MyStory exists at the correct distribution list id and correct distribution id.
|
||||
*/
|
||||
object V153_MyStoryMigration : SignalDatabaseMigration {
|
||||
|
||||
private val TAG = Log.tag(V153_MyStoryMigration::class.java)
|
||||
|
||||
private const val TABLE_NAME = "distribution_list"
|
||||
private const val NAME = "name"
|
||||
private const val DISTRIBUTION_LIST_ID = "_id"
|
||||
private const val DISTRIBUTION_ID = "distribution_id"
|
||||
private const val RECIPIENT_ID = "recipient_id"
|
||||
private const val PRIVACY_MODE = "privacy_mode"
|
||||
private const val MY_STORY_DISTRIBUTION_LIST_ID = 1
|
||||
private const val MY_STORY_DISTRIBUTION_ID = "00000000-0000-0000-0000-000000000000"
|
||||
private const val MY_STORY_PRIVACY_MODE = 2
|
||||
|
||||
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
val result: MyStoryExistsResult = getMyStoryCursor(db).use { cursor ->
|
||||
if (cursor.moveToNext()) {
|
||||
val distributionId = CursorUtil.requireString(cursor, DISTRIBUTION_ID)
|
||||
if (distributionId != MY_STORY_DISTRIBUTION_ID) {
|
||||
Log.d(TAG, "[migrate] Invalid MyStory DistributionId: $distributionId")
|
||||
MyStoryExistsResult.REQUIRES_DISTRIBUTION_ID_UPDATE
|
||||
} else {
|
||||
Log.d(TAG, "[migrate] MyStory DistributionId matches expected value.")
|
||||
MyStoryExistsResult.MATCHES_EXPECTED_VALUE
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "[migrate] My Story does not exist.")
|
||||
MyStoryExistsResult.DOES_NOT_EXIST
|
||||
}
|
||||
}
|
||||
|
||||
when (result) {
|
||||
MyStoryExistsResult.REQUIRES_DISTRIBUTION_ID_UPDATE -> updateDistributionIdToExpectedValue(db)
|
||||
MyStoryExistsResult.MATCHES_EXPECTED_VALUE -> Unit
|
||||
MyStoryExistsResult.DOES_NOT_EXIST -> createMyStory(db)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDistributionIdToExpectedValue(db: SQLiteDatabase) {
|
||||
Log.d(TAG, "[updateDistributionIdToExpectedValue] Overwriting My Story DistributionId with expected value.")
|
||||
db.update(
|
||||
TABLE_NAME,
|
||||
contentValuesOf(DISTRIBUTION_ID to MY_STORY_DISTRIBUTION_ID),
|
||||
"$DISTRIBUTION_LIST_ID = ?",
|
||||
arrayOf(MY_STORY_DISTRIBUTION_LIST_ID.toString())
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMyStory(db: SQLiteDatabase) {
|
||||
Log.d(TAG, "[createMyStory] Attempting to create My Story.")
|
||||
|
||||
val recipientId: Long = getMyStoryRecipientId(db) ?: createMyStoryRecipientId(db)
|
||||
|
||||
db.insert(
|
||||
TABLE_NAME,
|
||||
null,
|
||||
contentValuesOf(
|
||||
DISTRIBUTION_LIST_ID to MY_STORY_DISTRIBUTION_LIST_ID,
|
||||
NAME to MY_STORY_DISTRIBUTION_ID,
|
||||
DISTRIBUTION_ID to MY_STORY_DISTRIBUTION_ID,
|
||||
RECIPIENT_ID to recipientId,
|
||||
PRIVACY_MODE to MY_STORY_PRIVACY_MODE
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMyStoryRecipientId(db: SQLiteDatabase): Long {
|
||||
return db.insert(
|
||||
"recipient",
|
||||
null,
|
||||
contentValuesOf(
|
||||
"group_type" to 4,
|
||||
"distribution_list_id" to MY_STORY_DISTRIBUTION_LIST_ID,
|
||||
"storage_service_key" to Base64.encodeBytes(StorageSyncHelper.generateKey()),
|
||||
"profile_sharing" to 1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getMyStoryRecipientId(db: SQLiteDatabase): Long? {
|
||||
return db.query(
|
||||
"recipient",
|
||||
arrayOf("_id"),
|
||||
"distribution_list_id = ?",
|
||||
SqlUtil.buildArgs(MY_STORY_DISTRIBUTION_LIST_ID),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).use {
|
||||
if (it.moveToNext()) {
|
||||
CursorUtil.requireLong(it, "_id")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMyStoryCursor(db: SQLiteDatabase): Cursor {
|
||||
return db.query(
|
||||
TABLE_NAME,
|
||||
arrayOf(DISTRIBUTION_ID),
|
||||
"$DISTRIBUTION_LIST_ID = ?",
|
||||
arrayOf(MY_STORY_DISTRIBUTION_LIST_ID.toString()),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private enum class MyStoryExistsResult {
|
||||
REQUIRES_DISTRIBUTION_ID_UPDATE,
|
||||
MATCHES_EXPECTED_VALUE,
|
||||
DOES_NOT_EXIST
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
/**
|
||||
* Introduces the tables and fields required to keep track of whether we need to send a PNI signature message and if the ones we've sent out have been received.
|
||||
*/
|
||||
object PniSignaturesMigration : SignalDatabaseMigration {
|
||||
object V154_PniSignaturesMigration : SignalDatabaseMigration {
|
||||
|
||||
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("ALTER TABLE recipient ADD COLUMN needs_pni_signature")
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package org.thoughtcrime.securesms.database.helpers.migration
|
||||
|
||||
import android.app.Application
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
|
||||
/**
|
||||
* Adds necessary book-keeping columns to SMS and MMS tables for SMS export.
|
||||
*/
|
||||
object V155_SmsExporterMigration : SignalDatabaseMigration {
|
||||
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("ALTER TABLE mms ADD COLUMN export_state BLOB DEFAULT NULL")
|
||||
db.execSQL("ALTER TABLE mms ADD COLUMN exported INTEGER DEFAULT 0")
|
||||
db.execSQL("ALTER TABLE sms ADD COLUMN export_state BLOB DEFAULT NULL")
|
||||
db.execSQL("ALTER TABLE sms ADD COLUMN exported INTEGER DEFAULT 0")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package org.thoughtcrime.securesms.exporter
|
||||
|
||||
import android.database.Cursor
|
||||
import org.signal.smsexporter.ExportableMessage
|
||||
import org.signal.smsexporter.SmsExportState
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.JsonUtils
|
||||
import java.io.Closeable
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class SignalSmsExportReader(
|
||||
smsCursor: Cursor,
|
||||
mmsCursor: Cursor
|
||||
) : Iterable<ExportableMessage>, Closeable {
|
||||
|
||||
private val smsReader = SmsDatabase.readerFor(smsCursor)
|
||||
private val mmsReader = MmsDatabase.readerFor(mmsCursor)
|
||||
|
||||
override fun iterator(): Iterator<ExportableMessage> {
|
||||
return ExportableMessageIterator()
|
||||
}
|
||||
|
||||
fun getCount(): Int {
|
||||
return smsReader.count + mmsReader.count
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
smsReader.close()
|
||||
mmsReader.close()
|
||||
}
|
||||
|
||||
private inner class ExportableMessageIterator : Iterator<ExportableMessage> {
|
||||
|
||||
private val smsIterator = smsReader.iterator()
|
||||
private val mmsIterator = mmsReader.iterator()
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return smsIterator.hasNext() || mmsIterator.hasNext()
|
||||
}
|
||||
|
||||
override fun next(): ExportableMessage {
|
||||
return if (smsIterator.hasNext()) {
|
||||
readExportableSmsMessageFromRecord(smsIterator.next())
|
||||
} else if (mmsIterator.hasNext()) {
|
||||
readExportableMmsMessageFromRecord(mmsIterator.next())
|
||||
} else {
|
||||
throw NoSuchElementException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readExportableMmsMessageFromRecord(record: MessageRecord): ExportableMessage {
|
||||
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(record.threadId)!!
|
||||
val addresses = if (threadRecipient.isMmsGroup) {
|
||||
Recipient.resolvedList(threadRecipient.participantIds).map { it.requireSmsAddress() }.toSet()
|
||||
} else {
|
||||
setOf(threadRecipient.requireSmsAddress())
|
||||
}
|
||||
|
||||
val parts: MutableList<ExportableMessage.Mms.Part> = mutableListOf()
|
||||
if (record.body.isNotBlank()) {
|
||||
parts.add(ExportableMessage.Mms.Part.Text(record.body))
|
||||
}
|
||||
|
||||
if (record is MmsMessageRecord) {
|
||||
val slideDeck = record.slideDeck
|
||||
slideDeck.slides.forEach {
|
||||
parts.add(
|
||||
ExportableMessage.Mms.Part.Stream(
|
||||
id = JsonUtils.toJson((it.asAttachment() as DatabaseAttachment).attachmentId),
|
||||
contentType = it.contentType
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val sender = if (record.isOutgoing) Recipient.self().requireSmsAddress() else record.individualRecipient.requireSmsAddress()
|
||||
|
||||
return ExportableMessage.Mms(
|
||||
id = record.id.toString(),
|
||||
exportState = mapExportState(mmsReader.messageExportStateForCurrentRecord),
|
||||
addresses = addresses,
|
||||
dateReceived = record.dateReceived.milliseconds,
|
||||
dateSent = record.dateSent.milliseconds,
|
||||
isRead = true,
|
||||
isOutgoing = record.isOutgoing,
|
||||
parts = parts,
|
||||
sender = sender
|
||||
)
|
||||
}
|
||||
|
||||
private fun readExportableSmsMessageFromRecord(record: MessageRecord): ExportableMessage {
|
||||
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(record.threadId)!!
|
||||
|
||||
return if (threadRecipient.isMmsGroup) {
|
||||
readExportableMmsMessageFromRecord(record)
|
||||
} else {
|
||||
ExportableMessage.Sms(
|
||||
id = record.id.toString(),
|
||||
exportState = mapExportState(smsReader.messageExportStateForCurrentRecord),
|
||||
address = record.recipient.requireSmsAddress(),
|
||||
dateReceived = record.dateReceived.milliseconds,
|
||||
dateSent = record.dateSent.milliseconds,
|
||||
isRead = true,
|
||||
isOutgoing = record.isOutgoing,
|
||||
body = record.body
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapExportState(messageExportState: MessageExportState): SmsExportState {
|
||||
return SmsExportState(
|
||||
messageId = messageExportState.messageId,
|
||||
startedRecipients = messageExportState.startedRecipientsList.toSet(),
|
||||
completedRecipients = messageExportState.completedRecipientsList.toSet(),
|
||||
startedAttachments = messageExportState.startedAttachmentsList.toSet(),
|
||||
completedAttachments = messageExportState.completedAttachmentsList.toSet(),
|
||||
progress = messageExportState.progress.let {
|
||||
when (it) {
|
||||
MessageExportState.Progress.INIT -> SmsExportState.Progress.INIT
|
||||
MessageExportState.Progress.STARTED -> SmsExportState.Progress.STARTED
|
||||
MessageExportState.Progress.COMPLETED -> SmsExportState.Progress.COMPLETED
|
||||
MessageExportState.Progress.UNRECOGNIZED -> SmsExportState.Progress.INIT
|
||||
null -> SmsExportState.Progress.INIT
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package org.thoughtcrime.securesms.exporter
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.signal.smsexporter.ExportableMessage
|
||||
import org.signal.smsexporter.SmsExportService
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||
import org.thoughtcrime.securesms.notifications.NotificationIds
|
||||
import org.thoughtcrime.securesms.util.JsonUtils
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Service which integrates the SMS exporter functionality.
|
||||
*/
|
||||
class SignalSmsExportService : SmsExportService() {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Launches the export service and immediately begins exporting messages.
|
||||
*/
|
||||
fun start(context: Context) {
|
||||
ContextCompat.startForegroundService(context, Intent(context, SignalSmsExportService::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
private var reader: SignalSmsExportReader? = null
|
||||
|
||||
override fun getNotification(progress: Int, total: Int): ExportNotification {
|
||||
return ExportNotification(
|
||||
NotificationIds.SMS_EXPORT_SERVICE,
|
||||
NotificationCompat.Builder(this, NotificationChannels.BACKUPS)
|
||||
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||
.setContentTitle(getString(R.string.SignalSmsExportService__exporting_messages))
|
||||
.setProgress(total, progress, false)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun getUnexportedMessageCount(): Int {
|
||||
ensureReader()
|
||||
return reader!!.getCount()
|
||||
}
|
||||
|
||||
override fun getUnexportedMessages(): Iterable<ExportableMessage> {
|
||||
ensureReader()
|
||||
return reader!!
|
||||
}
|
||||
|
||||
override fun onMessageExportStarted(exportableMessage: ExportableMessage) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().setProgress(MessageExportState.Progress.STARTED).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMessageExportSucceeded(exportableMessage: ExportableMessage) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().setProgress(MessageExportState.Progress.COMPLETED).build()
|
||||
}
|
||||
|
||||
SignalDatabase.mmsSms.markMessageExported(exportableMessage.getMessageId())
|
||||
}
|
||||
|
||||
override fun onMessageExportFailed(exportableMessage: ExportableMessage) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().setProgress(MessageExportState.Progress.INIT).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMessageIdCreated(exportableMessage: ExportableMessage, messageId: Long) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().setMessageId(messageId).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachmentPartExportStarted(exportableMessage: ExportableMessage, part: ExportableMessage.Mms.Part) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().addStartedAttachments(part.contentId).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachmentPartExportSucceeded(exportableMessage: ExportableMessage, part: ExportableMessage.Mms.Part) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().addCompletedAttachments(part.contentId).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachmentPartExportFailed(exportableMessage: ExportableMessage, part: ExportableMessage.Mms.Part) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
val startedAttachments = it.startedAttachmentsList - part.contentId
|
||||
it.toBuilder().clearStartedAttachments().addAllStartedAttachments(startedAttachments).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRecipientExportStarted(exportableMessage: ExportableMessage, recipient: String) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().addStartedRecipients(recipient).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRecipientExportSucceeded(exportableMessage: ExportableMessage, recipient: String) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
it.toBuilder().addCompletedRecipients(recipient).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRecipientExportFailed(exportableMessage: ExportableMessage, recipient: String) {
|
||||
SignalDatabase.mmsSms.updateMessageExportState(exportableMessage.getMessageId()) {
|
||||
val startedAttachments = it.startedRecipientsList - recipient
|
||||
it.toBuilder().clearStartedRecipients().addAllStartedRecipients(startedAttachments).build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getInputStream(part: ExportableMessage.Mms.Part): InputStream {
|
||||
return SignalDatabase.attachments.getAttachmentStream(JsonUtils.fromJson(part.contentId, AttachmentId::class.java), 0)
|
||||
}
|
||||
|
||||
override fun onExportPassCompleted() {
|
||||
reader?.close()
|
||||
}
|
||||
|
||||
private fun ExportableMessage.getMessageId(): MessageId {
|
||||
return when (this) {
|
||||
is ExportableMessage.Mms -> MessageId(id.toLong(), true)
|
||||
is ExportableMessage.Sms -> MessageId(id.toLong(), false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureReader() {
|
||||
if (reader == null) {
|
||||
reader = SignalSmsExportReader(
|
||||
smsCursor = SignalDatabase.sms.unexportedInsecureMessages,
|
||||
mmsCursor = SignalDatabase.mms.unexportedInsecureMessages
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.thoughtcrime.securesms.exporter.flow
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.smsexporter.DefaultSmsHelper
|
||||
import org.signal.smsexporter.ReleaseSmsAppFailure
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.ChooseANewDefaultSmsAppFragmentBinding
|
||||
|
||||
/**
|
||||
* Fragment which can launch the user into picking an alternative
|
||||
* SMS app, or give them instructions on how to do so manually.
|
||||
*/
|
||||
class ChooseANewDefaultSmsAppFragment : Fragment(R.layout.choose_a_new_default_sms_app_fragment) {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(ChooseANewDefaultSmsAppFragment::class.java)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val binding = ChooseANewDefaultSmsAppFragmentBinding.bind(view)
|
||||
|
||||
DefaultSmsHelper.releaseDefaultSms(requireContext()).either(
|
||||
onSuccess = {
|
||||
binding.continueButton.setOnClickListener { _ -> startActivity(it) }
|
||||
},
|
||||
onFailure = {
|
||||
when (it) {
|
||||
ReleaseSmsAppFailure.APP_IS_INELIGIBLE_TO_RELEASE_SMS_SELECTION -> {
|
||||
Log.w(TAG, "App is ineligible to release sms selection")
|
||||
binding.continueButton.setOnClickListener { requireActivity().finish() }
|
||||
}
|
||||
ReleaseSmsAppFailure.NO_METHOD_TO_RELEASE_SMS_AVIALABLE -> {
|
||||
Log.w(TAG, "We can't navigate the user to a specific spot so we should display instructions instead.")
|
||||
binding.continueButton.setOnClickListener { requireActivity().finish() }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (!DefaultSmsHelper.isDefaultSms(requireContext())) {
|
||||
requireActivity().setResult(Activity.RESULT_OK)
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package org.thoughtcrime.securesms.exporter.flow
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.signal.smsexporter.BecomeSmsAppFailure
|
||||
import org.signal.smsexporter.DefaultSmsHelper
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.ExportYourSmsMessagesFragmentBinding
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
* "Welcome" screen for exporting sms
|
||||
*/
|
||||
class ExportYourSmsMessagesFragment : Fragment(R.layout.export_your_sms_messages_fragment) {
|
||||
|
||||
companion object {
|
||||
private val REQUEST_CODE = 1
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val binding = ExportYourSmsMessagesFragmentBinding.bind(view)
|
||||
|
||||
binding.toolbar.setOnClickListener {
|
||||
requireActivity().finish()
|
||||
}
|
||||
|
||||
DefaultSmsHelper.becomeDefaultSms(requireContext()).either(
|
||||
onSuccess = {
|
||||
binding.continueButton.setOnClickListener { _ ->
|
||||
startActivityForResult(it, REQUEST_CODE)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
when (it) {
|
||||
BecomeSmsAppFailure.ALREADY_DEFAULT_SMS -> {
|
||||
binding.continueButton.setOnClickListener {
|
||||
navigateToExporter()
|
||||
}
|
||||
}
|
||||
BecomeSmsAppFailure.ROLE_IS_NOT_AVAILABLE -> {
|
||||
error("Should never happen.")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == REQUEST_CODE && DefaultSmsHelper.isDefaultSms(requireContext())) {
|
||||
navigateToExporter()
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToExporter() {
|
||||
findNavController().safeNavigate(ExportYourSmsMessagesFragmentDirections.actionExportYourSmsMessagesFragmentToExportingSmsMessagesFragment())
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.thoughtcrime.securesms.exporter.flow
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import org.signal.smsexporter.SmsExportProgress
|
||||
import org.signal.smsexporter.SmsExportService
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.ExportingSmsMessagesFragmentBinding
|
||||
import org.thoughtcrime.securesms.exporter.SignalSmsExportService
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
* "Export in progress" fragment which should be displayed
|
||||
* when we start exporting messages.
|
||||
*/
|
||||
class ExportingSmsMessagesFragment : Fragment(R.layout.exporting_sms_messages_fragment) {
|
||||
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val binding = ExportingSmsMessagesFragmentBinding.bind(view)
|
||||
|
||||
lifecycleDisposable.bindTo(viewLifecycleOwner)
|
||||
lifecycleDisposable += SmsExportService.progressState.observeOn(AndroidSchedulers.mainThread()).subscribe {
|
||||
when (it) {
|
||||
SmsExportProgress.Done -> {
|
||||
findNavController().safeNavigate(ExportingSmsMessagesFragmentDirections.actionExportingSmsMessagesFragmentToChooseANewDefaultSmsAppFragment())
|
||||
}
|
||||
is SmsExportProgress.InProgress -> {
|
||||
binding.progress.isIndeterminate = false
|
||||
binding.progress.max = it.total
|
||||
binding.progress.progress = it.progress
|
||||
binding.progressLabel.text = getString(R.string.ExportingSmsMessagesFragment__exporting_d_of_d, it.progress, it.total)
|
||||
}
|
||||
SmsExportProgress.Init -> binding.progress.isIndeterminate = true
|
||||
SmsExportProgress.Starting -> binding.progress.isIndeterminate = true
|
||||
}
|
||||
}
|
||||
|
||||
SignalSmsExportService.start(requireContext())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.thoughtcrime.securesms.exporter.flow
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.FragmentWrapperActivity
|
||||
|
||||
class SmsExportActivity : FragmentWrapperActivity() {
|
||||
override fun getFragment(): Fragment {
|
||||
return NavHostFragment.create(R.navigation.sms_export)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createIntent(context: Context): Intent = Intent(context, SmsExportActivity::class.java)
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.whispersystems.signalservice.api.push.ACI
|
||||
import org.whispersystems.signalservice.api.push.PNI
|
||||
import org.whispersystems.signalservice.api.push.ServiceIds
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import java.lang.IllegalStateException
|
||||
import java.security.SecureRandom
|
||||
|
||||
internal class AccountValues internal constructor(store: KeyValueStore) : SignalStoreValues(store) {
|
||||
@@ -121,6 +122,12 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal
|
||||
val e164: String?
|
||||
get() = getString(KEY_E164, null)
|
||||
|
||||
/** The local user's e164. Will throw if not present. */
|
||||
fun requireE164(): String {
|
||||
val e164: String? = getString(KEY_E164, null)
|
||||
return e164 ?: throw IllegalStateException("No e164!")
|
||||
}
|
||||
|
||||
fun setE164(e164: String) {
|
||||
putString(KEY_E164, e164)
|
||||
}
|
||||
|
||||
@@ -25,10 +25,12 @@ import android.widget.ImageView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.camera.core.AspectRatio;
|
||||
import androidx.camera.core.CameraSelector;
|
||||
import androidx.camera.core.ImageCapture;
|
||||
import androidx.camera.core.ImageCaptureException;
|
||||
import androidx.camera.core.ImageProxy;
|
||||
import androidx.camera.core.Preview;
|
||||
import androidx.camera.view.CameraController;
|
||||
import androidx.camera.view.LifecycleCameraController;
|
||||
import androidx.camera.view.PreviewView;
|
||||
@@ -75,7 +77,8 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
||||
private static final String IS_VIDEO_ENABLED = "is_video_enabled";
|
||||
|
||||
|
||||
private static final Rational ASPECT_RATIO_16_9 = new Rational(16, 9);
|
||||
private static final Rational ASPECT_RATIO_16_9 = new Rational(16, 9);
|
||||
private static final PreviewView.ScaleType PREVIEW_SCALE_TYPE = PreviewView.ScaleType.FILL_CENTER;
|
||||
|
||||
private PreviewView previewView;
|
||||
private ViewGroup controlsContainer;
|
||||
@@ -141,7 +144,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
||||
cameraController.setImageCaptureMode(CameraXUtil.getOptimalCaptureMode());
|
||||
cameraController.setEnabledUseCases(getSupportedUseCases());
|
||||
|
||||
previewView.setScaleType(PreviewView.ScaleType.FIT_CENTER);
|
||||
previewView.setScaleType(PREVIEW_SCALE_TYPE);
|
||||
previewView.setController(cameraController);
|
||||
|
||||
onOrientationChanged(getResources().getConfiguration().orientation);
|
||||
@@ -160,6 +163,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
||||
params.height = (int) height;
|
||||
|
||||
cameraParent.setLayoutParams(params);
|
||||
cameraController.setPreviewTargetSize(new CameraController.OutputSize(new Size((int) width, (int) height)));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -312,7 +316,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
||||
onCaptureClicked();
|
||||
});
|
||||
|
||||
previewView.setScaleType(PreviewView.ScaleType.FILL_CENTER);
|
||||
previewView.setScaleType(PREVIEW_SCALE_TYPE);
|
||||
|
||||
cameraController.getInitializationFuture()
|
||||
.addListener(() -> initializeFlipButton(flipButton, flashButton), Executors.mainThreadExecutor());
|
||||
@@ -454,6 +458,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
||||
selfieFlash
|
||||
);
|
||||
|
||||
flashHelper.onWillTakePicture();
|
||||
cameraController.takePicture(Executors.mainThreadExecutor(), new ImageCapture.OnImageCapturedCallback() {
|
||||
@Override
|
||||
public void onCaptureSuccess(@NonNull ImageProxy image) {
|
||||
|
||||
+12
-1
@@ -23,6 +23,7 @@ final class CameraXSelfieFlashHelper {
|
||||
|
||||
private float brightnessBeforeFlash;
|
||||
private boolean inFlash;
|
||||
private int flashMode = -1;
|
||||
|
||||
CameraXSelfieFlashHelper(@NonNull Window window,
|
||||
@NonNull CameraController camera,
|
||||
@@ -33,6 +34,13 @@ final class CameraXSelfieFlashHelper {
|
||||
this.selfieFlash = selfieFlash;
|
||||
}
|
||||
|
||||
void onWillTakePicture() {
|
||||
if (!inFlash && shouldUseViewBasedFlash()) {
|
||||
flashMode = camera.getImageCaptureFlashMode();
|
||||
camera.setImageCaptureFlashMode(ImageCapture.FLASH_MODE_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
void startFlash() {
|
||||
if (inFlash || !shouldUseViewBasedFlash()) return;
|
||||
inFlash = true;
|
||||
@@ -56,6 +64,9 @@ final class CameraXSelfieFlashHelper {
|
||||
params.screenBrightness = brightnessBeforeFlash;
|
||||
window.setAttributes(params);
|
||||
|
||||
camera.setImageCaptureFlashMode(flashMode);
|
||||
flashMode = -1;
|
||||
|
||||
selfieFlash.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(SELFIE_FLASH_DURATION_MS);
|
||||
@@ -66,7 +77,7 @@ final class CameraXSelfieFlashHelper {
|
||||
private boolean shouldUseViewBasedFlash() {
|
||||
CameraSelector cameraSelector = camera.getCameraSelector() ;
|
||||
|
||||
return camera.getImageCaptureFlashMode() == ImageCapture.FLASH_MODE_ON &&
|
||||
return (camera.getImageCaptureFlashMode() == ImageCapture.FLASH_MODE_ON || flashMode == ImageCapture.FLASH_MODE_ON) &&
|
||||
cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +107,10 @@ public class ApplicationMigrations {
|
||||
static final int EMOJI_SEARCH_INDEX_10 = 63;
|
||||
static final int REFRESH_PNI_REGISTRATION_ID = 64;
|
||||
static final int KBS_MIGRATION_2 = 65;
|
||||
static final int PNI_2 = 66;
|
||||
}
|
||||
|
||||
public static final int CURRENT_VERSION = 65;
|
||||
public static final int CURRENT_VERSION = 66;
|
||||
|
||||
/**
|
||||
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
|
||||
@@ -471,6 +472,10 @@ public class ApplicationMigrations {
|
||||
jobs.put(Version.KBS_MIGRATION_2, new KbsEnclaveMigrationJob());
|
||||
}
|
||||
|
||||
if (lastSeenVersion < Version.PNI_2) {
|
||||
jobs.put(Version.PNI_2, new PniMigrationJob());
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,14 +49,13 @@ public class PniMigrationJob extends MigrationJob {
|
||||
return;
|
||||
}
|
||||
|
||||
RecipientId self = Recipient.self().getId();
|
||||
PNI pni = PNI.parseOrNull(ApplicationDependencies.getSignalServiceAccountManager().getWhoAmI().getPni());
|
||||
PNI pni = PNI.parseOrNull(ApplicationDependencies.getSignalServiceAccountManager().getWhoAmI().getPni());
|
||||
|
||||
if (pni == null) {
|
||||
throw new IOException("Invalid PNI!");
|
||||
}
|
||||
|
||||
SignalDatabase.recipients().setPni(self, pni);
|
||||
SignalDatabase.recipients().linkIdsForSelf(SignalStore.account().requireAci(), pni, SignalStore.account().requireE164());
|
||||
SignalStore.account().setPni(pni);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public final class NotificationIds {
|
||||
public static final int DEVICE_TRANSFER = 625420;
|
||||
public static final int DONOR_BADGE_FAILURE = 630001;
|
||||
public static final int FCM_FETCH = 630002;
|
||||
public static final int SMS_EXPORT_SERVICE = 630003;
|
||||
public static final int STORY_THREAD = 700000;
|
||||
public static final int MESSAGE_DELIVERY_FAILURE = 800000;
|
||||
public static final int STORY_MESSAGE_DELIVERY_FAILURE = 900000;
|
||||
|
||||
@@ -56,12 +56,12 @@ final class MobileCoinTestNetConfig extends MobileCoinConfig {
|
||||
ClientConfig config = new ClientConfig();
|
||||
String[] hardeningAdvisories = { "INTEL-SA-00334", "INTEL-SA-00615" };
|
||||
VerifierFactory verifierFactory = new VerifierFactory(hardeningAdvisories,
|
||||
// ~July 15, 2022
|
||||
// ~August 15, 2022
|
||||
new ServiceConfig(
|
||||
"4f134dcfd9c0885956f2f9af0f05c2050d8bdee2dc63b468a640670d7adeb7f8",
|
||||
"8f2f3bf81f24bf493fa6d76e29e0f081815022592b1e854f95bda750aece7452",
|
||||
"685481b33f2846585f33506ab65649c98a4a6d1244989651fd0fcde904ebd82f",
|
||||
"719ca43abbe02f507bb91ea11ff8bc900aa86363a7d7e77b8130426fc53d8684"
|
||||
"01746f4dd25f8623d603534425ed45833687eca2b3ba25bdd87180b9471dac28",
|
||||
"3e9bf61f3191add7b054f0e591b62f832854606f6594fd63faef1e2aedec4021",
|
||||
"92fb35d0f603ceb5eaf2988b24a41d4a4a83f8fb9cd72e67c3bc37960d864ad6",
|
||||
"3d6e528ee0574ae3299915ea608b71ddd17cbe855d4f5e1c46df9b0d22b04cdb"
|
||||
));
|
||||
|
||||
config.logAdapter = new MobileCoinLogAdapter();
|
||||
|
||||
@@ -54,13 +54,17 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
public final class Wallet {
|
||||
|
||||
private static final String TAG = Log.tag(Wallet.class);
|
||||
private static final String TAG = Log.tag(Wallet.class);
|
||||
private static final Object LEDGER_LOCK = new Object();
|
||||
|
||||
private final MobileCoinConfig mobileCoinConfig;
|
||||
private final MobileCoinClient mobileCoinClient;
|
||||
private final AccountKey account;
|
||||
private final MobileCoinPublicAddress publicAddress;
|
||||
|
||||
private AccountSnapshot cachedAccountSnapshot;
|
||||
private Amount cachedMinimumTxFee;
|
||||
|
||||
public Wallet(@NonNull MobileCoinConfig mobileCoinConfig, @NonNull Entropy paymentsEntropy) {
|
||||
this.mobileCoinConfig = mobileCoinConfig;
|
||||
try {
|
||||
@@ -122,6 +126,12 @@ public final class Wallet {
|
||||
return getCachedLedger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a user owned ledger
|
||||
* @param minimumBlockIndex require the returned ledger to include all TxOuts to at least minimumBlockIndex
|
||||
* @return a wrapped MobileCoin ledger that contains only TxOuts owned by the AccountKey
|
||||
* or null if the requested minimumBlockIndex cannot be retrieved
|
||||
*/
|
||||
@WorkerThread
|
||||
public @Nullable MobileCoinLedgerWrapper tryGetFullLedger(@Nullable Long minimumBlockIndex) throws IOException, FogSyncException {
|
||||
try {
|
||||
@@ -130,8 +140,16 @@ public final class Wallet {
|
||||
long highestBlockTimeStamp = 0;
|
||||
UnsignedLong highestBlockIndex = UnsignedLong.ZERO;
|
||||
final long asOfTimestamp = System.currentTimeMillis();
|
||||
AccountSnapshot accountSnapshot = mobileCoinClient.getAccountSnapshot();
|
||||
final Amount minimumTxFee = mobileCoinClient.getOrFetchMinimumTxFee(TokenId.MOB);
|
||||
Amount minimumTxFee;
|
||||
AccountSnapshot accountSnapshot;
|
||||
|
||||
synchronized (LEDGER_LOCK) {
|
||||
minimumTxFee = mobileCoinClient.getOrFetchMinimumTxFee(TokenId.MOB);
|
||||
accountSnapshot = mobileCoinClient.getAccountSnapshot();
|
||||
|
||||
cachedMinimumTxFee = minimumTxFee;
|
||||
cachedAccountSnapshot = accountSnapshot;
|
||||
}
|
||||
|
||||
if (minimumBlockIndex != null) {
|
||||
long snapshotBlockIndex = accountSnapshot.getBlockIndex().longValue();
|
||||
@@ -212,8 +230,14 @@ public final class Wallet {
|
||||
@WorkerThread
|
||||
public @NonNull Money.MobileCoin getFee(@NonNull Money.MobileCoin amount) throws IOException {
|
||||
try {
|
||||
BigInteger picoMob = amount.requireMobileCoin().toPicoMobBigInteger();
|
||||
return Money.picoMobileCoin(mobileCoinClient.estimateTotalFee(Amount.ofMOB(picoMob)).getValue());
|
||||
BigInteger picoMob = amount.requireMobileCoin().toPicoMobBigInteger();
|
||||
AccountSnapshot accountSnapshot = getCachedAccountSnapshot();
|
||||
Amount minimumFee = getCachedMinimumTxFee();
|
||||
if (accountSnapshot != null && minimumFee != null) {
|
||||
return Money.picoMobileCoin(accountSnapshot.estimateTotalFee(Amount.ofMOB(picoMob), minimumFee).getValue());
|
||||
} else {
|
||||
return Money.picoMobileCoin(mobileCoinClient.estimateTotalFee(Amount.ofMOB(picoMob)).getValue());
|
||||
}
|
||||
} catch (InvalidFogResponse | AttestationException | InsufficientFundsException e) {
|
||||
Log.w(TAG, "Failed to get fee", e);
|
||||
return Money.MobileCoin.ZERO;
|
||||
@@ -238,9 +262,7 @@ public final class Wallet {
|
||||
try {
|
||||
PaymentTransactionId.MobileCoin mobcoinTransaction = (PaymentTransactionId.MobileCoin) transactionId;
|
||||
Transaction transaction = Transaction.fromBytes(mobcoinTransaction.getTransaction());
|
||||
Transaction.Status status = mobileCoinClient.getAccountSnapshot()
|
||||
.getTransactionStatus(transaction);
|
||||
|
||||
Transaction.Status status = mobileCoinClient.getTransactionStatusQuick(transaction);
|
||||
switch (status) {
|
||||
case UNKNOWN:
|
||||
Log.w(TAG, "Unknown sent Transaction Status");
|
||||
@@ -252,10 +274,10 @@ public final class Wallet {
|
||||
default:
|
||||
throw new IllegalStateException("Unknown Transaction Status: " + status);
|
||||
}
|
||||
} catch (SerializationException | InvalidFogResponse e) {
|
||||
} catch (SerializationException e) {
|
||||
Log.w(TAG, e);
|
||||
return TransactionStatusResult.failed();
|
||||
} catch (NetworkException | AttestationException e) {
|
||||
} catch (NetworkException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
@@ -324,10 +346,18 @@ public final class Wallet {
|
||||
}
|
||||
|
||||
try {
|
||||
pendingTransaction = mobileCoinClient.prepareTransaction(to.getAddress(),
|
||||
Amount.ofMOB(picoMob),
|
||||
Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()),
|
||||
TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account));
|
||||
AccountSnapshot accountSnapshot = getCachedAccountSnapshot();
|
||||
if (accountSnapshot != null) {
|
||||
pendingTransaction = accountSnapshot.prepareTransaction(to.getAddress(),
|
||||
Amount.ofMOB(picoMob),
|
||||
Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()),
|
||||
TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account));
|
||||
} else {
|
||||
pendingTransaction = mobileCoinClient.prepareTransaction(to.getAddress(),
|
||||
Amount.ofMOB(picoMob),
|
||||
Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()),
|
||||
TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account));
|
||||
}
|
||||
} catch (InsufficientFundsException e) {
|
||||
Log.w(TAG, "Insufficient funds", e);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, false));
|
||||
@@ -408,6 +438,30 @@ public final class Wallet {
|
||||
getFullLedger();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return cached account snapshot or null if it's not available
|
||||
* @apiNote This method is synchronized with {@link #tryGetFullLedger}
|
||||
* to wait for an updated value if ledger update is in progress.
|
||||
*/
|
||||
@WorkerThread
|
||||
private @Nullable AccountSnapshot getCachedAccountSnapshot() {
|
||||
synchronized (LEDGER_LOCK) {
|
||||
return cachedAccountSnapshot;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return cached minimum transaction fee or null if it's not available
|
||||
* @apiNote This method is synchronized with {@link #tryGetFullLedger}
|
||||
* to wait for an updated value if ledger update is in progress.
|
||||
*/
|
||||
@WorkerThread
|
||||
private @Nullable Amount getCachedMinimumTxFee() {
|
||||
synchronized (LEDGER_LOCK) {
|
||||
return cachedMinimumTxFee;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TransactionStatus {
|
||||
COMPLETE,
|
||||
IN_PROGRESS,
|
||||
|
||||
+2
@@ -144,6 +144,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
activity.startActivity(StoryViewerActivity.createIntent(
|
||||
activity,
|
||||
new StoryViewerArgs.Builder(recipientDialogRepository.getRecipientId(), recipient.getValue().shouldHideStory())
|
||||
.isFromQuote(true)
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
@@ -183,6 +184,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
activity.startActivity(StoryViewerActivity.createIntent(
|
||||
activity,
|
||||
new StoryViewerArgs.Builder(recipientDialogRepository.getRecipientId(), recipient.getValue().shouldHideStory())
|
||||
.isFromQuote(true)
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -159,11 +159,11 @@ public final class RegistrationRepository {
|
||||
}
|
||||
|
||||
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
|
||||
RecipientId selfId = Recipient.externalPush(new SignalServiceAddress(aci, registrationData.getE164())).getId();
|
||||
RecipientId selfId = Recipient.trustedPush(aci, pni, registrationData.getE164()).getId();
|
||||
|
||||
recipientDatabase.setProfileSharing(selfId, true);
|
||||
recipientDatabase.markRegisteredOrThrow(selfId, aci);
|
||||
recipientDatabase.setPni(selfId, pni);
|
||||
recipientDatabase.linkIdsForSelf(aci, pni, registrationData.getE164());
|
||||
recipientDatabase.setProfileKey(selfId, registrationData.getProfileKey());
|
||||
|
||||
ApplicationDependencies.getRecipientCache().clearSelf();
|
||||
|
||||
+1
-1
@@ -199,7 +199,7 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor {
|
||||
}
|
||||
|
||||
try {
|
||||
currentState.getCallInfoState().requireGroupCall().requestVideo(resolutionRequests);
|
||||
currentState.getCallInfoState().requireGroupCall().requestVideo(resolutionRequests, 0);
|
||||
} catch (CallException e) {
|
||||
return groupCallFailure(currentState, "Unable to set rendered resolutions", e);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ object Stories {
|
||||
ApplicationDependencies.getJobManager().add(job)
|
||||
}
|
||||
|
||||
if (record.hasLinkPreview()) {
|
||||
if (record.hasLinkPreview() && record.linkPreviews[0].attachmentId != null) {
|
||||
ApplicationDependencies.getJobManager().add(
|
||||
AttachmentDownloadJob(record.id, record.linkPreviews[0].attachmentId, true)
|
||||
)
|
||||
|
||||
@@ -197,7 +197,7 @@ object StoriesLandingItem {
|
||||
else -> model.data.storyRecipient.getDisplayName(context)
|
||||
}
|
||||
|
||||
icon.visible = model.data.hasReplies || model.data.hasRepliesFromSelf
|
||||
icon.visible = (model.data.hasReplies || model.data.hasRepliesFromSelf) && !model.data.storyRecipient.isMyStory
|
||||
icon.setImageResource(
|
||||
when {
|
||||
model.data.hasReplies -> R.drawable.ic_messages_solid_20
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
|
||||
return configure {
|
||||
sectionHeaderPref(R.string.MyStorySettingsFragment__who_can_see_this_story)
|
||||
sectionHeaderPref(R.string.MyStorySettingsFragment__who_can_view_this_story)
|
||||
customPref(
|
||||
PrivateStoryItem.AddViewerModel(
|
||||
onClick = {
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ class MyStorySettingsFragment : DSLSettingsFragment(
|
||||
|
||||
private fun getConfiguration(state: MyStorySettingsState): DSLConfiguration {
|
||||
return configure {
|
||||
sectionHeaderPref(R.string.MyStorySettingsFragment__who_can_see_this_story)
|
||||
sectionHeaderPref(R.string.MyStorySettingsFragment__who_can_view_this_story)
|
||||
|
||||
radioPref(
|
||||
title = DSLSettingsText.from(R.string.MyStorySettingsFragment__all_signal_connections),
|
||||
@@ -64,7 +64,7 @@ class MyStorySettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
|
||||
radioPref(
|
||||
title = DSLSettingsText.from(R.string.MyStorySettingsFragment__all_signal_connections_except),
|
||||
title = DSLSettingsText.from(R.string.MyStorySettingsFragment__all_except),
|
||||
summary = exceptText,
|
||||
isChecked = state.myStoryPrivacyState.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT,
|
||||
onClick = {
|
||||
|
||||
@@ -102,6 +102,8 @@ public final class FeatureFlags {
|
||||
private static final String CAMERAX_MODEL_BLOCKLIST = "android.cameraXModelBlockList";
|
||||
private static final String RECIPIENT_MERGE_V2 = "android.recipientMergeV2";
|
||||
private static final String CDS_V2_LOAD_TEST = "android.cdsV2LoadTest";
|
||||
private static final String SMS_EXPORTER = "android.sms.exporter";
|
||||
private static final String CDS_V2_COMPAT = "android.cdsV2Compat";
|
||||
|
||||
/**
|
||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||
@@ -156,7 +158,9 @@ public final class FeatureFlags {
|
||||
TELECOM_MODEL_BLOCKLIST,
|
||||
CAMERAX_MODEL_BLOCKLIST,
|
||||
RECIPIENT_MERGE_V2,
|
||||
CDS_V2_LOAD_TEST
|
||||
CDS_V2_LOAD_TEST,
|
||||
SMS_EXPORTER,
|
||||
CDS_V2_COMPAT
|
||||
);
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -220,7 +224,8 @@ public final class FeatureFlags {
|
||||
TELECOM_MODEL_BLOCKLIST,
|
||||
CAMERAX_MODEL_BLOCKLIST,
|
||||
RECIPIENT_MERGE_V2,
|
||||
CDS_V2_LOAD_TEST
|
||||
CDS_V2_LOAD_TEST,
|
||||
CDS_V2_COMPAT
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -555,6 +560,23 @@ public final class FeatureFlags {
|
||||
return getBoolean(CDS_V2_LOAD_TEST, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not we should enable the SMS exporter
|
||||
*
|
||||
* WARNING: This feature is under active development and is off for a reason. The exporter writes messages out to your
|
||||
* system SMS / MMS database, and hasn't been adequately tested for public use. Don't enable this. You've been warned.
|
||||
*/
|
||||
public static boolean smsExporter() {
|
||||
return getBoolean(SMS_EXPORTER, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not we should use CDSv2 with the compat flag on as our primary CDS.
|
||||
*/
|
||||
public static boolean cdsV2Compat() {
|
||||
return getBoolean(CDS_V2_COMPAT, false);
|
||||
}
|
||||
|
||||
/** Only for rendering debug info. */
|
||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||
return new TreeMap<>(REMOTE_VALUES);
|
||||
|
||||
@@ -131,13 +131,16 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
||||
final View child = parent.getChildAt(translatedChildPosition(parent, layoutPos));
|
||||
|
||||
final int adapterPos = parent.getChildAdapterPosition(child);
|
||||
if (adapterPos == RecyclerView.NO_POSITION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final long key = adapter.getHeaderId(adapterPos);
|
||||
if (key == StickyHeaderAdapter.NO_HEADER_ID) {
|
||||
start = layoutPos + 1;
|
||||
}
|
||||
|
||||
if (adapterPos != RecyclerView.NO_POSITION && ((layoutPos == start && sticky) || hasHeader(parent, adapter, adapterPos))) {
|
||||
if (((layoutPos == start && sticky) || hasHeader(parent, adapter, adapterPos))) {
|
||||
View header = getHeader(parent, adapter, adapterPos).itemView;
|
||||
c.save();
|
||||
final int left = parent.getLeft();
|
||||
|
||||
@@ -246,3 +246,19 @@ message PendingChangeNumberMetadata {
|
||||
int32 pniRegistrationId = 3;
|
||||
int32 pniSignedPreKeyId = 4;
|
||||
}
|
||||
|
||||
message MessageExportState {
|
||||
|
||||
enum Progress {
|
||||
INIT = 0;
|
||||
STARTED = 1;
|
||||
COMPLETED = 2;
|
||||
}
|
||||
|
||||
int64 messageId = 1;
|
||||
repeated string startedRecipients = 2;
|
||||
repeated string completedRecipients = 3;
|
||||
repeated string startedAttachments = 4;
|
||||
repeated string completedAttachments = 5;
|
||||
Progress progress = 6;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/camerax_camera_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
app:cardCornerRadius="18dp"
|
||||
app:cardElevation="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/hero"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="84dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/ChooseANewDefaultSmsAppFragment__choose_a_new"
|
||||
android:textAppearance="@style/Signal.Text.HeadlineLarge"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/hero" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintTop_toBottomOf="@id/headline"
|
||||
tools:text="An explanation about choosing a new default sms app. This will remove SMS conversations from Signal." />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/continue_button"
|
||||
style="@style/Signal.Widget.Button.Large.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="44dp"
|
||||
android:minWidth="221dp"
|
||||
android:text="@string/ChooseANewDefaultSmsAppFragment__continue"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/description"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -92,7 +92,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="@string/ChooseInitialMyStoryMembershipFragment__all_signal_connections_except"
|
||||
android:text="@string/ChooseInitialMyStoryMembershipFragment__all_except"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/choose_initial_my_story_all_signal_connnections_except_count"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:minHeight="64dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_arrow_left_24" />
|
||||
|
||||
<!-- TODO [alex] - Image -->
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/ExportYourSmsMessagesFragment__export_your_sms_messages"
|
||||
android:textAppearance="@style/Signal.Text.HeadlineLarge"
|
||||
app:layout_constraintTop_toBottomOf="@id/image" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
tools:text="WIP Placeholder text"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
app:layout_constraintTop_toBottomOf="@id/headline" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/continue_button"
|
||||
style="@style/Signal.Widget.Button.Large.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="44dp"
|
||||
android:minWidth="221dp"
|
||||
android:text="@string/ExportYourSmsMessagesFragment__continue"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/description"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/ExportingSmsMessagesFragment__exporting_sms_messages"
|
||||
android:textAppearance="@style/Signal.Text.HeadlineLarge"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- TODO [alex] - Final text -->
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
tools:text="Please don't close the app..."
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/headline" />
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_max="260dp" />
|
||||
|
||||
<TextView
|
||||
app:layout_constraintTop_toBottomOf="@id/progress"
|
||||
android:gravity="center"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="11dp"
|
||||
tools:text="Exporting 5 of 264..."
|
||||
android:id="@+id/progress_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/Signal.Text.BodySmall"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_blur_hash"
|
||||
@@ -15,86 +15,104 @@
|
||||
android:id="@+id/edu_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@color/transparent_black_70"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_tap_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_tap_label"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_tap_72"
|
||||
app:tint="@color/core_white" />
|
||||
android:src="@color/transparent_black_70" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_tap_label"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/button_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__tap_to_advance"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_tap_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_tap_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_tap_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_swipe_up_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_swipe_up_label"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_tap_icon"
|
||||
app:srcCompat="@drawable/ic_swipe_up_72"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_up_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_up_to_skip"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_swipe_up_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_swipe_right_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_got_it"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_swipe_right_label"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_up_icon"
|
||||
app:srcCompat="@drawable/ic_swipe_right_72"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_right_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_right_to_exit"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_swipe_right_icon" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_tap_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_tap_label"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_tap_72"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_tap_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__tap_to_advance"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_tap_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_tap_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_tap_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_swipe_up_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_swipe_up_label"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_tap_icon"
|
||||
app:srcCompat="@drawable/ic_swipe_up_72"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_up_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_up_to_skip"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_swipe_up_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_swipe_right_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_swipe_right_label"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_up_icon"
|
||||
app:srcCompat="@drawable/ic_swipe_right_72"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_right_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_right_to_exit"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_swipe_right_icon" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/edu_got_it"
|
||||
@@ -107,6 +125,6 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_right_icon" />
|
||||
app:layout_constraintTop_toBottomOf="@id/button_container" />
|
||||
|
||||
</merge>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/sms_export"
|
||||
app:startDestination="@id/exportYourSmsMessagesFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/exportYourSmsMessagesFragment"
|
||||
android:name="org.thoughtcrime.securesms.exporter.flow.ExportYourSmsMessagesFragment"
|
||||
android:label="fragment_export_your_sms_messages"
|
||||
tools:layout="@layout/export_your_sms_messages_fragment">
|
||||
<action
|
||||
android:id="@+id/action_exportYourSmsMessagesFragment_to_exportingSmsMessagesFragment"
|
||||
app:destination="@id/exportingSmsMessagesFragment"
|
||||
app:enterAnim="@anim/fragment_open_enter"
|
||||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/exportingSmsMessagesFragment"
|
||||
android:name="org.thoughtcrime.securesms.exporter.flow.ExportingSmsMessagesFragment"
|
||||
android:label="fragment_exporting_sms_messages"
|
||||
tools:layout="@layout/exporting_sms_messages_fragment">
|
||||
<action
|
||||
android:id="@+id/action_exportingSmsMessagesFragment_to_chooseANewDefaultSmsAppFragment"
|
||||
app:destination="@id/chooseANewDefaultSmsAppFragment"
|
||||
app:enterAnim="@anim/fragment_open_enter"
|
||||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/chooseANewDefaultSmsAppFragment"
|
||||
android:name="org.thoughtcrime.securesms.exporter.flow.ChooseANewDefaultSmsAppFragment"
|
||||
android:label="fragment_choose_a_new_default_sms_app"
|
||||
tools:layout="@layout/choose_a_new_default_sms_app_fragment" />
|
||||
|
||||
</navigation>
|
||||
@@ -4059,7 +4059,7 @@ Tik op + om iets by jou storie te voeg.</string>
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">My Storie</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Wie kan hierdie storie sien?</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Wie kan hierdie storie sien?</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Versteek storie vir</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4067,7 +4067,7 @@ Tik op + om iets by jou storie te voeg.</string>
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Deel met alle verbindings</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Alle Signal-verbindings behalwe…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Alle Signal-verbindings behalwe…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Versteek jou storie van spesifieke persone</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4398,7 +4398,7 @@ Tik op + om iets by jou storie te voeg.</string>
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Alle Signal-verbindings</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Alle Signal-verbindings behalwe…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Alle Signal-verbindings behalwe…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Deel slegs met…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4475,7 +4475,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">قصتي</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">من يمكنه رؤية هذه القصة</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">من يمكنه رؤية هذه القصة</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">إخفاء القصة عن</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4483,7 +4483,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">المشاركة مع جميع جهات الاتصال</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">جميع جهات الاتصال في Signal إلا…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">جميع جهات الاتصال في Signal إلا…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">إخفاء قصتك من أناس مُحددين</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4850,7 +4850,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">كل معارف Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">كل معارف Signal إلا…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">كل معارف Signal إلا…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">مشاركة فقط مع…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<string name="ApplicationPreferencesActivity_Off">Bağlı</string>
|
||||
<string name="ApplicationPreferencesActivity_sms_mms_summary">SMS %1$s, MMS %2$s</string>
|
||||
<string name="ApplicationPreferencesActivity_privacy_summary">Ekran kilidi %1$s, Qeydiyyat kilidi %2$s</string>
|
||||
<string name="ApplicationPreferencesActivity_appearance_summary">Tema%1$s, Dil %2$s</string>
|
||||
<string name="ApplicationPreferencesActivity_appearance_summary">Tema %1$s, Dil %2$s</string>
|
||||
<string name="ApplicationPreferencesActivity_pins_are_required_for_registration_lock">PIN-lər qeydiyyat kilidi üçün tələb olunur. PIN-ləri sıradan çıxartmaq üçün əvvəlcə zəhmət olmasa qeydiyyat kilidini sıradan çıxardın.</string>
|
||||
<string name="ApplicationPreferencesActivity_pin_created">PIN yaradıldı.</string>
|
||||
<string name="ApplicationPreferencesActivity_pin_disabled">PIN sıradan çıxarıldı.</string>
|
||||
@@ -4060,7 +4060,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Hekayəm</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Bu hekayəni kim görə bilər</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Bu hekayəni kim görə bilər</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Hekayəni gizlət</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4068,7 +4068,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Bütün bağlantılarla paylaşın</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Aşağıdakılar xaricində bütün Signal bağlantıları…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Aşağıdakılar xaricində bütün Signal bağlantıları…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Hekayənizi bəzi insanlardan gizlədin</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4407,7 +4407,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Bütün Signal bağlantıları</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Aşağıdakılar xaricində bütün Signal bağlantıları…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Aşağıdakılar xaricində bütün Signal bağlantıları…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Yalnız bunlarla paylaşın…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4057,7 +4057,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Моята история</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Кой може да вижда тази история</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Кой може да вижда тази история</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Скриване на историята от</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4065,7 +4065,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Споделяне с всички връзки</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Всички връзки в Signal освен…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Всички връзки в Signal освен…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Скрийте историята си от определени хора</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4396,7 +4396,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Всички връзки в Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Всички връзки в Signal освен…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Всички връзки в Signal освен…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Споделяне само с…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4055,7 +4055,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">আমার স্টোরি</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">যারা স্টোরি দেখতে পাবেন</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">যারা স্টোরি দেখতে পাবেন</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">যাদের কাছ থেকে স্টোরি লুকাবেন</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4063,7 +4063,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">সকল কানেকশনের সাথে শেয়ার করুন</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">…ছাড়া সকল Signal কানেকশন</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">…ছাড়া সকল Signal কানেকশন</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">কিছু নির্দিষ্ট ব্যক্তির কাছ থেকে আপনার স্টোরি লুকান</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4402,7 +4402,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">সকল Signal কানেকশন</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">…ছাড়া সকল Signal কানেকশন</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">…ছাড়া সকল Signal কানেকশন</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">শেয়ার করুন শুধু…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4172,7 +4172,7 @@ prenijeli račun na svoj novi Android uređaj.</string>
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Moja priča</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Ko može vidjeti ovu priču</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Ko može vidjeti ovu priču</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Sakrij priču od</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4180,7 +4180,7 @@ prenijeli račun na svoj novi Android uređaj.</string>
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Dijelite sa svim vezama</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Sve Signal veze osim…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Sve Signal veze osim…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Sakrijte priču od određenih osoba</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4520,7 +4520,7 @@ prenijeli račun na svoj novi Android uređaj.</string>
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Sve Signal veze</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Sve Signal veze izuzev…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Sve Signal veze izuzev…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Dijeli samo sa…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4054,7 +4054,7 @@ S\'ha rebut un missatge d\'intercanvi de claus per a una versió del protocol no
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">La meva història</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Qui pot veure aquesta història</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Qui pot veure aquesta història</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Amaga la història de</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4062,7 +4062,7 @@ S\'ha rebut un missatge d\'intercanvi de claus per a una versió del protocol no
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Compartir amb tots els contactes</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Tots els contactes de Signal menys…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Tots els contactes de Signal menys…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Oculta la teva història a persones en concret</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4393,7 +4393,7 @@ S\'ha rebut un missatge d\'intercanvi de claus per a una versió del protocol no
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Totes les connexions de Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Totes les connexions de Signal menys…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Totes les connexions de Signal menys…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Compartir només amb…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4273,7 +4273,7 @@ Obdržen požadavek na výměnu klíčů pro neplatnou verzi protokolu.
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Můj příběh</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Kdo může zobrazit tento příběh</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Kdo může zobrazit tento příběh</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Skrýt příběhy od</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4281,7 +4281,7 @@ Obdržen požadavek na výměnu klíčů pro neplatnou verzi protokolu.
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Sdílet se všemi spojeními</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Všechna spojení Signal kromě…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Všechna spojení Signal kromě…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Skryjte svůj příběh před konkrétními lidmi</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4642,7 +4642,7 @@ Obdržen požadavek na výměnu klíčů pro neplatnou verzi protokolu.
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Všechna spojení Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Všechna spojení Signal kromě…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Všechna spojení Signal kromě…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Sdílet jen s…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4202,7 +4202,7 @@ Anfon neges heb ei diogelu?</string>
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Fy Stori</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Pwy all weld y stori hon</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Pwy all weld y stori hon</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Cuddio\'r stori rhag</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
|
||||
@@ -430,7 +430,7 @@
|
||||
<string name="CreateProfileActivity_error_setting_profile_photo">Fejl opstået ved oprettelse af profilbilledet</string>
|
||||
<string name="CreateProfileActivity_problem_setting_profile">Fejl opstod ved oprettelse af profil</string>
|
||||
<string name="CreateProfileActivity_set_up_your_profile">Konfigurer din profil</string>
|
||||
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Din profil og ændringerne vil være synlige for folk du skriver til, dine kontakter og dine grupper.</string>
|
||||
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Din profil og ændringerne vil være synlige for folk, du skriver til, dine kontakter og dine grupper.</string>
|
||||
<string name="CreateProfileActivity_set_avatar_description">Vælg avatar</string>
|
||||
<!--ProfileCreateFragment-->
|
||||
<!--Displayed at the top of the screen and explains how profiles can be viewed.-->
|
||||
@@ -2407,9 +2407,9 @@ nummer (%s) er ugyldigt</string>
|
||||
<string name="PaymentsHomeFragment__view_mobile_coin_terms">Se vilkår for MobileCoin</string>
|
||||
<string name="PaymentsHomeFragment__payments_not_available">Betalinger i Signal er ikke længere tilgængelige. Du kan stadig overføre penge til en børs, men du kan ikke længere sende og modtage betalinger eller tilføje midler.</string>
|
||||
<!--Alert dialog title which shows up after a payment to turn on payment lock-->
|
||||
<string name="PaymentsHomeFragment__turn_on">Slå Betalingslås til for fremtidige overførsler?</string>
|
||||
<string name="PaymentsHomeFragment__turn_on">Slå betalingslås til for fremtidige overførsler?</string>
|
||||
<!--Alert dialog description for why payment lock should be enabled before sending payments-->
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">Tilføj yderligere sikkerhed, og bed Android om at påkræve skærmlås eller fingeraftryk for at overføre penge.</string>
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">Tilføj yderligere sikkerhed, og bed Android om at kræve skærmlås eller fingeraftryk for at overføre penge.</string>
|
||||
<!--Alert dialog button to enable payment lock-->
|
||||
<string name="PaymentsHomeFragment__enable">Aktivér</string>
|
||||
<!--Alert dialog button to not enable payment lock for now-->
|
||||
@@ -2499,7 +2499,7 @@ nummer (%s) er ugyldigt</string>
|
||||
<!--Title of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__failed_to_show_payment_lock">Kunne ikke vise betalingslåsen</string>
|
||||
<!--Body of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">Du har slået betalingslås til i dine indstillinger, men vi kan ikke vise den.</string>
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">Du har slået betalingslås til under dine indstillinger, men vi kan ikke vise den.</string>
|
||||
<!--Button in a dialog that will take the user to the privacy settings-->
|
||||
<string name="ConfirmPaymentFragment__go_to_settings">Gå til indstillinger</string>
|
||||
<string name="ConfirmPaymentFragment__this_person_has_not_activated_payments">Denne person har ikke aktiveret betalinger</string>
|
||||
@@ -3321,13 +3321,13 @@ nummer (%s) er ugyldigt</string>
|
||||
<string name="PrivacySettingsFragment__set_a_default_disappearing_message_timer_for_all_new_chats_started_by_you">Indstil en udløbstid for forsvindende beskeder, der skal gælde som standard for alle nye samtaler startet af dig.</string>
|
||||
<!--Summary for stories preference to launch into story privacy settings-->
|
||||
<string name="PrivacySettingsFragment__manage_your_stories">Administrér dine historier og hvem, der kan se dem</string>
|
||||
<string name="PrivacySettingsFragment__payment_lock_require_lock">Bed Android om at påkræve skærmlås eller fingeraftryk for at overføre penge</string>
|
||||
<string name="PrivacySettingsFragment__payment_lock_require_lock">Bed Android om at kræve skærmlås eller fingeraftryk for at overføre penge</string>
|
||||
<!--Alert dialog title when payment lock cannot be enabled-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_title">Kunne ikke slå betalingslås til</string>
|
||||
<!--Alert dialog description to setup screen lock or fingerprint in phone settings-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_description">For at bruge Betalingslås, skal du først slå skærmlås eller fingeraftryk til i din telefons indstillinger.</string>
|
||||
<string name="PrivacySettingsFragment__cant_enable_description">For at bruge betalingslås skal du først slå skærmlås eller fingeraftryk til under din telefons indstillinger.</string>
|
||||
<!--Shown in a toast when we can\'t navigate to the user\'s system fingerprint settings-->
|
||||
<string name="PrivacySettingsFragment__failed_to_navigate_to_system_settings">Kunne ikke navigere systemets indstillinger</string>
|
||||
<string name="PrivacySettingsFragment__failed_to_navigate_to_system_settings">Kunne ikke gå til systemindstillinger</string>
|
||||
<!--Alert dialog button to go to phone settings-->
|
||||
<string name="PrivacySettingsFragment__go_to_settings">Gå til indstillinger</string>
|
||||
<!--Alert dialog button to cancel the dialog-->
|
||||
@@ -3726,7 +3726,7 @@ nummer (%s) er ugyldigt</string>
|
||||
<!--Displayed as a dialog title when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__cant_send_gift">Gave kan ikke sendes</string>
|
||||
<!--Displayed as a dialog message when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__target_does_not_support_gifting">Modtageren bruger en udgave af Signal, der ikke modtager gavebadges. De modtager dine gaver, når de opdaterer til den seneste udgave.</string>
|
||||
<string name="DonationsErrors__target_does_not_support_gifting">Modtageren bruger en udgave af Signal, der ikke kan modtage gavebadges. Vedkommende kan modtage dine gaver, når personen opdaterer til den seneste udgave.</string>
|
||||
<!--Displayed as a dialog title when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
<string name="DonationsErrors__couldnt_send_gift">Gave kunne ikke sendes</string>
|
||||
<!--Displayed as a dialog message when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
@@ -4059,7 +4059,7 @@ nummer (%s) er ugyldigt</string>
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Min historie</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Hvem kan se denne historie</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Hvem kan se denne historie</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Skjul historie for</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4067,7 +4067,7 @@ nummer (%s) er ugyldigt</string>
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Del med alle forbindelser</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Alle Signal-forbindelser med undtagelse af…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Alle Signal-forbindelser med undtagelse af…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Gem din historie for bestemte personer</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4398,7 +4398,7 @@ nummer (%s) er ugyldigt</string>
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Alle Signal-forbindelser</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Alle Signal-forbindelser med undtagelse af …</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Alle Signal-forbindelser med undtagelse af …</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Del kun med …</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -430,9 +430,11 @@
|
||||
<string name="CreateProfileActivity_error_setting_profile_photo">Fehler beim Festlegen des Profilfotos</string>
|
||||
<string name="CreateProfileActivity_problem_setting_profile">Problem beim Festlegen des Profils</string>
|
||||
<string name="CreateProfileActivity_set_up_your_profile">Richte dein Profil ein</string>
|
||||
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Dein Profil und Änderungen daran sind für Personen, an die du Nachrichten verschickst, Kontakte und Gruppen sichtbar.</string>
|
||||
<string name="CreateProfileActivity_set_avatar_description">Avatar festlegen</string>
|
||||
<!--ProfileCreateFragment-->
|
||||
<!--Displayed at the top of the screen and explains how profiles can be viewed.-->
|
||||
<string name="ProfileCreateFragment__profiles_are_visible_to_contacts_and_people_you_message">Profile sind für Personen, an die du Nachrichten schickst, Kontakte und Gruppen sichtbar.</string>
|
||||
<!--Title of clickable row to select phone number privacy settings-->
|
||||
<string name="ProfileCreateFragment__who_can_find_me">Wer kann mich anhand der Nummer finden?</string>
|
||||
<!--WhoCanSeeMyPhoneNumberFragment-->
|
||||
@@ -2225,6 +2227,7 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<string name="preferences__storage">Speicher</string>
|
||||
<string name="preferences__payments">Zahlungen</string>
|
||||
<!--Privacy settings payments section description-->
|
||||
<string name="preferences__payment_lock">Zahlungssperre</string>
|
||||
<string name="preferences__payments_beta">Zahlungen (Beta)</string>
|
||||
<string name="preferences__conversation_length_limit">Höchstzahl an Nachrichten</string>
|
||||
<string name="preferences__keep_messages">Nachrichten behalten</string>
|
||||
@@ -2394,7 +2397,9 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<string name="PaymentsHomeFragment__view_mobile_coin_terms">MobileCoin-Bedingungen anzeigen</string>
|
||||
<string name="PaymentsHomeFragment__payments_not_available">Zahlungen in Signal sind nicht mehr verfügbar. Du kannst weiterhin Guthaben an eine Handelsplattform überweisen, aber keine Zahlungen senden und empfangen oder Guthaben hinzufügen.</string>
|
||||
<!--Alert dialog title which shows up after a payment to turn on payment lock-->
|
||||
<string name="PaymentsHomeFragment__turn_on">Zahlungssperre für zukünftige Spenden aktivieren?</string>
|
||||
<!--Alert dialog description for why payment lock should be enabled before sending payments-->
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">Füge eine zusätzliche Sicherheitsebene hinzu und richte eine Android-Bildschirmsperre oder eine Fingerabdruck-ID ein, um Geld zu überweisen.</string>
|
||||
<!--Alert dialog button to enable payment lock-->
|
||||
<string name="PaymentsHomeFragment__enable">Einschalten</string>
|
||||
<!--Alert dialog button to not enable payment lock for now-->
|
||||
@@ -2480,8 +2485,11 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<string name="ConfirmPayment__payment_will_continue_processing">Verarbeitung der Zahlung wird fortgesetzt</string>
|
||||
<string name="ConfirmPaymentFragment__invalid_recipient">Ungültiger Empfänger</string>
|
||||
<!--Biometric/Device authentication prompt title which comes up before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__unlock_to_send_payment">Entsperren, um Zahlung zu senden</string>
|
||||
<!--Title of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__failed_to_show_payment_lock">Zahlungssperre konnte nicht angezeigt werden</string>
|
||||
<!--Body of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">Du hast eine Zahlungssperre in den Einstellungen aktiviert, sie kann jedoch nicht angezeigt werden.</string>
|
||||
<!--Button in a dialog that will take the user to the privacy settings-->
|
||||
<string name="ConfirmPaymentFragment__go_to_settings">Zu Einstellungen</string>
|
||||
<string name="ConfirmPaymentFragment__this_person_has_not_activated_payments">Diese Person hat Zahlungen nicht aktiviert</string>
|
||||
@@ -3303,9 +3311,13 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<string name="PrivacySettingsFragment__set_a_default_disappearing_message_timer_for_all_new_chats_started_by_you">Lege eine Standardablaufzeit für verschwindende Nachrichten aller von dir begonnenen neuen Unterhaltungen fest.</string>
|
||||
<!--Summary for stories preference to launch into story privacy settings-->
|
||||
<string name="PrivacySettingsFragment__manage_your_stories">Verwalte deine Storys und wer sie sehen kann</string>
|
||||
<string name="PrivacySettingsFragment__payment_lock_require_lock">Richte eine Android-Bildschirmsperre oder eine Fingerabdruck-ID ein, um Geld zu überweisen</string>
|
||||
<!--Alert dialog title when payment lock cannot be enabled-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_title">Zahlungssperre kann nicht aktiviert werden</string>
|
||||
<!--Alert dialog description to setup screen lock or fingerprint in phone settings-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_description">Um die Zahlungssperre zu verwenden, müsst du zuerst eine Bildschirmsperre oder eine Fingerabdruck-ID in deinen Telefoneinstellungen aktivieren.</string>
|
||||
<!--Shown in a toast when we can\'t navigate to the user\'s system fingerprint settings-->
|
||||
<string name="PrivacySettingsFragment__failed_to_navigate_to_system_settings">Systemeinstellungen konnten nicht angezeigt werden</string>
|
||||
<!--Alert dialog button to go to phone settings-->
|
||||
<string name="PrivacySettingsFragment__go_to_settings">Zu Einstellungen</string>
|
||||
<!--Alert dialog button to cancel the dialog-->
|
||||
@@ -3702,9 +3714,13 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<string name="NetworkFailure__network_error_check_your_connection_and_try_again">Netzfehler. Überprüfe deine Internetverbindung und versuche es erneut.</string>
|
||||
<string name="NetworkFailure__retry">Erneut versuchen</string>
|
||||
<!--Displayed as a dialog title when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__cant_send_gift">Geschenk kann nicht gesendet werden</string>
|
||||
<!--Displayed as a dialog message when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__target_does_not_support_gifting">Diese*r Empfänger*in verwendet eine Version von Signal, die keine Geschenkabzeichen erhalten kann. Geschenke können nur erhalten werden, wenn auf die neueste Version aktualisiert wird.</string>
|
||||
<!--Displayed as a dialog title when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
<string name="DonationsErrors__couldnt_send_gift">Geschenk konnte nicht gesendet werden</string>
|
||||
<!--Displayed as a dialog message when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
<string name="DonationsErrors__please_check_your_network_connection">Dein Geschenk konnte aufgrund eines Netzwerkfehlers nicht versendet werden. Überprüfe deine Verbindung und versuche es erneut.</string>
|
||||
<!--Gift message view title-->
|
||||
<string name="GiftMessageView__gift_badge">Geschenkabzeichen</string>
|
||||
<!--Gift message view expiry information-->
|
||||
@@ -4033,7 +4049,7 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Mein Story</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Diese Story kann sehen</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Diese Story kann sehen</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Story verbergen vor</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4041,7 +4057,7 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Mit allen Verbindungen teilen.</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Mit allen Signal-Verbindungen außer …</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Mit allen Signal-Verbindungen außer …</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Deine Story vor bestimmten Personen verbergen</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4271,7 +4287,15 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<!--Expired gift sheet not now button-->
|
||||
<string name="ExpiredGiftSheetConfiguration__not_now">Jetzt nicht</string>
|
||||
<!--Label under name for private stories-->
|
||||
<plurals name="ContactSearchItems__private_story_d_viewers">
|
||||
<item quantity="one">Private Story · %1$d Ansichten</item>
|
||||
<item quantity="other">Private Story · %1$d Ansichten</item>
|
||||
</plurals>
|
||||
<!--Label under name for group stories-->
|
||||
<plurals name="ContactSearchItems__group_story_d_viewers">
|
||||
<item quantity="one">Gruppen-Story · %1$d Ansichten</item>
|
||||
<item quantity="other">Gruppen-Story · %1$d Ansichten</item>
|
||||
</plurals>
|
||||
<!--Label under name for my story-->
|
||||
<plurals name="ContactSearchItems__my_story_s_dot_d_viewers">
|
||||
<item quantity="one">%1$s · %2$d Betrachter</item>
|
||||
@@ -4372,7 +4396,7 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Alle Signal-Verbindungen</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Mit allen Signal-Verbindungen außer…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Mit allen Signal-Verbindungen außer…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Nur teilen mit…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4055,7 +4055,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Η ιστορία μου</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Ποιοι μπορούν να δουν αυτή την ιστορία</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Ποιοι μπορούν να δουν αυτή την ιστορία</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Απόκρυψη ιστορίας από</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4063,7 +4063,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Κοινοποίηση σε όλες τις επαφές</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Όλες οι επαφές Signal εκτός από…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Όλες οι επαφές Signal εκτός από…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Κρύψε την ιστορία σου από συγκεκριμένα άτομα</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4402,7 +4402,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Όλες οι επαφές Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Όλες οι επαφές Signal εκτός από…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Όλες οι επαφές Signal εκτός από…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Κοινοποίηση μόνο σε…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -3984,7 +3984,7 @@ Ricevis mesaĝon pri interŝanĝo de ŝlosiloj por nevalida protokola versio.
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Mia rakonto</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Kiu povas vidi tiun rakonton</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Kiu povas vidi tiun rakonton</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Kaŝi la rakonton al</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<string name="yes">Sí</string>
|
||||
<string name="no">No</string>
|
||||
<string name="delete">Eliminar</string>
|
||||
<string name="please_wait">Espera, por favor …</string>
|
||||
<string name="please_wait">Espera, por favor…</string>
|
||||
<string name="save">Guardar</string>
|
||||
<string name="note_to_self">Notas privadas</string>
|
||||
<!--AbstractNotificationBuilder-->
|
||||
@@ -451,11 +451,11 @@
|
||||
<string name="CreateProfileActivity_error_setting_profile_photo">Fallo al establecer la foto de perfil</string>
|
||||
<string name="CreateProfileActivity_problem_setting_profile">Problema al crear el perfil</string>
|
||||
<string name="CreateProfileActivity_set_up_your_profile">Completa tu perfil</string>
|
||||
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Tu perfil y los cambios que hagas en él seran visibles para la gente a la que mandes mensajes, tus contactos y tus grupos.</string>
|
||||
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Tu perfil y los cambios que hagas en él serán visibles para la gente a la que mandes mensajes, tus contactos y tus grupos.</string>
|
||||
<string name="CreateProfileActivity_set_avatar_description">Configurar avatar de perfil</string>
|
||||
<!--ProfileCreateFragment-->
|
||||
<!--Displayed at the top of the screen and explains how profiles can be viewed.-->
|
||||
<string name="ProfileCreateFragment__profiles_are_visible_to_contacts_and_people_you_message">Los perfiles seran visibles para la gente a la que mandes mensajes, tus contactos y tus grupos.</string>
|
||||
<string name="ProfileCreateFragment__profiles_are_visible_to_contacts_and_people_you_message">Los perfiles serán visibles para la gente a la que mandes mensajes, tus contactos y tus grupos.</string>
|
||||
<!--Title of clickable row to select phone number privacy settings-->
|
||||
<string name="ProfileCreateFragment__who_can_find_me">¿Quién puede encontrarme a través de mi número?</string>
|
||||
<!--WhoCanSeeMyPhoneNumberFragment-->
|
||||
@@ -2494,7 +2494,7 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<string name="PaymentsHomeFragment__view_mobile_coin_terms">Términos de uso MobileCoin</string>
|
||||
<string name="PaymentsHomeFragment__payments_not_available">Los pagos en Signal no están disponibles. Todavía puedes transferir tus fondos a un «exchange» pero no podrás recibir o enviar más pagos a través de Signal.</string>
|
||||
<!--Alert dialog title which shows up after a payment to turn on payment lock-->
|
||||
<string name="PaymentsHomeFragment__turn_on">Quieres activar el bloqueo de pago para futuros envíos?</string>
|
||||
<string name="PaymentsHomeFragment__turn_on">¿Quieres activar el bloqueo de pago para futuros envíos?</string>
|
||||
<!--Alert dialog description for why payment lock should be enabled before sending payments-->
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">Añade una capa extra de seguridad y pide el bloqueo de pantalla de Android o la huella dactilar para transferir fondos.</string>
|
||||
<!--Alert dialog button to enable payment lock-->
|
||||
@@ -4167,7 +4167,7 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Mi historia</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Quién puede ver esta historia</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Quién puede ver esta historia</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Ocultar historia para</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4175,7 +4175,7 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Compartir con todos los contactos</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Todos los contactos de Signal menos…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Todos los contactos de Signal menos…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Oculta tu historia a ciertas personas</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4515,7 +4515,7 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Todos los contactos de Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Todos los contactos de Signal menos…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Todos los contactos de Signal menos…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Compartir solo con…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4060,7 +4060,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Minu lugu</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Kes seda lugu näevad</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Kes seda lugu näevad</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Peida lugu kasutaja(te) eest</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4068,7 +4068,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Jaga kõigi kontaktidega</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Kõik Signali kontaktid, välja arvatud …</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Kõik Signali kontaktid, välja arvatud …</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Peida oma lugu konkreetsete inimeste eest</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4407,7 +4407,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Kõik Signali kontaktid</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Kõik Signali kontaktid, välja arvatud …</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Kõik Signali kontaktid, välja arvatud …</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Jaga ainult nendega …</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4061,7 +4061,7 @@ Gakoaren elkar-trukeraro mezua jaso da protokoloaren bertsio baliogabe baterako.
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Nire istorioa</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Nork ikus dezake story hau</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Nork ikus dezake story hau</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Ezkutatu storie-a honi:</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4069,7 +4069,7 @@ Gakoaren elkar-trukeraro mezua jaso da protokoloaren bertsio baliogabe baterako.
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Partekatu konexio guztiekin</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Signal-eko konexio guztiak hauek izan ezik…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Signal-eko konexio guztiak hauek izan ezik…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Ezkutatu zure istorioa pertsona jakinei</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4408,7 +4408,7 @@ Gakoaren elkar-trukeraro mezua jaso da protokoloaren bertsio baliogabe baterako.
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Signal konexio guztiak</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Signal konexio guztiak… ezik</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Signal konexio guztiak… ezik</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">…rekin bakarrik partekatu</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4058,7 +4058,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">استوری من</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">کسانی که میتوانند این استوری را ببینند</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">کسانی که میتوانند این استوری را ببینند</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">استوری پنهان شود از</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4066,7 +4066,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">اشتراکگذاری با تمام آشنایان</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">تمام آشنایان سیگنال به جز…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">تمام آشنایان سیگنال به جز…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">استوری خود را از افراد خاصی پنهان کنید</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4397,7 +4397,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">تمام آشنایان در سیگنال</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">تمام آشنایان سیگنال به جز…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">تمام آشنایان سیگنال به جز…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">تنها اشتراکگذاری شود با…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4054,7 +4054,7 @@ Vastaanotetiin avaintenvaihtoviesti, joka kuuluu väärälle protokollaversiolle
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Minun tarinani</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Kuka voi nähdä tämän tarinan</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Kuka voi nähdä tämän tarinan</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Piilota tarina näiltä</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4062,7 +4062,7 @@ Vastaanotetiin avaintenvaihtoviesti, joka kuuluu väärälle protokollaversiolle
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Jaa kaikille kontakteille</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Kaikki Signal-kontaktit paitsi…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Kaikki Signal-kontaktit paitsi…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Piilota tarina tietyiltä henkilöiltä</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4393,7 +4393,7 @@ Vastaanotetiin avaintenvaihtoviesti, joka kuuluu väärälle protokollaversiolle
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Kaikki Signal-kontaktit</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Kaikki Signal-kontaktit paitsi…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Kaikki Signal-kontaktit paitsi…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Jaa vain seuraaville…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -451,9 +451,11 @@
|
||||
<string name="CreateProfileActivity_error_setting_profile_photo">Erreur de définition de la photo de profil</string>
|
||||
<string name="CreateProfileActivity_problem_setting_profile">Problème de définition du profil</string>
|
||||
<string name="CreateProfileActivity_set_up_your_profile">Configurez votre profil</string>
|
||||
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Votre profil et les modifications qui y sont apportées seront visibles par vos interlocuteurs, vos contacts et vos groupes.</string>
|
||||
<string name="CreateProfileActivity_set_avatar_description">Définir un avatar</string>
|
||||
<!--ProfileCreateFragment-->
|
||||
<!--Displayed at the top of the screen and explains how profiles can be viewed.-->
|
||||
<string name="ProfileCreateFragment__profiles_are_visible_to_contacts_and_people_you_message">Les profils sont visibles par vos interlocuteurs, vos contacts et vos groupes.</string>
|
||||
<!--Title of clickable row to select phone number privacy settings-->
|
||||
<string name="ProfileCreateFragment__who_can_find_me">Qui peut me retrouver par mon numéro ?</string>
|
||||
<!--WhoCanSeeMyPhoneNumberFragment-->
|
||||
@@ -2313,6 +2315,7 @@
|
||||
<string name="preferences__storage">Mémoire</string>
|
||||
<string name="preferences__payments">Paiements</string>
|
||||
<!--Privacy settings payments section description-->
|
||||
<string name="preferences__payment_lock">Blocage des paiements</string>
|
||||
<string name="preferences__payments_beta">Paiements (bêta)</string>
|
||||
<string name="preferences__conversation_length_limit">Taille maximale des conversations</string>
|
||||
<string name="preferences__keep_messages">Conserver les messages</string>
|
||||
@@ -2482,7 +2485,9 @@
|
||||
<string name="PaymentsHomeFragment__view_mobile_coin_terms">Afficher les CGU de MobileCoin</string>
|
||||
<string name="PaymentsHomeFragment__payments_not_available">Les paiements ne sont plus proposés dans Signal. Vous pouvez encore transférer des fonds vers une plateforme de change, mais vous ne pouvez plus ni envoyer, ni recevoir de paiements, ni ajouter de fonds.</string>
|
||||
<!--Alert dialog title which shows up after a payment to turn on payment lock-->
|
||||
<string name="PaymentsHomeFragment__turn_on">Activer le blocage des paiements pour les prochains envois ?</string>
|
||||
<!--Alert dialog description for why payment lock should be enabled before sending payments-->
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">Ajoutez une couche de sécurité et activez le verrouillage de l\'écran ou la demande de l\'empreinte digitale d\'Android pour effectuer un paiement.</string>
|
||||
<!--Alert dialog button to enable payment lock-->
|
||||
<string name="PaymentsHomeFragment__enable">Activer</string>
|
||||
<!--Alert dialog button to not enable payment lock for now-->
|
||||
@@ -2568,8 +2573,11 @@
|
||||
<string name="ConfirmPayment__payment_will_continue_processing">Le traitement du paiement va se poursuivre</string>
|
||||
<string name="ConfirmPaymentFragment__invalid_recipient">Le destinataire est invalide</string>
|
||||
<!--Biometric/Device authentication prompt title which comes up before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__unlock_to_send_payment">Déverrouiller pour envoyer le paiement</string>
|
||||
<!--Title of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__failed_to_show_payment_lock">Impossible d\'afficher le blocage du paiement</string>
|
||||
<!--Body of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">Le blocage du paiement a été activé dans les paramètres, mais il ne peut pas être affiché.</string>
|
||||
<!--Button in a dialog that will take the user to the privacy settings-->
|
||||
<string name="ConfirmPaymentFragment__go_to_settings">Rendez-vous dans les Paramètres</string>
|
||||
<string name="ConfirmPaymentFragment__this_person_has_not_activated_payments">Cette personne n’a pas activé les paiements</string>
|
||||
@@ -3406,9 +3414,13 @@ Nouvelle tentative…</string>
|
||||
<string name="PrivacySettingsFragment__set_a_default_disappearing_message_timer_for_all_new_chats_started_by_you">Définissez une minuterie par défaut pour les messages éphémères pour toutes les nouvelles conversations que vous démarrez.</string>
|
||||
<!--Summary for stories preference to launch into story privacy settings-->
|
||||
<string name="PrivacySettingsFragment__manage_your_stories">Gérez vos stories et les personnes autorisées à les consulter</string>
|
||||
<string name="PrivacySettingsFragment__payment_lock_require_lock">Exiger le verrouillage de l\'écran ou l\'empreinte digitale pour effectuer un paiement</string>
|
||||
<!--Alert dialog title when payment lock cannot be enabled-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_title">Impossible d\'activer le blocage du paiement</string>
|
||||
<!--Alert dialog description to setup screen lock or fingerprint in phone settings-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_description">Pour utiliser la fonction de blocage du paiement, vous devez d\'abord activer le verrouillage de l\'écran ou l\'identification digitale dans les paramètres de votre téléphone.</string>
|
||||
<!--Shown in a toast when we can\'t navigate to the user\'s system fingerprint settings-->
|
||||
<string name="PrivacySettingsFragment__failed_to_navigate_to_system_settings">Impossible de consulter les paramètres du système</string>
|
||||
<!--Alert dialog button to go to phone settings-->
|
||||
<string name="PrivacySettingsFragment__go_to_settings">Rendez-vous dans les Paramètres</string>
|
||||
<!--Alert dialog button to cancel the dialog-->
|
||||
@@ -3811,9 +3823,13 @@ Nouvelle tentative…</string>
|
||||
<string name="NetworkFailure__network_error_check_your_connection_and_try_again">Erreur réseau. Vérifiez votre connexion et réessayez.</string>
|
||||
<string name="NetworkFailure__retry">Réessayer</string>
|
||||
<!--Displayed as a dialog title when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__cant_send_gift">Impossible d\'envoyer un cadeau</string>
|
||||
<!--Displayed as a dialog message when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__target_does_not_support_gifting">Ce destinataire utilise une version de Signal qui ne lui permet pas de recevoir de macaron en cadeau. Pour recevoir des cadeaux, il devra effectuer une mise à jour.</string>
|
||||
<!--Displayed as a dialog title when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
<string name="DonationsErrors__couldnt_send_gift">Impossible d\'envoyer un cadeau</string>
|
||||
<!--Displayed as a dialog message when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
<string name="DonationsErrors__please_check_your_network_connection">Suite à un problème de réseau, votre cadeau n\'a pas pu être envoyé. Vérifiez votre connexion puis réessayez.</string>
|
||||
<!--Gift message view title-->
|
||||
<string name="GiftMessageView__gift_badge">Macaron cadeau</string>
|
||||
<!--Gift message view expiry information-->
|
||||
@@ -4146,7 +4162,7 @@ Nouvelle tentative…</string>
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Mon histoire</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Qui peut voir cette histoire</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Qui peut voir cette histoire</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Cacher l’histoire pour</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4154,7 +4170,7 @@ Nouvelle tentative…</string>
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Partager avec toutes les connexions</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Toutes les connexions Signal sauf…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Toutes les connexions Signal sauf…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Cacher votre histoire de certains contacts</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4494,7 +4510,7 @@ Nouvelle tentative…</string>
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Toutes les relations Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Toutes les relations Signal sauf…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Toutes les relations Signal sauf…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Partager uniquement avec…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -3982,7 +3982,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Myn ferhaal</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Wa meie dit ferhaal sjen</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Wa meie dit ferhaal sjen</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Ferstopje dit ferhaal fan</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
|
||||
@@ -4060,7 +4060,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">A miña historia</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Quen pode ver esta historia</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Quen pode ver esta historia</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Ocultar historia a</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4068,7 +4068,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Compartir con todas as conexións</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Todas as conexións de Signal agás…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Todas as conexións de Signal agás…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Ocultar a túa historia a persoas específicas</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4399,7 +4399,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Todas as conexións de Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Todas as conexións de Signal agás…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Todas as conexións de Signal agás…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Compartir só con…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4054,7 +4054,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">મારી સ્ટોરી</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">આ સ્ટોરી કોણ જોઇ શકે છે</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">આ સ્ટોરી કોણ જોઇ શકે છે</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">આમની સ્ટોરી છુપાવો</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4062,7 +4062,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">બધા કનેક્શન સાથે શેર કરો</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">બધા Signal કનેક્શન, સિવાય કે…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">બધા Signal કનેક્શન, સિવાય કે…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">ચોક્કસ લોકોથી તમારી સ્ટોરી છુપાવો</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4401,7 +4401,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">બધા Signal સંપર્કો</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">બધા Signal કનેક્શન, સિવાય કે…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">બધા Signal કનેક્શન, સિવાય કે…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">ફક્ત આમની સાથે શેર કરો…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4053,7 +4053,7 @@ href=\"https://signal.org/redirect/safety-numbers\">ज़्यादा जा
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">मेरी स्टोरी</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">इस स्टोरी को कौन देख सकता है</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">इस स्टोरी को कौन देख सकता है</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">इनसे स्टोरी छिपाएँ</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4061,7 +4061,7 @@ href=\"https://signal.org/redirect/safety-numbers\">ज़्यादा जा
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">सभी कनेक्शन के साथ शेयर करें</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">निम्न को छोड़कर सभी Signal कनेकशन…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">निम्न को छोड़कर सभी Signal कनेकशन…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">अपनी स्टोरी को कुछ निश्चित लोगों से छिपाएँ</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4400,7 +4400,7 @@ href=\"https://signal.org/redirect/safety-numbers\">ज़्यादा जा
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">सभी Signal कनेक्शन</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">निम्न को छोड़कर सभी Signal कनेकशन…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">निम्न को छोड़कर सभी Signal कनेकशन…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">केवल इनके साथ शेयर करें…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4164,7 +4164,7 @@ broj telefona</string>
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Moja priča</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Tko može pogledati ovu priču</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Tko može pogledati ovu priču</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Sakrij priču od</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4172,7 +4172,7 @@ broj telefona</string>
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Podijeli sa svim kontaktima</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Svi Signal kontakti osim…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Svi Signal kontakti osim…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Sakrij svoju priču od određenih osoba</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4512,7 +4512,7 @@ broj telefona</string>
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Svi Signal kontakti</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Svi Signal kontakti osim…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Svi Signal kontakti osim…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Podijeli samo s…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4061,7 +4061,7 @@ Kulcs-csere üzenet érkezett érvénytelen protokoll verzióhoz.
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Történetem</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Ki tekintheti meg ezt a történetet?</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Ki tekintheti meg ezt a történetet?</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Történet elrejtése előlük:</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4069,7 +4069,7 @@ Kulcs-csere üzenet érkezett érvénytelen protokoll verzióhoz.
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Megosztás az összes névjeggyel</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Minden Signal-névjegy, kivéve…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Minden Signal-névjegy, kivéve…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Rejtsd el a Történeted bizonyos személyek elől</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4400,7 +4400,7 @@ Kulcs-csere üzenet érkezett érvénytelen protokoll verzióhoz.
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Minden Signal-névjegy</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Minden Signal-névjegy, kivéve…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Minden Signal-névjegy, kivéve…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Megosztás kizárólag a következőkkel…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -3953,7 +3953,7 @@ Menerima pesan pertukaran kunci untuk versi protokol yang tidak valid.
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Cerita saya</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Siapa yang bisa melihat cerita ini</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Siapa yang bisa melihat cerita ini</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Sembunyikan serita dari</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -3961,7 +3961,7 @@ Menerima pesan pertukaran kunci untuk versi protokol yang tidak valid.
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Bagikan dengan semua koneksi</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Semua koneksi Signal kecuali…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Semua koneksi Signal kecuali…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Sembunyikan Cerita dari orang tertentu</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4284,7 +4284,7 @@ Menerima pesan pertukaran kunci untuk versi protokol yang tidak valid.
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Semua koneksi Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Semua koneksi Signal kecuali…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Semua koneksi Signal kecuali…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Hanya bagikan dengan…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -3984,7 +3984,7 @@ hegðun þína.</string>
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Sagan mín</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Hver getur séð þessa sögu</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Hver getur séð þessa sögu</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Fela sögu fyrir</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
|
||||
@@ -4167,7 +4167,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">La mia storia</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Chi può vedere questa storia</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Chi può vedere questa storia</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Nascondi storia a</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4175,7 +4175,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Condividi con tutte le tue Amicizie di Signal</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Tutte le Amicizie di Signal eccetto…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Tutte le Amicizie di Signal eccetto…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Non mostrare la tua Storia a determinate persone</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4515,7 +4515,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Tutte le Amicizie di Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Tutte le Amicizie di Signal eccetto…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Tutte le Amicizie di Signal eccetto…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Condividi solo con…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4274,7 +4274,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">הסיפור שלי</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">מי יכול לראות את הסיפור הזה</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">מי יכול לראות את הסיפור הזה</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">הסתר סיפור מפני</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4282,7 +4282,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">שיתוף עם כל חברי ה-Signal</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">כל חברי ה-Signal שלך חוץ מ…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">כל חברי ה-Signal שלך חוץ מ…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">הסתרת הסטורי שלך מאנשים ספציפיים</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4631,7 +4631,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">כל חברי ה-Signal</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">כל חברי ה-Signal שלך חוץ מ…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">כל חברי ה-Signal שלך חוץ מ…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">שיתוף רק עם…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -3944,7 +3944,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">マイストーリー</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">ストーリーを閲覧できる人</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">ストーリーを閲覧できる人</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">ストーリを非表示にする</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -3952,7 +3952,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">すべてのコネクションと共有</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">下記を除くすべてのSignalコネクション…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">下記を除くすべてのSignalコネクション…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">特定の人に自分のストーリーを隠す</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4274,7 +4274,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">すべてのSignalコネクション</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">…を除くすべてのSignalコネクション</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">…を除くすべてのSignalコネクション</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">共有するのは…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4060,7 +4060,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">ჩემი Story-ი</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">ვის შეუძლია ამ Story-ის ნახვა</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">ვის შეუძლია ამ Story-ის ნახვა</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">ამათგან Story-ის დამალვა </string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4068,7 +4068,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">ყველა კონტაქტთან გაზიარება</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Signal-ის ყველა კონტაქტთან, გარდა…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Signal-ის ყველა კონტაქტთან, გარდა…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">დამალე შენი Story-ი კონკრეტული ადამიანებისგან</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4399,7 +4399,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Signal-ის ყველა კონტაქტი </string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Signal-ის ყველა კონტაქტთან, გარდა…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Signal-ის ყველა კონტაქტთან, გარდა…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">მხოლოდ გაუზიარე…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4060,7 +4060,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Менің сториім</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Бұл стористі кім көре алады</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Бұл стористі кім көре алады</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Стористі мына адамдардан жасыру</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4068,7 +4068,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Барлық байланыспен бөлісу</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Мынадан басқа Signal байланыстарының барлығы…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Мынадан басқа Signal байланыстарының барлығы…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Белгілі бір адамдардан стористеріңізді жасыру</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4399,7 +4399,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Барлық Signal байланысы</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Мынадан басқа Signal байланыстарының барлығы…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Мынадан басқа Signal байланыстарының барлығы…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Тек мына адамдармен бөлісу…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -3947,7 +3947,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">រឿងរ៉ាវរបស់ខ្ញុំ</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">អ្នកដែលអាចមើលឃើញរឿងរ៉ាវនេះ</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">អ្នកដែលអាចមើលឃើញរឿងរ៉ាវនេះ</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">លាក់រឿងរ៉ាវពី</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -3955,7 +3955,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">ចែករំលែកជាមួយអ្នកភ្ជាប់ទំនាក់ទំនងទាំងអស់</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">អ្នកភ្ជាប់ទំនាក់ទំនងក្នុង Signal ទាំងអស់លើកលែងតែ…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">អ្នកភ្ជាប់ទំនាក់ទំនងក្នុង Signal ទាំងអស់លើកលែងតែ…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">លាក់រឿងរ៉ាវរបស់អ្នកពីមនុស្សជាក់លាក់</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4283,7 +4283,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">ទំនាក់ទំនង Signal ទាំងអស់</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">ទំនាក់ទំនង Signal ទាំងអស់លើកលែងតែ…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">ទំនាក់ទំនង Signal ទាំងអស់លើកលែងតែ…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">គ្រាន់តែចែករំលែកជាមួយ…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -2407,7 +2407,7 @@
|
||||
<!--Alert dialog title which shows up after a payment to turn on payment lock-->
|
||||
<string name="PaymentsHomeFragment__turn_on">ಭವಿಷ್ಯದ ರವಾನೆಗಳಿಗಾಗಿ ಪಾವತಿ ಲಾಕ್ ಆನ್ ಮಾಡುವುದೇ?</string>
|
||||
<!--Alert dialog description for why payment lock should be enabled before sending payments-->
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">ಭದ್ರತೆಯ ಹೆಚ್ಚುವರಿ ಪದರವನ್ನು ಸೇರಿಸಿ ಮತ್ತು ಹಣ ವರ್ಗಾಯಿಸಲು ಆಂಡ್ರಾಯ್ಡ್ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅಥವಾ ಬೆರಳಚ್ಚು ಬೇಕಾಗುತ್ತದೆ.</string>
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">ಭದ್ರತೆಯ ಹೆಚ್ಚುವರಿ ಪದರವನ್ನು ಸೇರಿಸಿರಿ. ಹಣ ವರ್ಗಾಯಿಸಲು ಆಂಡ್ರಾಯ್ಡ್ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅಥವಾ ಬೆರಳಚ್ಚು ಬೇಕಾಗುತ್ತದೆ.</string>
|
||||
<!--Alert dialog button to enable payment lock-->
|
||||
<string name="PaymentsHomeFragment__enable">ಆನ್ ಮಾಡಿ</string>
|
||||
<!--Alert dialog button to not enable payment lock for now-->
|
||||
@@ -2497,7 +2497,7 @@
|
||||
<!--Title of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__failed_to_show_payment_lock">ಪಾವತಿ ಲಾಕ್ ತೋರಿಸಲು ವಿಫಲವಾಗಿದೆ</string>
|
||||
<!--Body of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">ಸೆಟ್ಟಿಂಗ್ಗಳು ನಲ್ಲಿ ಪಾವತಿ ಲಾಕ್ ಅನ್ನು ನೀವು ಸಕ್ರಿಯಗೊಳಿಸಿದ್ದೀರಿ, ಆದರೆ ಇದನ್ನು ತೋರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ.</string>
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">ಸೆಟ್ಟಿಂಗ್ಸ್ ನಲ್ಲಿ ನೀವು ಪಾವತಿ ಲಾಕ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದ್ದೀರಿ, ಆದರೆ ಇದನ್ನು ತೋರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ.</string>
|
||||
<!--Button in a dialog that will take the user to the privacy settings-->
|
||||
<string name="ConfirmPaymentFragment__go_to_settings">ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ</string>
|
||||
<string name="ConfirmPaymentFragment__this_person_has_not_activated_payments">ಈ ವ್ಯಕ್ತಿ ಪೇಮೆಂಟ್ಸ್ ಸಕ್ರಿಯಗೊಳಿಸಿಲ್ಲ</string>
|
||||
@@ -3323,9 +3323,9 @@
|
||||
<!--Alert dialog title when payment lock cannot be enabled-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_title">ಪಾವತಿ ಲಾಕ್ ಸಕ್ರಿಯಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ</string>
|
||||
<!--Alert dialog description to setup screen lock or fingerprint in phone settings-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_description">ಪಾವತಿ ಲಾಕ್ ಉಪಯೋಗಿಸಲು, ನೀವು ಮೊದಲು ನಿಮ್ಮ ಫೋನ್ನ ಸೆಟ್ಟಿಂಗ್ಗಳು ನಲ್ಲಿ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅಥವಾ ಬೆರಳಚ್ಚು ಐಡಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕು.</string>
|
||||
<string name="PrivacySettingsFragment__cant_enable_description">ಪಾವತಿ ಲಾಕ್ ಉಪಯೋಗಿಸಲು, ಮೊದಲು ನೀವು ನಿಮ್ಮ ಫೋನ್ನ ಸೆಟ್ಟಿಂಗ್ಸ್ ನಲ್ಲಿ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅಥವಾ ಬೆರಳಚ್ಚು ಐಡಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕು.</string>
|
||||
<!--Shown in a toast when we can\'t navigate to the user\'s system fingerprint settings-->
|
||||
<string name="PrivacySettingsFragment__failed_to_navigate_to_system_settings">ಸಿಸ್ಟಮ್ ಸೆಟ್ಟಿಂಗ್ಗಳು ಗೆ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಲು ವಿಫಲಗೊಂಡಿದೆ</string>
|
||||
<string name="PrivacySettingsFragment__failed_to_navigate_to_system_settings">ಸಿಸ್ಟಮ್ ಸೆಟ್ಟಿಂಗ್ಸ್ ಗೆ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ</string>
|
||||
<!--Alert dialog button to go to phone settings-->
|
||||
<string name="PrivacySettingsFragment__go_to_settings">ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ</string>
|
||||
<!--Alert dialog button to cancel the dialog-->
|
||||
@@ -4057,7 +4057,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">ನನ್ನ ಸ್ಟೋರಿ</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">ಈ ಸ್ಟೋರಿಯನ್ನು ಯಾರು ನೋಡಬಹುದು</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">ಈ ಸ್ಟೋರಿಯನ್ನು ಯಾರು ನೋಡಬಹುದು</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">ಇವರಿಂದ ಸ್ಟೋರಿಯನ್ನು ಮರೆಮಾಡಿ</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4065,7 +4065,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">ಎಲ್ಲಾ ಸಂಪರ್ಕಗಳೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಿ</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">ಇದರ ಹೊರತಾದ ಎಲ್ಲಾ Signal ಸಂರ್ಕಗಳು…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">ಇದರ ಹೊರತಾದ ಎಲ್ಲಾ Signal ಸಂರ್ಕಗಳು…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">ನಿರ್ದಿಷ್ಟ ಜನರಿಂದ ನಿಮ್ಮ ಸ್ಟೋರಿಯನ್ನು ಮರೆಯಾಗಿಸಿ</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4396,7 +4396,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">ಎಲ್ಲಾ Signal ಸಂಪರ್ಕಗಳು</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">ಇದರ ಹೊರತಾದ ಎಲ್ಲಾ Signal ಸಂರ್ಕಗಳು…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">ಇದರ ಹೊರತಾದ ಎಲ್ಲಾ Signal ಸಂರ್ಕಗಳು…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">ಇವರೊಂದಿಗೆ ಮಾತ್ರ ಹಂಚಿಕೊಳ್ಳಿ…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -2322,9 +2322,9 @@
|
||||
<string name="PaymentsHomeFragment__view_mobile_coin_terms">MobileCoin 용어 보기</string>
|
||||
<string name="PaymentsHomeFragment__payments_not_available">Signal 결제는 더 이상 사용할 수 없습니다. 여전히 거래소로 자금을 이체할 수 있지만 더 이상 지불을 주고받거나 자금을 추가할 수 없습니다.</string>
|
||||
<!--Alert dialog title which shows up after a payment to turn on payment lock-->
|
||||
<string name="PaymentsHomeFragment__turn_on">앞으로 결제를 보낼 때 결제 잠금을 켤까요?</string>
|
||||
<string name="PaymentsHomeFragment__turn_on">지금부터 결제 잠금을 켤까요?</string>
|
||||
<!--Alert dialog description for why payment lock should be enabled before sending payments-->
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">자금 이체 시 Android 화면 잠금 또는 지문을 요구하여 보안을 한층 강화하세요.</string>
|
||||
<string name="PaymentsHomeFragment__add_an_additional_layer">자금 이체 시 Android 화면 잠금 또는 지문 인증을 통해 보안을 한층 강화하세요.</string>
|
||||
<!--Alert dialog button to enable payment lock-->
|
||||
<string name="PaymentsHomeFragment__enable">켜기</string>
|
||||
<!--Alert dialog button to not enable payment lock for now-->
|
||||
@@ -2414,7 +2414,7 @@
|
||||
<!--Title of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__failed_to_show_payment_lock">결제 잠금을 표시하지 못함</string>
|
||||
<!--Body of a dialog show when we were unable to present the user\'s screenlock before sending a payment-->
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">설정에서 결제 잠금을 활성화했으나 표시할 수 없습니다.</string>
|
||||
<string name="ConfirmPaymentFragment__you_enabled_payment_lock_in_the_settings">설정에서 결제 잠금 기능을 활성화했으나 표시할 수 없습니다.</string>
|
||||
<!--Button in a dialog that will take the user to the privacy settings-->
|
||||
<string name="ConfirmPaymentFragment__go_to_settings">설정으로 이동</string>
|
||||
<string name="ConfirmPaymentFragment__this_person_has_not_activated_payments">해당 사용자는 결제를 활성화하지 않았습니다.</string>
|
||||
@@ -3225,9 +3225,9 @@
|
||||
<string name="PrivacySettingsFragment__set_a_default_disappearing_message_timer_for_all_new_chats_started_by_you">내가 새로 시작한 모든 채팅에 기본적으로 사라지는 메시지 타이머를 적용합니다.</string>
|
||||
<!--Summary for stories preference to launch into story privacy settings-->
|
||||
<string name="PrivacySettingsFragment__manage_your_stories">스토리와 스토리를 볼 수 있는 사람을 관리하세요.</string>
|
||||
<string name="PrivacySettingsFragment__payment_lock_require_lock">자금 이체 시 Android 화면 잠금 또는 지문 요구</string>
|
||||
<string name="PrivacySettingsFragment__payment_lock_require_lock">자금 이체 시 Android 화면을 잠그거나 지문 인증하기</string>
|
||||
<!--Alert dialog title when payment lock cannot be enabled-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_title">결제 잠금을 활성화할 수 없습니다.</string>
|
||||
<string name="PrivacySettingsFragment__cant_enable_title">결제 잠금 기능을 활성화할 수 없습니다.</string>
|
||||
<!--Alert dialog description to setup screen lock or fingerprint in phone settings-->
|
||||
<string name="PrivacySettingsFragment__cant_enable_description">결제 잠금을 사용하려면 휴대폰 설정에서 화면 잠금 또는 지문 ID를 활성화해야 합니다.</string>
|
||||
<!--Shown in a toast when we can\'t navigate to the user\'s system fingerprint settings-->
|
||||
@@ -3462,7 +3462,7 @@
|
||||
<item quantity="other">메시지를 더는 사용할 수 없어 전달하지 못했습니다.</item>
|
||||
</plurals>
|
||||
<!--Error message shown when attempting to select a group to forward/share but it\'s announcement only and you are not an admin-->
|
||||
<string name="MultiselectForwardFragment__only_admins_can_send_messages_to_this_group">관리자만 그룹에 메시지를 전송할 수 있습니다.</string>
|
||||
<string name="MultiselectForwardFragment__only_admins_can_send_messages_to_this_group">관리자만 이 그룹에 메시지를 전송할 수 있습니다.</string>
|
||||
<string name="MultiselectForwardFragment__limit_reached">한계에 도달함</string>
|
||||
<!--Media V2-->
|
||||
<string name="MediaReviewFragment__add_a_message">메시지 추가</string>
|
||||
@@ -3624,9 +3624,9 @@
|
||||
<!--Displayed as a dialog title when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__cant_send_gift">선물을 보낼 수 없습니다.</string>
|
||||
<!--Displayed as a dialog message when the selected recipient for a gift doesn\'t support gifting-->
|
||||
<string name="DonationsErrors__target_does_not_support_gifting">이 받는 사람은 선물 배지를 받을 수 없는 Signal 버전을 사용합니다. 최신 버전으로 업데이트하면 선물이 전송됩니다.</string>
|
||||
<string name="DonationsErrors__target_does_not_support_gifting">선물 배지를 받을 수 없는 Signal 버전 사용자입니다. 최신 버전으로 업데이트하면 선물이 전송됩니다.</string>
|
||||
<!--Displayed as a dialog title when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
<string name="DonationsErrors__couldnt_send_gift">선물을 받지 못했습니다.</string>
|
||||
<string name="DonationsErrors__couldnt_send_gift">선물을 보내지 못했습니다.</string>
|
||||
<!--Displayed as a dialog message when the user\'s profile could not be fetched, likely due to lack of internet-->
|
||||
<string name="DonationsErrors__please_check_your_network_connection">네트워크 오류로 인해 선물을 보내지 못했습니다. 인터넷 연결을 확인하고 다시 시도하세요.</string>
|
||||
<!--Gift message view title-->
|
||||
@@ -3953,7 +3953,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">내 스토리</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">이 스토리를 볼 수 있는 사람</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">이 스토리를 볼 수 있는 사람</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">스토리 숨길 대상</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -3961,7 +3961,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">모든 커넥션과 공유</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">다음을 제외한 모든 Signal 커넥션…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">다음을 제외한 모든 Signal 커넥션…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">특정 사용자에게서 스토리 숨기기</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4283,7 +4283,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">모든 Signal 커넥션</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">다음을 제외한 모든 Signal 커넥션…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">다음을 제외한 모든 Signal 커넥션…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">다음 사용자와만 공유…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -3950,7 +3950,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Менин окуям</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Бул окуяны ким көрө алат</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Бул окуяны ким көрө алат</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Окуяны жашыруу</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -3958,7 +3958,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Бардык байланыштар менен бөлүшүү</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Signal\'дагы бардык байланыштар, бул байланыштан тышкары…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Signal\'дагы бардык байланыштар, бул байланыштан тышкары…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Окуяңызды белгилүү бир адамдардан жашырып койсоңуз болот</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4280,7 +4280,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Signal\'дагы бардык байланыштар</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Signal\'дагы бардык байланыштар, бул байланыштан тышкары…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Signal\'дагы бардык байланыштар, бул байланыштан тышкары…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Ушул байланыштарга гана көрүнсүн…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4274,7 +4274,7 @@
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Mano istorija</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Kas gali matyti šią istoriją</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Kas gali matyti šią istoriją</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Slėpti istoriją nuo</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4282,7 +4282,7 @@
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Bendrinti su visais kontaktais</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Visi „Signal“ kontaktai, išskyrus…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Visi „Signal“ kontaktai, išskyrus…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Slėpti jūsų istoriją nuo tam tikrų žmonių</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4631,7 +4631,7 @@
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Visi „Signal“ kontaktai</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Visi „Signal“ kontaktai, išskyrus…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Visi „Signal“ kontaktai, išskyrus…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Dalintis tik su…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
@@ -4161,7 +4161,7 @@ Saņemts nederīgas protokola versijas atslēgas apmaiņas ziņojums.
|
||||
<!--Page title for My Story options-->
|
||||
<string name="MyStorySettingsFragment__my_story">Mans stāsts</string>
|
||||
<!--Section heading for story visibility-->
|
||||
<string name="MyStorySettingsFragment__who_can_see_this_story">Kurš var redzēt šo stāstu</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__who_can_see_this_story">Kurš var redzēt šo stāstu</string> -->
|
||||
<!--Clickable option for selecting people to hide your story from-->
|
||||
<string name="MyStorySettingsFragment__hide_story_from">Paslēpt stāstu no</string>
|
||||
<!--Privacy setting title for sending stories to all your signal connections-->
|
||||
@@ -4169,7 +4169,7 @@ Saņemts nederīgas protokola versijas atslēgas apmaiņas ziņojums.
|
||||
<!--Privacy setting description for sending stories to all your signal connections-->
|
||||
<string name="MyStorySettingsFragment__share_with_all_connections">Kopīgot ar visiem Signal draugiem</string>
|
||||
<!--Privacy setting title for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__all_signal_connections_except">Visi Signal draugi, izņemot…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="MyStorySettingsFragment__all_signal_connections_except">Visi Signal draugi, izņemot…</string> -->
|
||||
<!--Privacy setting description for sending stories to all except the specified connections-->
|
||||
<string name="MyStorySettingsFragment__hide_your_story_from_specific_people">Nerādīt stāstu noteiktām personām</string>
|
||||
<!--Summary of clickable option displaying how many people you have excluded from your story-->
|
||||
@@ -4509,7 +4509,7 @@ Saņemts nederīgas protokola versijas atslēgas apmaiņas ziņojums.
|
||||
<!--All connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">Visi Signal draugi</string>
|
||||
<!--All connections except option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Visi Signal draugi, izņemot…</string>
|
||||
<!-- Removed by excludeNonTranslatables <string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">Visi Signal draugi, izņemot…</string> -->
|
||||
<!--Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time-->
|
||||
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Kopīgot tikai ar…</string>
|
||||
<!--Story info header sent heading-->
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user