Merge branch 'pr/1451' into feature/compose-cleanup

This commit is contained in:
ThomasSession 2024-07-17 15:54:13 +10:00
commit a524933a4d
166 changed files with 639 additions and 986 deletions

View File

@ -322,6 +322,7 @@ dependencies {
implementation "com.google.protobuf:protobuf-java:$protobufVersion"
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.phrase:phrase:$phraseVersion"
implementation 'app.cash.copper:copper-flow:1.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"

View File

@ -136,29 +136,29 @@ class SodiumUtilitiesTest {
}
@Test
fun sessionIdSuccess() {
val result = SodiumUtilities.sessionId("05$publicKey", "15$blindedPublicKey", serverPublicKey)
fun accountIdSuccess() {
val result = SodiumUtilities.accountId("05$publicKey", "15$blindedPublicKey", serverPublicKey)
assertTrue(result)
}
@Test
fun sessionIdFailureInvalidAccountId() {
val result = SodiumUtilities.sessionId("AB$publicKey", "15$blindedPublicKey", serverPublicKey)
fun accountIdFailureInvalidAccountId() {
val result = SodiumUtilities.accountId("AB$publicKey", "15$blindedPublicKey", serverPublicKey)
assertFalse(result)
}
@Test
fun sessionIdFailureInvalidBlindedId() {
val result = SodiumUtilities.sessionId("05$publicKey", "AB$blindedPublicKey", serverPublicKey)
fun accountIdFailureInvalidBlindedId() {
val result = SodiumUtilities.accountId("05$publicKey", "AB$blindedPublicKey", serverPublicKey)
assertFalse(result)
}
@Test
fun sessionIdFailureBlindingFactor() {
val result = SodiumUtilities.sessionId("05$publicKey", "15$blindedPublicKey", "Test")
fun accountIdFailureBlindingFactor() {
val result = SodiumUtilities.accountId("05$publicKey", "15$blindedPublicKey", "Test")
assertFalse(result)
}

View File

@ -156,8 +156,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
private volatile boolean isAppVisible;
public boolean newAccount = false;
@Override
public Object getSystemService(String name) {
if (MessagingModuleConfiguration.MESSAGING_MODULE_SERVICE.equals(name)) {

View File

@ -18,6 +18,7 @@ import org.session.libsession.utilities.Address
import org.session.libsession.utilities.AppTextSecurePreferences
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
@ -25,6 +26,8 @@ import org.thoughtcrime.securesms.mms.GlideRequests
class ProfilePictureView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : RelativeLayout(context, attrs) {
private val TAG = "ProfilePictureView"
private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this)
private val glide: GlideRequests = GlideApp.with(this)
private val prefs = AppTextSecurePreferences(context)
@ -91,7 +94,7 @@ class ProfilePictureView @JvmOverloads constructor(
}
fun update() {
val publicKey = publicKey ?: return
val publicKey = publicKey ?: return Log.w(TAG, "Could not find public key to update profile picture")
val additionalPublicKey = additionalPublicKey
if (additionalPublicKey != null) {
setProfilePictureIfNeeded(binding.doubleModeImageView1, publicKey, displayName)

View File

@ -89,8 +89,8 @@ private fun EnterAccountId(
SessionOutlinedTextField(
text = state.newMessageIdOrOns,
modifier = Modifier
.padding(horizontal = LocalDimensions.current.spacing)
.contentDescription("Session id input box"),
.padding(horizontal = LocalDimensions.current.spacing),
contentDescription = "Session id input box",
placeholder = stringResource(R.string.accountIdOrOnsEnter),
onChange = callbacks::onChange,
onContinue = callbacks::onContinue,

View File

@ -45,7 +45,7 @@ class NewMessageFragment : Fragment() {
viewModel,
onClose = { delegate.onDialogClosePressed() },
onBack = { delegate.onDialogBackPressed() },
onHelp = { requireContext().openUrl("https://sessionapp.zendesk.com/hc/en-us/articles/4439132747033-How-do-Session-ID-usernames-work") }
onHelp = { requireContext().openUrl("https://sessionapp.zendesk.com/hc/en-us/articles/4439132747033-How-do-Account-ID-usernames-work") }
)
}

View File

@ -239,12 +239,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
intent.getParcelableExtra<Address>(ADDRESS)?.let { it ->
threadId = threadDb.getThreadIdIfExistsFor(it.serialize())
if (threadId == -1L) {
val sessionId = AccountId(it.serialize())
val accountId = AccountId(it.serialize())
val openGroup = lokiThreadDb.getOpenGroupChat(intent.getLongExtra(FROM_GROUP_THREAD_ID, -1))
val address = if (sessionId.prefix == IdPrefix.BLINDED && openGroup != null) {
storage.getOrCreateBlindedIdMapping(sessionId.hexString, openGroup.server, openGroup.publicKey).sessionId?.let {
val address = if (accountId.prefix == IdPrefix.BLINDED && openGroup != null) {
storage.getOrCreateBlindedIdMapping(accountId.hexString, openGroup.server, openGroup.publicKey).accountId?.let {
fromSerialized(it)
} ?: GroupUtil.getEncodedOpenGroupInboxID(openGroup, sessionId)
} ?: GroupUtil.getEncodedOpenGroupInboxID(openGroup, accountId)
} else {
it
}
@ -1131,8 +1131,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
}
override fun copyAccountID(sessionId: String) {
val clip = ClipData.newPlainText("Account ID", sessionId)
override fun copyAccountID(accountId: String) {
val clip = ClipData.newPlainText("Account ID", accountId)
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
manager.setPrimaryClip(clip)
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()

View File

@ -2,19 +2,16 @@ package org.thoughtcrime.securesms.conversation.v2.input_bar
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.graphics.PointF
import android.net.Uri
import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.core.view.isGone
@ -31,11 +28,8 @@ import org.thoughtcrime.securesms.conversation.v2.messages.QuoteViewDelegate
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.SimpleTextWatcher
import org.thoughtcrime.securesms.util.addTextChangedListener
import org.thoughtcrime.securesms.util.contains
import org.thoughtcrime.securesms.util.toDp
import org.thoughtcrime.securesms.util.toPx
// Enums to keep track of the state of our voice recording mechanism as the user can
// manipulate the UI faster than we can setup & teardown.
@ -46,16 +40,24 @@ enum class VoiceRecorderState {
ShuttingDownAfterRecord
}
class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, LinkPreviewDraftViewDelegate,
@SuppressLint("ClickableViewAccessibility")
class InputBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(
context,
attrs,
defStyleAttr
), InputBarEditTextDelegate,
QuoteViewDelegate,
LinkPreviewDraftViewDelegate,
TextView.OnEditorActionListener {
private lateinit var binding: ViewInputBarBinding
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
private val vMargin by lazy { toDp(4, resources) }
private val minHeight by lazy { toPx(56, resources) }
private var binding: ViewInputBarBinding = ViewInputBarBinding.inflate(LayoutInflater.from(context), this, true)
private var linkPreviewDraftView: LinkPreviewDraftView? = null
private var quoteView: QuoteView? = null
var delegate: InputBarDelegate? = null
var additionalContentHeight = 0
var quote: MessageRecord? = null
var linkPreview: LinkPreview? = null
var showInput: Boolean = true
@ -68,7 +70,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
}
var text: String
get() { return binding.inputBarEditText.text?.toString() ?: "" }
get() = binding.inputBarEditText.text?.toString() ?: ""
set(value) { binding.inputBarEditText.setText(value) }
// Keep track of when the user pressed the record voice message button, the duration that
@ -77,21 +79,11 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
var voiceMessageDurationMS = 0L
var voiceRecorderState = VoiceRecorderState.Idle
val attachmentButtonsContainerHeight: Int
get() = binding.attachmentsButtonContainer.height
private val attachmentsButton = InputBarButton(context, R.drawable.ic_plus_24).apply { contentDescription = context.getString(R.string.AccessibilityId_attachments_button)}
val microphoneButton = InputBarButton(context, R.drawable.ic_microphone).apply { contentDescription = context.getString(R.string.AccessibilityId_microphone_button)}
private val sendButton = InputBarButton(context, R.drawable.ic_arrow_up, true).apply { contentDescription = context.getString(R.string.AccessibilityId_send_message_button)}
private val attachmentsButton by lazy { InputBarButton(context, R.drawable.ic_plus_24).apply { contentDescription = context.getString(R.string.AccessibilityId_attachments_button)} }
private val microphoneButton by lazy { InputBarButton(context, R.drawable.ic_microphone).apply { contentDescription = context.getString(R.string.AccessibilityId_microphone_button)} }
private val sendButton by lazy { InputBarButton(context, R.drawable.ic_arrow_up, true).apply { contentDescription = context.getString(R.string.AccessibilityId_send_message_button)} }
// region Lifecycle
constructor(context: Context) : super(context) { initialize() }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
@SuppressLint("ClickableViewAccessibility")
private fun initialize() {
binding = ViewInputBarBinding.inflate(LayoutInflater.from(context), this, true)
init {
// Attachments button
binding.attachmentsButtonContainer.addView(attachmentsButton)
attachmentsButton.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
@ -110,6 +102,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
// `microphoneButton.onUp` and tap the button then the logged output order is onUp and THEN onPress!
microphoneButton.setOnTouchListener(object : OnTouchListener {
override fun onTouch(v: View, event: MotionEvent): Boolean {
if (!microphoneButton.snIsEnabled) return true
// We only handle single finger touch events so just consume the event and bail if there are more
if (event.pointerCount > 1) return true
@ -160,12 +153,11 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
binding.inputBarEditText.setOnEditorActionListener(this)
if (TextSecurePreferences.isEnterSendsEnabled(context)) {
binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_SEND
binding.inputBarEditText.inputType =
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
binding.inputBarEditText.inputType = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
} else {
binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_NONE
binding.inputBarEditText.inputType =
binding.inputBarEditText.inputType or
binding.inputBarEditText.inputType
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
}
val incognitoFlag = if (TextSecurePreferences.isIncognitoKeyboardEnabled(context)) 16777216 else 0
@ -182,9 +174,6 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
return false
}
// endregion
// region Updating
override fun inputBarEditTextContentChanged(text: CharSequence) {
microphoneButton.isVisible = text.trim().isEmpty()
sendButton.isVisible = microphoneButton.isGone
@ -286,12 +275,9 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
fun setInputBarEditableFactory(factory: Editable.Factory) {
binding.inputBarEditText.setEditableFactory(factory)
}
// endregion
}
interface InputBarDelegate {
fun inputBarEditTextContentChanged(newContent: CharSequence)
fun toggleAttachmentOptions()
fun showVoiceMessageUI()
@ -301,4 +287,4 @@ interface InputBarDelegate {
fun onMicrophoneButtonUp(event: MotionEvent)
fun sendMessage()
fun commitInputContent(contentUri: Uri)
}
}

View File

@ -57,7 +57,7 @@ object ConversationMenuHelper {
if (!isOpenGroup && (thread.hasApprovedMe() || thread.isClosedGroupRecipient || thread.isLocalNumber)) {
inflater.inflate(R.menu.menu_conversation_expiration, menu)
}
// One-on-one chat menu allows copying the session id
// One-on-one chat menu allows copying the account id
if (thread.isContactRecipient) {
inflater.inflate(R.menu.menu_conversation_copy_account_id, menu)
}
@ -325,7 +325,7 @@ object ConversationMenuHelper {
interface ConversationMenuListener {
fun block(deleteThread: Boolean = false)
fun unblock()
fun copyAccountID(sessionId: String)
fun copyAccountID(accountId: String)
fun copyOpenGroupUrl(thread: Recipient)
fun showDisappearingMessages(thread: Recipient)
}

View File

@ -1,15 +1,12 @@
package org.thoughtcrime.securesms.conversation.v2.utilities
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.text.Spannable
import android.text.SpannableString
import android.text.style.BackgroundColorSpan
import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan
import android.util.Range
import androidx.core.content.res.ResourcesCompat
import network.loki.messenger.R
import nl.komponents.kovenant.combine.Tuple2
import org.session.libsession.messaging.contacts.Contact
@ -22,7 +19,6 @@ import org.session.libsession.utilities.truncateIdForDisplay
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.util.RoundedBackgroundSpan
import org.thoughtcrime.securesms.util.getAccentColor
import org.thoughtcrime.securesms.util.toPx
import java.util.regex.Pattern
object MentionUtilities {
@ -161,7 +157,7 @@ object MentionUtilities {
}
private fun isYou(mentionedPublicKey: String, userPublicKey: String, openGroup: OpenGroup?): Boolean {
val isUserBlindedPublicKey = openGroup?.let { SodiumUtilities.sessionId(userPublicKey, mentionedPublicKey, it.publicKey) } ?: false
val isUserBlindedPublicKey = openGroup?.let { SodiumUtilities.accountId(userPublicKey, mentionedPublicKey, it.publicKey) } ?: false
return mentionedPublicKey.equals(userPublicKey, ignoreCase = true) || isUserBlindedPublicKey
}
}

View File

@ -31,7 +31,7 @@ class BlindedIdMappingDatabase(context: Context, helper: SQLCipherOpenHelper) :
private fun readBlindedIdMapping(cursor: Cursor): BlindedIdMapping {
return BlindedIdMapping(
blindedId = cursor.getString(cursor.getColumnIndexOrThrow(BLINDED_PK)),
sessionId = cursor.getStringOrNull(cursor.getColumnIndexOrThrow(SESSION_PK)),
accountId = cursor.getStringOrNull(cursor.getColumnIndexOrThrow(SESSION_PK)),
serverUrl = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_URL)),
serverId = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_PK)),
)
@ -58,7 +58,7 @@ class BlindedIdMappingDatabase(context: Context, helper: SQLCipherOpenHelper) :
try {
val values = ContentValues().apply {
put(BLINDED_PK, blindedIdMapping.blindedId)
put(SERVER_PK, blindedIdMapping.sessionId)
put(SERVER_PK, blindedIdMapping.accountId)
put(SERVER_URL, blindedIdMapping.serverUrl)
put(SERVER_PK, blindedIdMapping.serverId)
}

View File

@ -1242,73 +1242,50 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
}
private fun getNotificationMmsMessageRecord(cursor: Cursor): NotificationMmsMessageRecord {
val id = cursor.getLong(cursor.getColumnIndexOrThrow(ID))
val dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT))
val dateReceived = cursor.getLong(
cursor.getColumnIndexOrThrow(
NORMALIZED_DATE_RECEIVED
)
)
val threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID))
val mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX))
val address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))
val addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(ADDRESS_DEVICE_ID))
val recipient = getRecipientFor(address)
val contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION))
val transactionId = cursor.getString(cursor.getColumnIndexOrThrow(TRANSACTION_ID))
val messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_SIZE))
val expiry = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRY))
val status = cursor.getInt(cursor.getColumnIndexOrThrow(STATUS))
val deliveryReceiptCount = cursor.getInt(
cursor.getColumnIndexOrThrow(
DELIVERY_RECEIPT_COUNT
)
)
val readReceiptCount = if (isReadReceiptsEnabled(context)) cursor.getInt(cursor.getColumnIndexOrThrow(READ_RECEIPT_COUNT)) else 0
val hasMention = (cursor.getInt(cursor.getColumnIndexOrThrow(HAS_MENTION)) == 1)
val contentLocationBytes: ByteArray? = contentLocation?.takeUnless { it.isEmpty() }?.let(::toIsoBytes)
val transactionIdBytes: ByteArray? = transactionId?.takeUnless { it.isEmpty() }?.let(::toIsoBytes)
val slideDeck = SlideDeck(context, MmsNotificationAttachment(status, messageSize))
// Note: Additional details such as ADDRESS_DEVICE_ID, CONTENT_LOCATION, and TRANSACTION_ID are available if required.
val id = cursor.getLong(cursor.getColumnIndexOrThrow(ID))
val dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT))
val dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_RECEIVED))
val threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID))
val mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX))
val address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))
val recipient = getRecipientFor(address)
val messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_SIZE))
val expiry = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRY))
val status = cursor.getInt(cursor.getColumnIndexOrThrow(STATUS))
val deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(DELIVERY_RECEIPT_COUNT))
val readReceiptCount = if (isReadReceiptsEnabled(context)) cursor.getInt(cursor.getColumnIndexOrThrow(READ_RECEIPT_COUNT)) else 0
val hasMention = (cursor.getInt(cursor.getColumnIndexOrThrow(HAS_MENTION)) == 1)
val slideDeck = SlideDeck(context, MmsNotificationAttachment(status, messageSize))
return NotificationMmsMessageRecord(
id, recipient, recipient,
dateSent, dateReceived, deliveryReceiptCount, threadId,
contentLocationBytes, messageSize, expiry, status,
transactionIdBytes, mailbox, slideDeck,
messageSize, expiry, status, mailbox, slideDeck,
readReceiptCount, hasMention
)
}
private fun getMediaMmsMessageRecord(cursor: Cursor, getQuote: Boolean): MediaMmsMessageRecord {
val id = cursor.getLong(cursor.getColumnIndexOrThrow(ID))
val dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT))
val dateReceived = cursor.getLong(
cursor.getColumnIndexOrThrow(
NORMALIZED_DATE_RECEIVED
)
)
val box = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX))
val threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID))
val address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))
val addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(ADDRESS_DEVICE_ID))
val deliveryReceiptCount = cursor.getInt(
cursor.getColumnIndexOrThrow(
DELIVERY_RECEIPT_COUNT
)
)
var readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(READ_RECEIPT_COUNT))
val body = cursor.getString(cursor.getColumnIndexOrThrow(BODY))
val partCount = cursor.getInt(cursor.getColumnIndexOrThrow(PART_COUNT))
val mismatchDocument = cursor.getString(
cursor.getColumnIndexOrThrow(
MISMATCHED_IDENTITIES
)
)
val networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(NETWORK_FAILURE))
val subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID))
val expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN))
val expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRE_STARTED))
val unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1
val hasMention = cursor.getInt(cursor.getColumnIndexOrThrow(HAS_MENTION)) == 1
val id = cursor.getLong(cursor.getColumnIndexOrThrow(ID))
val dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT))
val dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_RECEIVED))
val box = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX))
val threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID))
val address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))
val addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(ADDRESS_DEVICE_ID))
val deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(DELIVERY_RECEIPT_COUNT))
var readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(READ_RECEIPT_COUNT))
val body = cursor.getString(cursor.getColumnIndexOrThrow(BODY))
val partCount = cursor.getInt(cursor.getColumnIndexOrThrow(PART_COUNT))
val mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MISMATCHED_IDENTITIES))
val networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(NETWORK_FAILURE))
val subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID))
val expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN))
val expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRE_STARTED))
val unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1
val hasMention = cursor.getInt(cursor.getColumnIndexOrThrow(HAS_MENTION)) == 1
if (!isReadReceiptsEnabled(context)) {
readReceiptCount = 0
}

View File

@ -15,7 +15,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
companion object {
private const val sessionContactTable = "session_contact_database"
const val accountID = "account_id"
const val accountID = "session_id"
const val name = "name"
const val nickname = "nickname"
const val profilePictureURL = "profile_picture_url"
@ -42,12 +42,12 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
}
}
fun getContacts(sessionIDs: Collection<String>): List<Contact> {
fun getContacts(accountIDs: Collection<String>): List<Contact> {
val database = databaseHelper.readableDatabase
return database.getAll(
sessionContactTable,
"$accountID IN (SELECT value FROM json_each(?))",
arrayOf(JSONArray(sessionIDs).toString())
arrayOf(JSONArray(accountIDs).toString())
) { cursor -> contactFromCursor(cursor) }
}
@ -56,8 +56,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
return database.getAll(sessionContactTable, null, null) { cursor ->
contactFromCursor(cursor)
}.filter { contact ->
val sessionId = AccountId(contact.accountID)
sessionId.prefix == IdPrefix.STANDARD
contact.accountID.let(::AccountId).prefix == IdPrefix.STANDARD
}.toSet()
}

View File

@ -6,6 +6,7 @@ import java.security.MessageDigest
import network.loki.messenger.libsession_util.ConfigBase
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_HIDDEN
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_PINNED
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_VISIBLE
import network.loki.messenger.libsession_util.Contacts
import network.loki.messenger.libsession_util.ConversationVolatileConfig
import network.loki.messenger.libsession_util.UserGroupsConfig
@ -111,12 +112,12 @@ open class Storage(
if (address.isGroup) {
val groups = configFactory.userGroups ?: return
if (address.isClosedGroup) {
val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize())
val accountId = GroupUtil.doubleDecodeGroupId(address.serialize())
val closedGroup = getGroup(address.toGroupString())
if (closedGroup != null && closedGroup.isActive) {
val legacyGroup = groups.getOrConstructLegacyGroupInfo(sessionId)
val legacyGroup = groups.getOrConstructLegacyGroupInfo(accountId)
groups.set(legacyGroup)
val newVolatileParams = volatile.getOrConstructLegacyGroup(sessionId).copy(
val newVolatileParams = volatile.getOrConstructLegacyGroup(accountId).copy(
lastRead = SnodeAPI.nowWithOffset,
)
volatile.set(newVolatileParams)
@ -132,11 +133,11 @@ open class Storage(
if (getUserPublicKey() != address.serialize()) {
val contacts = configFactory.contacts ?: return
contacts.upsertContact(address.serialize()) {
priority = ConfigBase.PRIORITY_VISIBLE
priority = PRIORITY_VISIBLE
}
} else {
val userProfile = configFactory.user ?: return
userProfile.setNtsPriority(ConfigBase.PRIORITY_VISIBLE)
userProfile.setNtsPriority(PRIORITY_VISIBLE)
DatabaseComponent.get(context).threadDatabase().setHasSent(threadId, true)
}
val newVolatileParams = volatile.getOrConstructOneToOne(address.serialize())
@ -149,9 +150,9 @@ open class Storage(
if (address.isGroup) {
val groups = configFactory.userGroups ?: return
if (address.isClosedGroup) {
val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize())
volatile.eraseLegacyClosedGroup(sessionId)
groups.eraseLegacyGroup(sessionId)
val accountId = GroupUtil.doubleDecodeGroupId(address.serialize())
volatile.eraseLegacyClosedGroup(accountId)
groups.eraseLegacyGroup(accountId)
} else if (address.isCommunity) {
// these should be removed in the group leave / handling new configs
Log.w("Loki", "Thread delete called for open group address, expecting to be handled elsewhere")
@ -265,10 +266,8 @@ open class Storage(
}
// otherwise recipient is one to one
recipient.isContactRecipient -> {
// don't process non-standard session IDs though
val sessionId = AccountId(recipient.address.serialize())
if (sessionId.prefix != IdPrefix.STANDARD) return
// don't process non-standard account IDs though
if (AccountId(recipient.address.serialize()).prefix != IdPrefix.STANDARD) return
config.getOrConstructOneToOne(recipient.address.serialize())
}
else -> throw NullPointerException("Weren't expecting to have a convo with address ${recipient.address.serialize()}")
@ -299,8 +298,8 @@ open class Storage(
var messageID: Long? = null
val senderAddress = fromSerialized(message.sender!!)
val isUserSender = (message.sender!! == getUserPublicKey())
val isUserBlindedSender = message.threadID?.takeIf { it >= 0 }?.let { getOpenGroup(it)?.publicKey }
?.let { SodiumUtilities.sessionId(getUserPublicKey()!!, message.sender!!, it) } ?: false
val isUserBlindedSender = message.threadID?.takeIf { it >= 0 }?.let(::getOpenGroup)?.publicKey
?.let { SodiumUtilities.accountId(getUserPublicKey()!!, message.sender!!, it) } ?: false
val group: Optional<SignalServiceGroup> = when {
openGroupID != null -> Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT))
groupPublicKey != null -> {
@ -540,7 +539,7 @@ open class Storage(
val extracted = convos.all()
for (conversation in extracted) {
val threadId = when (conversation) {
is Conversation.OneToOne -> getThreadIdFor(conversation.sessionId, null, null, createThread = false)
is Conversation.OneToOne -> getThreadIdFor(conversation.accountId, null, null, createThread = false)
is Conversation.LegacyGroup -> getThreadIdFor("", conversation.groupId,null, createThread = false)
is Conversation.Community -> getThreadIdFor("",null, "${conversation.baseCommunityInfo.baseUrl.removeSuffix("/")}.${conversation.baseCommunityInfo.room}", createThread = false)
}
@ -571,7 +570,7 @@ open class Storage(
val existingJoinUrls = existingCommunities.values.map { it.joinURL }
val existingClosedGroups = getAllGroups(includeInactive = true).filter { it.isClosedGroup }
val lgcIds = lgc.map { it.sessionId }
val lgcIds = lgc.map { it.accountId }
val toDeleteClosedGroups = existingClosedGroups.filter { group ->
GroupUtil.doubleDecodeGroupId(group.encodedId) !in lgcIds
}
@ -605,8 +604,8 @@ open class Storage(
}
for (group in lgc) {
val groupId = GroupUtil.doubleEncodeGroupID(group.sessionId)
val existingGroup = existingClosedGroups.firstOrNull { GroupUtil.doubleDecodeGroupId(it.encodedId) == group.sessionId }
val groupId = GroupUtil.doubleEncodeGroupID(group.accountId)
val existingGroup = existingClosedGroups.firstOrNull { GroupUtil.doubleDecodeGroupId(it.encodedId) == group.accountId }
val existingThread = existingGroup?.let { getThreadId(existingGroup.encodedId) }
if (existingGroup != null) {
if (group.priority == PRIORITY_HIDDEN && existingThread != null) {
@ -625,19 +624,19 @@ open class Storage(
createGroup(groupId, title, admins + members, null, null, admins, formationTimestamp)
setProfileSharing(Address.fromSerialized(groupId), true)
// Add the group to the user's set of public keys to poll for
addClosedGroupPublicKey(group.sessionId)
addClosedGroupPublicKey(group.accountId)
// Store the encryption key pair
val keyPair = ECKeyPair(DjbECPublicKey(group.encPubKey), DjbECPrivateKey(group.encSecKey))
addClosedGroupEncryptionKeyPair(keyPair, group.sessionId, SnodeAPI.nowWithOffset)
addClosedGroupEncryptionKeyPair(keyPair, group.accountId, SnodeAPI.nowWithOffset)
// Notify the PN server
PushRegistryV1.subscribeGroup(group.sessionId, publicKey = localUserPublicKey)
PushRegistryV1.subscribeGroup(group.accountId, publicKey = localUserPublicKey)
// Notify the user
val threadID = getOrCreateThreadIdFor(Address.fromSerialized(groupId))
threadDb.setDate(threadID, formationTimestamp)
insertOutgoingInfoMessage(context, groupId, SignalServiceGroup.Type.CREATION, title, members.map { it.serialize() }, admins.map { it.serialize() }, threadID, formationTimestamp)
// Don't create config group here, it's from a config update
// Start polling
ClosedGroupPollerV2.shared.startPolling(group.sessionId)
ClosedGroupPollerV2.shared.startPolling(group.accountId)
}
getThreadId(Address.fromSerialized(groupId))?.let {
setExpirationConfiguration(
@ -938,10 +937,10 @@ open class Storage(
groupVolatileConfig.lastRead = formationTimestamp
volatiles.set(groupVolatileConfig)
val groupInfo = GroupInfo.LegacyGroupInfo(
sessionId = groupPublicKey,
accountId = groupPublicKey,
name = name,
members = members,
priority = ConfigBase.PRIORITY_VISIBLE,
priority = PRIORITY_VISIBLE,
encPubKey = (encryptionKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
encSecKey = encryptionKeyPair.privateKey.serialize(),
disappearingTimer = expirationTimer.toLong(),
@ -975,7 +974,7 @@ open class Storage(
members = membersMap,
encPubKey = (latestKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
encSecKey = latestKeyPair.privateKey.serialize(),
priority = if (isPinned(threadID)) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE,
priority = if (isPinned(threadID)) PRIORITY_PINNED else PRIORITY_VISIBLE,
disappearingTimer = getExpirationConfiguration(threadID)?.expiryMode?.expirySeconds ?: 0L,
joinedAt = (existingGroup.formationTimestamp / 1000L)
)
@ -1209,7 +1208,7 @@ open class Storage(
val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase()
val moreContacts = contacts.filter { contact ->
val id = AccountId(contact.id)
id.prefix?.isBlinded() == false || mappingDb.getBlindedIdMapping(contact.id).none { it.sessionId != null }
id.prefix?.isBlinded() == false || mappingDb.getBlindedIdMapping(contact.id).none { it.accountId != null }
}
val profileManager = SSKEnvironment.shared.profileManager
moreContacts.forEach { contact ->
@ -1262,7 +1261,7 @@ open class Storage(
val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase()
val moreContacts = contacts.filter { contact ->
val id = AccountId(contact.publicKey)
id.prefix != IdPrefix.BLINDED || mappingDb.getBlindedIdMapping(contact.publicKey).none { it.sessionId != null }
id.prefix != IdPrefix.BLINDED || mappingDb.getBlindedIdMapping(contact.publicKey).none { it.accountId != null }
}
for (contact in moreContacts) {
val address = fromSerialized(contact.publicKey)
@ -1329,25 +1328,25 @@ open class Storage(
val threadRecipient = getRecipientForThread(threadID) ?: return
if (threadRecipient.isLocalNumber) {
val user = configFactory.user ?: return
user.setNtsPriority(if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE)
user.setNtsPriority(if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE)
} else if (threadRecipient.isContactRecipient) {
val contacts = configFactory.contacts ?: return
contacts.upsertContact(threadRecipient.address.serialize()) {
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE
priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
}
} else if (threadRecipient.isGroupRecipient) {
val groups = configFactory.userGroups ?: return
if (threadRecipient.isClosedGroupRecipient) {
val sessionId = GroupUtil.doubleDecodeGroupId(threadRecipient.address.serialize())
val newGroupInfo = groups.getOrConstructLegacyGroupInfo(sessionId).copy (
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE
)
groups.set(newGroupInfo)
threadRecipient.address.serialize()
.let(GroupUtil::doubleDecodeGroupId)
.let(groups::getOrConstructLegacyGroupInfo)
.copy (priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE)
.let(groups::set)
} else if (threadRecipient.isCommunityRecipient) {
val openGroup = getOpenGroup(threadID) ?: return
val (baseUrl, room, pubKeyHex) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: return
val newGroupInfo = groups.getOrConstructCommunityInfo(baseUrl, room, Hex.toStringCondensed(pubKeyHex)).copy (
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE
priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
)
groups.set(newGroupInfo)
}
@ -1505,10 +1504,10 @@ open class Storage(
}
}
for (mapping in mappings) {
if (!SodiumUtilities.sessionId(senderPublicKey, mapping.value.blindedId, mapping.value.serverId)) {
if (!SodiumUtilities.accountId(senderPublicKey, mapping.value.blindedId, mapping.value.serverId)) {
continue
}
mappingDb.addBlindedIdMapping(mapping.value.copy(sessionId = senderPublicKey))
mappingDb.addBlindedIdMapping(mapping.value.copy(accountId = senderPublicKey))
val blindedThreadId = threadDB.getOrCreateThreadIdFor(Recipient.from(context, fromSerialized(mapping.key), false))
mmsDb.updateThreadId(blindedThreadId, threadId)
@ -1614,20 +1613,20 @@ open class Storage(
): BlindedIdMapping {
val db = DatabaseComponent.get(context).blindedIdMappingDatabase()
val mapping = db.getBlindedIdMapping(blindedId).firstOrNull() ?: BlindedIdMapping(blindedId, null, server, serverPublicKey)
if (mapping.sessionId != null) {
if (mapping.accountId != null) {
return mapping
}
getAllContacts().forEach { contact ->
val sessionId = AccountId(contact.accountID)
if (sessionId.prefix == IdPrefix.STANDARD && SodiumUtilities.sessionId(sessionId.hexString, blindedId, serverPublicKey)) {
val contactMapping = mapping.copy(sessionId = sessionId.hexString)
val accountId = AccountId(contact.accountID)
if (accountId.prefix == IdPrefix.STANDARD && SodiumUtilities.accountId(accountId.hexString, blindedId, serverPublicKey)) {
val contactMapping = mapping.copy(accountId = accountId.hexString)
db.addBlindedIdMapping(contactMapping)
return contactMapping
}
}
db.getBlindedIdMappingsExceptFor(server).forEach {
if (SodiumUtilities.sessionId(it.sessionId!!, blindedId, serverPublicKey)) {
val otherMapping = mapping.copy(sessionId = it.sessionId)
if (SodiumUtilities.accountId(it.accountId!!, blindedId, serverPublicKey)) {
val otherMapping = mapping.copy(accountId = it.accountId)
db.addBlindedIdMapping(otherMapping)
return otherMapping
}

View File

@ -1,113 +0,0 @@
/*
* Copyright (C) 2012 Moxie Marlinspike
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database.model;
import static java.util.Collections.emptyList;
import android.content.Context;
import android.text.SpannableString;
import androidx.annotation.NonNull;
import org.session.libsession.utilities.recipients.Recipient;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.mms.SlideDeck;
import network.loki.messenger.R;
/**
* Represents the message record model for MMS messages that are
* notifications (ie: they're pointers to undownloaded media).
*
* @author Moxie Marlinspike
*
*/
public class NotificationMmsMessageRecord extends MmsMessageRecord {
private final byte[] contentLocation;
private final long messageSize;
private final long expiry;
private final int status;
private final byte[] transactionId;
public NotificationMmsMessageRecord(long id, Recipient conversationRecipient,
Recipient individualRecipient,
long dateSent, long dateReceived, int deliveryReceiptCount,
long threadId, byte[] contentLocation, long messageSize,
long expiry, int status, byte[] transactionId, long mailbox,
SlideDeck slideDeck, int readReceiptCount, boolean hasMention)
{
super(id, "", conversationRecipient, individualRecipient,
dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
emptyList(), emptyList(),
0, 0, slideDeck, readReceiptCount, null, emptyList(), emptyList(), false, emptyList(), hasMention);
this.contentLocation = contentLocation;
this.messageSize = messageSize;
this.expiry = expiry;
this.status = status;
this.transactionId = transactionId;
}
public byte[] getTransactionId() {
return transactionId;
}
public int getStatus() {
return this.status;
}
public byte[] getContentLocation() {
return contentLocation;
}
public long getMessageSize() {
return (messageSize + 1023) / 1024;
}
public long getExpiration() {
return expiry * 1000;
}
@Override
public boolean isOutgoing() {
return false;
}
@Override
public boolean isPending() {
return false;
}
@Override
public boolean isMmsNotification() {
return true;
}
@Override
public boolean isMediaPending() {
return true;
}
@Override
public SpannableString getDisplayBody(@NonNull Context context) {
if (status == MmsDatabase.Status.DOWNLOAD_INITIALIZED) {
return emphasisAdded(context.getString(R.string.NotificationMmsMessageRecord_multimedia_message));
} else if (status == MmsDatabase.Status.DOWNLOAD_CONNECTING) {
return emphasisAdded(context.getString(R.string.NotificationMmsMessageRecord_downloading_mms_message));
} else {
return emphasisAdded(context.getString(R.string.NotificationMmsMessageRecord_error_downloading_mms_message));
}
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2012 Moxie Marlinspike
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database.model
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.database.SmsDatabase
import org.thoughtcrime.securesms.mms.SlideDeck
/**
* Represents the message record model for MMS messages that are
* notifications (ie: they're pointers to undownloaded media).
*
* @author Moxie Marlinspike
*/
class NotificationMmsMessageRecord(
id: Long, conversationRecipient: Recipient?,
individualRecipient: Recipient?,
dateSent: Long,
dateReceived: Long,
deliveryReceiptCount: Int,
threadId: Long,
private val messageSize: Long,
private val expiry: Long,
val status: Int,
mailbox: Long,
slideDeck: SlideDeck?,
readReceiptCount: Int,
hasMention: Boolean
) : MmsMessageRecord(
id, "", conversationRecipient, individualRecipient,
dateSent, dateReceived, threadId, SmsDatabase.Status.STATUS_NONE, deliveryReceiptCount, mailbox,
emptyList(), emptyList(),
0, 0, slideDeck!!, readReceiptCount, null, emptyList(), emptyList(), false, emptyList(), hasMention
) {
fun getMessageSize(): Long {
return (messageSize + 1023) / 1024
}
val expiration: Long
get() = expiry * 1000
override fun isOutgoing(): Boolean {
return false
}
override fun isPending(): Boolean {
return false
}
override fun isMmsNotification(): Boolean {
return true
}
override fun isMediaPending(): Boolean {
return true
}
}

View File

@ -4,7 +4,6 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.text.TextUtils
import android.util.AttributeSet
import android.util.TypedValue
@ -23,7 +22,6 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_ALL
import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_NONE
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.getAccentColor
import org.thoughtcrime.securesms.util.getConversationUnread
@ -51,7 +49,7 @@ class ConversationView : LinearLayout {
// endregion
// region Updating
fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) {
fun bind(thread: ThreadRecord, isTyping: Boolean) {
this.thread = thread
if (thread.isPinned) {
binding.conversationViewDisplayNameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(
@ -142,11 +140,10 @@ class ConversationView : LinearLayout {
else -> recipient.toShortString() // Internally uses the Contact API
}
private fun ThreadRecord.getSnippet(): CharSequence =
concatSnippet(getSnippetPrefix(), getDisplayBody(context))
private fun concatSnippet(prefix: CharSequence?, body: CharSequence): CharSequence =
prefix?.let { TextUtils.concat(it, ": ", body) } ?: body
private fun ThreadRecord.getSnippet(): CharSequence = listOfNotNull(
getSnippetPrefix(),
getDisplayBody(context)
).joinToString(": ")
private fun ThreadRecord.getSnippetPrefix(): CharSequence? = when {
recipient.isLocalNumber || lastMessage?.isControlMessage == true -> null

View File

@ -8,7 +8,6 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Telephony.Mms.Addr
import android.widget.Toast
import androidx.activity.viewModels
import androidx.core.os.bundleOf
@ -23,6 +22,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import network.loki.messenger.R
@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.groups.OpenGroupManager
import org.thoughtcrime.securesms.home.search.GlobalSearchAdapter
import org.thoughtcrime.securesms.home.search.GlobalSearchInputLayout
import org.thoughtcrime.securesms.home.search.GlobalSearchResult
import org.thoughtcrime.securesms.home.search.GlobalSearchViewModel
import org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity
import org.thoughtcrime.securesms.mms.GlideApp
@ -85,6 +86,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
GlobalSearchInputLayout.GlobalSearchInputLayoutListener {
companion object {
const val NEW_ACCOUNT = "HomeActivity_NEW_ACCOUNT"
const val FROM_ONBOARDING = "HomeActivity_FROM_ONBOARDING"
}
@ -135,6 +137,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
}
}
private val isNewAccount: Boolean get() = intent.getBooleanExtra(FROM_ONBOARDING, false)
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady)
@ -175,7 +179,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
// Set up empty state view
binding.emptyStateContainer.setThemedContent {
EmptyView(ApplicationContext.getInstance(this).newAccount)
EmptyView(isNewAccount)
}
IP2Country.configureIfNeeded(this@HomeActivity)
@ -240,67 +244,25 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
}
// Get group results and display them
launch {
globalSearchViewModel.result.collect { result ->
if (result.query.isEmpty()) {
class NamedValue<T>(val name: String?, val value: T)
// Unknown is temporarily to be grouped together with numbers title.
// https://optf.atlassian.net/browse/SES-2287
val numbersTitle = "#"
val unknownTitle = numbersTitle
listOf(
GlobalSearchAdapter.Model.Header(R.string.contacts),
GlobalSearchAdapter.Model.SavedMessages(publicKey)
) + result.contacts
// Remove ourself, we're shown above.
.filter { it.accountID != publicKey }
// Get the name that we will display and sort by, and uppercase it to
// help with sorting and we need the char uppercased later.
.map { (it.nickname?.takeIf(String::isNotEmpty) ?: it.name?.takeIf(String::isNotEmpty))
.let { name -> NamedValue(name?.uppercase(), it) } }
// Digits are all grouped under a #, the rest are grouped by their first character.uppercased()
// If there is no name, they go under Unknown
.groupBy { it.name?.run { first().takeUnless(Char::isDigit)?.toString() ?: numbersTitle } ?: unknownTitle }
// place the # at the end, after all the names starting with alphabetic chars
.toSortedMap(compareBy {
when (it) {
unknownTitle -> Char.MAX_VALUE
numbersTitle -> Char.MAX_VALUE - 1
else -> it.first()
}
})
// Flatten the map of char to lists into an actual List that can be displayed.
.flatMap { (key, contacts) ->
listOf(
GlobalSearchAdapter.Model.SubHeader(key)
) + contacts.sortedBy { it.name ?: it.value.accountID }.map { it.value }.map { GlobalSearchAdapter.Model.Contact(it, it.accountID == publicKey) }
globalSearchViewModel.result.map { result ->
result.query to when {
result.query.isEmpty() -> buildList {
add(GlobalSearchAdapter.Model.Header(R.string.contacts))
add(GlobalSearchAdapter.Model.SavedMessages(publicKey))
addAll(result.groupedContacts)
}
else -> buildList {
result.contactAndGroupList.takeUnless { it.isEmpty() }?.let {
add(GlobalSearchAdapter.Model.Header(R.string.contacts))
addAll(it)
}
result.messageResults.takeUnless { it.isEmpty() }?.let {
add(GlobalSearchAdapter.Model.Header(R.string.global_search_messages))
addAll(it)
}
} else {
val contactAndGroupList = result.contacts.map { GlobalSearchAdapter.Model.Contact(it, it.accountID == publicKey) } +
result.threads.map(GlobalSearchAdapter.Model::GroupConversation)
val contactResults = contactAndGroupList.toMutableList()
if (contactResults.isNotEmpty()) {
contactResults.add(0, GlobalSearchAdapter.Model.Header(R.string.conversations))
}
val unreadThreadMap = result.messages
.map { it.threadId }.toSet()
.associateWith { mmsSmsDatabase.getUnreadCount(it) }
val messageResults: MutableList<GlobalSearchAdapter.Model> = result.messages
.map { GlobalSearchAdapter.Model.Message(it, unreadThreadMap[it.threadId] ?: 0, it.conversationRecipient.isLocalNumber) }
.toMutableList()
if (messageResults.isNotEmpty()) {
messageResults.add(0, GlobalSearchAdapter.Model.Header(R.string.global_search_messages))
}
contactResults + messageResults
}.let { globalSearchAdapter.setNewData(result.query, it) }
}
}
}.collectLatest(globalSearchAdapter::setNewData)
}
}
EventBus.getDefault().register(this@HomeActivity)
@ -318,6 +280,54 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
}
}
private val GlobalSearchResult.groupedContacts: List<GlobalSearchAdapter.Model> get() {
class NamedValue<T>(val name: String?, val value: T)
// Unknown is temporarily to be grouped together with numbers title.
// https://optf.atlassian.net/browse/SES-2287
val numbersTitle = "#"
val unknownTitle = numbersTitle
return contacts
// Remove ourself, we're shown above.
.filter { it.accountID != publicKey }
// Get the name that we will display and sort by, and uppercase it to
// help with sorting and we need the char uppercased later.
.map { (it.nickname?.takeIf(String::isNotEmpty) ?: it.name?.takeIf(String::isNotEmpty))
.let { name -> NamedValue(name?.uppercase(), it) } }
// Digits are all grouped under a #, the rest are grouped by their first character.uppercased()
// If there is no name, they go under Unknown
.groupBy { it.name?.run { first().takeUnless(Char::isDigit)?.toString() ?: numbersTitle } ?: unknownTitle }
// place the # at the end, after all the names starting with alphabetic chars
.toSortedMap(compareBy {
when (it) {
unknownTitle -> Char.MAX_VALUE
numbersTitle -> Char.MAX_VALUE - 1
else -> it.first()
}
})
// Flatten the map of char to lists into an actual List that can be displayed.
.flatMap { (key, contacts) ->
listOf(
GlobalSearchAdapter.Model.SubHeader(key)
) + contacts.sortedBy { it.name ?: it.value.accountID }.map { it.value }.map { GlobalSearchAdapter.Model.Contact(it, it.nickname ?: it.name, it.accountID == publicKey) }
}
}
private val GlobalSearchResult.contactAndGroupList: List<GlobalSearchAdapter.Model> get() =
contacts.map { GlobalSearchAdapter.Model.Contact(it, it.nickname ?: it.name, it.accountID == publicKey) } +
threads.map(GlobalSearchAdapter.Model::GroupConversation)
private val GlobalSearchResult.messageResults: List<GlobalSearchAdapter.Model> get() {
val unreadThreadMap = messages
.map { it.threadId }.toSet()
.associateWith { mmsSmsDatabase.getUnreadCount(it) }
return messages.map {
GlobalSearchAdapter.Model.Message(it, unreadThreadMap[it.threadId] ?: 0, it.conversationRecipient.isLocalNumber)
}
}
override fun onInputFocusChanged(hasFocus: Boolean) {
setSearchShown(hasFocus || binding.globalSearchInputLayout.query.value.isNotEmpty())
}
@ -629,9 +639,10 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
}
}
fun Context.startHomeActivity() {
fun Context.startHomeActivity(isNewAccount: Boolean) {
Intent(this, HomeActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra(HomeActivity.NEW_ACCOUNT, true)
putExtra(HomeActivity.FROM_ONBOARDING, true)
}.also(::startActivity)
}

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.home
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback
@ -12,8 +11,6 @@ import network.loki.messenger.R
import network.loki.messenger.databinding.ViewMessageRequestBannerBinding
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils
import java.util.Locale
class HomeAdapter(
private val context: Context,
@ -115,7 +112,7 @@ class HomeAdapter(
val offset = if (hasHeaderView()) position - 1 else position
val thread = data.threads[offset]
val isTyping = data.typingThreadIDs.contains(thread.threadId)
holder.view.bind(thread, isTyping, glide)
holder.view.bind(thread, isTyping)
}
}
}

View File

@ -28,6 +28,8 @@ class GlobalSearchAdapter(private val modelCallback: (Model)->Unit): RecyclerVie
private var data: List<Model> = listOf()
private var query: String? = null
fun setNewData(data: Pair<String, List<Model>>) = setNewData(data.first, data.second)
fun setNewData(query: String, newData: List<Model>) {
val diffResult = DiffUtil.calculateDiff(GlobalSearchDiff(this.query, query, data, newData))
this.query = query
@ -134,7 +136,7 @@ class GlobalSearchAdapter(private val modelCallback: (Model)->Unit): RecyclerVie
constructor(title: String): this(GetString(title))
}
data class SavedMessages(val currentUserPublicKey: String): Model()
data class Contact(val contact: ContactModel, val isSelf: Boolean): Model()
data class Contact(val contact: ContactModel, val name: String?, val isSelf: Boolean): Model()
data class GroupConversation(val groupRecord: GroupRecord): Model()
data class Message(val messageResult: MessageResult, val unread: Int, val isSelf: Boolean): Model()
}

View File

@ -6,22 +6,18 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import org.session.libsignal.utilities.SettableFuture
@ -35,16 +31,32 @@ import javax.inject.Inject
class GlobalSearchViewModel @Inject constructor(
private val searchRepository: SearchRepository,
) : ViewModel() {
private val executor = viewModelScope + SupervisorJob()
private val _result: MutableStateFlow<GlobalSearchResult> = MutableStateFlow(GlobalSearchResult.EMPTY)
val result: StateFlow<GlobalSearchResult> = _result
private val scope = viewModelScope + SupervisorJob()
private val refreshes = MutableSharedFlow<Unit>()
private val _queryText = MutableStateFlow<CharSequence>("")
private val _queryText: MutableStateFlow<CharSequence> = MutableStateFlow("")
val result = _queryText
.reEmit(refreshes)
.buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST)
.mapLatest { query ->
if (query.trim().isEmpty()) {
// searching for 05 as contactDb#getAllContacts was not returning contacts
// without a nickname/name who haven't approved us.
GlobalSearchResult(query.toString(), searchRepository.queryContacts("05").first.toList())
} else {
// User input delay in case we get a new query within a few hundred ms this
// coroutine will be cancelled and the expensive query will not be run.
delay(300)
val settableFuture = SettableFuture<SearchResult>()
searchRepository.query(query.toString(), settableFuture::set)
try {
// search repository doesn't play nicely with suspend functions (yet)
settableFuture.get(10_000, TimeUnit.MILLISECONDS).toGlobalSearchResult()
} catch (e: Exception) {
GlobalSearchResult(query.toString())
}
}
}
fun setQuery(charSequence: CharSequence) {
_queryText.value = charSequence
@ -55,34 +67,6 @@ class GlobalSearchViewModel @Inject constructor(
refreshes.emit(Unit)
}
}
init {
_queryText
.reEmit(refreshes)
.buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST)
.mapLatest { query ->
if (query.trim().isEmpty()) {
// searching for 05 as contactDb#getAllContacts was not returning contacts
// without a nickname/name who haven't approved us.
GlobalSearchResult(query.toString(), searchRepository.queryContacts("05").first.toList())
} else {
// User input delay in case we get a new query within a few hundred ms this
// coroutine will be cancelled and the expensive query will not be run.
delay(300)
val settableFuture = SettableFuture<SearchResult>()
searchRepository.query(query.toString(), settableFuture::set)
try {
// search repository doesn't play nicely with suspend functions (yet)
settableFuture.get(10_000, TimeUnit.MILLISECONDS).toGlobalSearchResult()
} catch (e: Exception) {
GlobalSearchResult(query.toString())
}
}
}.onEach { result ->
// update the latest _result value
_result.value = result
}.launchIn(executor)
}
}
/**

View File

@ -34,13 +34,13 @@ class MessageRequestView : LinearLayout {
// region Updating
fun bind(thread: ThreadRecord, glide: GlideRequests) {
this.thread = thread
val senderDisplayName = getUserDisplayName(thread.recipient)
?: thread.recipient.address.toString()
val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString()
binding.displayNameTextView.text = senderDisplayName
binding.timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), thread.date)
val rawSnippet = thread.getDisplayBody(context)
val snippet = highlightMentions(
text = rawSnippet,
text = thread.getDisplayBody(context),
formatOnly = true, // no styling here, only text formatting
threadID = thread.threadId,
context = context

View File

@ -16,24 +16,23 @@
*/
package org.thoughtcrime.securesms.mms;
import static org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY;
import android.content.Context;
import android.content.res.Resources.Theme;
import android.net.Uri;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.session.libsignal.utilities.guava.Optional;
import com.squareup.phrase.Phrase;
import java.security.SecureRandom;
import network.loki.messenger.R;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment;
import org.session.libsession.utilities.Util;
import java.security.SecureRandom;
import network.loki.messenger.R;
import org.session.libsignal.utilities.guava.Optional;
import org.thoughtcrime.securesms.util.MediaUtil;
public abstract class Slide {
@ -72,20 +71,23 @@ public abstract class Slide {
return Optional.fromNullable("🎤 " + attachmentString);
}
}
return Optional.fromNullable(emojiForMimeType() + attachmentString);
String txt = Phrase.from(context, R.string.attachmentsNotification)
.put(EMOJI_KEY, emojiForMimeType())
.format().toString();
return Optional.fromNullable(txt);
}
private String emojiForMimeType() {
if (MediaUtil.isImage(attachment)) {
return "📷 ";
return "📷";
} else if (MediaUtil.isVideo(attachment)) {
return "🎥 ";
return "🎥";
} else if (MediaUtil.isAudio(attachment)) {
return "🎧 ";
return "🎧";
} else if (MediaUtil.isFile(attachment)) {
return "📎 ";
return "📎";
} else {
return "🎡 ";
return "🎡"; // `isGif`
}
}
@ -155,20 +157,20 @@ public abstract class Slide {
return false;
}
protected static Attachment constructAttachmentFromUri(@NonNull Context context,
@NonNull Uri uri,
@NonNull String defaultMime,
long size,
int width,
int height,
boolean hasThumbnail,
@Nullable String fileName,
@Nullable String caption,
boolean voiceNote,
boolean quote)
protected static Attachment constructAttachmentFromUri(@NonNull Context context,
@NonNull Uri uri,
@NonNull String defaultMime,
long size,
int width,
int height,
boolean hasThumbnail,
@Nullable String fileName,
@Nullable String caption,
boolean voiceNote,
boolean quote)
{
String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime);
String fastPreflightId = String.valueOf(new SecureRandom().nextLong());
String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime);
String fastPreflightId = String.valueOf(new SecureRandom().nextLong());
return new UriAttachment(uri,
hasThumbnail ? uri : null,
resolvedType,

View File

@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsignal.utilities.Log;
import org.session.libsignal.utilities.guava.Optional;
import org.thoughtcrime.securesms.util.MediaUtil;
@ -47,8 +48,7 @@ public class SlideDeck {
if (slide != null) slides.add(slide);
}
public SlideDeck() {
}
public SlideDeck() { }
public void clear() {
slides.clear();
@ -65,7 +65,6 @@ public class SlideDeck {
body = slideBody.get();
}
}
return body;
}

View File

@ -29,6 +29,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.service.notification.StatusBarNotification;
import android.text.SpannableString;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -145,9 +146,8 @@ public class DefaultMessageNotifier implements MessageNotifier {
}
public void notifyMessagesPending(Context context) {
if (!TextSecurePreferences.isNotificationsEnabled(context)) {
return;
}
if (!TextSecurePreferences.isNotificationsEnabled(context)) { return; }
PendingMessageNotificationBuilder builder = new PendingMessageNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context));
ServiceUtil.getNotificationManager(context).notify(PENDING_MESSAGES_ID, builder.build());
@ -185,9 +185,9 @@ public class DefaultMessageNotifier implements MessageNotifier {
for (StatusBarNotification notification : activeNotifications) {
boolean validNotification = false;
if (notification.getId() != SUMMARY_NOTIFICATION_ID &&
notification.getId() != KeyCachingService.SERVICE_RUNNING_ID &&
notification.getId() != FOREGROUND_ID &&
if (notification.getId() != SUMMARY_NOTIFICATION_ID &&
notification.getId() != KeyCachingService.SERVICE_RUNNING_ID &&
notification.getId() != FOREGROUND_ID &&
notification.getId() != PENDING_MESSAGES_ID)
{
for (NotificationItem item : notificationState.getNotifications()) {
@ -197,9 +197,7 @@ public class DefaultMessageNotifier implements MessageNotifier {
}
}
if (!validNotification) {
notifications.cancel(notification.getId());
}
if (!validNotification) { notifications.cancel(notification.getId()); }
}
}
} catch (Throwable e) {
@ -231,7 +229,7 @@ public class DefaultMessageNotifier implements MessageNotifier {
@Override
public void updateNotification(@NonNull Context context, long threadId, boolean signal)
{
boolean isVisible = visibleThread == threadId;
boolean isVisible = visibleThread == threadId;
ThreadDatabase threads = DatabaseComponent.get(context).threadDatabase();
Recipient recipient = threads.getRecipientForThreadId(threadId);
@ -348,14 +346,19 @@ public class DefaultMessageNotifier implements MessageNotifier {
builder.setThread(notifications.get(0).getRecipient());
builder.setMessageCount(notificationState.getMessageCount());
// TODO: Removing highlighting mentions in the notification because this context is the libsession one which
// TODO: doesn't have access to the `R.attr.message_sent_text_color` and `R.attr.message_received_text_color`
// TODO: attributes to perform the colour lookup. Also, it makes little sense to highlight the mentions using
// TODO: the app theme as it may result in insufficient contrast with the notification background which will
// TODO: be using the SYSTEM theme.
builder.setPrimaryMessageBody(recipient, notifications.get(0).getIndividualRecipient(),
//MentionUtilities.highlightMentions(text == null ? "" : text, notifications.get(0).getThreadId(), context), // Removing hightlighting mentions -ACL
text == null ? "" : text,
CharSequence builderCS = text == null ? "" : text;
SpannableString ss = MentionUtilities.highlightMentions(
builderCS,
false,
false,
true,
bundled ? notifications.get(0).getThreadId() : 0,
context
);
builder.setPrimaryMessageBody(recipient,
notifications.get(0).getIndividualRecipient(),
ss,
notifications.get(0).getSlideDeck());
builder.setContentIntent(notifications.get(0).getPendingIntent(context));
@ -505,24 +508,39 @@ public class DefaultMessageNotifier implements MessageNotifier {
continue;
}
}
// If this is a message request from an unknown user..
if (messageRequest) {
body = SpanUtil.italic(context.getString(R.string.message_requests_notification));
// If we received some manner of notification but Session is locked..
} else if (KeyCachingService.isLocked(context)) {
body = SpanUtil.italic(context.getString(R.string.MessageNotifier_locked_message));
// ----- All further cases assume we know the contact and that Session isn't locked -----
// If this is a notification about a multimedia message from a contact we know about..
} else if (record.isMms() && !((MmsMessageRecord) record).getSharedContacts().isEmpty()) {
Contact contact = ((MmsMessageRecord) record).getSharedContacts().get(0);
body = ContactUtil.getStringSummary(context, contact);
// If this is a notification about a multimedia message which contains no text but DOES contain a slide deck with at least one slide..
} else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) {
slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck();
body = SpanUtil.italic(slideDeck.getBody());
// If this is a notification about a multimedia message, but it's not ITSELF a multimedia notification AND it contains a slide deck with at least one slide..
} else if (record.isMms() && !record.isMmsNotification() && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) {
slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck();
String message = slideDeck.getBody() + ": " + record.getBody();
int italicLength = message.length() - body.length();
body = SpanUtil.italic(message, italicLength);
// If this is a notification about an invitation to a community..
} else if (record.isOpenGroupInvitation()) {
body = SpanUtil.italic(context.getString(R.string.ThreadRecord_open_group_invitation));
}
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
String blindedPublicKey = cache.get(threadId);
if (blindedPublicKey == null) {

View File

@ -1,11 +1,9 @@
package org.thoughtcrime.securesms.notifications;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;

View File

@ -8,7 +8,6 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.home.startHomeActivity
@ -33,7 +32,7 @@ class LoadingActivity: BaseActionBarActivity() {
when {
loadFailed -> startPickDisplayNameActivity(loadFailed = true)
else -> startHomeActivity()
else -> startHomeActivity(isNewAccount = false)
}
finish()
@ -44,8 +43,6 @@ class LoadingActivity: BaseActionBarActivity() {
setUpActionBarSessionLogo()
ApplicationContext.getInstance(this).newAccount = false
setComposeContent {
val progress by viewModel.progress.collectAsState()
LoadingScreen(progress)

View File

@ -49,7 +49,7 @@ class MessageNotificationsActivity : BaseActionBarActivity() {
viewModel.events.collect {
when (it) {
Event.Loading -> start<LoadingActivity>()
Event.OnboardingComplete -> startHomeActivity()
Event.OnboardingComplete -> startHomeActivity(isNewAccount = true)
}
}
}

View File

@ -45,7 +45,7 @@ class PickDisplayNameActivity : BaseActionBarActivity() {
viewModel.events.collect {
when (it) {
is Event.CreateAccount -> startMessageNotificationsActivity(it.profileName)
Event.LoadAccountComplete -> startHomeActivity()
Event.LoadAccountComplete -> startHomeActivity(isNewAccount = false)
}
}
}
@ -72,8 +72,6 @@ class PickDisplayNameActivity : BaseActionBarActivity() {
}
fun Context.startPickDisplayNameActivity(loadFailed: Boolean = false, flags: Int = 0) {
ApplicationContext.getInstance(this).newAccount = !loadFailed
Intent(this, PickDisplayNameActivity::class.java)
.apply { putExtra(EXTRA_LOAD_FAILED, loadFailed) }
.also { it.flags = flags }

View File

@ -72,7 +72,7 @@ class QRCodeActivity : PassphraseRequiredActionBarActivity() {
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun Tabs(sessionId: String, errors: Flow<String>, onScan: (String) -> Unit) {
private fun Tabs(accountId: String, errors: Flow<String>, onScan: (String) -> Unit) {
val pagerState = rememberPagerState { TITLES.size }
Column {
@ -82,7 +82,7 @@ private fun Tabs(sessionId: String, errors: Flow<String>, onScan: (String) -> Un
modifier = Modifier.weight(1f)
) { page ->
when (TITLES[page]) {
R.string.view -> QrPage(sessionId)
R.string.view -> QrPage(accountId)
R.string.scan -> MaybeScanQrCode(errors, onScan = onScan)
}
}

View File

@ -35,9 +35,6 @@ import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import dagger.hilt.android.AndroidEntryPoint
import java.io.File
import java.security.SecureRandom
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@ -49,7 +46,6 @@ import network.loki.messenger.BuildConfig
import network.loki.messenger.R
import network.loki.messenger.databinding.ActivitySettingsBinding
import network.loki.messenger.libsession_util.util.UserPic
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.ui.alwaysUi
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
@ -90,8 +86,12 @@ import org.thoughtcrime.securesms.ui.setThemedContent
import org.thoughtcrime.securesms.util.BitmapDecodingException
import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.NetworkUtils
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.show
import java.io.File
import java.security.SecureRandom
import javax.inject.Inject
@AndroidEntryPoint
class SettingsActivity : PassphraseRequiredActionBarActivity() {
@ -197,7 +197,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
try {
val profilePictureToBeUploaded = BitmapUtil.createScaledBytes(this@SettingsActivity, AvatarSelection.getResultUri(data), ProfileMediaConstraints()).bitmap
launch(Dispatchers.Main) {
updateProfile(true, profilePictureToBeUploaded)
updateProfilePicture(profilePictureToBeUploaded)
}
} catch (e: BitmapDecodingException) {
Log.e(TAG, e)
@ -246,57 +246,60 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
}
private fun updateProfile(
isUpdatingProfilePicture: Boolean,
profilePicture: ByteArray? = null,
displayName: String? = null
) {
private fun updateDisplayName(displayName: String): Boolean {
binding.loader.isVisible = true
if (displayName != null) {
TextSecurePreferences.setProfileName(this, displayName)
configFactory.user?.setName(displayName)
}
// We'll assume we fail & flip the flag on success
var updateWasSuccessful = false
// Bail if we're not updating the profile picture in any way
if (!isUpdatingProfilePicture) return
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
val uploadProfilePicturePromise: Promise<*, Exception>
var removingProfilePic = false
// Adding a new profile picture?
if (profilePicture != null) {
uploadProfilePicturePromise = ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this)
val haveNetworkConnection = NetworkUtils.haveValidNetworkConnection(this@SettingsActivity);
if (!haveNetworkConnection) {
Log.w(TAG, "Cannot update display name - no network connection.")
} else {
// If not then we must be removing the existing one.
// Note: To get a promise that will resolve / sync correctly we overwrite the existing profile picture with
// a 0 byte image.
removingProfilePic = true
val emptyByteArray = ByteArray(0)
uploadProfilePicturePromise = ProfilePictureUtilities.upload(emptyByteArray, encodedProfileKey, this)
// if we have a network connection then attempt to update the display name
TextSecurePreferences.setProfileName(this, displayName)
val user = configFactory.user
if (user == null) {
Log.w(TAG, "Cannot update display name - missing user details from configFactory.")
} else {
user.setName(displayName)
binding.btnGroupNameDisplay.text = displayName
updateWasSuccessful = true
}
}
// If the upload picture promise succeeded then we hit this successUi block
uploadProfilePicturePromise.successUi {
// Inform the user if we failed to update the display name
if (!updateWasSuccessful) {
Toast.makeText(this@SettingsActivity, R.string.profileErrorUpdate, Toast.LENGTH_LONG).show()
}
// If we successfully removed the profile picture on the network then we can clear the
// local data - otherwise it's weird to fail the online section but it _looks_ like it
// worked because we cleared the local image (also it denies them the chance to retry
// removal if we do it locally, and may result in them having a visible profile picture
// everywhere EXCEPT on their own device!).
if (removingProfilePic) {
binding.loader.isVisible = false
return updateWasSuccessful
}
// Helper method used by updateProfilePicture and removeProfilePicture to sync it online
private fun syncProfilePicture(profilePicture: ByteArray, onFail: () -> Unit) {
binding.loader.isVisible = true
// Grab the profile key and kick of the promise to update the profile picture
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
val updateProfilePicturePromise = ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this)
// If the online portion of the update succeeded then update the local state
updateProfilePicturePromise.successUi {
// When removing the profile picture the supplied ByteArray is empty so we'll clear the local data
if (profilePicture.isEmpty()) {
MessagingModuleConfiguration.shared.storage.clearUserPic()
}
val userConfig = configFactory.user
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
prefs.setProfileAvatarId(profilePicture?.let { SecureRandom().nextInt() } ?: 0 )
prefs.setProfileAvatarId(SecureRandom().nextInt() )
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
// new config
val url = TextSecurePreferences.getProfilePictureURL(this)
// Attempt to grab the details we require to update the profile picture
val url = prefs.getProfilePictureURL()
val profileKey = ProfileKeyUtil.getProfileKey(this)
// If we have a URL and a profile key then set the user's profile picture
@ -309,30 +312,52 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
// Update our visuals
binding.profilePictureView.recycle()
binding.profilePictureView.update()
}
// Or if the promise failed to upload the new profile picture then we hit this failUi block
uploadProfilePicturePromise.failUi {
if (removingProfilePic) {
Log.e(TAG, "Failed to remove profile picture")
Toast.makeText(this@SettingsActivity, R.string.profileDisplayPictureRemoveError, Toast.LENGTH_LONG).show()
} else {
Log.e(TAG, "Failed to upload profile picture")
Toast.makeText(this@SettingsActivity, R.string.profileErrorUpdate, Toast.LENGTH_LONG).show()
}
// If the sync failed then inform the user
updateProfilePicturePromise.failUi { onFail() }
// Finally, remove the loader animation after we've waited for the attempt to succeed or fail
updateProfilePicturePromise.alwaysUi { binding.loader.isVisible = false }
}
private fun updateProfilePicture(profilePicture: ByteArray) {
val haveNetworkConnection = NetworkUtils.haveValidNetworkConnection(this@SettingsActivity);
if (!haveNetworkConnection) {
Log.w(TAG, "Cannot update profile picture - no network connection.")
Toast.makeText(this@SettingsActivity, R.string.profileErrorUpdate, Toast.LENGTH_LONG).show()
return
}
// Finally, regardless of whether the promise succeeded or failed, we always hit this `alwaysUi` block
uploadProfilePicturePromise.alwaysUi {
if (displayName != null) {
binding.btnGroupNameDisplay.text = displayName
}
if (isUpdatingProfilePicture) {
binding.profilePictureView.recycle() // Clear the cached image before updating
binding.profilePictureView.update()
}
binding.loader.isVisible = false
val onFail: () -> Unit = {
Log.e(TAG, "Sync failed when uploading profile picture.")
Toast.makeText(this@SettingsActivity, R.string.profileErrorUpdate, Toast.LENGTH_LONG).show()
}
syncProfilePicture(profilePicture, onFail)
}
private fun removeProfilePicture() {
val haveNetworkConnection = NetworkUtils.haveValidNetworkConnection(this@SettingsActivity);
if (!haveNetworkConnection) {
Log.w(TAG, "Cannot remove profile picture - no network connection.")
Toast.makeText(this@SettingsActivity, R.string.profileDisplayPictureRemoveError, Toast.LENGTH_LONG).show()
return
}
val onFail: () -> Unit = {
Log.e(TAG, "Sync failed when removing profile picture.")
Toast.makeText(this@SettingsActivity, R.string.profileDisplayPictureRemoveError, Toast.LENGTH_LONG).show()
}
val emptyProfilePicture = ByteArray(0)
syncProfilePicture(emptyProfilePicture, onFail)
}
// endregion
@ -351,8 +376,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show()
return false
}
updateProfile(false, displayName = displayName)
return true
return updateDisplayName(displayName)
}
private fun showEditProfilePictureUI() {
@ -361,7 +385,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
view(R.layout.dialog_change_avatar)
button(R.string.activity_settings_upload) { startAvatarSelection() }
if (prefs.getProfileAvatarId() != 0) {
button(R.string.activity_settings_remove) { removeAvatar() }
button(R.string.activity_settings_remove) { removeProfilePicture() }
}
cancelButton()
}.apply {
@ -379,10 +403,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
}
private fun removeAvatar() {
updateProfile(true)
}
private fun startAvatarSelection() {
// Ask for an optional camera permission.
Permissions.with(this)

View File

@ -293,7 +293,7 @@ class DefaultConversationRepository @Inject constructor(
override suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): ResultOf<Unit> =
suspendCoroutine { continuation ->
// Note: This sessionId could be the blinded Id
// Note: This accountId could be the blinded Id
val accountID = recipient.address.toString()
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)!!

View File

@ -92,8 +92,8 @@ class ProfileManager(private val context: Context, private val configFactory: Co
override fun contactUpdatedInternal(contact: Contact): String? {
val contactConfig = configFactory.contacts ?: return null
if (contact.accountID == TextSecurePreferences.getLocalNumber(context)) return null
val sessionId = AccountId(contact.accountID)
if (sessionId.prefix != IdPrefix.STANDARD) return null // only internally store standard session IDs
val accountId = AccountId(contact.accountID)
if (accountId.prefix != IdPrefix.STANDARD) return null // only internally store standard account IDs
contactConfig.upsertContact(contact.accountID) {
this.name = contact.name.orEmpty()
this.nickname = contact.nickname.orEmpty()

View File

@ -205,7 +205,7 @@ object ConfigurationMessageUtilities {
val admins = group.admins.map { it.serialize() to true }.toMap()
val members = group.members.filterNot { it.serialize() !in admins.keys }.map { it.serialize() to false }.toMap()
GroupInfo.LegacyGroupInfo(
sessionId = groupPublicKey,
accountId = groupPublicKey,
name = group.title,
members = admins + members,
priority = if (isPinned) ConfigBase.PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE,

View File

@ -186,9 +186,6 @@
<string name="MediaOverviewActivity_Select_all">أختر الكل</string>
<string name="MediaOverviewActivity_collecting_attachments">جارٍ جمع المرفقات...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">رسالة وسائط متعددة</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">تنزيل رسالة الوسائط المتعددة</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">خطأ في تنزيل رسالة الوسائط المتعددة، انقر لاعادة المحاولة</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">إرسال إلى %s</string>
<!-- MediaSendActivity -->

View File

@ -186,9 +186,6 @@
<string name="MediaOverviewActivity_Select_all">أختر الكل</string>
<string name="MediaOverviewActivity_collecting_attachments">جارٍ جمع المرفقات...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">رسالة وسائط متعددة</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">تنزيل رسالة الوسائط المتعددة</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">خطأ في تنزيل رسالة الوسائط المتعددة، انقر لاعادة المحاولة</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">إرسال إلى %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Hamısını seç</string>
<string name="MediaOverviewActivity_collecting_attachments">Qoşmalar yığılır...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimedia mesajı</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS mesaj endirilir</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS mesajı endirmə xətası, yenidən sınamaq üçün toxunun</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s - göndər</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Hamısını seç</string>
<string name="MediaOverviewActivity_collecting_attachments">Qoşmalar yığılır...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimedia mesajı</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS mesaj endirilir</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS mesajı endirmə xətası, yenidən sınamaq üçün toxunun</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s - göndər</string>
<!-- MediaSendActivity -->

View File

@ -96,9 +96,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan lampiran...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Pesan multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Mengunduh pesan MMS.</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Gagal saat mengunduh pesan MMS, ketuk untuk mencoba lagi</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity -->

View File

@ -96,9 +96,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan lampiran...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Pesan multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Mengunduh pesan MMS.</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Gagal saat mengunduh pesan MMS, ketuk untuk mencoba lagi</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity -->

View File

@ -142,9 +142,6 @@
<string name="MediaOverviewActivity_Select_all">Избери всичко</string>
<string name="MediaOverviewActivity_collecting_attachments">Събиране на прикачени файлове...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Мултимедийно съобщение</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Изтегляне на MMS съобщение</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Грешка при изтегляне на MMS съобщение, натиснете за да опитате повторно</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Изпрати на %s</string>
<!-- MediaSendActivity -->

View File

@ -142,9 +142,6 @@
<string name="MediaOverviewActivity_Select_all">Избери всичко</string>
<string name="MediaOverviewActivity_collecting_attachments">Събиране на прикачени файлове...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Мултимедийно съобщение</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Изтегляне на MMS съобщение</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Грешка при изтегляне на MMS съобщение, натиснете за да опитате повторно</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Изпрати на %s</string>
<!-- MediaSendActivity -->

View File

@ -147,9 +147,6 @@
<string name="MediaOverviewActivity_Select_all">Selecciona-ho tot</string>
<string name="MediaOverviewActivity_collecting_attachments">S\'estan adjuntant els fitxers...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Missatge multimèdia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">S\'està baixant el missatge MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">S\'ha produït un error en baixar el missatge MMS. Toqueu per tornar a intentar-ho</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envia-ho a %s</string>
<!-- MediaSendActivity -->

View File

@ -147,9 +147,6 @@
<string name="MediaOverviewActivity_Select_all">Selecciona-ho tot</string>
<string name="MediaOverviewActivity_collecting_attachments">S\'estan adjuntant els fitxers...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Missatge multimèdia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">S\'està baixant el missatge MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">S\'ha produït un error en baixar el missatge MMS. Toqueu per tornar a intentar-ho</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envia-ho a %s</string>
<!-- MediaSendActivity -->

View File

@ -168,9 +168,6 @@
<string name="MediaOverviewActivity_Select_all">Označit vše</string>
<string name="MediaOverviewActivity_collecting_attachments">Shromažďuji přílohy...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediální zpráva</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Stahuji MMS zprávu</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Chyba při stahování MMS zprávy, ťukněte pro opakování</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Poslat %s</string>
<!-- MediaSendActivity -->

View File

@ -168,9 +168,6 @@
<string name="MediaOverviewActivity_Select_all">Označit vše</string>
<string name="MediaOverviewActivity_collecting_attachments">Shromažďuji přílohy...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediální zpráva</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Stahuji MMS zprávu</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Chyba při stahování MMS zprávy, ťukněte pro opakování</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Poslat %s</string>
<!-- MediaSendActivity -->

View File

@ -172,9 +172,6 @@
<string name="MediaOverviewActivity_Select_all">Dewis popeth</string>
<string name="MediaOverviewActivity_collecting_attachments">Casglu atodiadau...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Neges amlgyfrwng</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Llwytho i lawr neges MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Gwall wrth lawrlwytho neges MMS, tapio i geisio eto</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Anfon i %s</string>
<!-- MediaSendActivity -->

View File

@ -172,9 +172,6 @@
<string name="MediaOverviewActivity_Select_all">Dewis popeth</string>
<string name="MediaOverviewActivity_collecting_attachments">Casglu atodiadau...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Neges amlgyfrwng</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Llwytho i lawr neges MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Gwall wrth lawrlwytho neges MMS, tapio i geisio eto</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Anfon i %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Vælg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Samler vedhæftninger...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimedie besked</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Downloader MMS...</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS besked kunne ikke downloades, tap for at prøve igen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Vælg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Samler vedhæftninger...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimedie besked</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Downloader MMS...</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS besked kunne ikke downloades, tap for at prøve igen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Alle auswählen</string>
<string name="MediaOverviewActivity_collecting_attachments">Anhänge werden gesammelt </string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">MMS</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS wird heruntergeladen </string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Fehler beim Herunterladen der MMS. Für erneuten Versuch antippen.</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">An %s senden</string>
<!-- MediaSendActivity -->
@ -569,7 +566,7 @@
<string name="activity_create_private_chat_scan_qr_code_explanation">Scannen Sie den QR-Code eines Benutzers, um eine Session zu starten. QR-Codes finden Sie, indem Sie in den Einstellungen auf das QR-Code-Symbol tippen.</string>
<string name="fragment_enter_public_key_edit_text_hint">Sitzungs-ID oder ONS-Name eingeben</string>
<string name="fragment_enter_public_key_explanation">Benutzer können ihre Account ID freigeben, indem sie in ihren Einstellungen auf \"Account ID freigeben\" tippen oder ihren QR-Code freigeben.</string>
<string name="fragment_enter_public_key_error_message">Bitte überprüfe die Session-ID oder den ONS-Namen und versuche es erneut.</string>
<string name="fragment_enter_public_key_error_message">Bitte überprüfe die Account-ID oder den ONS-Namen und versuche es erneut.</string>
<string name="fragment_scan_qr_code_camera_access_explanation">Session benötigt Kamerazugriff, um die QR-Codes scannen zu können.</string>
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Kamerazugriff gewähren</string>
<string name="activity_create_closed_group_title">Neue geschlossene Gruppe</string>
@ -641,7 +638,7 @@
<string name="dialog_ui_mode_option_day">Tag</string>
<string name="dialog_ui_mode_option_night">Nacht</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</string>
<string name="activity_conversation_menu_copy_account_id">Session-ID kopieren</string>
<string name="activity_conversation_menu_copy_account_id">Account-ID kopieren</string>
<string name="attachment">Anhang</string>
<string name="attachment_type_voice_message">Sprachnachricht</string>
<string name="details">Details</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Alle auswählen</string>
<string name="MediaOverviewActivity_collecting_attachments">Anhänge werden gesammelt </string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">MMS</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS wird heruntergeladen </string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Fehler beim Herunterladen der MMS. Für erneuten Versuch antippen.</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">An %s senden</string>
<!-- MediaSendActivity -->
@ -569,7 +566,7 @@
<string name="activity_create_private_chat_scan_qr_code_explanation">Scannen Sie den QR-Code eines Benutzers, um eine Session zu starten. QR-Codes finden Sie, indem Sie in den Einstellungen auf das QR-Code-Symbol tippen.</string>
<string name="fragment_enter_public_key_edit_text_hint">Sitzungs-ID oder ONS-Name eingeben</string>
<string name="fragment_enter_public_key_explanation">Benutzer können ihre Account ID freigeben, indem sie in ihren Einstellungen auf \"Account ID freigeben\" tippen oder ihren QR-Code freigeben.</string>
<string name="fragment_enter_public_key_error_message">Bitte überprüfe die Session-ID oder den ONS-Namen und versuche es erneut.</string>
<string name="fragment_enter_public_key_error_message">Bitte überprüfe die Account-ID oder den ONS-Namen und versuche es erneut.</string>
<string name="fragment_scan_qr_code_camera_access_explanation">Session benötigt Kamerazugriff, um die QR-Codes scannen zu können.</string>
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Kamerazugriff gewähren</string>
<string name="activity_create_closed_group_title">Neue geschlossene Gruppe</string>
@ -641,7 +638,7 @@
<string name="dialog_ui_mode_option_day">Tag</string>
<string name="dialog_ui_mode_option_night">Nacht</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</string>
<string name="activity_conversation_menu_copy_account_id">Session-ID kopieren</string>
<string name="activity_conversation_menu_copy_account_id">Account-ID kopieren</string>
<string name="attachment">Anhang</string>
<string name="attachment_type_voice_message">Sprachnachricht</string>
<string name="details">Details</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Επιλογή όλων</string>
<string name="MediaOverviewActivity_collecting_attachments">Τα συνημμένα συλλέγονται...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Μήνυμα πολυμέσων</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Το μήνυμα MMS λαμβάνεται</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Πρόβλημα κατά τη λήψη μηνύματος MMS, πατήστε για να ξαναδοκιμάσουμε</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Αποστολή σε %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Επιλογή όλων</string>
<string name="MediaOverviewActivity_collecting_attachments">Τα συνημμένα συλλέγονται...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Μήνυμα πολυμέσων</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Το μήνυμα MMS λαμβάνεται</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Πρόβλημα κατά τη λήψη μηνύματος MMS, πατήστε για να ξαναδοκιμάσουμε</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Αποστολή σε %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Elekti ĉiujn</string>
<string name="MediaOverviewActivity_collecting_attachments">Kolekto de kunsendaĵoj...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Aŭdvida mesaĝo</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Elŝutante MMS-mesaĝon</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Eraro dum elŝuto de MMS-mesaĝo, tuŝetu por reprovi</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Sendi al %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Elekti ĉiujn</string>
<string name="MediaOverviewActivity_collecting_attachments">Kolekto de kunsendaĵoj...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Aŭdvida mesaĝo</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Elŝutante MMS-mesaĝon</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Eraro dum elŝuto de MMS-mesaĝo, tuŝetu por reprovi</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Sendi al %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recopilando adjuntos ...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Mensaje multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Descargando mensaje MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Fallo al descargar mensaje MMS, toca para reintentar</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recopilando adjuntos ...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Mensaje multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Descargando mensaje MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Fallo al descargar mensaje MMS, toca para reintentar</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Vali kõik</string>
<string name="MediaOverviewActivity_collecting_attachments">Kogun manuseid...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimeediasõnum</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Laadin alla MMS-sõnumit</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS-sõnumi allalaadimisel tekkis viga, koputa uuesti proovimiseks</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Saada kohta %s</string>
<!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Vali kõik</string>
<string name="MediaOverviewActivity_collecting_attachments">Kogun manuseid...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimeediasõnum</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Laadin alla MMS-sõnumit</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS-sõnumi allalaadimisel tekkis viga, koputa uuesti proovimiseks</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Saada kohta %s</string>
<!-- MediaSendActivity -->

View File

@ -156,9 +156,6 @@
<string name="MediaOverviewActivity_Select_all">انتخاب همه</string>
<string name="MediaOverviewActivity_collecting_attachments">در حال جمع‌آوری پیوست‌ها...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">پیام های چند رسانه ای</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">در حال بارگیری پیام چندرسانه‌ای...</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">بروز مشکل در دانلود پیام MMS، جهت تلاش دوباره تپ کنید</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">ارسال به %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">انتخاب همه</string>
<string name="MediaOverviewActivity_collecting_attachments">در حال جمع‌آوری پیوست‌ها...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">پیام های چند رسانه ای</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">در حال بارگیری پیام چندرسانه‌ای...</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">بروز مشکل در دانلود پیام MMS، جهت تلاش دوباره تپ کنید</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">ارسال به %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Valitse kaikki</string>
<string name="MediaOverviewActivity_collecting_attachments">Kerätään liitetiedostoja...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediaviesti</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Ladataan MMS-viestiä</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Virhe ladattaessa MMS-viestiä. Yritä uudelleen napauttamalla.</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Lähetä yhteystiedolle %s</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Valitse kaikki</string>
<string name="MediaOverviewActivity_collecting_attachments">Kerätään liitetiedostoja...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediaviesti</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Ladataan MMS-viestiä</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Virhe ladattaessa MMS-viestiä. Yritä uudelleen napauttamalla.</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Lähetä yhteystiedolle %s</string>
<!-- MediaSendActivity -->

View File

@ -157,9 +157,6 @@
<string name="MediaOverviewActivity_Select_all">Tout sélectionner</string>
<string name="MediaOverviewActivity_collecting_attachments">Récupération des pièces jointes…</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Message multimédia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Téléchargement du message multimédia</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Erreur de téléchargement du message multimédia. Touchez pour ressayer</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envoyer à %s</string>
<!-- MediaSendActivity -->

View File

@ -157,9 +157,6 @@
<string name="MediaOverviewActivity_Select_all">Tout sélectionner</string>
<string name="MediaOverviewActivity_collecting_attachments">Récupération des pièces jointes…</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Message multimédia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Téléchargement du message multimédia</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Erreur de téléchargement du message multimédia. Touchez pour ressayer</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envoyer à %s</string>
<!-- MediaSendActivity -->

View File

@ -149,9 +149,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recompilando anexos...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Mensaxe multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Descargando mensaxe MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Erro ao descargar a mensaxe MMS, toca para volver tentar</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity -->

View File

@ -149,9 +149,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recompilando anexos...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Mensaxe multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Descargando mensaxe MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Erro ao descargar a mensaxe MMS, toca para volver tentar</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Gişan bibijêre</string>
<string name="MediaOverviewActivity_collecting_attachments">...Servehî tên berhevkirin</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Peyama multîmedyayê</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Peyama MMS tê daxistin</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Peyama MMSê nehate daxistin, ji bo dîsa hewldanê bitepîne</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ji %s re bişîne</string>
<!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Gişan bibijêre</string>
<string name="MediaOverviewActivity_collecting_attachments">...Servehî tên berhevkirin</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Peyama multîmedyayê</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Peyama MMS tê daxistin</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Peyama MMSê nehate daxistin, ji bo dîsa hewldanê bitepîne</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ji %s re bişîne</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">सभी को चुन लो स`</string>
<string name="MediaOverviewActivity_collecting_attachments">अटैचमेंट्स इकट्ठे कर रहे हैं...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">मल्टीमीडिया संदेश</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">एमएमएस संदेश डाउनलोड किया जा रहा है</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">एमएमएस संदेश डाउनलोड करने में त्रुटि, पुनः प्रयास करने के लिए टैप करें</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s को भेजें</string>
<!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">सभी को चुन लो स`</string>
<string name="MediaOverviewActivity_collecting_attachments">अटैचमेंट्स इकट्ठे कर रहे हैं...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">मल्टीमीडिया संदेश</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">एमएमएस संदेश डाउनलोड किया जा रहा है</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">एमएमएस संदेश डाउनलोड करने में त्रुटि, पुनः प्रयास करने के लिए टैप करें</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s को भेजें</string>
<!-- MediaSendActivity -->

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Összes kiválasztása</string>
<string name="MediaOverviewActivity_collecting_attachments">Mellékletek összegyűjtése...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimédia üzenet</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS üzenet letöltése</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Hiba történt az MMS üzenet letöltése során, koppints az újrapróbálkozáshoz</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Küldés neki: %s</string>
<!-- MediaSendActivity -->

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Összes kiválasztása</string>
<string name="MediaOverviewActivity_collecting_attachments">Mellékletek összegyűjtése...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimédia üzenet</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS üzenet letöltése</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Hiba történt az MMS üzenet letöltése során, koppints az újrapróbálkozáshoz</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Küldés neki: %s</string>
<!-- MediaSendActivity -->

View File

@ -155,9 +155,6 @@
<string name="MediaOverviewActivity_Select_all">Ընտրել բոլորը</string>
<string name="MediaOverviewActivity_collecting_attachments">Կցորդների հավաքում...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Մուլտիմեդիա հաղորդագրություններ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Ներբեռնում է MMS հաղորդագրությունը</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Խնդիր առաջացավ MMS հաղորդագրությունը ներբեռնելիս, սեղմեք կրկին փորձելու համար</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ուղարկել %s֊ին</string>
<!-- MediaSendActivity -->

View File

@ -155,9 +155,6 @@
<string name="MediaOverviewActivity_Select_all">Ընտրել բոլորը</string>
<string name="MediaOverviewActivity_collecting_attachments">Կցորդների հավաքում...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Մուլտիմեդիա հաղորդագրություններ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Ներբեռնում է MMS հաղորդագրությունը</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Խնդիր առաջացավ MMS հաղորդագրությունը ներբեռնելիս, սեղմեք կրկին փորձելու համար</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ուղարկել %s֊ին</string>
<!-- MediaSendActivity -->

View File

@ -126,9 +126,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan semua lampiran...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Pesan Multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Mengunduh pesan MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Gagal mengunduh pesan MMS, sentuh untuk coba kembali</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity -->

View File

@ -126,9 +126,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan semua lampiran...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Pesan Multimedia</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Mengunduh pesan MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Gagal mengunduh pesan MMS, sentuh untuk coba kembali</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity -->

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Seleziona tutto</string>
<string name="MediaOverviewActivity_collecting_attachments">Recupero allegati...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Messaggio multimediale</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Scarico MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Errore nello scaricare MMS, premi per riprovare</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Invia a %s</string>
<!-- MediaSendActivity -->

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Seleziona tutto</string>
<string name="MediaOverviewActivity_collecting_attachments">Recupero allegati...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Messaggio multimediale</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Scarico MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Errore nello scaricare MMS, premi per riprovare</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Invia a %s</string>
<!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">בחר הכל</string>
<string name="MediaOverviewActivity_collecting_attachments">אוסף צרופות...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">הודעת מולטימדיה</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">מוריד הודעת MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">שגיאה בהורדת הודעת MMS, הקש כדי לנסות שוב</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">שלח אל %s</string>
<!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">בחר הכל</string>
<string name="MediaOverviewActivity_collecting_attachments">אוסף צרופות...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">הודעת מולטימדיה</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">מוריד הודעת MMS</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">שגיאה בהורדת הודעת MMS, הקש כדי לנסות שוב</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">שלח אל %s</string>
<!-- MediaSendActivity -->

View File

@ -140,9 +140,6 @@
<string name="MediaOverviewActivity_Select_all">すべて選択</string>
<string name="MediaOverviewActivity_collecting_attachments">添付ファイルを集めています...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">マルチメディアメッセージ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMSメッセージをダウンロード中</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMSメッセージのダウンロード中にエラーが発生しました。タップして再試行してください。</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%sに送信</string>
<!-- MediaSendActivity -->

View File

@ -140,9 +140,6 @@
<string name="MediaOverviewActivity_Select_all">すべて選択</string>
<string name="MediaOverviewActivity_collecting_attachments">添付ファイルを集めています...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">マルチメディアメッセージ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMSメッセージをダウンロード中</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMSメッセージのダウンロード中にエラーが発生しました。タップして再試行してください。</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%sに送信</string>
<!-- MediaSendActivity -->

View File

@ -127,9 +127,6 @@
<string name="MediaOverviewActivity_Select_all">ជ្រើសរើសទាំងអស់</string>
<string name="MediaOverviewActivity_collecting_attachments">កំពុងប្រមូលឯកសារភ្ជាប់...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">សារចម្រុះ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">កំពុងទាញយកសារ MMS </string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">មានបញ្ហាទាញយកសារMMS សូមចុច ដើម្បីព្យាយាមម្តងទៀត</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">ផ្ញើទៅកាន់ %s</string>
<!-- MediaSendActivity -->

View File

@ -127,9 +127,6 @@
<string name="MediaOverviewActivity_Select_all">ជ្រើសរើសទាំងអស់</string>
<string name="MediaOverviewActivity_collecting_attachments">កំពុងប្រមូលឯកសារភ្ជាប់...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">សារចម្រុះ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">កំពុងទាញយកសារ MMS </string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">មានបញ្ហាទាញយកសារMMS សូមចុច ដើម្បីព្យាយាមម្តងទៀត</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">ផ្ញើទៅកាន់ %s</string>
<!-- MediaSendActivity -->

View File

@ -75,7 +75,6 @@
<string name="MediaOverviewActivity_Documents">ದಾಖಲೆಗಳು</string>
<string name="MediaOverviewActivity_Select_all">ಎಲ್ಲ ಆಯ್ದುಕೊಳ್ಳಿ</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">ಮಲ್ಟಿಮೀಡಿಯಾ ಸಂದೇಶ</string>
<!-- MediaPickerActivity -->
<!-- MediaSendActivity -->
<!-- MediaRepository -->

View File

@ -75,7 +75,6 @@
<string name="MediaOverviewActivity_Documents">ದಾಖಲೆಗಳು</string>
<string name="MediaOverviewActivity_Select_all">ಎಲ್ಲ ಆಯ್ದುಕೊಳ್ಳಿ</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">ಮಲ್ಟಿಮೀಡಿಯಾ ಸಂದೇಶ</string>
<!-- MediaPickerActivity -->
<!-- MediaSendActivity -->
<!-- MediaRepository -->

View File

@ -136,9 +136,6 @@
<string name="MediaOverviewActivity_Select_all">모두 선택</string>
<string name="MediaOverviewActivity_collecting_attachments">첨부파일 저장 준비 중…</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">멀티미디어 메시지</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS 메시지 내려 받는 중</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS 내려 받기 오류, 다시 보내려면 다시 시도를 눌러주세요.</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s에게 전송</string>
<!-- MediaSendActivity -->

View File

@ -136,9 +136,6 @@
<string name="MediaOverviewActivity_Select_all">모두 선택</string>
<string name="MediaOverviewActivity_collecting_attachments">첨부파일 저장 준비 중…</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">멀티미디어 메시지</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS 메시지 내려 받는 중</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS 내려 받기 오류, 다시 보내려면 다시 시도를 눌러주세요.</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s에게 전송</string>
<!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">Žymėti visus</string>
<string name="MediaOverviewActivity_collecting_attachments">Renkami priedai...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimedijos žinutė</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Atsiunčiama MMS žinutė</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Klaida, atsiunčiant MMS žinutę, bakstelėkite, norėdami bandyti dar kartą</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Siųsti adresatui %s</string>
<!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">Žymėti visus</string>
<string name="MediaOverviewActivity_collecting_attachments">Renkami priedai...</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimedijos žinutė</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Atsiunčiama MMS žinutė</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Klaida, atsiunčiant MMS žinutę, bakstelėkite, norėdami bandyti dar kartą</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Siųsti adresatui %s</string>
<!-- MediaSendActivity -->

View File

@ -87,9 +87,6 @@
<string name="MediaOverviewActivity_Select_all">အားလုံးကို ရွေးပါ</string>
<string name="MediaOverviewActivity_collecting_attachments">ပူးတွဲဖိုင်များကို စုစည်းနေသည် </string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">ရုပ်၊သံပါ အချက်အလက်များ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS များအား ဒေါင်းလုပ်ဆွဲမည်</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS များအားဒေါင်းလုပ်ဆွဲနေစဉ် ရပ်တန့်သွားသည်၊ ပြန်စရန် နှိပ်ပါ</string>
<!-- MediaPickerActivity -->
<!-- MediaSendActivity -->
<!-- MediaRepository -->

View File

@ -87,9 +87,6 @@
<string name="MediaOverviewActivity_Select_all">အားလုံးကို ရွေးပါ</string>
<string name="MediaOverviewActivity_collecting_attachments">ပူးတွဲဖိုင်များကို စုစည်းနေသည် </string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">ရုပ်၊သံပါ အချက်အလက်များ</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">MMS များအား ဒေါင်းလုပ်ဆွဲမည်</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">MMS များအားဒေါင်းလုပ်ဆွဲနေစဉ် ရပ်တန့်သွားသည်၊ ပြန်စရန် နှိပ်ပါ</string>
<!-- MediaPickerActivity -->
<!-- MediaSendActivity -->
<!-- MediaRepository -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Velg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Henter vedlegg …</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediemelding</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Laster ned MMS-melding</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Feil under nedlasting av MMS-melding. Trykk for å prøve igjen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity -->
@ -554,7 +551,7 @@ nøkkelutvekslingsmelding.</string>
<string name="activity_home_delete_conversation_dialog_message">Er du sikker på at du vil slette denne samtalen?</string>
<string name="activity_home_conversation_deleted_message">Samtalen slettet</string>
<string name="view_seed_reminder_subtitle_1">Sikre kontoen din ved å lagre din gjenopprettingsfrase</string>
<string name="view_seed_reminder_subtitle_2">Trykk og hold inne de overflødige ordene for å hente gjenopprettingsfrasen din, og lagre den trygt å sikre din Session-ID.</string>
<string name="view_seed_reminder_subtitle_2">Trykk og hold inne de overflødige ordene for å hente gjenopprettingsfrasen din, og lagre den trygt å sikre din Account-ID.</string>
<string name="view_seed_reminder_subtitle_3">Pass på å lagre gjenopprettingsfrasen på et sikkert sted</string>
<string name="activity_path_title">Bane</string>
<string name="activity_path_explanation">Session skjuler din IP ved å laste ned meldingene dine gjennom flere Service Noder i Sessions desentraliserte nettverk. Disse er landene som koblingen din for øyeblikket blir kontaktet gjennom:</string>
@ -568,9 +565,9 @@ nøkkelutvekslingsmelding.</string>
<string name="activity_create_private_chat_enter_account_id_tab_title">Skriv inn Account ID</string>
<string name="activity_create_private_chat_scan_qr_code_tab_title">Skann QR-kode</string>
<string name="activity_create_private_chat_scan_qr_code_explanation">Skann en brukers QR-kode for å starte en økt. QR-koder finnes ved å trykke på QR-koden i kontoinnstillingene.</string>
<string name="fragment_enter_public_key_edit_text_hint">Angi Session-ID eller ONS-navn</string>
<string name="fragment_enter_public_key_explanation">Brukere kan dele sin Session-ID ved å gå inn i sine kontoinnstillinger og trykke på \"Del Session-ID\", eller ved å dele sin QR-kode.</string>
<string name="fragment_enter_public_key_error_message">Vennligst sjekk Session-ID\'en eller ONS-navnet og prøv igjen.</string>
<string name="fragment_enter_public_key_edit_text_hint">Angi Account-ID eller ONS-navn</string>
<string name="fragment_enter_public_key_explanation">Brukere kan dele sin Account-ID ved å gå inn i sine kontoinnstillinger og trykke på \"Del Account-ID\", eller ved å dele sin QR-kode.</string>
<string name="fragment_enter_public_key_error_message">Vennligst sjekk Account-ID\'en eller ONS-navnet og prøv igjen.</string>
<string name="fragment_scan_qr_code_camera_access_explanation">Session trenger kameratilgang for å skanne QR-koder</string>
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Gi kameratilgang</string>
<string name="activity_create_closed_group_title">Ny lukket gruppe</string>
@ -642,7 +639,7 @@ nøkkelutvekslingsmelding.</string>
<string name="dialog_ui_mode_option_day">Dag</string>
<string name="dialog_ui_mode_option_night">Natt</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</string>
<string name="activity_conversation_menu_copy_account_id">Kopier Session-ID</string>
<string name="activity_conversation_menu_copy_account_id">Kopier Account-ID</string>
<string name="attachment">Vedlegg</string>
<string name="attachment_type_voice_message">Talemelding</string>
<string name="details">Detaljer</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Velg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Henter vedlegg …</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediemelding</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Laster ned MMS-melding</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Feil under nedlasting av MMS-melding. Trykk for å prøve igjen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity -->
@ -554,7 +551,7 @@ nøkkelutvekslingsmelding.</string>
<string name="activity_home_delete_conversation_dialog_message">Er du sikker på at du vil slette denne samtalen?</string>
<string name="activity_home_conversation_deleted_message">Samtalen slettet</string>
<string name="view_seed_reminder_subtitle_1">Sikre kontoen din ved å lagre din gjenopprettingsfrase</string>
<string name="view_seed_reminder_subtitle_2">Trykk og hold inne de overflødige ordene for å hente gjenopprettingsfrasen din, og lagre den trygt å sikre din Session-ID.</string>
<string name="view_seed_reminder_subtitle_2">Trykk og hold inne de overflødige ordene for å hente gjenopprettingsfrasen din, og lagre den trygt å sikre din Account-ID.</string>
<string name="view_seed_reminder_subtitle_3">Pass på å lagre gjenopprettingsfrasen på et sikkert sted</string>
<string name="activity_path_title">Bane</string>
<string name="activity_path_explanation">Session skjuler din IP ved å laste ned meldingene dine gjennom flere Service Noder i Sessions desentraliserte nettverk. Disse er landene som koblingen din for øyeblikket blir kontaktet gjennom:</string>
@ -568,9 +565,9 @@ nøkkelutvekslingsmelding.</string>
<string name="activity_create_private_chat_enter_account_id_tab_title">Skriv inn Account ID</string>
<string name="activity_create_private_chat_scan_qr_code_tab_title">Skann QR-kode</string>
<string name="activity_create_private_chat_scan_qr_code_explanation">Skann en brukers QR-kode for å starte en økt. QR-koder finnes ved å trykke på QR-koden i kontoinnstillingene.</string>
<string name="fragment_enter_public_key_edit_text_hint">Angi Session-ID eller ONS-navn</string>
<string name="fragment_enter_public_key_explanation">Brukere kan dele sin Session-ID ved å gå inn i sine kontoinnstillinger og trykke på \"Del Session-ID\", eller ved å dele sin QR-kode.</string>
<string name="fragment_enter_public_key_error_message">Vennligst sjekk Session-ID\'en eller ONS-navnet og prøv igjen.</string>
<string name="fragment_enter_public_key_edit_text_hint">Angi Account-ID eller ONS-navn</string>
<string name="fragment_enter_public_key_explanation">Brukere kan dele sin Account-ID ved å gå inn i sine kontoinnstillinger og trykke på \"Del Account-ID\", eller ved å dele sin QR-kode.</string>
<string name="fragment_enter_public_key_error_message">Vennligst sjekk Account-ID\'en eller ONS-navnet og prøv igjen.</string>
<string name="fragment_scan_qr_code_camera_access_explanation">Session trenger kameratilgang for å skanne QR-koder</string>
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Gi kameratilgang</string>
<string name="activity_create_closed_group_title">Ny lukket gruppe</string>
@ -642,7 +639,7 @@ nøkkelutvekslingsmelding.</string>
<string name="dialog_ui_mode_option_day">Dag</string>
<string name="dialog_ui_mode_option_night">Natt</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</string>
<string name="activity_conversation_menu_copy_account_id">Kopier Session-ID</string>
<string name="activity_conversation_menu_copy_account_id">Kopier Account-ID</string>
<string name="attachment">Vedlegg</string>
<string name="attachment_type_voice_message">Talemelding</string>
<string name="details">Detaljer</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Alles selecteren</string>
<string name="MediaOverviewActivity_collecting_attachments">Bijlagen aan het verzamelen…</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediabericht</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Mms-bericht aan het downloaden</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Fout bij downloaden van mms-bericht, tik om opnieuw te proberen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Verzenden naar %s</string>
<!-- MediaSendActivity -->
@ -570,7 +567,7 @@
<string name="activity_create_private_chat_scan_qr_code_tab_title">Scan QR-code</string>
<string name="activity_create_private_chat_scan_qr_code_explanation">Scan de QR-code van een gebruiker om een sessie te starten. QR-codes kunnen worden gevonden door op het QR-icoon in de accountinstellingen te tikken.</string>
<string name="fragment_enter_public_key_edit_text_hint">Voer uw Account ID of ONS naam in</string>
<string name="fragment_enter_public_key_explanation">Gebruikers kunnen hun Session-ID delen door naar hun accountinstellingen te gaan en op \"Deel Session-ID\" te tikken, of door hun QR-code te delen.</string>
<string name="fragment_enter_public_key_explanation">Gebruikers kunnen hun Account-ID delen door naar hun accountinstellingen te gaan en op \"Deel Account-ID\" te tikken, of door hun QR-code te delen.</string>
<string name="fragment_enter_public_key_error_message">Controleer de sessie-ID of ONS naam en probeer het opnieuw.</string>
<string name="fragment_scan_qr_code_camera_access_explanation">Sessie heeft cameratoegang nodig om QR-codes te scannen</string>
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Toegang tot camera verlenen</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Alles selecteren</string>
<string name="MediaOverviewActivity_collecting_attachments">Bijlagen aan het verzamelen…</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediabericht</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Mms-bericht aan het downloaden</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Fout bij downloaden van mms-bericht, tik om opnieuw te proberen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Verzenden naar %s</string>
<!-- MediaSendActivity -->
@ -570,7 +567,7 @@
<string name="activity_create_private_chat_scan_qr_code_tab_title">Scan QR-code</string>
<string name="activity_create_private_chat_scan_qr_code_explanation">Scan de QR-code van een gebruiker om een sessie te starten. QR-codes kunnen worden gevonden door op het QR-icoon in de accountinstellingen te tikken.</string>
<string name="fragment_enter_public_key_edit_text_hint">Voer uw Account ID of ONS naam in</string>
<string name="fragment_enter_public_key_explanation">Gebruikers kunnen hun Session-ID delen door naar hun accountinstellingen te gaan en op \"Deel Session-ID\" te tikken, of door hun QR-code te delen.</string>
<string name="fragment_enter_public_key_explanation">Gebruikers kunnen hun Account-ID delen door naar hun accountinstellingen te gaan en op \"Deel Account-ID\" te tikken, of door hun QR-code te delen.</string>
<string name="fragment_enter_public_key_error_message">Controleer de sessie-ID of ONS naam en probeer het opnieuw.</string>
<string name="fragment_scan_qr_code_camera_access_explanation">Sessie heeft cameratoegang nodig om QR-codes te scannen</string>
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Toegang tot camera verlenen</string>

View File

@ -132,9 +132,6 @@
<string name="MediaOverviewActivity_Select_all">Vel alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Hentar vedlegg …</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediemelding</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Lastar ned MMS-melding</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Klarte ikkje lasta ned MMS-melding, trykk for å prøva igjen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity -->

View File

@ -132,9 +132,6 @@
<string name="MediaOverviewActivity_Select_all">Vel alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Hentar vedlegg …</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimediemelding</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Lastar ned MMS-melding</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">Klarte ikkje lasta ned MMS-melding, trykk for å prøva igjen</string>
<!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity -->

Some files were not shown because too many files have changed in this diff Show More