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.google.protobuf:protobuf-java:$protobufVersion"
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion" implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.phrase:phrase:$phraseVersion"
implementation 'app.cash.copper:copper-flow:1.0.0' implementation 'app.cash.copper:copper-flow:1.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"

View File

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

View File

@ -156,8 +156,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
private volatile boolean isAppVisible; private volatile boolean isAppVisible;
public boolean newAccount = false;
@Override @Override
public Object getSystemService(String name) { public Object getSystemService(String name) {
if (MessagingModuleConfiguration.MESSAGING_MODULE_SERVICE.equals(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.AppTextSecurePreferences
import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
@ -25,6 +26,8 @@ import org.thoughtcrime.securesms.mms.GlideRequests
class ProfilePictureView @JvmOverloads constructor( class ProfilePictureView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null context: Context, attrs: AttributeSet? = null
) : RelativeLayout(context, attrs) { ) : RelativeLayout(context, attrs) {
private val TAG = "ProfilePictureView"
private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this) private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this)
private val glide: GlideRequests = GlideApp.with(this) private val glide: GlideRequests = GlideApp.with(this)
private val prefs = AppTextSecurePreferences(context) private val prefs = AppTextSecurePreferences(context)
@ -91,7 +94,7 @@ class ProfilePictureView @JvmOverloads constructor(
} }
fun update() { 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 val additionalPublicKey = additionalPublicKey
if (additionalPublicKey != null) { if (additionalPublicKey != null) {
setProfilePictureIfNeeded(binding.doubleModeImageView1, publicKey, displayName) setProfilePictureIfNeeded(binding.doubleModeImageView1, publicKey, displayName)

View File

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

View File

@ -45,7 +45,7 @@ class NewMessageFragment : Fragment() {
viewModel, viewModel,
onClose = { delegate.onDialogClosePressed() }, onClose = { delegate.onDialogClosePressed() },
onBack = { delegate.onDialogBackPressed() }, 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 -> intent.getParcelableExtra<Address>(ADDRESS)?.let { it ->
threadId = threadDb.getThreadIdIfExistsFor(it.serialize()) threadId = threadDb.getThreadIdIfExistsFor(it.serialize())
if (threadId == -1L) { 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 openGroup = lokiThreadDb.getOpenGroupChat(intent.getLongExtra(FROM_GROUP_THREAD_ID, -1))
val address = if (sessionId.prefix == IdPrefix.BLINDED && openGroup != null) { val address = if (accountId.prefix == IdPrefix.BLINDED && openGroup != null) {
storage.getOrCreateBlindedIdMapping(sessionId.hexString, openGroup.server, openGroup.publicKey).sessionId?.let { storage.getOrCreateBlindedIdMapping(accountId.hexString, openGroup.server, openGroup.publicKey).accountId?.let {
fromSerialized(it) fromSerialized(it)
} ?: GroupUtil.getEncodedOpenGroupInboxID(openGroup, sessionId) } ?: GroupUtil.getEncodedOpenGroupInboxID(openGroup, accountId)
} else { } else {
it it
} }
@ -1131,8 +1131,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
} }
override fun copyAccountID(sessionId: String) { override fun copyAccountID(accountId: String) {
val clip = ClipData.newPlainText("Account ID", sessionId) val clip = ClipData.newPlainText("Account ID", accountId)
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
manager.setPrimaryClip(clip) manager.setPrimaryClip(clip)
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() 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.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Resources
import android.graphics.PointF import android.graphics.PointF
import android.net.Uri import android.net.Uri
import android.text.Editable import android.text.Editable
import android.text.InputType import android.text.InputType
import android.text.TextWatcher
import android.util.AttributeSet import android.util.AttributeSet
import android.view.KeyEvent import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isGone 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.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.SimpleTextWatcher
import org.thoughtcrime.securesms.util.addTextChangedListener import org.thoughtcrime.securesms.util.addTextChangedListener
import org.thoughtcrime.securesms.util.contains 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 // 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. // manipulate the UI faster than we can setup & teardown.
@ -46,16 +40,24 @@ enum class VoiceRecorderState {
ShuttingDownAfterRecord 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 { TextView.OnEditorActionListener {
private lateinit var binding: ViewInputBarBinding
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels private var binding: ViewInputBarBinding = ViewInputBarBinding.inflate(LayoutInflater.from(context), this, true)
private val vMargin by lazy { toDp(4, resources) }
private val minHeight by lazy { toPx(56, resources) }
private var linkPreviewDraftView: LinkPreviewDraftView? = null private var linkPreviewDraftView: LinkPreviewDraftView? = null
private var quoteView: QuoteView? = null private var quoteView: QuoteView? = null
var delegate: InputBarDelegate? = null var delegate: InputBarDelegate? = null
var additionalContentHeight = 0
var quote: MessageRecord? = null var quote: MessageRecord? = null
var linkPreview: LinkPreview? = null var linkPreview: LinkPreview? = null
var showInput: Boolean = true var showInput: Boolean = true
@ -68,7 +70,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
} }
var text: String var text: String
get() { return binding.inputBarEditText.text?.toString() ?: "" } get() = binding.inputBarEditText.text?.toString() ?: ""
set(value) { binding.inputBarEditText.setText(value) } set(value) { binding.inputBarEditText.setText(value) }
// Keep track of when the user pressed the record voice message button, the duration that // 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 voiceMessageDurationMS = 0L
var voiceRecorderState = VoiceRecorderState.Idle var voiceRecorderState = VoiceRecorderState.Idle
val attachmentButtonsContainerHeight: Int private val attachmentsButton = InputBarButton(context, R.drawable.ic_plus_24).apply { contentDescription = context.getString(R.string.AccessibilityId_attachments_button)}
get() = binding.attachmentsButtonContainer.height 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)} } init {
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)
// Attachments button // Attachments button
binding.attachmentsButtonContainer.addView(attachmentsButton) binding.attachmentsButtonContainer.addView(attachmentsButton)
attachmentsButton.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) 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.onUp` and tap the button then the logged output order is onUp and THEN onPress!
microphoneButton.setOnTouchListener(object : OnTouchListener { microphoneButton.setOnTouchListener(object : OnTouchListener {
override fun onTouch(v: View, event: MotionEvent): Boolean { 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 // We only handle single finger touch events so just consume the event and bail if there are more
if (event.pointerCount > 1) return true if (event.pointerCount > 1) return true
@ -160,12 +153,11 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
binding.inputBarEditText.setOnEditorActionListener(this) binding.inputBarEditText.setOnEditorActionListener(this)
if (TextSecurePreferences.isEnterSendsEnabled(context)) { if (TextSecurePreferences.isEnterSendsEnabled(context)) {
binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_SEND binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_SEND
binding.inputBarEditText.inputType = binding.inputBarEditText.inputType = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
} else { } else {
binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_NONE binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_NONE
binding.inputBarEditText.inputType = binding.inputBarEditText.inputType =
binding.inputBarEditText.inputType or binding.inputBarEditText.inputType
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
} }
val incognitoFlag = if (TextSecurePreferences.isIncognitoKeyboardEnabled(context)) 16777216 else 0 val incognitoFlag = if (TextSecurePreferences.isIncognitoKeyboardEnabled(context)) 16777216 else 0
@ -182,9 +174,6 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
return false return false
} }
// endregion
// region Updating
override fun inputBarEditTextContentChanged(text: CharSequence) { override fun inputBarEditTextContentChanged(text: CharSequence) {
microphoneButton.isVisible = text.trim().isEmpty() microphoneButton.isVisible = text.trim().isEmpty()
sendButton.isVisible = microphoneButton.isGone sendButton.isVisible = microphoneButton.isGone
@ -286,12 +275,9 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
fun setInputBarEditableFactory(factory: Editable.Factory) { fun setInputBarEditableFactory(factory: Editable.Factory) {
binding.inputBarEditText.setEditableFactory(factory) binding.inputBarEditText.setEditableFactory(factory)
} }
// endregion
} }
interface InputBarDelegate { interface InputBarDelegate {
fun inputBarEditTextContentChanged(newContent: CharSequence) fun inputBarEditTextContentChanged(newContent: CharSequence)
fun toggleAttachmentOptions() fun toggleAttachmentOptions()
fun showVoiceMessageUI() fun showVoiceMessageUI()

View File

@ -57,7 +57,7 @@ object ConversationMenuHelper {
if (!isOpenGroup && (thread.hasApprovedMe() || thread.isClosedGroupRecipient || thread.isLocalNumber)) { if (!isOpenGroup && (thread.hasApprovedMe() || thread.isClosedGroupRecipient || thread.isLocalNumber)) {
inflater.inflate(R.menu.menu_conversation_expiration, menu) 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) { if (thread.isContactRecipient) {
inflater.inflate(R.menu.menu_conversation_copy_account_id, menu) inflater.inflate(R.menu.menu_conversation_copy_account_id, menu)
} }
@ -325,7 +325,7 @@ object ConversationMenuHelper {
interface ConversationMenuListener { interface ConversationMenuListener {
fun block(deleteThread: Boolean = false) fun block(deleteThread: Boolean = false)
fun unblock() fun unblock()
fun copyAccountID(sessionId: String) fun copyAccountID(accountId: String)
fun copyOpenGroupUrl(thread: Recipient) fun copyOpenGroupUrl(thread: Recipient)
fun showDisappearingMessages(thread: Recipient) fun showDisappearingMessages(thread: Recipient)
} }

View File

@ -1,15 +1,12 @@
package org.thoughtcrime.securesms.conversation.v2.utilities package org.thoughtcrime.securesms.conversation.v2.utilities
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.text.style.BackgroundColorSpan
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan import android.text.style.StyleSpan
import android.util.Range import android.util.Range
import androidx.core.content.res.ResourcesCompat
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.combine.Tuple2 import nl.komponents.kovenant.combine.Tuple2
import org.session.libsession.messaging.contacts.Contact 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.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.util.RoundedBackgroundSpan import org.thoughtcrime.securesms.util.RoundedBackgroundSpan
import org.thoughtcrime.securesms.util.getAccentColor import org.thoughtcrime.securesms.util.getAccentColor
import org.thoughtcrime.securesms.util.toPx
import java.util.regex.Pattern import java.util.regex.Pattern
object MentionUtilities { object MentionUtilities {
@ -161,7 +157,7 @@ object MentionUtilities {
} }
private fun isYou(mentionedPublicKey: String, userPublicKey: String, openGroup: OpenGroup?): Boolean { 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 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 { private fun readBlindedIdMapping(cursor: Cursor): BlindedIdMapping {
return BlindedIdMapping( return BlindedIdMapping(
blindedId = cursor.getString(cursor.getColumnIndexOrThrow(BLINDED_PK)), 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)), serverUrl = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_URL)),
serverId = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_PK)), serverId = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_PK)),
) )
@ -58,7 +58,7 @@ class BlindedIdMappingDatabase(context: Context, helper: SQLCipherOpenHelper) :
try { try {
val values = ContentValues().apply { val values = ContentValues().apply {
put(BLINDED_PK, blindedIdMapping.blindedId) put(BLINDED_PK, blindedIdMapping.blindedId)
put(SERVER_PK, blindedIdMapping.sessionId) put(SERVER_PK, blindedIdMapping.accountId)
put(SERVER_URL, blindedIdMapping.serverUrl) put(SERVER_URL, blindedIdMapping.serverUrl)
put(SERVER_PK, blindedIdMapping.serverId) put(SERVER_PK, blindedIdMapping.serverId)
} }

View File

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

View File

@ -15,7 +15,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
companion object { companion object {
private const val sessionContactTable = "session_contact_database" private const val sessionContactTable = "session_contact_database"
const val accountID = "account_id" const val accountID = "session_id"
const val name = "name" const val name = "name"
const val nickname = "nickname" const val nickname = "nickname"
const val profilePictureURL = "profile_picture_url" 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 val database = databaseHelper.readableDatabase
return database.getAll( return database.getAll(
sessionContactTable, sessionContactTable,
"$accountID IN (SELECT value FROM json_each(?))", "$accountID IN (SELECT value FROM json_each(?))",
arrayOf(JSONArray(sessionIDs).toString()) arrayOf(JSONArray(accountIDs).toString())
) { cursor -> contactFromCursor(cursor) } ) { cursor -> contactFromCursor(cursor) }
} }
@ -56,8 +56,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
return database.getAll(sessionContactTable, null, null) { cursor -> return database.getAll(sessionContactTable, null, null) { cursor ->
contactFromCursor(cursor) contactFromCursor(cursor)
}.filter { contact -> }.filter { contact ->
val sessionId = AccountId(contact.accountID) contact.accountID.let(::AccountId).prefix == IdPrefix.STANDARD
sessionId.prefix == IdPrefix.STANDARD
}.toSet() }.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
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_HIDDEN 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_PINNED
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_VISIBLE
import network.loki.messenger.libsession_util.Contacts import network.loki.messenger.libsession_util.Contacts
import network.loki.messenger.libsession_util.ConversationVolatileConfig import network.loki.messenger.libsession_util.ConversationVolatileConfig
import network.loki.messenger.libsession_util.UserGroupsConfig import network.loki.messenger.libsession_util.UserGroupsConfig
@ -111,12 +112,12 @@ open class Storage(
if (address.isGroup) { if (address.isGroup) {
val groups = configFactory.userGroups ?: return val groups = configFactory.userGroups ?: return
if (address.isClosedGroup) { if (address.isClosedGroup) {
val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize()) val accountId = GroupUtil.doubleDecodeGroupId(address.serialize())
val closedGroup = getGroup(address.toGroupString()) val closedGroup = getGroup(address.toGroupString())
if (closedGroup != null && closedGroup.isActive) { if (closedGroup != null && closedGroup.isActive) {
val legacyGroup = groups.getOrConstructLegacyGroupInfo(sessionId) val legacyGroup = groups.getOrConstructLegacyGroupInfo(accountId)
groups.set(legacyGroup) groups.set(legacyGroup)
val newVolatileParams = volatile.getOrConstructLegacyGroup(sessionId).copy( val newVolatileParams = volatile.getOrConstructLegacyGroup(accountId).copy(
lastRead = SnodeAPI.nowWithOffset, lastRead = SnodeAPI.nowWithOffset,
) )
volatile.set(newVolatileParams) volatile.set(newVolatileParams)
@ -132,11 +133,11 @@ open class Storage(
if (getUserPublicKey() != address.serialize()) { if (getUserPublicKey() != address.serialize()) {
val contacts = configFactory.contacts ?: return val contacts = configFactory.contacts ?: return
contacts.upsertContact(address.serialize()) { contacts.upsertContact(address.serialize()) {
priority = ConfigBase.PRIORITY_VISIBLE priority = PRIORITY_VISIBLE
} }
} else { } else {
val userProfile = configFactory.user ?: return val userProfile = configFactory.user ?: return
userProfile.setNtsPriority(ConfigBase.PRIORITY_VISIBLE) userProfile.setNtsPriority(PRIORITY_VISIBLE)
DatabaseComponent.get(context).threadDatabase().setHasSent(threadId, true) DatabaseComponent.get(context).threadDatabase().setHasSent(threadId, true)
} }
val newVolatileParams = volatile.getOrConstructOneToOne(address.serialize()) val newVolatileParams = volatile.getOrConstructOneToOne(address.serialize())
@ -149,9 +150,9 @@ open class Storage(
if (address.isGroup) { if (address.isGroup) {
val groups = configFactory.userGroups ?: return val groups = configFactory.userGroups ?: return
if (address.isClosedGroup) { if (address.isClosedGroup) {
val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize()) val accountId = GroupUtil.doubleDecodeGroupId(address.serialize())
volatile.eraseLegacyClosedGroup(sessionId) volatile.eraseLegacyClosedGroup(accountId)
groups.eraseLegacyGroup(sessionId) groups.eraseLegacyGroup(accountId)
} else if (address.isCommunity) { } else if (address.isCommunity) {
// these should be removed in the group leave / handling new configs // 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") 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 // otherwise recipient is one to one
recipient.isContactRecipient -> { recipient.isContactRecipient -> {
// don't process non-standard session IDs though // don't process non-standard account IDs though
val sessionId = AccountId(recipient.address.serialize()) if (AccountId(recipient.address.serialize()).prefix != IdPrefix.STANDARD) return
if (sessionId.prefix != IdPrefix.STANDARD) return
config.getOrConstructOneToOne(recipient.address.serialize()) config.getOrConstructOneToOne(recipient.address.serialize())
} }
else -> throw NullPointerException("Weren't expecting to have a convo with address ${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 var messageID: Long? = null
val senderAddress = fromSerialized(message.sender!!) val senderAddress = fromSerialized(message.sender!!)
val isUserSender = (message.sender!! == getUserPublicKey()) val isUserSender = (message.sender!! == getUserPublicKey())
val isUserBlindedSender = message.threadID?.takeIf { it >= 0 }?.let { getOpenGroup(it)?.publicKey } val isUserBlindedSender = message.threadID?.takeIf { it >= 0 }?.let(::getOpenGroup)?.publicKey
?.let { SodiumUtilities.sessionId(getUserPublicKey()!!, message.sender!!, it) } ?: false ?.let { SodiumUtilities.accountId(getUserPublicKey()!!, message.sender!!, it) } ?: false
val group: Optional<SignalServiceGroup> = when { val group: Optional<SignalServiceGroup> = when {
openGroupID != null -> Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT)) openGroupID != null -> Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT))
groupPublicKey != null -> { groupPublicKey != null -> {
@ -540,7 +539,7 @@ open class Storage(
val extracted = convos.all() val extracted = convos.all()
for (conversation in extracted) { for (conversation in extracted) {
val threadId = when (conversation) { 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.LegacyGroup -> getThreadIdFor("", conversation.groupId,null, createThread = false)
is Conversation.Community -> getThreadIdFor("",null, "${conversation.baseCommunityInfo.baseUrl.removeSuffix("/")}.${conversation.baseCommunityInfo.room}", 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 existingJoinUrls = existingCommunities.values.map { it.joinURL }
val existingClosedGroups = getAllGroups(includeInactive = true).filter { it.isClosedGroup } 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 -> val toDeleteClosedGroups = existingClosedGroups.filter { group ->
GroupUtil.doubleDecodeGroupId(group.encodedId) !in lgcIds GroupUtil.doubleDecodeGroupId(group.encodedId) !in lgcIds
} }
@ -605,8 +604,8 @@ open class Storage(
} }
for (group in lgc) { for (group in lgc) {
val groupId = GroupUtil.doubleEncodeGroupID(group.sessionId) val groupId = GroupUtil.doubleEncodeGroupID(group.accountId)
val existingGroup = existingClosedGroups.firstOrNull { GroupUtil.doubleDecodeGroupId(it.encodedId) == group.sessionId } val existingGroup = existingClosedGroups.firstOrNull { GroupUtil.doubleDecodeGroupId(it.encodedId) == group.accountId }
val existingThread = existingGroup?.let { getThreadId(existingGroup.encodedId) } val existingThread = existingGroup?.let { getThreadId(existingGroup.encodedId) }
if (existingGroup != null) { if (existingGroup != null) {
if (group.priority == PRIORITY_HIDDEN && existingThread != null) { if (group.priority == PRIORITY_HIDDEN && existingThread != null) {
@ -625,19 +624,19 @@ open class Storage(
createGroup(groupId, title, admins + members, null, null, admins, formationTimestamp) createGroup(groupId, title, admins + members, null, null, admins, formationTimestamp)
setProfileSharing(Address.fromSerialized(groupId), true) setProfileSharing(Address.fromSerialized(groupId), true)
// Add the group to the user's set of public keys to poll for // 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 // Store the encryption key pair
val keyPair = ECKeyPair(DjbECPublicKey(group.encPubKey), DjbECPrivateKey(group.encSecKey)) 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 // Notify the PN server
PushRegistryV1.subscribeGroup(group.sessionId, publicKey = localUserPublicKey) PushRegistryV1.subscribeGroup(group.accountId, publicKey = localUserPublicKey)
// Notify the user // Notify the user
val threadID = getOrCreateThreadIdFor(Address.fromSerialized(groupId)) val threadID = getOrCreateThreadIdFor(Address.fromSerialized(groupId))
threadDb.setDate(threadID, formationTimestamp) threadDb.setDate(threadID, formationTimestamp)
insertOutgoingInfoMessage(context, groupId, SignalServiceGroup.Type.CREATION, title, members.map { it.serialize() }, admins.map { it.serialize() }, 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 // Don't create config group here, it's from a config update
// Start polling // Start polling
ClosedGroupPollerV2.shared.startPolling(group.sessionId) ClosedGroupPollerV2.shared.startPolling(group.accountId)
} }
getThreadId(Address.fromSerialized(groupId))?.let { getThreadId(Address.fromSerialized(groupId))?.let {
setExpirationConfiguration( setExpirationConfiguration(
@ -938,10 +937,10 @@ open class Storage(
groupVolatileConfig.lastRead = formationTimestamp groupVolatileConfig.lastRead = formationTimestamp
volatiles.set(groupVolatileConfig) volatiles.set(groupVolatileConfig)
val groupInfo = GroupInfo.LegacyGroupInfo( val groupInfo = GroupInfo.LegacyGroupInfo(
sessionId = groupPublicKey, accountId = groupPublicKey,
name = name, name = name,
members = members, members = members,
priority = ConfigBase.PRIORITY_VISIBLE, priority = PRIORITY_VISIBLE,
encPubKey = (encryptionKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte encPubKey = (encryptionKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
encSecKey = encryptionKeyPair.privateKey.serialize(), encSecKey = encryptionKeyPair.privateKey.serialize(),
disappearingTimer = expirationTimer.toLong(), disappearingTimer = expirationTimer.toLong(),
@ -975,7 +974,7 @@ open class Storage(
members = membersMap, members = membersMap,
encPubKey = (latestKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte encPubKey = (latestKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
encSecKey = latestKeyPair.privateKey.serialize(), 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, disappearingTimer = getExpirationConfiguration(threadID)?.expiryMode?.expirySeconds ?: 0L,
joinedAt = (existingGroup.formationTimestamp / 1000L) joinedAt = (existingGroup.formationTimestamp / 1000L)
) )
@ -1209,7 +1208,7 @@ open class Storage(
val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase() val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase()
val moreContacts = contacts.filter { contact -> val moreContacts = contacts.filter { contact ->
val id = AccountId(contact.id) 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 val profileManager = SSKEnvironment.shared.profileManager
moreContacts.forEach { contact -> moreContacts.forEach { contact ->
@ -1262,7 +1261,7 @@ open class Storage(
val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase() val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase()
val moreContacts = contacts.filter { contact -> val moreContacts = contacts.filter { contact ->
val id = AccountId(contact.publicKey) 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) { for (contact in moreContacts) {
val address = fromSerialized(contact.publicKey) val address = fromSerialized(contact.publicKey)
@ -1329,25 +1328,25 @@ open class Storage(
val threadRecipient = getRecipientForThread(threadID) ?: return val threadRecipient = getRecipientForThread(threadID) ?: return
if (threadRecipient.isLocalNumber) { if (threadRecipient.isLocalNumber) {
val user = configFactory.user ?: return 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) { } else if (threadRecipient.isContactRecipient) {
val contacts = configFactory.contacts ?: return val contacts = configFactory.contacts ?: return
contacts.upsertContact(threadRecipient.address.serialize()) { 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) { } else if (threadRecipient.isGroupRecipient) {
val groups = configFactory.userGroups ?: return val groups = configFactory.userGroups ?: return
if (threadRecipient.isClosedGroupRecipient) { if (threadRecipient.isClosedGroupRecipient) {
val sessionId = GroupUtil.doubleDecodeGroupId(threadRecipient.address.serialize()) threadRecipient.address.serialize()
val newGroupInfo = groups.getOrConstructLegacyGroupInfo(sessionId).copy ( .let(GroupUtil::doubleDecodeGroupId)
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE .let(groups::getOrConstructLegacyGroupInfo)
) .copy (priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE)
groups.set(newGroupInfo) .let(groups::set)
} else if (threadRecipient.isCommunityRecipient) { } else if (threadRecipient.isCommunityRecipient) {
val openGroup = getOpenGroup(threadID) ?: return val openGroup = getOpenGroup(threadID) ?: return
val (baseUrl, room, pubKeyHex) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: return val (baseUrl, room, pubKeyHex) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: return
val newGroupInfo = groups.getOrConstructCommunityInfo(baseUrl, room, Hex.toStringCondensed(pubKeyHex)).copy ( 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) groups.set(newGroupInfo)
} }
@ -1505,10 +1504,10 @@ open class Storage(
} }
} }
for (mapping in mappings) { 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 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)) val blindedThreadId = threadDB.getOrCreateThreadIdFor(Recipient.from(context, fromSerialized(mapping.key), false))
mmsDb.updateThreadId(blindedThreadId, threadId) mmsDb.updateThreadId(blindedThreadId, threadId)
@ -1614,20 +1613,20 @@ open class Storage(
): BlindedIdMapping { ): BlindedIdMapping {
val db = DatabaseComponent.get(context).blindedIdMappingDatabase() val db = DatabaseComponent.get(context).blindedIdMappingDatabase()
val mapping = db.getBlindedIdMapping(blindedId).firstOrNull() ?: BlindedIdMapping(blindedId, null, server, serverPublicKey) val mapping = db.getBlindedIdMapping(blindedId).firstOrNull() ?: BlindedIdMapping(blindedId, null, server, serverPublicKey)
if (mapping.sessionId != null) { if (mapping.accountId != null) {
return mapping return mapping
} }
getAllContacts().forEach { contact -> getAllContacts().forEach { contact ->
val sessionId = AccountId(contact.accountID) val accountId = AccountId(contact.accountID)
if (sessionId.prefix == IdPrefix.STANDARD && SodiumUtilities.sessionId(sessionId.hexString, blindedId, serverPublicKey)) { if (accountId.prefix == IdPrefix.STANDARD && SodiumUtilities.accountId(accountId.hexString, blindedId, serverPublicKey)) {
val contactMapping = mapping.copy(sessionId = sessionId.hexString) val contactMapping = mapping.copy(accountId = accountId.hexString)
db.addBlindedIdMapping(contactMapping) db.addBlindedIdMapping(contactMapping)
return contactMapping return contactMapping
} }
} }
db.getBlindedIdMappingsExceptFor(server).forEach { db.getBlindedIdMappingsExceptFor(server).forEach {
if (SodiumUtilities.sessionId(it.sessionId!!, blindedId, serverPublicKey)) { if (SodiumUtilities.accountId(it.accountId!!, blindedId, serverPublicKey)) {
val otherMapping = mapping.copy(sessionId = it.sessionId) val otherMapping = mapping.copy(accountId = it.accountId)
db.addBlindedIdMapping(otherMapping) db.addBlindedIdMapping(otherMapping)
return 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.content.res.Resources
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.text.TextUtils import android.text.TextUtils
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue 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.RecipientDatabase.NOTIFY_TYPE_NONE
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.getAccentColor import org.thoughtcrime.securesms.util.getAccentColor
import org.thoughtcrime.securesms.util.getConversationUnread import org.thoughtcrime.securesms.util.getConversationUnread
@ -51,7 +49,7 @@ class ConversationView : LinearLayout {
// endregion // endregion
// region Updating // region Updating
fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) { fun bind(thread: ThreadRecord, isTyping: Boolean) {
this.thread = thread this.thread = thread
if (thread.isPinned) { if (thread.isPinned) {
binding.conversationViewDisplayNameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds( binding.conversationViewDisplayNameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(
@ -142,11 +140,10 @@ class ConversationView : LinearLayout {
else -> recipient.toShortString() // Internally uses the Contact API else -> recipient.toShortString() // Internally uses the Contact API
} }
private fun ThreadRecord.getSnippet(): CharSequence = private fun ThreadRecord.getSnippet(): CharSequence = listOfNotNull(
concatSnippet(getSnippetPrefix(), getDisplayBody(context)) getSnippetPrefix(),
getDisplayBody(context)
private fun concatSnippet(prefix: CharSequence?, body: CharSequence): CharSequence = ).joinToString(": ")
prefix?.let { TextUtils.concat(it, ": ", body) } ?: body
private fun ThreadRecord.getSnippetPrefix(): CharSequence? = when { private fun ThreadRecord.getSnippetPrefix(): CharSequence? = when {
recipient.isLocalNumber || lastMessage?.isControlMessage == true -> null recipient.isLocalNumber || lastMessage?.isControlMessage == true -> null

View File

@ -8,7 +8,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Telephony.Mms.Addr
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
@ -23,6 +22,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import network.loki.messenger.R 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.groups.OpenGroupManager
import org.thoughtcrime.securesms.home.search.GlobalSearchAdapter import org.thoughtcrime.securesms.home.search.GlobalSearchAdapter
import org.thoughtcrime.securesms.home.search.GlobalSearchInputLayout 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.home.search.GlobalSearchViewModel
import org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity import org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
@ -85,6 +86,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
GlobalSearchInputLayout.GlobalSearchInputLayoutListener { GlobalSearchInputLayout.GlobalSearchInputLayoutListener {
companion object { companion object {
const val NEW_ACCOUNT = "HomeActivity_NEW_ACCOUNT"
const val FROM_ONBOARDING = "HomeActivity_FROM_ONBOARDING" 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 // region Lifecycle
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
@ -175,7 +179,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
// Set up empty state view // Set up empty state view
binding.emptyStateContainer.setThemedContent { binding.emptyStateContainer.setThemedContent {
EmptyView(ApplicationContext.getInstance(this).newAccount) EmptyView(isNewAccount)
} }
IP2Country.configureIfNeeded(this@HomeActivity) IP2Country.configureIfNeeded(this@HomeActivity)
@ -240,67 +244,25 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} }
// Get group results and display them // Get group results and display them
launch { launch {
globalSearchViewModel.result.collect { result -> globalSearchViewModel.result.map { result ->
if (result.query.isEmpty()) { result.query to when {
class NamedValue<T>(val name: String?, val value: T) result.query.isEmpty() -> buildList {
add(GlobalSearchAdapter.Model.Header(R.string.contacts))
// Unknown is temporarily to be grouped together with numbers title. add(GlobalSearchAdapter.Model.SavedMessages(publicKey))
// https://optf.atlassian.net/browse/SES-2287 addAll(result.groupedContacts)
val numbersTitle = "#" }
val unknownTitle = numbersTitle else -> buildList {
result.contactAndGroupList.takeUnless { it.isEmpty() }?.let {
listOf( add(GlobalSearchAdapter.Model.Header(R.string.contacts))
GlobalSearchAdapter.Model.Header(R.string.contacts), addAll(it)
GlobalSearchAdapter.Model.SavedMessages(publicKey) }
) + result.contacts result.messageResults.takeUnless { it.isEmpty() }?.let {
// Remove ourself, we're shown above. add(GlobalSearchAdapter.Model.Header(R.string.global_search_messages))
.filter { it.accountID != publicKey } addAll(it)
// 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) }
} }
} 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 }.collectLatest(globalSearchAdapter::setNewData)
.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) }
}
} }
} }
EventBus.getDefault().register(this@HomeActivity) 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) { override fun onInputFocusChanged(hasFocus: Boolean) {
setSearchShown(hasFocus || binding.globalSearchInputLayout.query.value.isNotEmpty()) 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 { Intent(this, HomeActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra(HomeActivity.NEW_ACCOUNT, true)
putExtra(HomeActivity.FROM_ONBOARDING, true) putExtra(HomeActivity.FROM_ONBOARDING, true)
}.also(::startActivity) }.also(::startActivity)
} }

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.home
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.ListUpdateCallback
@ -12,8 +11,6 @@ import network.loki.messenger.R
import network.loki.messenger.databinding.ViewMessageRequestBannerBinding import network.loki.messenger.databinding.ViewMessageRequestBannerBinding
import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils
import java.util.Locale
class HomeAdapter( class HomeAdapter(
private val context: Context, private val context: Context,
@ -115,7 +112,7 @@ class HomeAdapter(
val offset = if (hasHeaderView()) position - 1 else position val offset = if (hasHeaderView()) position - 1 else position
val thread = data.threads[offset] val thread = data.threads[offset]
val isTyping = data.typingThreadIDs.contains(thread.threadId) 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 data: List<Model> = listOf()
private var query: String? = null private var query: String? = null
fun setNewData(data: Pair<String, List<Model>>) = setNewData(data.first, data.second)
fun setNewData(query: String, newData: List<Model>) { fun setNewData(query: String, newData: List<Model>) {
val diffResult = DiffUtil.calculateDiff(GlobalSearchDiff(this.query, query, data, newData)) val diffResult = DiffUtil.calculateDiff(GlobalSearchDiff(this.query, query, data, newData))
this.query = query this.query = query
@ -134,7 +136,7 @@ class GlobalSearchAdapter(private val modelCallback: (Model)->Unit): RecyclerVie
constructor(title: String): this(GetString(title)) constructor(title: String): this(GetString(title))
} }
data class SavedMessages(val currentUserPublicKey: String): Model() 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 GroupConversation(val groupRecord: GroupRecord): Model()
data class Message(val messageResult: MessageResult, val unread: Int, val isSelf: Boolean): 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.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.session.libsignal.utilities.SettableFuture import org.session.libsignal.utilities.SettableFuture
@ -35,16 +31,32 @@ import javax.inject.Inject
class GlobalSearchViewModel @Inject constructor( class GlobalSearchViewModel @Inject constructor(
private val searchRepository: SearchRepository, private val searchRepository: SearchRepository,
) : ViewModel() { ) : ViewModel() {
private val scope = viewModelScope + SupervisorJob()
private val executor = viewModelScope + SupervisorJob()
private val _result: MutableStateFlow<GlobalSearchResult> = MutableStateFlow(GlobalSearchResult.EMPTY)
val result: StateFlow<GlobalSearchResult> = _result
private val refreshes = MutableSharedFlow<Unit>() 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) { fun setQuery(charSequence: CharSequence) {
_queryText.value = charSequence _queryText.value = charSequence
@ -55,34 +67,6 @@ class GlobalSearchViewModel @Inject constructor(
refreshes.emit(Unit) 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 // region Updating
fun bind(thread: ThreadRecord, glide: GlideRequests) { fun bind(thread: ThreadRecord, glide: GlideRequests) {
this.thread = thread 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.displayNameTextView.text = senderDisplayName
binding.timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), thread.date) binding.timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), thread.date)
val rawSnippet = thread.getDisplayBody(context)
val snippet = highlightMentions( val snippet = highlightMentions(
text = rawSnippet, text = thread.getDisplayBody(context),
formatOnly = true, // no styling here, only text formatting formatOnly = true, // no styling here, only text formatting
threadID = thread.threadId, threadID = thread.threadId,
context = context context = context

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -293,7 +293,7 @@ class DefaultConversationRepository @Inject constructor(
override suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): ResultOf<Unit> = override suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): ResultOf<Unit> =
suspendCoroutine { continuation -> suspendCoroutine { continuation ->
// Note: This sessionId could be the blinded Id // Note: This accountId could be the blinded Id
val accountID = recipient.address.toString() val accountID = recipient.address.toString()
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)!! 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? { override fun contactUpdatedInternal(contact: Contact): String? {
val contactConfig = configFactory.contacts ?: return null val contactConfig = configFactory.contacts ?: return null
if (contact.accountID == TextSecurePreferences.getLocalNumber(context)) return null if (contact.accountID == TextSecurePreferences.getLocalNumber(context)) return null
val sessionId = AccountId(contact.accountID) val accountId = AccountId(contact.accountID)
if (sessionId.prefix != IdPrefix.STANDARD) return null // only internally store standard session IDs if (accountId.prefix != IdPrefix.STANDARD) return null // only internally store standard account IDs
contactConfig.upsertContact(contact.accountID) { contactConfig.upsertContact(contact.accountID) {
this.name = contact.name.orEmpty() this.name = contact.name.orEmpty()
this.nickname = contact.nickname.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 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() val members = group.members.filterNot { it.serialize() !in admins.keys }.map { it.serialize() to false }.toMap()
GroupInfo.LegacyGroupInfo( GroupInfo.LegacyGroupInfo(
sessionId = groupPublicKey, accountId = groupPublicKey,
name = group.title, name = group.title,
members = admins + members, members = admins + members,
priority = if (isPinned) ConfigBase.PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE, 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_Select_all">أختر الكل</string>
<string name="MediaOverviewActivity_collecting_attachments">جارٍ جمع المرفقات...</string> <string name="MediaOverviewActivity_collecting_attachments">جارٍ جمع المرفقات...</string>
<!-- NotificationMmsMessageRecord --> <!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">رسالة وسائط متعددة</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">تنزيل رسالة الوسائط المتعددة</string>
<string name="NotificationMmsMessageRecord_error_downloading_mms_message">خطأ في تنزيل رسالة الوسائط المتعددة، انقر لاعادة المحاولة</string>
<!-- MediaPickerActivity --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">إرسال إلى %s</string> <string name="MediaPickerActivity_send_to">إرسال إلى %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

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

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Hamısını seç</string> <string name="MediaOverviewActivity_Select_all">Hamısını seç</string>
<string name="MediaOverviewActivity_collecting_attachments">Qoşmalar yığılır...</string> <string name="MediaOverviewActivity_collecting_attachments">Qoşmalar yığılır...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s - göndər</string> <string name="MediaPickerActivity_send_to">%s - göndər</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Hamısını seç</string> <string name="MediaOverviewActivity_Select_all">Hamısını seç</string>
<string name="MediaOverviewActivity_collecting_attachments">Qoşmalar yığılır...</string> <string name="MediaOverviewActivity_collecting_attachments">Qoşmalar yığılır...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s - göndər</string> <string name="MediaPickerActivity_send_to">%s - göndər</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -96,9 +96,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string> <string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan lampiran...</string> <string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan lampiran...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string> <string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -96,9 +96,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string> <string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan lampiran...</string> <string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan lampiran...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string> <string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -142,9 +142,6 @@
<string name="MediaOverviewActivity_Select_all">Избери всичко</string> <string name="MediaOverviewActivity_Select_all">Избери всичко</string>
<string name="MediaOverviewActivity_collecting_attachments">Събиране на прикачени файлове...</string> <string name="MediaOverviewActivity_collecting_attachments">Събиране на прикачени файлове...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Изпрати на %s</string> <string name="MediaPickerActivity_send_to">Изпрати на %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -142,9 +142,6 @@
<string name="MediaOverviewActivity_Select_all">Избери всичко</string> <string name="MediaOverviewActivity_Select_all">Избери всичко</string>
<string name="MediaOverviewActivity_collecting_attachments">Събиране на прикачени файлове...</string> <string name="MediaOverviewActivity_collecting_attachments">Събиране на прикачени файлове...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Изпрати на %s</string> <string name="MediaPickerActivity_send_to">Изпрати на %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -147,9 +147,6 @@
<string name="MediaOverviewActivity_Select_all">Selecciona-ho tot</string> <string name="MediaOverviewActivity_Select_all">Selecciona-ho tot</string>
<string name="MediaOverviewActivity_collecting_attachments">S\'estan adjuntant els fitxers...</string> <string name="MediaOverviewActivity_collecting_attachments">S\'estan adjuntant els fitxers...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envia-ho a %s</string> <string name="MediaPickerActivity_send_to">Envia-ho a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -147,9 +147,6 @@
<string name="MediaOverviewActivity_Select_all">Selecciona-ho tot</string> <string name="MediaOverviewActivity_Select_all">Selecciona-ho tot</string>
<string name="MediaOverviewActivity_collecting_attachments">S\'estan adjuntant els fitxers...</string> <string name="MediaOverviewActivity_collecting_attachments">S\'estan adjuntant els fitxers...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envia-ho a %s</string> <string name="MediaPickerActivity_send_to">Envia-ho a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -168,9 +168,6 @@
<string name="MediaOverviewActivity_Select_all">Označit vše</string> <string name="MediaOverviewActivity_Select_all">Označit vše</string>
<string name="MediaOverviewActivity_collecting_attachments">Shromažďuji přílohy...</string> <string name="MediaOverviewActivity_collecting_attachments">Shromažďuji přílohy...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Poslat %s</string> <string name="MediaPickerActivity_send_to">Poslat %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -168,9 +168,6 @@
<string name="MediaOverviewActivity_Select_all">Označit vše</string> <string name="MediaOverviewActivity_Select_all">Označit vše</string>
<string name="MediaOverviewActivity_collecting_attachments">Shromažďuji přílohy...</string> <string name="MediaOverviewActivity_collecting_attachments">Shromažďuji přílohy...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Poslat %s</string> <string name="MediaPickerActivity_send_to">Poslat %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -172,9 +172,6 @@
<string name="MediaOverviewActivity_Select_all">Dewis popeth</string> <string name="MediaOverviewActivity_Select_all">Dewis popeth</string>
<string name="MediaOverviewActivity_collecting_attachments">Casglu atodiadau...</string> <string name="MediaOverviewActivity_collecting_attachments">Casglu atodiadau...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Anfon i %s</string> <string name="MediaPickerActivity_send_to">Anfon i %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -172,9 +172,6 @@
<string name="MediaOverviewActivity_Select_all">Dewis popeth</string> <string name="MediaOverviewActivity_Select_all">Dewis popeth</string>
<string name="MediaOverviewActivity_collecting_attachments">Casglu atodiadau...</string> <string name="MediaOverviewActivity_collecting_attachments">Casglu atodiadau...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Anfon i %s</string> <string name="MediaPickerActivity_send_to">Anfon i %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Vælg alle</string> <string name="MediaOverviewActivity_Select_all">Vælg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Samler vedhæftninger...</string> <string name="MediaOverviewActivity_collecting_attachments">Samler vedhæftninger...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string> <string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Vælg alle</string> <string name="MediaOverviewActivity_Select_all">Vælg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Samler vedhæftninger...</string> <string name="MediaOverviewActivity_collecting_attachments">Samler vedhæftninger...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string> <string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Alle auswählen</string> <string name="MediaOverviewActivity_Select_all">Alle auswählen</string>
<string name="MediaOverviewActivity_collecting_attachments">Anhänge werden gesammelt </string> <string name="MediaOverviewActivity_collecting_attachments">Anhänge werden gesammelt </string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">An %s senden</string> <string name="MediaPickerActivity_send_to">An %s senden</string>
<!-- MediaSendActivity --> <!-- 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="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_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_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_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="fragment_scan_qr_code_grant_camera_access_button_title">Kamerazugriff gewähren</string>
<string name="activity_create_closed_group_title">Neue geschlossene Gruppe</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_day">Tag</string>
<string name="dialog_ui_mode_option_night">Nacht</string> <string name="dialog_ui_mode_option_night">Nacht</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</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">Anhang</string>
<string name="attachment_type_voice_message">Sprachnachricht</string> <string name="attachment_type_voice_message">Sprachnachricht</string>
<string name="details">Details</string> <string name="details">Details</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Alle auswählen</string> <string name="MediaOverviewActivity_Select_all">Alle auswählen</string>
<string name="MediaOverviewActivity_collecting_attachments">Anhänge werden gesammelt </string> <string name="MediaOverviewActivity_collecting_attachments">Anhänge werden gesammelt </string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">An %s senden</string> <string name="MediaPickerActivity_send_to">An %s senden</string>
<!-- MediaSendActivity --> <!-- 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="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_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_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_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="fragment_scan_qr_code_grant_camera_access_button_title">Kamerazugriff gewähren</string>
<string name="activity_create_closed_group_title">Neue geschlossene Gruppe</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_day">Tag</string>
<string name="dialog_ui_mode_option_night">Nacht</string> <string name="dialog_ui_mode_option_night">Nacht</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</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">Anhang</string>
<string name="attachment_type_voice_message">Sprachnachricht</string> <string name="attachment_type_voice_message">Sprachnachricht</string>
<string name="details">Details</string> <string name="details">Details</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Επιλογή όλων</string> <string name="MediaOverviewActivity_Select_all">Επιλογή όλων</string>
<string name="MediaOverviewActivity_collecting_attachments">Τα συνημμένα συλλέγονται...</string> <string name="MediaOverviewActivity_collecting_attachments">Τα συνημμένα συλλέγονται...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Αποστολή σε %s</string> <string name="MediaPickerActivity_send_to">Αποστολή σε %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Επιλογή όλων</string> <string name="MediaOverviewActivity_Select_all">Επιλογή όλων</string>
<string name="MediaOverviewActivity_collecting_attachments">Τα συνημμένα συλλέγονται...</string> <string name="MediaOverviewActivity_collecting_attachments">Τα συνημμένα συλλέγονται...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Αποστολή σε %s</string> <string name="MediaPickerActivity_send_to">Αποστολή σε %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Elekti ĉiujn</string> <string name="MediaOverviewActivity_Select_all">Elekti ĉiujn</string>
<string name="MediaOverviewActivity_collecting_attachments">Kolekto de kunsendaĵoj...</string> <string name="MediaOverviewActivity_collecting_attachments">Kolekto de kunsendaĵoj...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Sendi al %s</string> <string name="MediaPickerActivity_send_to">Sendi al %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Elekti ĉiujn</string> <string name="MediaOverviewActivity_Select_all">Elekti ĉiujn</string>
<string name="MediaOverviewActivity_collecting_attachments">Kolekto de kunsendaĵoj...</string> <string name="MediaOverviewActivity_collecting_attachments">Kolekto de kunsendaĵoj...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Sendi al %s</string> <string name="MediaPickerActivity_send_to">Sendi al %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string> <string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recopilando adjuntos ...</string> <string name="MediaOverviewActivity_collecting_attachments">Recopilando adjuntos ...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string> <string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string> <string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recopilando adjuntos ...</string> <string name="MediaOverviewActivity_collecting_attachments">Recopilando adjuntos ...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string> <string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Vali kõik</string> <string name="MediaOverviewActivity_Select_all">Vali kõik</string>
<string name="MediaOverviewActivity_collecting_attachments">Kogun manuseid...</string> <string name="MediaOverviewActivity_collecting_attachments">Kogun manuseid...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Saada kohta %s</string> <string name="MediaPickerActivity_send_to">Saada kohta %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Vali kõik</string> <string name="MediaOverviewActivity_Select_all">Vali kõik</string>
<string name="MediaOverviewActivity_collecting_attachments">Kogun manuseid...</string> <string name="MediaOverviewActivity_collecting_attachments">Kogun manuseid...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Saada kohta %s</string> <string name="MediaPickerActivity_send_to">Saada kohta %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

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

View File

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

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Valitse kaikki</string> <string name="MediaOverviewActivity_Select_all">Valitse kaikki</string>
<string name="MediaOverviewActivity_collecting_attachments">Kerätään liitetiedostoja...</string> <string name="MediaOverviewActivity_collecting_attachments">Kerätään liitetiedostoja...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Lähetä yhteystiedolle %s</string> <string name="MediaPickerActivity_send_to">Lähetä yhteystiedolle %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Valitse kaikki</string> <string name="MediaOverviewActivity_Select_all">Valitse kaikki</string>
<string name="MediaOverviewActivity_collecting_attachments">Kerätään liitetiedostoja...</string> <string name="MediaOverviewActivity_collecting_attachments">Kerätään liitetiedostoja...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Lähetä yhteystiedolle %s</string> <string name="MediaPickerActivity_send_to">Lähetä yhteystiedolle %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -157,9 +157,6 @@
<string name="MediaOverviewActivity_Select_all">Tout sélectionner</string> <string name="MediaOverviewActivity_Select_all">Tout sélectionner</string>
<string name="MediaOverviewActivity_collecting_attachments">Récupération des pièces jointes…</string> <string name="MediaOverviewActivity_collecting_attachments">Récupération des pièces jointes…</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envoyer à %s</string> <string name="MediaPickerActivity_send_to">Envoyer à %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -157,9 +157,6 @@
<string name="MediaOverviewActivity_Select_all">Tout sélectionner</string> <string name="MediaOverviewActivity_Select_all">Tout sélectionner</string>
<string name="MediaOverviewActivity_collecting_attachments">Récupération des pièces jointes…</string> <string name="MediaOverviewActivity_collecting_attachments">Récupération des pièces jointes…</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Envoyer à %s</string> <string name="MediaPickerActivity_send_to">Envoyer à %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -149,9 +149,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string> <string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recompilando anexos...</string> <string name="MediaOverviewActivity_collecting_attachments">Recompilando anexos...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string> <string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -149,9 +149,6 @@
<string name="MediaOverviewActivity_Select_all">Seleccionar todo</string> <string name="MediaOverviewActivity_Select_all">Seleccionar todo</string>
<string name="MediaOverviewActivity_collecting_attachments">Recompilando anexos...</string> <string name="MediaOverviewActivity_collecting_attachments">Recompilando anexos...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Enviar a %s</string> <string name="MediaPickerActivity_send_to">Enviar a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Gişan bibijêre</string> <string name="MediaOverviewActivity_Select_all">Gişan bibijêre</string>
<string name="MediaOverviewActivity_collecting_attachments">...Servehî tên berhevkirin</string> <string name="MediaOverviewActivity_collecting_attachments">...Servehî tên berhevkirin</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ji %s re bişîne</string> <string name="MediaPickerActivity_send_to">Ji %s re bişîne</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -134,9 +134,6 @@
<string name="MediaOverviewActivity_Select_all">Gişan bibijêre</string> <string name="MediaOverviewActivity_Select_all">Gişan bibijêre</string>
<string name="MediaOverviewActivity_collecting_attachments">...Servehî tên berhevkirin</string> <string name="MediaOverviewActivity_collecting_attachments">...Servehî tên berhevkirin</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ji %s re bişîne</string> <string name="MediaPickerActivity_send_to">Ji %s re bişîne</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

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

View File

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

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Összes kiválasztása</string> <string name="MediaOverviewActivity_Select_all">Összes kiválasztása</string>
<string name="MediaOverviewActivity_collecting_attachments">Mellékletek összegyűjtése...</string> <string name="MediaOverviewActivity_collecting_attachments">Mellékletek összegyűjtése...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Küldés neki: %s</string> <string name="MediaPickerActivity_send_to">Küldés neki: %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Összes kiválasztása</string> <string name="MediaOverviewActivity_Select_all">Összes kiválasztása</string>
<string name="MediaOverviewActivity_collecting_attachments">Mellékletek összegyűjtése...</string> <string name="MediaOverviewActivity_collecting_attachments">Mellékletek összegyűjtése...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Küldés neki: %s</string> <string name="MediaPickerActivity_send_to">Küldés neki: %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -155,9 +155,6 @@
<string name="MediaOverviewActivity_Select_all">Ընտրել բոլորը</string> <string name="MediaOverviewActivity_Select_all">Ընտրել բոլորը</string>
<string name="MediaOverviewActivity_collecting_attachments">Կցորդների հավաքում...</string> <string name="MediaOverviewActivity_collecting_attachments">Կցորդների հավաքում...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ուղարկել %s֊ին</string> <string name="MediaPickerActivity_send_to">Ուղարկել %s֊ին</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -155,9 +155,6 @@
<string name="MediaOverviewActivity_Select_all">Ընտրել բոլորը</string> <string name="MediaOverviewActivity_Select_all">Ընտրել բոլորը</string>
<string name="MediaOverviewActivity_collecting_attachments">Կցորդների հավաքում...</string> <string name="MediaOverviewActivity_collecting_attachments">Կցորդների հավաքում...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Ուղարկել %s֊ին</string> <string name="MediaPickerActivity_send_to">Ուղարկել %s֊ին</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -126,9 +126,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string> <string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan semua lampiran...</string> <string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan semua lampiran...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string> <string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -126,9 +126,6 @@
<string name="MediaOverviewActivity_Select_all">Pilih semua</string> <string name="MediaOverviewActivity_Select_all">Pilih semua</string>
<string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan semua lampiran...</string> <string name="MediaOverviewActivity_collecting_attachments">Mengumpulkan semua lampiran...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Kirim ke %s</string> <string name="MediaPickerActivity_send_to">Kirim ke %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Seleziona tutto</string> <string name="MediaOverviewActivity_Select_all">Seleziona tutto</string>
<string name="MediaOverviewActivity_collecting_attachments">Recupero allegati...</string> <string name="MediaOverviewActivity_collecting_attachments">Recupero allegati...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Invia a %s</string> <string name="MediaPickerActivity_send_to">Invia a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -151,9 +151,6 @@
<string name="MediaOverviewActivity_Select_all">Seleziona tutto</string> <string name="MediaOverviewActivity_Select_all">Seleziona tutto</string>
<string name="MediaOverviewActivity_collecting_attachments">Recupero allegati...</string> <string name="MediaOverviewActivity_collecting_attachments">Recupero allegati...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Invia a %s</string> <string name="MediaPickerActivity_send_to">Invia a %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">בחר הכל</string> <string name="MediaOverviewActivity_Select_all">בחר הכל</string>
<string name="MediaOverviewActivity_collecting_attachments">אוסף צרופות...</string> <string name="MediaOverviewActivity_collecting_attachments">אוסף צרופות...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">שלח אל %s</string> <string name="MediaPickerActivity_send_to">שלח אל %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">בחר הכל</string> <string name="MediaOverviewActivity_Select_all">בחר הכל</string>
<string name="MediaOverviewActivity_collecting_attachments">אוסף צרופות...</string> <string name="MediaOverviewActivity_collecting_attachments">אוסף צרופות...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">שלח אל %s</string> <string name="MediaPickerActivity_send_to">שלח אל %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -140,9 +140,6 @@
<string name="MediaOverviewActivity_Select_all">すべて選択</string> <string name="MediaOverviewActivity_Select_all">すべて選択</string>
<string name="MediaOverviewActivity_collecting_attachments">添付ファイルを集めています...</string> <string name="MediaOverviewActivity_collecting_attachments">添付ファイルを集めています...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%sに送信</string> <string name="MediaPickerActivity_send_to">%sに送信</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -140,9 +140,6 @@
<string name="MediaOverviewActivity_Select_all">すべて選択</string> <string name="MediaOverviewActivity_Select_all">すべて選択</string>
<string name="MediaOverviewActivity_collecting_attachments">添付ファイルを集めています...</string> <string name="MediaOverviewActivity_collecting_attachments">添付ファイルを集めています...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%sに送信</string> <string name="MediaPickerActivity_send_to">%sに送信</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -127,9 +127,6 @@
<string name="MediaOverviewActivity_Select_all">ជ្រើសរើសទាំងអស់</string> <string name="MediaOverviewActivity_Select_all">ជ្រើសរើសទាំងអស់</string>
<string name="MediaOverviewActivity_collecting_attachments">កំពុងប្រមូលឯកសារភ្ជាប់...</string> <string name="MediaOverviewActivity_collecting_attachments">កំពុងប្រមូលឯកសារភ្ជាប់...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">ផ្ញើទៅកាន់ %s</string> <string name="MediaPickerActivity_send_to">ផ្ញើទៅកាន់ %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -127,9 +127,6 @@
<string name="MediaOverviewActivity_Select_all">ជ្រើសរើសទាំងអស់</string> <string name="MediaOverviewActivity_Select_all">ជ្រើសរើសទាំងអស់</string>
<string name="MediaOverviewActivity_collecting_attachments">កំពុងប្រមូលឯកសារភ្ជាប់...</string> <string name="MediaOverviewActivity_collecting_attachments">កំពុងប្រមូលឯកសារភ្ជាប់...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">ផ្ញើទៅកាន់ %s</string> <string name="MediaPickerActivity_send_to">ផ្ញើទៅកាន់ %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

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

View File

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

View File

@ -136,9 +136,6 @@
<string name="MediaOverviewActivity_Select_all">모두 선택</string> <string name="MediaOverviewActivity_Select_all">모두 선택</string>
<string name="MediaOverviewActivity_collecting_attachments">첨부파일 저장 준비 중…</string> <string name="MediaOverviewActivity_collecting_attachments">첨부파일 저장 준비 중…</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s에게 전송</string> <string name="MediaPickerActivity_send_to">%s에게 전송</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -136,9 +136,6 @@
<string name="MediaOverviewActivity_Select_all">모두 선택</string> <string name="MediaOverviewActivity_Select_all">모두 선택</string>
<string name="MediaOverviewActivity_collecting_attachments">첨부파일 저장 준비 중…</string> <string name="MediaOverviewActivity_collecting_attachments">첨부파일 저장 준비 중…</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">%s에게 전송</string> <string name="MediaPickerActivity_send_to">%s에게 전송</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">Žymėti visus</string> <string name="MediaOverviewActivity_Select_all">Žymėti visus</string>
<string name="MediaOverviewActivity_collecting_attachments">Renkami priedai...</string> <string name="MediaOverviewActivity_collecting_attachments">Renkami priedai...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Siųsti adresatui %s</string> <string name="MediaPickerActivity_send_to">Siųsti adresatui %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -166,9 +166,6 @@
<string name="MediaOverviewActivity_Select_all">Žymėti visus</string> <string name="MediaOverviewActivity_Select_all">Žymėti visus</string>
<string name="MediaOverviewActivity_collecting_attachments">Renkami priedai...</string> <string name="MediaOverviewActivity_collecting_attachments">Renkami priedai...</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Siųsti adresatui %s</string> <string name="MediaPickerActivity_send_to">Siųsti adresatui %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -87,9 +87,6 @@
<string name="MediaOverviewActivity_Select_all">အားလုံးကို ရွေးပါ</string> <string name="MediaOverviewActivity_Select_all">အားလုံးကို ရွေးပါ</string>
<string name="MediaOverviewActivity_collecting_attachments">ပူးတွဲဖိုင်များကို စုစည်းနေသည် </string> <string name="MediaOverviewActivity_collecting_attachments">ပူးတွဲဖိုင်များကို စုစည်းနေသည် </string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<!-- MediaSendActivity --> <!-- MediaSendActivity -->
<!-- MediaRepository --> <!-- MediaRepository -->

View File

@ -87,9 +87,6 @@
<string name="MediaOverviewActivity_Select_all">အားလုံးကို ရွေးပါ</string> <string name="MediaOverviewActivity_Select_all">အားလုံးကို ရွေးပါ</string>
<string name="MediaOverviewActivity_collecting_attachments">ပူးတွဲဖိုင်များကို စုစည်းနေသည် </string> <string name="MediaOverviewActivity_collecting_attachments">ပူးတွဲဖိုင်များကို စုစည်းနေသည် </string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<!-- MediaSendActivity --> <!-- MediaSendActivity -->
<!-- MediaRepository --> <!-- MediaRepository -->

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Velg alle</string> <string name="MediaOverviewActivity_Select_all">Velg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Henter vedlegg …</string> <string name="MediaOverviewActivity_collecting_attachments">Henter vedlegg …</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string> <string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity --> <!-- 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_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="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_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="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_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> <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_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_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="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_edit_text_hint">Angi Account-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_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 Session-ID\'en eller ONS-navnet og prøv igjen.</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_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="fragment_scan_qr_code_grant_camera_access_button_title">Gi kameratilgang</string>
<string name="activity_create_closed_group_title">Ny lukket gruppe</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_day">Dag</string>
<string name="dialog_ui_mode_option_night">Natt</string> <string name="dialog_ui_mode_option_night">Natt</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</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">Vedlegg</string>
<string name="attachment_type_voice_message">Talemelding</string> <string name="attachment_type_voice_message">Talemelding</string>
<string name="details">Detaljer</string> <string name="details">Detaljer</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Velg alle</string> <string name="MediaOverviewActivity_Select_all">Velg alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Henter vedlegg …</string> <string name="MediaOverviewActivity_collecting_attachments">Henter vedlegg …</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string> <string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity --> <!-- 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_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="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_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="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_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> <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_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_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="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_edit_text_hint">Angi Account-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_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 Session-ID\'en eller ONS-navnet og prøv igjen.</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_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="fragment_scan_qr_code_grant_camera_access_button_title">Gi kameratilgang</string>
<string name="activity_create_closed_group_title">Ny lukket gruppe</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_day">Dag</string>
<string name="dialog_ui_mode_option_night">Natt</string> <string name="dialog_ui_mode_option_night">Natt</string>
<string name="dialog_ui_mode_option_system_default">Systemstandard</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">Vedlegg</string>
<string name="attachment_type_voice_message">Talemelding</string> <string name="attachment_type_voice_message">Talemelding</string>
<string name="details">Detaljer</string> <string name="details">Detaljer</string>

View File

@ -150,9 +150,6 @@
<string name="MediaOverviewActivity_Select_all">Alles selecteren</string> <string name="MediaOverviewActivity_Select_all">Alles selecteren</string>
<string name="MediaOverviewActivity_collecting_attachments">Bijlagen aan het verzamelen…</string> <string name="MediaOverviewActivity_collecting_attachments">Bijlagen aan het verzamelen…</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Verzenden naar %s</string> <string name="MediaPickerActivity_send_to">Verzenden naar %s</string>
<!-- MediaSendActivity --> <!-- 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_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="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_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_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_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> <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_Select_all">Alles selecteren</string>
<string name="MediaOverviewActivity_collecting_attachments">Bijlagen aan het verzamelen…</string> <string name="MediaOverviewActivity_collecting_attachments">Bijlagen aan het verzamelen…</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Verzenden naar %s</string> <string name="MediaPickerActivity_send_to">Verzenden naar %s</string>
<!-- MediaSendActivity --> <!-- 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_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="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_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_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_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> <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_Select_all">Vel alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Hentar vedlegg …</string> <string name="MediaOverviewActivity_collecting_attachments">Hentar vedlegg …</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string> <string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

View File

@ -132,9 +132,6 @@
<string name="MediaOverviewActivity_Select_all">Vel alle</string> <string name="MediaOverviewActivity_Select_all">Vel alle</string>
<string name="MediaOverviewActivity_collecting_attachments">Hentar vedlegg …</string> <string name="MediaOverviewActivity_collecting_attachments">Hentar vedlegg …</string>
<!-- NotificationMmsMessageRecord --> <!-- 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 --> <!-- MediaPickerActivity -->
<string name="MediaPickerActivity_send_to">Send til %s</string> <string name="MediaPickerActivity_send_to">Send til %s</string>
<!-- MediaSendActivity --> <!-- MediaSendActivity -->

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