diff --git a/app/build.gradle b/app/build.gradle
index 6723dfeddb..d86c8a3a02 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -143,8 +143,8 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.2'
}
-def canonicalVersionCode = 188
-def canonicalVersionName = "1.11.0"
+def canonicalVersionCode = 193
+def canonicalVersionName = "1.11.2"
def postFixSize = 10
def abiPostFix = ['armeabi-v7a' : 1,
@@ -194,8 +194,8 @@ android {
versionCode canonicalVersionCode * postFixSize
versionName canonicalVersionName
- minSdkVersion 23
- targetSdkVersion 30
+ minSdkVersion androidMinimumSdkVersion
+ targetSdkVersion androidCompileSdkVersion
multiDexEnabled = true
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 32869dd1f1..bc9e379d11 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -207,21 +207,14 @@
+ android:theme="@style/Theme.Session.DayNight.FlatActionBar">
-
-
- .
- */
-package org.thoughtcrime.securesms;
-
-import android.annotation.SuppressLint;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.drawable.ColorDrawable;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListView;
-import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.loader.app.LoaderManager.LoaderCallbacks;
-import androidx.loader.content.Loader;
-import org.session.libsession.messaging.messages.visible.LinkPreview;
-import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
-import org.session.libsession.messaging.messages.visible.Quote;
-import org.session.libsession.messaging.messages.visible.VisibleMessage;
-import org.session.libsession.messaging.open_groups.OpenGroupV2;
-import org.session.libsession.messaging.sending_receiving.MessageSender;
-import org.session.libsession.messaging.utilities.UpdateMessageData;
-import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
-import org.session.libsession.utilities.MaterialColor;
-import org.thoughtcrime.securesms.conversation.ConversationItem;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
-import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
-import org.thoughtcrime.securesms.database.MmsDatabase;
-import org.thoughtcrime.securesms.database.MmsSmsDatabase;
-import org.thoughtcrime.securesms.database.SmsDatabase;
-import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader;
-import org.thoughtcrime.securesms.database.model.MessageRecord;
-import org.session.libsignal.utilities.Log;
-import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
-import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
-import org.thoughtcrime.securesms.mms.GlideApp;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.session.libsession.utilities.recipients.RecipientModifiedListener;
-import org.thoughtcrime.securesms.util.DateUtils;
-import org.session.libsession.utilities.ExpirationUtil;
-import org.session.libsession.utilities.Util;
-import org.session.libsignal.utilities.guava.Optional;
-
-import java.lang.ref.WeakReference;
-import java.sql.Date;
-import java.text.SimpleDateFormat;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-
-import network.loki.messenger.R;
-
-/**
- * @author Jake McGinty
- */
-public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks, RecipientModifiedListener {
- private final static String TAG = MessageDetailsActivity.class.getSimpleName();
-
- public final static String MESSAGE_ID_EXTRA = "message_id";
- public final static String THREAD_ID_EXTRA = "thread_id";
- public final static String IS_PUSH_GROUP_EXTRA = "is_push_group";
- public final static String TYPE_EXTRA = "type";
- public final static String ADDRESS_EXTRA = "address";
-
- private GlideRequests glideRequests;
- private long threadId;
- private boolean isPushGroup;
- private ConversationItem conversationItem;
- private ViewGroup itemParent;
- private View metadataContainer;
- private View expiresContainer;
- private TextView errorText;
- private View resendButton;
- private TextView sentDate;
- private TextView receivedDate;
- private TextView expiresInText;
- private View receivedContainer;
- private TextView transport;
- private TextView toFrom;
- private View separator;
- private ListView recipientsList;
- private LayoutInflater inflater;
-
- private boolean running;
-
- @Override
- public void onCreate(Bundle bundle, boolean ready) {
- super.onCreate(bundle, ready);
- setContentView(R.layout.message_details_activity);
- running = true;
-
- initializeResources();
- initializeActionBar();
- getSupportLoaderManager().initLoader(0, null, this);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- assert getSupportActionBar() != null;
- getSupportActionBar().setTitle("Message Details");
-
- ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- running = false;
- }
-
- private void initializeActionBar() {
- assert getSupportActionBar() != null;
-
- Recipient recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
- recipient.addListener(this);
- }
-
- private void setActionBarColor(MaterialColor color) {
- assert getSupportActionBar() != null;
- getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
- }
-
- @Override
- public void onModified(final Recipient recipient) {
- Util.runOnMain(() -> setActionBarColor(recipient.getColor()));
- }
-
- private void initializeResources() {
- inflater = LayoutInflater.from(this);
- View header = inflater.inflate(R.layout.message_details_header, recipientsList, false);
-
- threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
- isPushGroup = getIntent().getBooleanExtra(IS_PUSH_GROUP_EXTRA, false);
- glideRequests = GlideApp.with(this);
- itemParent = header.findViewById(R.id.item_container);
- recipientsList = findViewById(R.id.recipients_list);
- metadataContainer = header.findViewById(R.id.metadata_container);
- errorText = header.findViewById(R.id.error_text);
- resendButton = header.findViewById(R.id.resend_button);
- sentDate = header.findViewById(R.id.sent_time);
- receivedContainer = header.findViewById(R.id.received_container);
- receivedDate = header.findViewById(R.id.received_time);
- transport = header.findViewById(R.id.transport);
- toFrom = header.findViewById(R.id.tofrom);
- separator = header.findViewById(R.id.separator);
- expiresContainer = header.findViewById(R.id.expires_container);
- expiresInText = header.findViewById(R.id.expires_in);
- recipientsList.setHeaderDividersEnabled(false);
- recipientsList.addHeaderView(header, null, false);
- }
-
- private void updateTransport(MessageRecord messageRecord) {
- final String transportText;
- if (messageRecord.isOutgoing() && messageRecord.isFailed()) {
- transportText = "-";
- } else if (messageRecord.isPending()) {
- transportText = getString(R.string.ConversationFragment_pending);
- } else if (messageRecord.isMms()) {
- transportText = getString(R.string.ConversationFragment_mms);
- } else {
- transportText = getString(R.string.ConversationFragment_sms);
- }
-
- transport.setText(transportText);
- }
-
- private void updateTime(MessageRecord messageRecord) {
- sentDate.setOnLongClickListener(null);
- receivedDate.setOnLongClickListener(null);
-
- if (messageRecord.isPending() || messageRecord.isFailed()) {
- sentDate.setText("-");
- receivedContainer.setVisibility(View.GONE);
- } else {
- Locale dateLocale = Locale.getDefault();
- SimpleDateFormat dateFormatter = DateUtils.getDetailedDateFormatter(this, dateLocale);
- sentDate.setText(dateFormatter.format(new Date(messageRecord.getDateSent())));
- sentDate.setOnLongClickListener(v -> {
- copyToClipboard(String.valueOf(messageRecord.getDateSent()));
- return true;
- });
-
- if (messageRecord.getDateReceived() != messageRecord.getDateSent() && !messageRecord.isOutgoing()) {
- receivedDate.setText(dateFormatter.format(new Date(messageRecord.getDateReceived())));
- receivedDate.setOnLongClickListener(v -> {
- copyToClipboard(String.valueOf(messageRecord.getDateReceived()));
- return true;
- });
- receivedContainer.setVisibility(View.VISIBLE);
- } else {
- receivedContainer.setVisibility(View.GONE);
- }
- }
- }
-
- private void updateExpirationTime(final MessageRecord messageRecord) {
- if (messageRecord.getExpiresIn() <= 0 || messageRecord.getExpireStarted() <= 0) {
- expiresContainer.setVisibility(View.GONE);
- return;
- }
-
- expiresContainer.setVisibility(View.VISIBLE);
- Util.runOnMain(new Runnable() {
- @Override
- public void run() {
- long elapsed = System.currentTimeMillis() - messageRecord.getExpireStarted();
- long remaining = messageRecord.getExpiresIn() - elapsed;
-
- String duration = ExpirationUtil.getExpirationDisplayValue(MessageDetailsActivity.this, Math.max((int)(remaining / 1000), 1));
- expiresInText.setText(duration);
-
- if (running) {
- Util.runOnMainDelayed(this, 500);
- }
- }
- });
- }
-
- private void updateRecipients(MessageRecord messageRecord, Recipient recipient, List recipients) {
- final int toFromRes;
- if (messageRecord.isOutgoing()) {
- toFromRes = R.string.message_details_header__to;
- } else {
- toFromRes = R.string.message_details_header__from;
- }
- toFrom.setText(toFromRes);
- long threadID = messageRecord.getThreadId();
- OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadID);
- if (openGroup != null && messageRecord.isOutgoing()) {
- toFrom.setVisibility(View.GONE);
- separator.setVisibility(View.GONE);
- }
- conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, Locale.getDefault(), new HashSet<>(), recipient, null, false);
- recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup));
- }
-
- private void inflateMessageViewIfAbsent(MessageRecord messageRecord) {
- if (conversationItem == null) {
- if (messageRecord.isOutgoing()) {
- conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_sent, itemParent, false);
- } else {
- conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_received, itemParent, false);
- }
- itemParent.addView(conversationItem);
- }
- }
-
- private @Nullable MessageRecord getMessageRecord(Context context, Cursor cursor, String type) {
- switch (type) {
- case MmsSmsDatabase.SMS_TRANSPORT:
- SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
- SmsDatabase.Reader reader = smsDatabase.readerFor(cursor);
- return reader.getNext();
- case MmsSmsDatabase.MMS_TRANSPORT:
- MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
- MmsDatabase.Reader mmsReader = mmsDatabase.readerFor(cursor);
- return mmsReader.getNext();
- default:
- throw new AssertionError("no valid message type specified");
- }
- }
-
- private void copyToClipboard(@NonNull String text) {
- ((ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("text", text));
- }
-
- @Override
- public @NonNull Loader onCreateLoader(int id, Bundle args) {
- return new MessageDetailsLoader(this, getIntent().getStringExtra(TYPE_EXTRA),
- getIntent().getLongExtra(MESSAGE_ID_EXTRA, -1));
- }
-
- @Override
- public void onLoadFinished(@NonNull Loader loader, Cursor cursor) {
- MessageRecord messageRecord = getMessageRecord(this, cursor, getIntent().getStringExtra(TYPE_EXTRA));
-
- if (messageRecord == null) {
- finish();
- } else {
- new MessageRecipientAsyncTask(this, messageRecord).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
-
- @Override
- public void onLoaderReset(@NonNull Loader loader) {
- recipientsList.setAdapter(null);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- super.onOptionsItemSelected(item);
-
- switch (item.getItemId()) {
- case android.R.id.home: finish(); return true;
- }
-
- return false;
- }
-
- @SuppressLint("StaticFieldLeak")
- private class MessageRecipientAsyncTask extends AsyncTask> {
-
- private final WeakReference weakContext;
- private final MessageRecord messageRecord;
-
- MessageRecipientAsyncTask(@NonNull Context context, @NonNull MessageRecord messageRecord) {
- this.weakContext = new WeakReference<>(context);
- this.messageRecord = messageRecord;
- }
-
- protected Context getContext() {
- return weakContext.get();
- }
-
- @Override
- public List doInBackground(Void... voids) {
- Context context = getContext();
-
- if (context == null) {
- Log.w(TAG, "associated context is destroyed, finishing early");
- return null;
- }
-
- List recipients = new LinkedList<>();
-
- if (!messageRecord.getRecipient().isGroupRecipient()) {
- recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), true, -1));
- } else {
- List receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
-
- if (receiptInfoList.isEmpty()) {
- List group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false);
-
- for (Recipient recipient : group) {
- recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
- }
- } else {
- for (GroupReceiptInfo info : receiptInfoList) {
- recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true),
- getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()),
- info.isUnidentified(),
- info.getTimestamp()));
- }
- }
- }
-
- return recipients;
- }
-
- @Override
- public void onPostExecute(List recipients) {
- if (getContext() == null) {
- Log.w(TAG, "AsyncTask finished with a destroyed context, leaving early.");
- return;
- }
-
- inflateMessageViewIfAbsent(messageRecord);
- updateRecipients(messageRecord, messageRecord.getRecipient(), recipients);
-
- boolean isGroupNetworkFailure = messageRecord.isFailed() && !messageRecord.getNetworkFailures().isEmpty();
- boolean isIndividualNetworkFailure = messageRecord.isFailed() && !isPushGroup;
-
- LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(getContext());
- String errorMessage = lokiMessageDatabase.getErrorMessage(messageRecord.id);
- if (errorMessage != null) {
- errorText.setText(errorMessage);
- }
-
- if (isGroupNetworkFailure || isIndividualNetworkFailure) {
- errorText.setVisibility(View.VISIBLE);
- resendButton.setVisibility(View.VISIBLE);
- resendButton.setOnClickListener(this::onResendClicked);
- metadataContainer.setVisibility(View.GONE);
- } else if (messageRecord.isFailed()) {
- errorText.setVisibility(View.VISIBLE);
- resendButton.setVisibility(View.GONE);
- resendButton.setOnClickListener(null);
- metadataContainer.setVisibility(View.GONE);
- } else {
- updateTransport(messageRecord);
- updateTime(messageRecord);
- updateExpirationTime(messageRecord);
- errorText.setVisibility(View.GONE);
- resendButton.setVisibility(View.GONE);
- resendButton.setOnClickListener(null);
- metadataContainer.setVisibility(View.VISIBLE);
- }
- }
-
- private RecipientDeliveryStatus.Status getStatusFor(int deliveryReceiptCount, int readReceiptCount, boolean pending) {
- if (readReceiptCount > 0) return RecipientDeliveryStatus.Status.READ;
- else if (deliveryReceiptCount > 0) return RecipientDeliveryStatus.Status.DELIVERED;
- else if (!pending) return RecipientDeliveryStatus.Status.SENT;
- else return RecipientDeliveryStatus.Status.PENDING;
- }
-
- private RecipientDeliveryStatus.Status getStatusFor(int groupStatus, boolean pending, boolean failed) {
- if (groupStatus == GroupReceiptDatabase.STATUS_READ) return RecipientDeliveryStatus.Status.READ;
- else if (groupStatus == GroupReceiptDatabase.STATUS_DELIVERED) return RecipientDeliveryStatus.Status.DELIVERED;
- else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && failed) return RecipientDeliveryStatus.Status.UNKNOWN;
- else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && !pending) return RecipientDeliveryStatus.Status.SENT;
- else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED) return RecipientDeliveryStatus.Status.PENDING;
- else if (groupStatus == GroupReceiptDatabase.STATUS_UNKNOWN) return RecipientDeliveryStatus.Status.UNKNOWN;
- throw new AssertionError();
- }
-
- private void onResendClicked(View v) {
- Recipient recipient = messageRecord.getRecipient();
- VisibleMessage message = new VisibleMessage();
- message.setId(messageRecord.getId());
- if (messageRecord.isOpenGroupInvitation()) {
- OpenGroupInvitation openGroupInvitation = new OpenGroupInvitation();
- UpdateMessageData updateMessageData = UpdateMessageData.Companion.fromJSON(messageRecord.getBody());
- if (updateMessageData.getKind() instanceof UpdateMessageData.Kind.OpenGroupInvitation) {
- UpdateMessageData.Kind.OpenGroupInvitation data = (UpdateMessageData.Kind.OpenGroupInvitation)updateMessageData.getKind();
- openGroupInvitation.setName(data.getGroupName());
- openGroupInvitation.setUrl(data.getGroupUrl());
- }
- message.setOpenGroupInvitation(openGroupInvitation);
- } else {
- message.setText(messageRecord.getBody());
- }
- message.setSentTimestamp(messageRecord.getTimestamp());
- if (recipient.isGroupRecipient()) {
- message.setGroupPublicKey(recipient.getAddress().toGroupString());
- } else {
- message.setRecipient(messageRecord.getRecipient().getAddress().serialize());
- }
- message.setThreadID(messageRecord.getThreadId());
- if (messageRecord.isMms()) {
- MmsMessageRecord mmsMessageRecord = (MmsMessageRecord) messageRecord;
- if (!mmsMessageRecord.getLinkPreviews().isEmpty()) {
- message.setLinkPreview(LinkPreview.Companion.from(mmsMessageRecord.getLinkPreviews().get(0)));
- }
- if (mmsMessageRecord.getQuote() != null) {
- message.setQuote(Quote.Companion.from(mmsMessageRecord.getQuote().getQuoteModel()));
- }
- message.addSignalAttachments(mmsMessageRecord.getSlideDeck().asAttachments());
- }
- MessageSender.send(message, recipient.getAddress());
- resendButton.setVisibility(View.GONE);
- }
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java
index 8f820cba54..ba7e145bf4 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java
@@ -37,7 +37,7 @@ import androidx.appcompat.widget.Toolbar;
import org.session.libsession.utilities.DistributionTypes;
import org.thoughtcrime.securesms.components.SearchToolbar;
-import org.thoughtcrime.securesms.conversation.ConversationActivity;
+
import org.session.libsession.utilities.Address;
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -219,7 +219,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
final Intent intent = getBaseShareIntent(ConversationActivityV2.class);
intent.putExtra(ConversationActivityV2.ADDRESS, address);
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
- intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
isPassingAlongMedia = true;
startActivity(intent);
@@ -227,11 +226,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
private Intent getBaseShareIntent(final @NonNull Class> target) {
final Intent intent = new Intent(this, target);
- final String textExtra = getIntent().getStringExtra(Intent.EXTRA_TEXT);
- final ArrayList mediaExtra = getIntent().getParcelableArrayListExtra(ConversationActivity.MEDIA_EXTRA);
-
- intent.putExtra(ConversationActivity.TEXT_EXTRA, textExtra);
- intent.putExtra(ConversationActivity.MEDIA_EXTRA, mediaExtra);
if (resolvedExtra != null) intent.setDataAndType(resolvedExtra, mimeType);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt
index d017e770f4..12a76739cf 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt
@@ -9,12 +9,12 @@ import org.session.libsession.messaging.sending_receiving.attachments.*
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.UploadResult
import org.session.libsession.utilities.Util
-import org.session.libsignal.utilities.guava.Optional
import org.session.libsignal.messages.SignalServiceAttachment
import org.session.libsignal.messages.SignalServiceAttachmentPointer
import org.session.libsignal.messages.SignalServiceAttachmentStream
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Log
+import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.database.AttachmentDatabase
import org.thoughtcrime.securesms.database.Database
import org.thoughtcrime.securesms.database.DatabaseFactory
@@ -97,6 +97,19 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
attachmentDatabase.insertAttachmentsForPlaceholder(messageId, attachmentId, stream)
}
+ override fun updateAudioAttachmentDuration(
+ attachmentId: AttachmentId,
+ durationMs: Long,
+ threadId: Long
+ ) {
+ val attachmentDb = DatabaseFactory.getAttachmentDatabase(context)
+ attachmentDb.setAttachmentAudioExtras(DatabaseAttachmentAudioExtras(
+ attachmentId = attachmentId,
+ visualSamples = byteArrayOf(),
+ durationMs = durationMs
+ ), threadId)
+ }
+
override fun isOutgoingMessage(timestamp: Long): Boolean {
val smsDatabase = DatabaseFactory.getSmsDatabase(context)
val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java
index 012086dc94..42825360c0 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java
@@ -28,166 +28,166 @@ import org.session.libsession.utilities.TextSecurePreferences;
public class ComposeText extends EmojiEditText {
- private CharSequence hint;
- private SpannableString subHint;
+ private CharSequence hint;
+ private SpannableString subHint;
- @Nullable private InputPanel.MediaListener mediaListener;
- @Nullable private CursorPositionChangedListener cursorPositionChangedListener;
+ @Nullable private InputPanel.MediaListener mediaListener;
+ @Nullable private CursorPositionChangedListener cursorPositionChangedListener;
- public ComposeText(Context context) {
- super(context);
- initialize();
- }
-
- public ComposeText(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize();
- }
-
- public ComposeText(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- initialize();
- }
-
- public String getTextTrimmed(){
- return getText().toString().trim();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- if (!TextUtils.isEmpty(hint)) {
- if (!TextUtils.isEmpty(subHint)) {
- setHint(new SpannableStringBuilder().append(ellipsizeToWidth(hint))
- .append("\n")
- .append(ellipsizeToWidth(subHint)));
- } else {
- setHint(ellipsizeToWidth(hint));
- }
- }
- }
-
- @Override
- protected void onSelectionChanged(int selStart, int selEnd) {
- super.onSelectionChanged(selStart, selEnd);
-
- if (cursorPositionChangedListener != null) {
- cursorPositionChangedListener.onCursorPositionChanged(selStart, selEnd);
- }
- }
-
- private CharSequence ellipsizeToWidth(CharSequence text) {
- return TextUtils.ellipsize(text,
- getPaint(),
- getWidth() - getPaddingLeft() - getPaddingRight(),
- TruncateAt.END);
- }
-
- public void setHint(@NonNull String hint, @Nullable CharSequence subHint) {
- this.hint = hint;
-
- if (subHint != null) {
- this.subHint = new SpannableString(subHint);
- this.subHint.setSpan(new RelativeSizeSpan(0.5f), 0, subHint.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- } else {
- this.subHint = null;
+ public ComposeText(Context context) {
+ super(context);
+ initialize();
}
- if (this.subHint != null) {
- super.setHint(new SpannableStringBuilder().append(ellipsizeToWidth(this.hint))
- .append("\n")
- .append(ellipsizeToWidth(this.subHint)));
- } else {
- super.setHint(ellipsizeToWidth(this.hint));
- }
- }
-
- public void setCursorPositionChangedListener(@Nullable CursorPositionChangedListener listener) {
- this.cursorPositionChangedListener = listener;
- }
-
- public void setTransport() {
- final boolean useSystemEmoji = TextSecurePreferences.isSystemEmojiPreferred(getContext());
- final boolean isIncognito = TextSecurePreferences.isIncognitoKeyboardEnabled(getContext());
-
- int imeOptions = (getImeOptions() & ~EditorInfo.IME_MASK_ACTION) | EditorInfo.IME_ACTION_SEND;
- int inputType = getInputType();
-
- setImeActionLabel(null, 0);
-
- if (useSystemEmoji) {
- inputType = (inputType & ~InputType.TYPE_MASK_VARIATION) | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE;
+ public ComposeText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialize();
}
- setInputType(inputType);
- if (isIncognito) {
- setImeOptions(imeOptions | 16777216);
- } else {
- setImeOptions(imeOptions);
- }
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
- InputConnection inputConnection = super.onCreateInputConnection(editorInfo);
-
- if(TextSecurePreferences.isEnterSendsEnabled(getContext())) {
- editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
+ public ComposeText(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initialize();
}
- if (Build.VERSION.SDK_INT < 21) return inputConnection;
- if (mediaListener == null) return inputConnection;
- if (inputConnection == null) return null;
-
- EditorInfoCompat.setContentMimeTypes(editorInfo, new String[] {"image/jpeg", "image/png", "image/gif"});
- return InputConnectionCompat.createWrapper(inputConnection, editorInfo, new CommitContentListener(mediaListener));
- }
-
- public void setMediaListener(@Nullable InputPanel.MediaListener mediaListener) {
- this.mediaListener = mediaListener;
- }
-
- private void initialize() {
- if (TextSecurePreferences.isIncognitoKeyboardEnabled(getContext())) {
- setImeOptions(getImeOptions() | 16777216);
- }
- }
-
- @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR2)
- private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener {
-
- private static final String TAG = CommitContentListener.class.getSimpleName();
-
- private final InputPanel.MediaListener mediaListener;
-
- private CommitContentListener(@NonNull InputPanel.MediaListener mediaListener) {
- this.mediaListener = mediaListener;
+ public String getTextTrimmed(){
+ return getText().toString().trim();
}
@Override
- public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts) {
- if (BuildCompat.isAtLeastNMR1() && (flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
- try {
- inputContentInfo.requestPermission();
- } catch (Exception e) {
- Log.w(TAG, e);
- return false;
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (!TextUtils.isEmpty(hint)) {
+ if (!TextUtils.isEmpty(subHint)) {
+ setHint(new SpannableStringBuilder().append(ellipsizeToWidth(hint))
+ .append("\n")
+ .append(ellipsizeToWidth(subHint)));
+ } else {
+ setHint(ellipsizeToWidth(hint));
+ }
}
- }
-
- if (inputContentInfo.getDescription().getMimeTypeCount() > 0) {
- mediaListener.onMediaSelected(inputContentInfo.getContentUri(),
- inputContentInfo.getDescription().getMimeType(0));
-
- return true;
- }
-
- return false;
}
- }
- public interface CursorPositionChangedListener {
- void onCursorPositionChanged(int start, int end);
- }
+ @Override
+ protected void onSelectionChanged(int selStart, int selEnd) {
+ super.onSelectionChanged(selStart, selEnd);
+
+ if (cursorPositionChangedListener != null) {
+ cursorPositionChangedListener.onCursorPositionChanged(selStart, selEnd);
+ }
+ }
+
+ private CharSequence ellipsizeToWidth(CharSequence text) {
+ return TextUtils.ellipsize(text,
+ getPaint(),
+ getWidth() - getPaddingLeft() - getPaddingRight(),
+ TruncateAt.END);
+ }
+
+ public void setHint(@NonNull String hint, @Nullable CharSequence subHint) {
+ this.hint = hint;
+
+ if (subHint != null) {
+ this.subHint = new SpannableString(subHint);
+ this.subHint.setSpan(new RelativeSizeSpan(0.5f), 0, subHint.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ } else {
+ this.subHint = null;
+ }
+
+ if (this.subHint != null) {
+ super.setHint(new SpannableStringBuilder().append(ellipsizeToWidth(this.hint))
+ .append("\n")
+ .append(ellipsizeToWidth(this.subHint)));
+ } else {
+ super.setHint(ellipsizeToWidth(this.hint));
+ }
+ }
+
+ public void setCursorPositionChangedListener(@Nullable CursorPositionChangedListener listener) {
+ this.cursorPositionChangedListener = listener;
+ }
+
+ public void setTransport() {
+ final boolean useSystemEmoji = TextSecurePreferences.isSystemEmojiPreferred(getContext());
+ final boolean isIncognito = TextSecurePreferences.isIncognitoKeyboardEnabled(getContext());
+
+ int imeOptions = (getImeOptions() & ~EditorInfo.IME_MASK_ACTION) | EditorInfo.IME_ACTION_SEND;
+ int inputType = getInputType();
+
+ setImeActionLabel(null, 0);
+
+ if (useSystemEmoji) {
+ inputType = (inputType & ~InputType.TYPE_MASK_VARIATION) | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE;
+ }
+
+ setInputType(inputType);
+ if (isIncognito) {
+ setImeOptions(imeOptions | 16777216);
+ } else {
+ setImeOptions(imeOptions);
+ }
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+ InputConnection inputConnection = super.onCreateInputConnection(editorInfo);
+
+ if(TextSecurePreferences.isEnterSendsEnabled(getContext())) {
+ editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
+ }
+
+ if (Build.VERSION.SDK_INT < 21) return inputConnection;
+ if (mediaListener == null) return inputConnection;
+ if (inputConnection == null) return null;
+
+ EditorInfoCompat.setContentMimeTypes(editorInfo, new String[] {"image/jpeg", "image/png", "image/gif"});
+ return InputConnectionCompat.createWrapper(inputConnection, editorInfo, new CommitContentListener(mediaListener));
+ }
+
+ public void setMediaListener(@Nullable InputPanel.MediaListener mediaListener) {
+ this.mediaListener = mediaListener;
+ }
+
+ private void initialize() {
+ if (TextSecurePreferences.isIncognitoKeyboardEnabled(getContext())) {
+ setImeOptions(getImeOptions() | 16777216);
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR2)
+ private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener {
+
+ private static final String TAG = CommitContentListener.class.getSimpleName();
+
+ private final InputPanel.MediaListener mediaListener;
+
+ private CommitContentListener(@NonNull InputPanel.MediaListener mediaListener) {
+ this.mediaListener = mediaListener;
+ }
+
+ @Override
+ public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts) {
+ if (BuildCompat.isAtLeastNMR1() && (flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+ try {
+ inputContentInfo.requestPermission();
+ } catch (Exception e) {
+ Log.w(TAG, e);
+ return false;
+ }
+ }
+
+ if (inputContentInfo.getDescription().getMimeTypeCount() > 0) {
+ mediaListener.onMediaSelected(inputContentInfo.getContentUri(),
+ inputContentInfo.getDescription().getMimeType(0));
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ public interface CursorPositionChangedListener {
+ void onCursorPositionChanged(int start, int end);
+ }
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java
deleted file mode 100644
index 603c4869a1..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.thoughtcrime.securesms.components;
-
-import android.content.Context;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.constraintlayout.widget.ConstraintLayout;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-import network.loki.messenger.R;
-
-/**
- * Bottom navigation bar shown in the {@link org.thoughtcrime.securesms.conversation.ConversationActivity}
- * when the user is searching within a conversation. Shows details about the results and allows the
- * user to move between them.
- */
-public class ConversationSearchBottomBar extends ConstraintLayout {
-
- private View searchDown;
- private View searchUp;
- private TextView searchPositionText;
- private View progressWheel;
-
- private EventListener eventListener;
-
-
- public ConversationSearchBottomBar(Context context) {
- super(context);
- }
-
- public ConversationSearchBottomBar(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- this.searchUp = findViewById(R.id.conversation_search_up);
- this.searchDown = findViewById(R.id.conversation_search_down);
- this.searchPositionText = findViewById(R.id.conversation_search_position);
- this.progressWheel = findViewById(R.id.conversation_search_progress_wheel);
- }
-
- public void setData(int position, int count) {
- progressWheel.setVisibility(GONE);
-
- searchUp.setOnClickListener(v -> {
- if (eventListener != null) {
- eventListener.onSearchMoveUpPressed();
- }
- });
-
- searchDown.setOnClickListener(v -> {
- if (eventListener != null) {
- eventListener.onSearchMoveDownPressed();
- }
- });
-
- if (count > 0) {
- searchPositionText.setText(getResources().getString(R.string.ConversationActivity_search_position, position + 1, count));
- } else {
- searchPositionText.setText(R.string.ConversationActivity_no_results);
- }
-
- setViewEnabled(searchUp, position < (count - 1));
- setViewEnabled(searchDown, position > 0);
- }
-
- public void showLoading() {
- progressWheel.setVisibility(VISIBLE);
- }
-
- private void setViewEnabled(@NonNull View view, boolean enabled) {
- view.setEnabled(enabled);
- view.setAlpha(enabled ? 1f : 0.25f);
- }
-
- public void setEventListener(@Nullable EventListener eventListener) {
- this.eventListener = eventListener;
- }
-
- public interface EventListener {
- void onSearchMoveUpPressed();
- void onSearchMoveDownPressed();
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationTypingView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationTypingView.java
deleted file mode 100644
index ddc782628a..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationTypingView.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.thoughtcrime.securesms.components;
-
-import android.content.Context;
-import android.graphics.PorterDuff;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import org.thoughtcrime.securesms.conversation.v2.components.TypingIndicatorView;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.session.libsession.utilities.ThemeUtil;
-
-
-import java.util.List;
-
-import network.loki.messenger.R;
-
-public class ConversationTypingView extends LinearLayout {
-
- private AvatarImageView avatar;
- private View bubble;
- private TypingIndicatorView indicator;
-
- public ConversationTypingView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- avatar = findViewById(R.id.typing_avatar);
- bubble = findViewById(R.id.typing_bubble);
- indicator = findViewById(R.id.typing_indicator);
- }
-
- public void setTypists(@NonNull GlideRequests glideRequests, @NonNull List typists, boolean isGroupThread) {
- if (typists.isEmpty()) {
- indicator.stopAnimation();
- return;
- }
-
- Recipient typist = typists.get(0);
-
- bubble.getBackground().setColorFilter(
- ThemeUtil.getThemedColor(getContext(), R.attr.message_received_background_color),
- PorterDuff.Mode.MULTIPLY);
-
- if (isGroupThread) {
- avatar.setAvatar(glideRequests, typist, false);
- avatar.setVisibility(VISIBLE);
- } else {
- avatar.setVisibility(GONE);
- }
-
- indicator.startAnimation();
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java
index 9b05c269a4..e81757026c 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java
@@ -4,443 +4,26 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
-import androidx.annotation.DimenRes;
-import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.view.ViewCompat;
-import android.text.format.DateUtils;
import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.Interpolator;
-import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
-import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
-import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
-import org.session.libsignal.utilities.Log;
-import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.mms.SlideDeck;
+public class InputPanel extends LinearLayout {
-import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
-import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.session.libsession.utilities.TextSecurePreferences;
-import org.session.libsession.utilities.Util;
-import org.session.libsession.utilities.ViewUtil;
-import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
-import org.session.libsignal.utilities.ListenableFuture;
-import org.session.libsignal.utilities.SettableFuture;
-import org.session.libsignal.utilities.guava.Optional;
-
-import java.util.concurrent.TimeUnit;
-
-import network.loki.messenger.R;
-
-public class InputPanel extends LinearLayout
- implements MicrophoneRecorderView.Listener,
- KeyboardAwareLinearLayout.OnKeyboardShownListener,
- EmojiKeyboardProvider.EmojiEventListener
-{
-
- private static final String TAG = InputPanel.class.getSimpleName();
-
- private static final int FADE_TIME = 150;
-
- private QuoteView quoteView;
- private LinkPreviewView linkPreview;
- private EmojiToggle mediaKeyboard;
- public ComposeText composeText;
- private View quickCameraToggle;
- private View quickAudioToggle;
- private View buttonToggle;
- private View recordingContainer;
- private View recordLockCancel;
-
- private MicrophoneRecorderView microphoneRecorderView;
- private SlideToCancel slideToCancel;
- private RecordTime recordTime;
-
- private @Nullable Listener listener;
- private boolean emojiVisible;
-
- public InputPanel(Context context) {
- super(context);
- }
-
- public InputPanel(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
-
- View quoteDismiss = findViewById(R.id.quote_dismiss);
-
- this.quoteView = findViewById(R.id.quote_view);
- this.linkPreview = findViewById(R.id.link_preview);
- this.mediaKeyboard = findViewById(R.id.emoji_toggle);
- this.composeText = findViewById(R.id.embedded_text_editor);
- this.quickCameraToggle = findViewById(R.id.quick_camera_toggle);
- this.quickAudioToggle = findViewById(R.id.quick_audio_toggle);
- this.buttonToggle = findViewById(R.id.button_toggle);
- this.recordingContainer = findViewById(R.id.recording_container);
- this.recordLockCancel = findViewById(R.id.record_cancel);
- View slideToCancelView = findViewById(R.id.slide_to_cancel);
- this.slideToCancel = new SlideToCancel(slideToCancelView);
- this.microphoneRecorderView = findViewById(R.id.recorder_view);
- this.microphoneRecorderView.setListener(this);
- this.recordTime = new RecordTime(findViewById(R.id.record_time),
- findViewById(R.id.microphone),
- TimeUnit.HOURS.toSeconds(1),
- () -> microphoneRecorderView.cancelAction());
-
- this.recordLockCancel.setOnClickListener(v -> microphoneRecorderView.cancelAction());
-
- if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) {
- mediaKeyboard.setVisibility(View.GONE);
- emojiVisible = false;
- } else {
- mediaKeyboard.setVisibility(View.VISIBLE);
- emojiVisible = true;
+ public InputPanel(Context context) {
+ super(context);
}
- quoteDismiss.setOnClickListener(v -> clearQuote());
-
- linkPreview.setCloseClickedListener(() -> {
- if (listener != null) {
- listener.onLinkPreviewCanceled();
- }
- });
- }
-
- public void setListener(final @NonNull Listener listener) {
- this.listener = listener;
-
- mediaKeyboard.setOnClickListener(v -> listener.onEmojiToggle());
- }
-
- public void setMediaListener(@NonNull MediaListener listener) {
- composeText.setMediaListener(listener);
- }
-
- public void setQuote(@NonNull GlideRequests glideRequests, long id, @NonNull Recipient author, @NonNull String body, @NonNull SlideDeck attachments, @NonNull Recipient conversationRecipient, long threadID) {
- this.quoteView.setQuote(glideRequests, id, author, MentionUtilities.highlightMentions(body, threadID, getContext()), false, attachments, conversationRecipient);
- this.quoteView.setVisibility(View.VISIBLE);
-
- if (this.linkPreview.getVisibility() == View.VISIBLE) {
- int cornerRadius = readDimen(R.dimen.message_corner_collapse_radius);
- this.linkPreview.setCorners(cornerRadius, cornerRadius);
- }
- }
-
- public void clearQuote() {
- this.quoteView.dismiss();
-
- if (this.linkPreview.getVisibility() == View.VISIBLE) {
- int cornerRadius = readDimen(R.dimen.message_corner_radius);
- this.linkPreview.setCorners(cornerRadius, cornerRadius);
- }
- }
-
- public Optional getQuote() {
- if (quoteView.getQuoteId() > 0 && quoteView.getVisibility() == View.VISIBLE) {
- return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getAddress(), quoteView.getBody(), false, quoteView.getAttachments()));
- } else {
- return Optional.absent();
- }
- }
-
- public void setLinkPreviewLoading() {
- this.linkPreview.setVisibility(View.VISIBLE);
- this.linkPreview.setLoading();
- }
-
- public void setLinkPreview(@NonNull GlideRequests glideRequests, @NonNull Optional preview) {
- if (preview.isPresent()) {
- this.linkPreview.setVisibility(View.VISIBLE);
- this.linkPreview.setLinkPreview(glideRequests, preview.get(), true);
- } else {
- this.linkPreview.setVisibility(View.GONE);
+ public InputPanel(Context context, AttributeSet attrs) {
+ super(context, attrs);
}
- int largeCornerRadius = (int)(16 * getResources().getDisplayMetrics().density);
- int cornerRadius = quoteView.getVisibility() == VISIBLE ? readDimen(R.dimen.message_corner_collapse_radius) : largeCornerRadius;
-
- this.linkPreview.setCorners(cornerRadius, cornerRadius);
- }
-
- public void setMediaKeyboard(@NonNull MediaKeyboard mediaKeyboard) {
- this.mediaKeyboard.attach(mediaKeyboard);
- }
-
- @Override
- public void onRecordPermissionRequired() {
- if (listener != null) listener.onRecorderPermissionRequired();
- }
-
- @Override
- public void onRecordPressed() {
- if (listener != null) listener.onRecorderStarted();
- recordTime.display();
- slideToCancel.display();
-
- if (emojiVisible) ViewUtil.fadeOut(mediaKeyboard, FADE_TIME, View.INVISIBLE);
- ViewUtil.fadeOut(composeText, FADE_TIME, View.INVISIBLE);
- ViewUtil.fadeOut(quickCameraToggle, FADE_TIME, View.INVISIBLE);
- ViewUtil.fadeOut(quickAudioToggle, FADE_TIME, View.INVISIBLE);
- buttonToggle.animate().alpha(0).setDuration(FADE_TIME).start();
- }
-
- @Override
- public void onRecordReleased() {
- long elapsedTime = onRecordHideEvent();
-
- if (listener != null) {
- Log.d(TAG, "Elapsed time: " + elapsedTime);
- if (elapsedTime > 1000) {
- listener.onRecorderFinished();
- } else {
- Toast.makeText(getContext(), R.string.InputPanel_tap_and_hold_to_record_a_voice_message_release_to_send, Toast.LENGTH_LONG).show();
- listener.onRecorderCanceled();
- }
- }
- }
-
- @Override
- public void onRecordMoved(float offsetX, float absoluteX) {
- slideToCancel.moveTo(offsetX);
-
- int direction = ViewCompat.getLayoutDirection(this);
- float position = absoluteX / recordingContainer.getWidth();
-
- if (direction == ViewCompat.LAYOUT_DIRECTION_LTR && position <= 0.5 ||
- direction == ViewCompat.LAYOUT_DIRECTION_RTL && position >= 0.6)
- {
- this.microphoneRecorderView.cancelAction();
- }
- }
-
- @Override
- public void onRecordCanceled() {
- onRecordHideEvent();
- if (listener != null) listener.onRecorderCanceled();
- }
-
- @Override
- public void onRecordLocked() {
- slideToCancel.hide();
- recordLockCancel.setVisibility(View.VISIBLE);
- buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start();
- if (listener != null) listener.onRecorderLocked();
- }
-
- public void onPause() {
- this.microphoneRecorderView.cancelAction();
- }
-
- public void setEnabled(boolean enabled) {
- composeText.setEnabled(enabled);
- mediaKeyboard.setEnabled(enabled);
- quickAudioToggle.setEnabled(enabled);
- quickCameraToggle.setEnabled(enabled);
- }
-
- public void setHint(@NonNull String hint) {
- composeText.setHint(hint, null);
- }
-
- private long onRecordHideEvent() {
- recordLockCancel.setVisibility(View.GONE);
-
- ListenableFuture future = slideToCancel.hide();
- long elapsedTime = recordTime.hide();
-
- future.addListener(new AssertedSuccessListener() {
- @Override
- public void onSuccess(Void result) {
- if (emojiVisible) ViewUtil.fadeIn(mediaKeyboard, FADE_TIME);
- ViewUtil.fadeIn(composeText, FADE_TIME);
- ViewUtil.fadeIn(quickCameraToggle, FADE_TIME);
- ViewUtil.fadeIn(quickAudioToggle, FADE_TIME);
- buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start();
- }
- });
-
- return elapsedTime;
- }
-
- @Override
- public void onKeyboardShown() {
- mediaKeyboard.setToMedia();
- }
-
- @Override
- public void onKeyEvent(KeyEvent keyEvent) {
- composeText.dispatchKeyEvent(keyEvent);
- }
-
- @Override
- public void onEmojiSelected(String emoji) {
- composeText.insertEmoji(emoji);
- }
-
- private int readDimen(@DimenRes int dimenRes) {
- return getResources().getDimensionPixelSize(dimenRes);
- }
-
- public boolean isRecordingInLockedMode() {
- return microphoneRecorderView.isRecordingLocked();
- }
-
- public void releaseRecordingLock() {
- microphoneRecorderView.unlockAction();
- }
-
- public interface Listener {
- void onRecorderStarted();
- void onRecorderLocked();
- void onRecorderFinished();
- void onRecorderCanceled();
- void onRecorderPermissionRequired();
- void onEmojiToggle();
- void onLinkPreviewCanceled();
- }
-
- private static class SlideToCancel {
-
- private final View slideToCancelView;
-
- SlideToCancel(View slideToCancelView) {
- this.slideToCancelView = slideToCancelView;
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
}
- public void display() {
- ViewUtil.fadeIn(this.slideToCancelView, FADE_TIME);
+ public interface MediaListener {
+ void onMediaSelected(@NonNull Uri uri, String contentType);
}
-
- public ListenableFuture hide() {
- final SettableFuture future = new SettableFuture<>();
-
- AnimationSet animation = new AnimationSet(true);
- animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, slideToCancelView.getTranslationX(),
- Animation.ABSOLUTE, 0,
- Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, 0));
- animation.addAnimation(new AlphaAnimation(1, 0));
-
- animation.setDuration(MicrophoneRecorderView.ANIMATION_DURATION);
- animation.setFillBefore(true);
- animation.setFillAfter(false);
-
- slideToCancelView.postDelayed(() -> future.set(null), MicrophoneRecorderView.ANIMATION_DURATION);
- slideToCancelView.setVisibility(View.GONE);
- slideToCancelView.startAnimation(animation);
-
- return future;
- }
-
- void moveTo(float offset) {
- Animation animation = new TranslateAnimation(Animation.ABSOLUTE, offset,
- Animation.ABSOLUTE, offset,
- Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, 0);
-
- animation.setDuration(0);
- animation.setFillAfter(true);
- animation.setFillBefore(true);
-
- slideToCancelView.startAnimation(animation);
- }
- }
-
- private static class RecordTime implements Runnable {
-
- private final @NonNull TextView recordTimeView;
- private final @NonNull View microphone;
- private final @NonNull Runnable onLimitHit;
- private final long limitSeconds;
- private long startTime;
-
- private RecordTime(@NonNull TextView recordTimeView, @NonNull View microphone, long limitSeconds, @NonNull Runnable onLimitHit) {
- this.recordTimeView = recordTimeView;
- this.microphone = microphone;
- this.limitSeconds = limitSeconds;
- this.onLimitHit = onLimitHit;
- }
-
- @MainThread
- public void display() {
- this.startTime = System.currentTimeMillis();
- this.recordTimeView.setText(DateUtils.formatElapsedTime(0));
- ViewUtil.fadeIn(this.recordTimeView, FADE_TIME);
- Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
- microphone.setVisibility(View.VISIBLE);
- microphone.startAnimation(pulseAnimation());
- }
-
- @MainThread
- public long hide() {
- long elapsedTime = System.currentTimeMillis() - startTime;
- this.startTime = 0;
- ViewUtil.fadeOut(this.recordTimeView, FADE_TIME, View.INVISIBLE);
- microphone.clearAnimation();
- ViewUtil.fadeOut(this.microphone, FADE_TIME, View.INVISIBLE);
- return elapsedTime;
- }
-
- @Override
- @MainThread
- public void run() {
- long localStartTime = startTime;
- if (localStartTime > 0) {
- long elapsedTime = System.currentTimeMillis() - localStartTime;
- long elapsedSeconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTime);
- if (elapsedSeconds >= limitSeconds) {
- onLimitHit.run();
- } else {
- recordTimeView.setText(DateUtils.formatElapsedTime(elapsedSeconds));
- Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
- }
- }
- }
-
- private static Animation pulseAnimation() {
- AlphaAnimation animation = new AlphaAnimation(0, 1);
-
- animation.setInterpolator(pulseInterpolator());
- animation.setRepeatCount(Animation.INFINITE);
- animation.setDuration(1000);
-
- return animation;
- }
-
- private static Interpolator pulseInterpolator() {
- return input -> {
- input *= 5;
- if (input > 1) {
- input = 4 - input;
- }
- return Math.max(0, Math.min(1, input));
- };
- }
- }
-
- public interface MediaListener {
- void onMediaSelected(@NonNull Uri uri, String contentType);
- }
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java b/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
deleted file mode 100644
index 26b3d4f62f..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
+++ /dev/null
@@ -1,272 +0,0 @@
-package org.thoughtcrime.securesms.components;
-
-import android.Manifest;
-import android.content.Context;
-import android.graphics.PorterDuff;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.view.ViewCompat;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnticipateOvershootInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import org.thoughtcrime.securesms.permissions.Permissions;
-
-import org.session.libsession.utilities.ViewUtil;
-
-import network.loki.messenger.R;
-
-public final class MicrophoneRecorderView extends FrameLayout implements View.OnTouchListener {
-
- enum State {
- NOT_RUNNING,
- RUNNING_HELD,
- RUNNING_LOCKED
- }
-
- public static final int ANIMATION_DURATION = 200;
-
- private FloatingRecordButton floatingRecordButton;
- private LockDropTarget lockDropTarget;
- private @Nullable Listener listener;
- private @NonNull State state = State.NOT_RUNNING;
-
- public MicrophoneRecorderView(Context context) {
- super(context);
- }
-
- public MicrophoneRecorderView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
-
- floatingRecordButton = new FloatingRecordButton(getContext(), findViewById(R.id.quick_audio_fab));
- lockDropTarget = new LockDropTarget (getContext(), findViewById(R.id.lock_drop_target));
-
- View recordButton = ViewUtil.findById(this, R.id.quick_audio_toggle);
- recordButton.setOnTouchListener(this);
- }
-
- public void cancelAction() {
- if (state != State.NOT_RUNNING) {
- state = State.NOT_RUNNING;
- hideUi();
-
- if (listener != null) listener.onRecordCanceled();
- }
- }
-
- public boolean isRecordingLocked() {
- return state == State.RUNNING_LOCKED;
- }
-
- private void lockAction() {
- if (state == State.RUNNING_HELD) {
- state = State.RUNNING_LOCKED;
- hideUi();
-
- if (listener != null) listener.onRecordLocked();
- }
- }
-
- public void unlockAction() {
- if (state == State.RUNNING_LOCKED) {
- state = State.NOT_RUNNING;
- hideUi();
-
- if (listener != null) listener.onRecordReleased();
- }
- }
-
- private void hideUi() {
- floatingRecordButton.hide();
- lockDropTarget.hide();
- }
-
- @Override
- public boolean onTouch(View v, final MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- if (!Permissions.hasAll(getContext(), Manifest.permission.RECORD_AUDIO)) {
- if (listener != null) listener.onRecordPermissionRequired();
- } else {
- state = State.RUNNING_HELD;
- floatingRecordButton.display(event.getX(), event.getY());
- lockDropTarget.display();
- if (listener != null) listener.onRecordPressed();
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- if (this.state == State.RUNNING_HELD) {
- state = State.NOT_RUNNING;
- hideUi();
- if (listener != null) listener.onRecordReleased();
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (this.state == State.RUNNING_HELD) {
- this.floatingRecordButton.moveTo(event.getX(), event.getY());
- if (listener != null) listener.onRecordMoved(floatingRecordButton.lastOffsetX, event.getRawX());
-
- int dimensionPixelSize = getResources().getDimensionPixelSize(R.dimen.recording_voice_lock_target);
- if (floatingRecordButton.lastOffsetY <= dimensionPixelSize) {
- lockAction();
- }
- }
- break;
- }
-
- return false;
- }
-
- public void setListener(@Nullable Listener listener) {
- this.listener = listener;
- }
-
- public interface Listener {
- void onRecordPressed();
- void onRecordReleased();
- void onRecordCanceled();
- void onRecordLocked();
- void onRecordMoved(float offsetX, float absoluteX);
- void onRecordPermissionRequired();
- }
-
- private static class FloatingRecordButton {
-
- private final ImageView recordButtonFab;
-
- private float startPositionX;
- private float startPositionY;
- private float lastOffsetX;
- private float lastOffsetY;
-
- FloatingRecordButton(Context context, ImageView recordButtonFab) {
- this.recordButtonFab = recordButtonFab;
- this.recordButtonFab.getBackground().setColorFilter(context.getResources()
- .getColor(R.color.destructive),
- PorterDuff.Mode.SRC_IN);
- }
-
- void display(float x, float y) {
- this.startPositionX = x;
- this.startPositionY = y;
-
- recordButtonFab.setVisibility(View.VISIBLE);
-
- AnimationSet animation = new AnimationSet(true);
- animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, 0,
- Animation.ABSOLUTE, 0,
- Animation.ABSOLUTE, 0,
- Animation.ABSOLUTE, 0));
-
- animation.addAnimation(new ScaleAnimation(.5f, 1f, .5f, 1f,
- Animation.RELATIVE_TO_SELF, .5f,
- Animation.RELATIVE_TO_SELF, .5f));
-
- animation.setDuration(ANIMATION_DURATION);
- animation.setInterpolator(new OvershootInterpolator());
-
- recordButtonFab.startAnimation(animation);
- }
-
- void moveTo(float x, float y) {
- lastOffsetX = getXOffset(x);
- lastOffsetY = getYOffset(y);
-
- if (Math.abs(lastOffsetX) > Math.abs(lastOffsetY)) {
- lastOffsetY = 0;
- } else {
- lastOffsetX = 0;
- }
-
- recordButtonFab.setTranslationX(lastOffsetX);
- recordButtonFab.setTranslationY(lastOffsetY);
- }
-
- void hide() {
- recordButtonFab.setTranslationX(0);
- recordButtonFab.setTranslationY(0);
- if (recordButtonFab.getVisibility() != VISIBLE) return;
-
- AnimationSet animation = new AnimationSet(false);
- Animation scaleAnimation = new ScaleAnimation(1, 0.5f, 1, 0.5f,
- Animation.RELATIVE_TO_SELF, 0.5f,
- Animation.RELATIVE_TO_SELF, 0.5f);
-
- Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, lastOffsetX,
- Animation.ABSOLUTE, 0,
- Animation.ABSOLUTE, lastOffsetY,
- Animation.ABSOLUTE, 0);
-
- scaleAnimation.setInterpolator(new AnticipateOvershootInterpolator(1.5f));
- translateAnimation.setInterpolator(new DecelerateInterpolator());
- animation.addAnimation(scaleAnimation);
- animation.addAnimation(translateAnimation);
- animation.setDuration(ANIMATION_DURATION);
- animation.setInterpolator(new AnticipateOvershootInterpolator(1.5f));
-
- recordButtonFab.setVisibility(View.GONE);
- recordButtonFab.clearAnimation();
- recordButtonFab.startAnimation(animation);
- }
-
- private float getXOffset(float x) {
- return ViewCompat.getLayoutDirection(recordButtonFab) == ViewCompat.LAYOUT_DIRECTION_LTR ?
- -Math.max(0, this.startPositionX - x) : Math.max(0, x - this.startPositionX);
- }
-
- private float getYOffset(float y) {
- return Math.min(0, y - this.startPositionY);
- }
- }
-
- private static class LockDropTarget {
-
- private final View lockDropTarget;
- private final int dropTargetPosition;
-
- LockDropTarget(Context context, View lockDropTarget) {
- this.lockDropTarget = lockDropTarget;
- this.dropTargetPosition = context.getResources().getDimensionPixelSize(R.dimen.recording_voice_lock_target);
- }
-
- void display() {
- lockDropTarget.setScaleX(1);
- lockDropTarget.setScaleY(1);
- lockDropTarget.setAlpha(0);
- lockDropTarget.setTranslationY(0);
- lockDropTarget.setVisibility(VISIBLE);
- lockDropTarget.animate()
- .setStartDelay(ANIMATION_DURATION * 2)
- .setDuration(ANIMATION_DURATION)
- .setInterpolator(new DecelerateInterpolator())
- .translationY(dropTargetPosition)
- .alpha(1)
- .start();
- }
-
- void hide() {
- lockDropTarget.animate()
- .setStartDelay(0)
- .setDuration(ANIMATION_DURATION)
- .setInterpolator(new LinearInterpolator())
- .scaleX(0).scaleY(0)
- .start();
- }
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java
deleted file mode 100644
index d6db6ec16d..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java
+++ /dev/null
@@ -1,2440 +0,0 @@
-/*
- * Copyright (C) 2011 Whisper Systems
- *
- * 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 .
- */
-package org.thoughtcrime.securesms.conversation;
-
-import android.Manifest;
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.hardware.Camera;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Vibrator;
-import android.provider.Browser;
-import android.provider.Telephony;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Pair;
-import android.util.TypedValue;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.view.View.OnKeyListener;
-import android.view.WindowManager;
-import android.view.inputmethod.EditorInfo;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.widget.SearchView;
-import androidx.appcompat.widget.Toolbar;
-import androidx.core.content.pm.ShortcutInfoCompat;
-import androidx.core.content.pm.ShortcutManagerCompat;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.core.view.MenuItemCompat;
-import androidx.lifecycle.ViewModelProviders;
-import androidx.loader.app.LoaderManager;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-import com.annimon.stream.Stream;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-import org.session.libsession.messaging.mentions.MentionsManager;
-import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
-import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
-import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
-import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
-import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
-import org.session.libsession.messaging.messages.visible.VisibleMessage;
-import org.session.libsession.messaging.open_groups.OpenGroupV2;
-import org.session.libsession.messaging.sending_receiving.MessageSender;
-import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
-import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
-import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
-import org.session.libsession.utilities.Contact;
-import org.session.libsession.utilities.Address;
-import org.session.libsession.utilities.DistributionTypes;
-import org.session.libsession.utilities.GroupRecord;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.session.libsession.utilities.recipients.RecipientFormattingException;
-import org.session.libsession.utilities.recipients.RecipientModifiedListener;
-import org.session.libsession.utilities.ExpirationUtil;
-import org.session.libsession.utilities.GroupUtil;
-import org.session.libsession.utilities.MediaTypes;
-import org.session.libsession.utilities.ServiceUtil;
-import org.session.libsession.utilities.TextSecurePreferences;
-import org.session.libsession.utilities.Util;
-import org.session.libsession.utilities.ViewUtil;
-import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
-import org.session.libsession.utilities.Stub;
-import org.session.libsignal.exceptions.InvalidMessageException;
-import org.session.libsignal.utilities.guava.Optional;
-import org.session.libsession.messaging.mentions.Mention;
-import org.session.libsignal.utilities.HexEncodingKt;
-import org.session.libsignal.utilities.PublicKeyValidation;
-import org.session.libsignal.utilities.ListenableFuture;
-import org.session.libsignal.utilities.SettableFuture;
-import org.session.libsignal.utilities.Log;
-import org.thoughtcrime.securesms.ApplicationContext;
-import org.thoughtcrime.securesms.ExpirationDialog;
-import org.thoughtcrime.securesms.MediaOverviewActivity;
-import org.thoughtcrime.securesms.MuteDialog;
-import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
-import org.thoughtcrime.securesms.ShortcutLauncherActivity;
-import org.thoughtcrime.securesms.audio.AudioRecorder;
-import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
-import org.thoughtcrime.securesms.components.AnimatingToggle;
-import org.thoughtcrime.securesms.components.AttachmentTypeSelector;
-import org.thoughtcrime.securesms.components.ComposeText;
-import org.thoughtcrime.securesms.components.ConversationSearchBottomBar;
-import org.thoughtcrime.securesms.components.HidingLinearLayout;
-import org.thoughtcrime.securesms.components.InputAwareLayout;
-import org.thoughtcrime.securesms.components.InputPanel;
-import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
-import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
-import org.thoughtcrime.securesms.components.emoji.EmojiStrings;
-import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
-import org.thoughtcrime.securesms.contacts.ContactAccessor;
-import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
-import org.thoughtcrime.securesms.contactshare.ContactUtil;
-import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.DraftDatabase;
-import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
-import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
-import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
-import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
-import org.thoughtcrime.securesms.database.ThreadDatabase;
-import org.thoughtcrime.securesms.database.model.MessageRecord;
-import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
-import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
-import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
-import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
-import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
-import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity;
-import org.thoughtcrime.securesms.loki.activities.HomeActivity;
-import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity;
-import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
-import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
-import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
-import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
-import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities;
-import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
-import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
-import org.thoughtcrime.securesms.mediasend.Media;
-import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
-import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager;
-import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager.MediaType;
-import org.thoughtcrime.securesms.mms.AudioSlide;
-import org.thoughtcrime.securesms.mms.GifSlide;
-import org.thoughtcrime.securesms.mms.GlideApp;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.mms.ImageSlide;
-import org.thoughtcrime.securesms.mms.MediaConstraints;
-import org.thoughtcrime.securesms.mms.MmsException;
-import org.thoughtcrime.securesms.mms.QuoteId;
-import org.thoughtcrime.securesms.mms.Slide;
-import org.thoughtcrime.securesms.mms.SlideDeck;
-import org.thoughtcrime.securesms.mms.TextSlide;
-import org.thoughtcrime.securesms.mms.VideoSlide;
-import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
-import org.thoughtcrime.securesms.permissions.Permissions;
-import org.thoughtcrime.securesms.providers.BlobProvider;
-import org.thoughtcrime.securesms.search.model.MessageResult;
-import org.thoughtcrime.securesms.service.ExpiringMessageManager;
-import org.thoughtcrime.securesms.util.BitmapUtil;
-import org.thoughtcrime.securesms.util.DateUtils;
-import org.thoughtcrime.securesms.util.MediaUtil;
-import org.thoughtcrime.securesms.util.PushCharacterCalculator;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import kotlin.Unit;
-import network.loki.messenger.R;
-
-/**
- * Activity for displaying a message thread, as well as
- * composing/sending a new message into that thread.
- *
- * @author Moxie Marlinspike
- *
- */
-@SuppressLint("StaticFieldLeak")
-public class ConversationActivity extends PassphraseRequiredActionBarActivity
- implements ConversationFragment.ConversationFragmentListener,
- AttachmentManager.AttachmentListener,
- RecipientModifiedListener,
- OnKeyboardShownListener,
- InputPanel.Listener,
- InputPanel.MediaListener,
- ComposeText.CursorPositionChangedListener,
- ConversationSearchBottomBar.EventListener
-{
- private static final String TAG = ConversationActivity.class.getSimpleName();
-
- public static final String ADDRESS_EXTRA = "address";
- public static final String THREAD_ID_EXTRA = "thread_id";
- public static final String IS_ARCHIVED_EXTRA = "is_archived";
- public static final String TEXT_EXTRA = "draft_text";
- public static final String MEDIA_EXTRA = "media_list";
- public static final String STICKER_EXTRA = "media_list";
- public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type";
- public static final String TIMING_EXTRA = "timing";
- public static final String LAST_SEEN_EXTRA = "last_seen";
- public static final String STARTING_POSITION_EXTRA = "starting_position";
-
- // private static final int PICK_GALLERY = 1;
- private static final int PICK_DOCUMENT = 2;
- private static final int PICK_AUDIO = 3;
- private static final int PICK_CONTACT = 4;
- // private static final int GET_CONTACT_DETAILS = 5;
-// private static final int GROUP_EDIT = 6;
- private static final int TAKE_PHOTO = 7;
- private static final int ADD_CONTACT = 8;
- private static final int PICK_LOCATION = 9;
- private static final int PICK_GIF = 10;
- private static final int SMS_DEFAULT = 11;
- private static final int MEDIA_SENDER = 12;
- private static final int INVITE_CONTACTS = 124;
-
- private GlideRequests glideRequests;
- protected ComposeText composeText;
- private AnimatingToggle buttonToggle;
- private ImageButton sendButton;
- private ImageButton attachButton;
- private ProfilePictureView profilePictureView;
- private TextView titleTextView;
- private ConversationFragment fragment;
- private Button unblockButton;
- private Button makeDefaultSmsButton;
- private InputAwareLayout container;
- private TypingStatusTextWatcher typingTextWatcher;
- private MentionTextWatcher mentionTextWatcher;
- private ConversationSearchBottomBar searchNav;
- private MenuItem searchViewItem;
- private ProgressBar messageStatusProgressBar;
- private ImageView muteIndicatorImageView;
- private TextView subtitleTextView;
- private View homeButtonContainer;
-
- private AttachmentTypeSelector attachmentTypeSelector;
- private AttachmentManager attachmentManager;
- private AudioRecorder audioRecorder;
- private Handler audioHandler;
- private Runnable stopRecordingTask;
- private Stub emojiDrawerStub;
- protected HidingLinearLayout quickAttachmentToggle;
- protected HidingLinearLayout inlineAttachmentToggle;
- private InputPanel inputPanel;
-
- private LinkPreviewViewModel linkPreviewViewModel;
- private ConversationSearchViewModel searchViewModel;
-
- private Recipient recipient;
- private long threadId;
- private int distributionType;
- private boolean isDefaultSms = false;
- private boolean isSecurityInitialized = false;
- private int expandedKeyboardHeight = 0;
- private int collapsedKeyboardHeight = Integer.MAX_VALUE;
- private int keyboardHeight = 0;
-
- // Message status bar
- private ArrayList broadcastReceivers = new ArrayList<>();
- private String messageStatus = null;
-
- // Mentions
- private View mentionCandidateSelectionViewContainer;
- private MentionCandidateSelectionView mentionCandidateSelectionView;
- private int currentMentionStartIndex = -1;
- private ArrayList mentions = new ArrayList<>();
- private String oldText = "";
-
- private final PushCharacterCalculator characterCalculator = new PushCharacterCalculator();
-
- @Override
- protected void onCreate(Bundle state, boolean ready) {
- Log.i(TAG, "onCreate()");
-
- setContentView(R.layout.conversation_activity);
-
- fragment = initFragment(R.id.fragment_content, new ConversationFragment(), Locale.getDefault());
-
- registerMessageStatusObserver("calculatingPoW");
- registerMessageStatusObserver("contactingNetwork");
- registerMessageStatusObserver("sendingMessage");
- registerMessageStatusObserver("messageSent");
- registerMessageStatusObserver("messageFailed");
- BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Toast.makeText(ConversationActivity.this, "Your clock is out of sync with the service node network.", Toast.LENGTH_LONG).show();
- }
- };
- broadcastReceivers.add(broadcastReceiver);
- LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("clockOutOfSync"));
-
- initializeActionBar();
- initializeViews();
- initializeResources();
- initializeLinkPreviewObserver();
- initializeSearchObserver();
- initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener() {
- @Override
- public void onSuccess(Boolean result) {
- initializeDraft().addListener(new AssertedSuccessListener() {
- @Override
- public void onSuccess(Boolean loadedDraft) {
- if (loadedDraft != null && loadedDraft) {
- Log.i(TAG, "Finished loading draft");
- Util.runOnMain(() -> {
- if (fragment != null && fragment.isResumed()) {
- fragment.moveToLastSeen();
- } else {
- Log.w(TAG, "Wanted to move to the last seen position, but the fragment was in an invalid state");
- }
- });
- }
-
- if (TextSecurePreferences.isTypingIndicatorsEnabled(ConversationActivity.this)) {
- composeText.addTextChangedListener(typingTextWatcher);
- }
- composeText.setSelection(composeText.length(), composeText.length());
- composeText.addTextChangedListener(mentionTextWatcher);
- mentionCandidateSelectionView.setGlide(glideRequests);
- mentionCandidateSelectionView.setOnMentionCandidateSelected( mentionCandidate -> {
- mentions.add(mentionCandidate);
- String oldText = composeText.getText().toString();
- String newText = oldText.substring(0, currentMentionStartIndex) + "@" + mentionCandidate.getDisplayName() + " ";
- composeText.setText(newText);
- composeText.setSelection(newText.length());
- currentMentionStartIndex = -1;
- mentionCandidateSelectionView.hide();
- ConversationActivity.this.oldText = newText;
- return Unit.INSTANCE;
- });
- }
- });
- }
- });
-
- MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this);
-
- OpenGroupV2 openGroupV2 = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId);
- if (openGroupV2 != null) {
- PublicChatInfoUpdateWorker.scheduleInstant(this, openGroupV2.getServer(), openGroupV2.getRoom());
- if (openGroupV2.getRoom().equals("session") || openGroupV2.getRoom().equals("oxen")
- || openGroupV2.getRoom().equals("lokinet") || openGroupV2.getRoom().equals("crypto")) {
- View openGroupGuidelinesView = findViewById(R.id.open_group_guidelines_view);
- openGroupGuidelinesView.setVisibility(View.VISIBLE);
- }
- }
-
- View rootView = findViewById(R.id.rootView);
- rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
- int height = rootView.getRootView().getHeight() - rootView.getHeight();
- int thresholdInDP = 120;
- float scale = getResources().getDisplayMetrics().density;
- int thresholdInPX = (int)(thresholdInDP * scale);
- if (expandedKeyboardHeight == 0 || height > thresholdInPX) {
- expandedKeyboardHeight = height;
- }
- collapsedKeyboardHeight = Math.min(collapsedKeyboardHeight, height);
- keyboardHeight = expandedKeyboardHeight - collapsedKeyboardHeight;
-
- // Use 300dp if the keyboard wasn't opened yet.
- if (keyboardHeight == 0) {
- keyboardHeight = (int)(300f * getResources().getDisplayMetrics().density);
- }
- });
- }
-
- private void registerMessageStatusObserver(String status) {
- BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- long timestamp = intent.getLongExtra("long", 0);
- handleMessageStatusChanged(status, timestamp);
- }
- };
- broadcastReceivers.add(broadcastReceiver);
- LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter(status));
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- Log.i(TAG, "onNewIntent()");
-
- if (isFinishing()) {
- Log.w(TAG, "Activity is finishing...");
- return;
- }
-
- if (!org.thoughtcrime.securesms.util.Util.isEmpty(composeText)) {
- saveDraft();
- attachmentManager.clear();
- silentlySetComposeText("");
- }
-
- setIntent(intent);
- initializeResources();
- initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener() {
- @Override
- public void onSuccess(Boolean result) {
- initializeDraft();
- }
- });
-
- if (fragment != null) {
- fragment.onNewIntent();
- }
-
- searchNav.setVisibility(View.GONE);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- EventBus.getDefault().register(this);
- initializeEnabledCheck();
- composeText.setTransport();
-
- updateTitleTextView(recipient);
- updateProfilePicture();
- updateSubtitleTextView();
- updateInputUI(recipient);
-
- ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
- markThreadAsRead();
-
- inputPanel.setHint(getResources().getString(R.string.ConversationActivity_message));
-
- Log.i(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0)));
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L);
- if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
- inputPanel.onPause();
-
- fragment.setLastSeen(System.currentTimeMillis());
- markLastSeen();
- AudioSlidePlayer.stopAll();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- Log.i(TAG, "onConfigurationChanged(" + newConfig.orientation + ")");
- super.onConfigurationChanged(newConfig);
- composeText.setTransport();
-
- if (emojiDrawerStub.resolved() && container.getCurrentInput() == emojiDrawerStub.get()) {
- container.hideAttachedInput(true);
- }
- }
-
- @Override
- protected void onDestroy() {
- saveDraft();
- if (recipient != null) recipient.removeListener(this);
- for (BroadcastReceiver broadcastReceiver : broadcastReceivers) {
- LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
- }
- super.onDestroy();
- }
-
- @Override
- public void onActivityResult(final int reqCode, int resultCode, Intent data) {
- Log.i(TAG, "onActivityResult called: " + reqCode + ", " + resultCode + " , " + data);
- super.onActivityResult(reqCode, resultCode, data);
-
- if ((data == null && reqCode != TAKE_PHOTO && reqCode != SMS_DEFAULT) ||
- (resultCode != RESULT_OK && reqCode != SMS_DEFAULT))
- {
- updateLinkPreviewState();
- return;
- }
-
- switch (reqCode) {
- case PICK_DOCUMENT:
- setMedia(data.getData(), MediaType.DOCUMENT);
- break;
- case PICK_AUDIO:
- setMedia(data.getData(), MediaType.AUDIO);
- break;
- case TAKE_PHOTO:
- if (attachmentManager.getCaptureUri() != null) {
- setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE);
- }
- break;
- case ADD_CONTACT:
- recipient = Recipient.from(this, recipient.getAddress(), true);
- recipient.addListener(this);
- fragment.reloadList();
- break;
- /*
- case PICK_LOCATION:
- SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this));
- attachmentManager.setLocation(place, getCurrentMediaConstraints());
- break;
- */
- case PICK_GIF:
- setMedia(data.getData(),
- MediaType.GIF,
- data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0),
- data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0));
- break;
- case SMS_DEFAULT:
- initializeSecurity(true, isDefaultSms);
- break;
- case MEDIA_SENDER:
- long expiresIn = recipient.getExpireMessages() * 1000L;
- int subscriptionId = -1;
- boolean initiating = threadId == -1;
- String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE);
- SlideDeck slideDeck = new SlideDeck();
-
- List mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA);
-
- for (Media mediaItem : mediaList) {
- if (MediaUtil.isVideoType(mediaItem.getMimeType())) {
- slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull()));
- } else if (MediaUtil.isGif(mediaItem.getMimeType())) {
- slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull()));
- } else if (MediaUtil.isImageType(mediaItem.getMimeType())) {
- slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull()));
- } else {
- Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping.");
- }
- }
-
- final Context context = ConversationActivity.this.getApplicationContext();
-
- sendMediaMessage(message,
- slideDeck,
- inputPanel.getQuote().orNull(),
- Optional.absent(),
- initiating).addListener(new AssertedSuccessListener() {
- @Override
- public void onSuccess(Void result) {
- AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- Stream.of(slideDeck.getSlides())
- .map(Slide::getUri)
- .withoutNulls()
- .filter(BlobProvider::isAuthority)
- .forEach(uri -> BlobProvider.getInstance().delete(context, uri));
- });
- }
- });
- break;
- case INVITE_CONTACTS:
- if (data.getExtras() == null || !data.hasExtra(SelectContactsActivity.Companion.getSelectedContactsKey())) return;
- String[] selectedContacts = data.getExtras().getStringArray(SelectContactsActivity.Companion.getSelectedContactsKey());
- sendOpenGroupInvitations(selectedContacts);
- break;
- }
- }
-
- @Override
- public void startActivity(Intent intent) {
- if (intent.getStringExtra(Browser.EXTRA_APPLICATION_ID) != null) {
- intent.removeExtra(Browser.EXTRA_APPLICATION_ID);
- }
-
- try {
- super.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, e);
- Toast.makeText(this, R.string.ConversationActivity_there_is_no_app_available_to_handle_this_link_on_your_device, Toast.LENGTH_LONG).show();
- }
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuInflater inflater = this.getMenuInflater();
- menu.clear();
-
- boolean isOpenGroupOrRSSFeed = recipient.getAddress().isOpenGroup();
-
- if (!isOpenGroupOrRSSFeed) {
- if (recipient.getExpireMessages() > 0) {
- inflater.inflate(R.menu.conversation_expiring_on, menu);
-
- final MenuItem item = menu.findItem(R.id.menu_expiring_messages);
- final View actionView = MenuItemCompat.getActionView(item);
- final ImageView iconView = actionView.findViewById(R.id.menu_badge_icon);
- final TextView badgeView = actionView.findViewById(R.id.expiration_badge);
-
- @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getTheme());
- iconView.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
- badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages()));
- actionView.setOnClickListener(v -> onOptionsItemSelected(item));
- } else {
- inflater.inflate(R.menu.conversation_expiring_off, menu);
- }
- }
-
- if (isSingleConversation()) {
- if (recipient.isBlocked()) {
- inflater.inflate(R.menu.conversation_unblock, menu);
- } else {
- inflater.inflate(R.menu.conversation_block, menu);
- }
- inflater.inflate(R.menu.conversation_copy_session_id, menu);
- } else if (isGroupConversation() && !isOpenGroupOrRSSFeed) {
-// inflater.inflate(R.menu.conversation_group_options, menu);
-
- if (!isPushGroupConversation()) {
- inflater.inflate(R.menu.conversation_mms_group_options, menu);
- if (distributionType == DistributionTypes.BROADCAST) {
- menu.findItem(R.id.menu_distribution_broadcast).setChecked(true);
- } else {
- menu.findItem(R.id.menu_distribution_conversation).setChecked(true);
- }
- } else if (isActiveGroup()) {
- inflater.inflate(R.menu.conversation_push_group_options, menu);
- }
- } else if (isOpenGroupOrRSSFeed) {
- inflater.inflate(R.menu.conversation_invite_open_group, menu);
- }
-
- inflater.inflate(R.menu.conversation, menu);
-
-// if (isSingleConversation()) {
-// inflater.inflate(R.menu.conversation_secure, menu);
-// }
-
- if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
- else inflater.inflate(R.menu.conversation_unmuted, menu);
-
- /*
- if (isSingleConversation() && getRecipient().getContactUri() == null) {
- inflater.inflate(R.menu.conversation_add_to_contacts, menu);
- }
- if (recipient != null && recipient.isLocalNumber()) {
- if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false);
- else menu.findItem(R.id.menu_call_insecure).setVisible(false);
- MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications);
- if (muteItem != null) {
- muteItem.setVisible(false);
- }
- }
- */
-
- searchViewItem = menu.findItem(R.id.menu_search);
-
- SearchView searchView = (SearchView)searchViewItem.getActionView();
- SearchView.OnQueryTextListener queryListener = new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- searchViewModel.onQueryUpdated(query, threadId);
- searchNav.showLoading();
- fragment.onSearchQueryUpdated(query);
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String query) {
- searchViewModel.onQueryUpdated(query, threadId);
- searchNav.showLoading();
- fragment.onSearchQueryUpdated(query);
- return true;
- }
- };
-
- searchViewItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
- @Override
- public boolean onMenuItemActionExpand(MenuItem item) {
- searchView.setOnQueryTextListener(queryListener);
- searchViewModel.onSearchOpened();
- searchNav.setVisibility(View.VISIBLE);
- searchNav.setData(0, 0);
- inputPanel.setVisibility(View.GONE);
-
- for (int i = 0; i < menu.size(); i++) {
- if (!menu.getItem(i).equals(searchViewItem)) {
- menu.getItem(i).setVisible(false);
- }
- }
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(MenuItem item) {
- searchView.setOnQueryTextListener(null);
- searchViewModel.onSearchClosed();
- searchNav.setVisibility(View.GONE);
- inputPanel.setVisibility(View.VISIBLE);
- updateInputUI(recipient);
- fragment.onSearchQueryUpdated(null);
- invalidateOptionsMenu();
- return true;
- }
- });
-
- super.onPrepareOptionsMenu(menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- super.onOptionsItemSelected(item);
- switch (item.getItemId()) {
-// case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
-// case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
- case R.id.menu_unblock: handleUnblock(); return true;
- case R.id.menu_block: handleBlock(); return true;
- case R.id.menu_copy_session_id: handleCopySessionID(); return true;
- case R.id.menu_view_media: handleViewMedia(); return true;
- case R.id.menu_add_shortcut: handleAddShortcut(); return true;
- case R.id.menu_search: handleSearch(); return true;
-// case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
-// case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
-// case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
- case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true;
- case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true;
- case R.id.menu_edit_group: handleEditPushGroup(); return true;
- case R.id.menu_leave: handleLeavePushGroup(); return true;
- case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
- case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
-// case R.id.menu_conversation_settings: handleConversationSettings(); return true;
- case R.id.menu_expiring_messages_off:
- case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true;
- case R.id.menu_invite_to_open_group: handleInviteToOpenGroup(); return true;
- case android.R.id.home: handleReturnToConversationList(); return true;
- }
-
- return false;
- }
-
- @Override
- public void onBackPressed() {
- Log.d(TAG, "onBackPressed()");
- if (container.isInputOpen()) container.hideCurrentInput(composeText);
- else super.onBackPressed();
- }
-
- @Override
- public void onKeyboardShown() {
- inputPanel.onKeyboardShown();
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
- }
-
- //////// Event Handlers
-
- private void handleReturnToConversationList() {
- Intent intent = new Intent(this, HomeActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- finish();
- }
-
- private void handleSelectMessageExpiration() {
- if (isPushGroupConversation() && !isActiveGroup()) {
- return;
- }
-
- //noinspection CodeBlock2Expr
- ExpirationDialog.show(this, recipient.getExpireMessages(), expirationTime -> {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
- ExpirationTimerUpdate message = new ExpirationTimerUpdate(expirationTime);
- message.setRecipient(recipient.getAddress().serialize()); // we need the recipient in ExpiringMessageManager.insertOutgoingExpirationTimerMessage
- message.setSentTimestamp(System.currentTimeMillis());
- ExpiringMessageManager expiringMessageManager = ApplicationContext.getInstance(getApplicationContext()).getExpiringMessageManager();
- expiringMessageManager.setExpirationTimer(message);
- MessageSender.send(message, recipient.getAddress());
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- invalidateOptionsMenu();
- if (fragment != null) fragment.setLastSeen(0);
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- });
- }
-
- private void handleMuteNotifications() {
- MuteDialog.show(this, until -> {
- recipient.setMuted(until);
-
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
- .setMuted(recipient, until);
-
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- });
- }
-
- private void handleUnmuteNotifications() {
- recipient.setMuted(0);
-
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
- .setMuted(recipient, 0);
-
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- private void handleUnblock() {
- int titleRes = R.string.ConversationActivity_unblock_this_contact_question;
- int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact;
-
- new AlertDialog.Builder(this)
- .setTitle(titleRes)
- .setMessage(bodyRes)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
- .setBlocked(recipient, false);
-
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }).show();
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private void handleMakeDefaultSms() {
- Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
- intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, getPackageName());
- startActivityForResult(intent, SMS_DEFAULT);
- }
-
- private void handleBlock() {
- int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question;
- int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact;
-
- new AlertDialog.Builder(this)
- .setTitle(titleRes)
- .setMessage(bodyRes)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
- .setBlocked(recipient, true);
-
- Util.runOnMain(() -> finish());
-
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }).show();
- }
-
- private void handleCopySessionID() {
- if (recipient.isGroupRecipient()) { return; }
- String sessionID = recipient.getAddress().toString();
- ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("Session ID", sessionID);
- clipboard.setPrimaryClip(clip);
- Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
- }
-
- private void handleViewMedia() {
- Intent intent = new Intent(this, MediaOverviewActivity.class);
- intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
- startActivity(intent);
- }
-
- private void handleAddShortcut() {
- Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.getAddress());
-
- new AsyncTask() {
-
- @Override
- protected IconCompat doInBackground(Void... voids) {
- Context context = getApplicationContext();
- IconCompat icon = null;
-
- if (recipient.getContactPhoto() != null) {
- try {
- Bitmap bitmap = BitmapFactory.decodeStream(recipient.getContactPhoto().openInputStream(context));
- bitmap = BitmapUtil.createScaledBitmap(bitmap, 300, 300);
- icon = IconCompat.createWithAdaptiveBitmap(bitmap);
- } catch (IOException e) {
- Log.w(TAG, "Failed to decode contact photo during shortcut creation. Falling back to generic icon.", e);
- }
- }
-
- if (icon == null) {
- icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut
- : R.mipmap.ic_person_shortcut);
- }
-
- return icon;
- }
-
- @Override
- protected void onPostExecute(IconCompat icon) {
- Context context = getApplicationContext();
- String name = Optional.fromNullable(recipient.getName())
- .or(Optional.fromNullable(recipient.getProfileName()))
- .or(recipient.toShortString());
-
- ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis())
- .setShortLabel(name)
- .setIcon(icon)
- .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress()))
- .build();
-
- if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) {
- Toast.makeText(context, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show();
- }
- }
- }.execute();
- }
-
- private void handleSearch() {
- searchViewModel.onSearchOpened();
- }
-
- private void handleLeavePushGroup() {
- if (getRecipient() == null) {
- Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient),
- Toast.LENGTH_LONG).show();
- return;
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.ConversationActivity_leave_group));
- builder.setIconAttribute(R.attr.dialog_info_icon);
- builder.setCancelable(true);
-
- GroupRecord group = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString()).orNull();
- List admins = group.getAdmins();
- String userPublicKey = TextSecurePreferences.getLocalNumber(this);
- String message = getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group);
- for (Address admin : admins) {
- if (admin.toString().equals(userPublicKey)) {
- message = "Because you are the creator of this group it will be deleted for everyone. This cannot be undone.";
- }
- }
-
- builder.setMessage(message);
- builder.setPositiveButton(R.string.yes, (dialog, which) -> {
- Recipient groupRecipient = getRecipient();
- String groupPublicKey;
- boolean isClosedGroup;
- try {
- groupPublicKey = HexEncodingKt.toHexString(GroupUtil.doubleDecodeGroupID(groupRecipient.getAddress().toString()));
- isClosedGroup = DatabaseFactory.getLokiAPIDatabase(this).isClosedGroup(groupPublicKey);
- } catch (IOException e) {
- groupPublicKey = null;
- isClosedGroup = false;
- }
- try {
- if (isClosedGroup) {
- MessageSender.explicitLeave(groupPublicKey, true);
- initializeEnabledCheck();
- } else {
- Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
- }
- } catch (Exception e) {
- Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
- }
- });
-
- builder.setNegativeButton(R.string.no, null);
- builder.show();
- }
-
- private void handleEditPushGroup() {
- Intent intent = new Intent(this, EditClosedGroupActivity.class);
- String groupID = this.recipient.getAddress().toGroupString();
- intent.putExtra(EditClosedGroupActivity.Companion.getGroupIDKey(), groupID);
- startActivity(intent);
- }
-
- private void handleInviteToOpenGroup() {
- Intent intent = new Intent(this, SelectContactsActivity.class);
- startActivityForResult(intent, INVITE_CONTACTS);
- }
-
- private void handleDistributionBroadcastEnabled(MenuItem item) {
- distributionType = DistributionTypes.BROADCAST;
- item.setChecked(true);
-
- if (threadId != -1) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- DatabaseFactory.getThreadDatabase(ConversationActivity.this)
- .setDistributionType(threadId, DistributionTypes.BROADCAST);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
-
- private void handleDistributionConversationEnabled(MenuItem item) {
- distributionType = DistributionTypes.CONVERSATION;
- item.setChecked(true);
-
- if (threadId != -1) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- DatabaseFactory.getThreadDatabase(ConversationActivity.this)
- .setDistributionType(threadId, DistributionTypes.CONVERSATION);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
-
- private void handleAddAttachment() {
- if (attachmentTypeSelector == null) {
- attachmentTypeSelector = new AttachmentTypeSelector(
- this,
- LoaderManager.getInstance(this),
- new AttachmentTypeListener(),
- keyboardHeight);
- }
- attachmentTypeSelector.show(this, attachButton);
- }
-
- private void handleSecurityChange(boolean isSecureText, boolean isDefaultSms) {
- Log.i(TAG, "handleSecurityChange(" + isSecureText + ", " + isDefaultSms + ")");
- if (isSecurityInitialized && isSecureText == true && isDefaultSms == this.isDefaultSms) {
- return;
- }
-
- this.isDefaultSms = isDefaultSms;
- this.isSecurityInitialized = true;
-
- if (recipient == null || attachmentManager == null) { return; }
-
- /* Loki - We don't support SMS
- if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE);
- if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS);
- if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) {
- sendButton.setDefaultTransport(Type.SMS);
- } else {
- if (isSecureText || isPushGroupConversation()) sendButton.setDefaultTransport(Type.TEXTSECURE);
- else sendButton.setDefaultTransport(Type.SMS);
- }
- */
-
- supportInvalidateOptionsMenu();
- updateInputUI(recipient);
- }
-
- ///// Initializers
-
- private ListenableFuture initializeDraft() {
- final SettableFuture result = new SettableFuture<>();
-
- final String draftText = getIntent().getStringExtra(TEXT_EXTRA);
- final Uri draftMedia = getIntent().getData();
- final MediaType draftMediaType = MediaType.from(getIntent().getType());
- final List mediaList = getIntent().getParcelableArrayListExtra(MEDIA_EXTRA);
-
- if (!Util.isEmpty(mediaList)) {
- Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient, draftText);
- startActivityForResult(sendIntent, MEDIA_SENDER);
- return new SettableFuture<>(false);
- }
-
- if (draftText != null) {
- composeText.setText("");
- composeText.append(draftText);
- result.set(true);
- }
-
- if (draftMedia != null && draftMediaType != null) {
- return setMedia(draftMedia, draftMediaType);
- }
-
- if (draftText == null && draftMedia == null && draftMediaType == null) {
- return initializeDraftFromDatabase();
- } else {
- updateToggleButtonState();
- result.set(false);
- }
-
- return result;
- }
-
- private void initializeEnabledCheck() {
- boolean enabled = !(isPushGroupConversation() && !isActiveGroup());
- inputPanel.setEnabled(enabled);
- sendButton.setEnabled(enabled);
- attachButton.setEnabled(enabled);
- }
-
- private ListenableFuture initializeDraftFromDatabase() {
- SettableFuture future = new SettableFuture<>();
-
- new AsyncTask>() {
- @Override
- protected List doInBackground(Void... params) {
- DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
- List results = draftDatabase.getDrafts(threadId);
-
- draftDatabase.clearDrafts(threadId);
-
- return results;
- }
-
- @Override
- protected void onPostExecute(List drafts) {
- if (drafts.isEmpty()) {
- future.set(false);
- updateToggleButtonState();
- return;
- }
-
- AtomicInteger draftsRemaining = new AtomicInteger(drafts.size());
- AtomicBoolean success = new AtomicBoolean(false);
- ListenableFuture.Listener listener = new AssertedSuccessListener() {
- @Override
- public void onSuccess(Boolean result) {
- success.compareAndSet(false, result);
-
- if (draftsRemaining.decrementAndGet() <= 0) {
- future.set(success.get());
- }
- }
- };
-
- for (Draft draft : drafts) {
- switch (draft.getType()) {
- case Draft.TEXT:
- composeText.setText(draft.getValue());
- listener.onSuccess(true);
- break;
- case Draft.IMAGE:
- setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE).addListener(listener);
- break;
- case Draft.AUDIO:
- setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO).addListener(listener);
- break;
- case Draft.VIDEO:
- setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO).addListener(listener);
- break;
- case Draft.QUOTE:
- SettableFuture quoteResult = new SettableFuture<>();
- new QuoteRestorationTask(draft.getValue(), quoteResult).execute();
- quoteResult.addListener(listener);
- break;
- }
- }
-
- updateToggleButtonState();
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-
- return future;
- }
-
- private ListenableFuture initializeSecurity(final boolean currentSecureText,
- final boolean currentIsDefaultSms)
- {
- final SettableFuture future = new SettableFuture<>();
-
- handleSecurityChange(currentSecureText || isPushGroupConversation(), currentIsDefaultSms);
-
- new AsyncTask() {
- @Override
- protected boolean[] doInBackground(Recipient... params) {
- // Loki - Override the flag below
- boolean signalEnabled = true; // TextSecurePreferences.isPushRegistered(context);
-
-
- return new boolean[] { signalEnabled, false};
- }
-
- @Override
- protected void onPostExecute(boolean[] result) {
- if (result[0] != currentSecureText || result[1] != currentIsDefaultSms) {
- Log.i(TAG, "onPostExecute() handleSecurityChange: " + result[0] + " , " + result[1]);
- handleSecurityChange(result[0], result[1]);
- }
- future.set(true);
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
-
- return future;
- }
-
- private void initializeViews() {
- profilePictureView = findViewById(R.id.profilePictureView);
- titleTextView = findViewById(R.id.titleTextView);
- buttonToggle = ViewUtil.findById(this, R.id.button_toggle);
- sendButton = ViewUtil.findById(this, R.id.send_button);
- attachButton = ViewUtil.findById(this, R.id.attach_button);
- composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
- emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
- unblockButton = ViewUtil.findById(this, R.id.unblock_button);
- makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
- container = ViewUtil.findById(this, R.id.layout_container);
- quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
- inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container);
- inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
- searchNav = ViewUtil.findById(this, R.id.conversation_search_nav);
- mentionCandidateSelectionViewContainer = ViewUtil.findById(this, R.id.mentionCandidateSelectionViewContainer);
- mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView);
- messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
- muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
- subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
- homeButtonContainer = ViewUtil.findById(this, R.id.homeButtonContainer);
-
- ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
- ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
-
- container.addOnKeyboardShownListener(this);
- inputPanel.setListener(this);
- inputPanel.setMediaListener(this);
-
- attachmentTypeSelector = null;
- attachmentManager = new AttachmentManager(this, this);
- audioRecorder = new AudioRecorder(this);
- typingTextWatcher = new TypingStatusTextWatcher();
- mentionTextWatcher = new MentionTextWatcher();
-
- SendButtonListener sendButtonListener = new SendButtonListener();
- ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener();
-
- composeText.setOnEditorActionListener(sendButtonListener);
- composeText.setCursorPositionChangedListener(this);
- attachButton.setOnClickListener(new AttachButtonListener());
- attachButton.setOnLongClickListener(new AttachButtonLongClickListener());
- sendButton.setOnClickListener(sendButtonListener);
- sendButton.setEnabled(true);
-
- unblockButton.setOnClickListener(v -> handleUnblock());
- makeDefaultSmsButton.setOnClickListener(v -> handleMakeDefaultSms());
-
- composeText.setOnKeyListener(composeKeyPressedListener);
- composeText.addTextChangedListener(composeKeyPressedListener);
- composeText.setOnEditorActionListener(sendButtonListener);
- composeText.setOnClickListener(composeKeyPressedListener);
- composeText.setOnFocusChangeListener(composeKeyPressedListener);
-
- if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) && Camera.getNumberOfCameras() > 0) {
- quickCameraToggle.setVisibility(View.VISIBLE);
- quickCameraToggle.setOnClickListener(new QuickCameraToggleListener());
- } else {
- quickCameraToggle.setVisibility(View.GONE);
- }
-
- searchNav.setEventListener(this);
-
- inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment());
-
- homeButtonContainer.setOnClickListener(v -> onSupportNavigateUp());
- }
-
- protected void initializeActionBar() {
- Toolbar toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar supportActionBar = getSupportActionBar();
- if (supportActionBar == null) throw new AssertionError();
-
-// supportActionBar.setDisplayHomeAsUpEnabled(true);
- supportActionBar.setDisplayShowTitleEnabled(false);
- }
-
- private void initializeResources() {
- if (recipient != null) recipient.removeListener(this);
-
- Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
- if (address == null) { finish(); return; }
- recipient = Recipient.from(this, address, true);
- threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
- distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT);
- glideRequests = GlideApp.with(this);
-
- recipient.addListener(this);
- }
-
- private void initializeLinkPreviewObserver() {
- linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository(this))).get(LinkPreviewViewModel.class);
-
- if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) {
- linkPreviewViewModel.onUserCancel();
- return;
- }
-
- linkPreviewViewModel.getLinkPreviewState().observe(this, previewState -> {
- if (previewState == null) return;
-
- if (previewState.isLoading()) {
- Log.d(TAG, "Loading link preview.");
- inputPanel.setLinkPreviewLoading();
- } else {
- Log.d(TAG, "Setting link preview: " + previewState.getLinkPreview().isPresent());
- inputPanel.setLinkPreview(glideRequests, previewState.getLinkPreview());
- }
-
- updateToggleButtonState();
- });
- }
-
- private void initializeSearchObserver() {
- searchViewModel = ViewModelProviders.of(this).get(ConversationSearchViewModel.class);
-
- searchViewModel.getSearchResults().observe(this, result -> {
- if (result == null) return;
-
- if (!result.getResults().isEmpty()) {
- MessageResult messageResult = result.getResults().get(result.getPosition());
- fragment.jumpToMessage(messageResult.messageRecipient.getAddress(), messageResult.receivedTimestampMs, searchViewModel::onMissingResult);
- }
-
- searchNav.setData(result.getPosition(), result.getResults().size());
- });
- }
-
- @Override
- public void onSearchMoveUpPressed() {
- searchViewModel.onMoveUp();
- }
-
- @Override
- public void onSearchMoveDownPressed() {
- searchViewModel.onMoveDown();
- }
-
- @Override
- public void onModified(final Recipient recipient) {
- Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
- Util.runOnMain(() -> {
- Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered());
- updateTitleTextView(recipient);
- updateProfilePicture();
- updateSubtitleTextView();
- updateInputUI(recipient);
- initializeSecurity(true, isDefaultSms);
-
- if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
- invalidateOptionsMenu();
- }
- });
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) {
- OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId);
- if (openGroup != null &&
- openGroup.getRoom().equals(event.getRoom()) &&
- openGroup.getServer().equals(event.getUrl())) {
- this.updateSubtitleTextView();
- }
- }
-
- //////// Helper Methods
-
- private void addAttachment(int type) {
- linkPreviewViewModel.onUserCancel();
-
- Log.i(TAG, "Selected: " + type);
- switch (type) {
- case AttachmentTypeSelector.ADD_GALLERY:
- AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break;
- case AttachmentTypeSelector.ADD_DOCUMENT:
- AttachmentManager.selectDocument(this, PICK_DOCUMENT); break;
- case AttachmentTypeSelector.ADD_SOUND:
- AttachmentManager.selectAudio(this, PICK_AUDIO); break;
- case AttachmentTypeSelector.ADD_CONTACT_INFO:
- break;
- case AttachmentTypeSelector.ADD_LOCATION:
- break;
- case AttachmentTypeSelector.TAKE_PHOTO:
- attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
- case AttachmentTypeSelector.ADD_GIF:
- boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this);
- if (!hasSeenGIFMetaDataWarning) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Search GIFs?");
- builder.setMessage("You will not have full metadata protection when sending GIFs.");
- builder.setPositiveButton("OK", (dialog, which) -> {
- AttachmentManager.selectGif(this, PICK_GIF);
- dialog.dismiss();
- });
- builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss());
- builder.create().show();
- TextSecurePreferences.setHasSeenGIFMetaDataWarning(this);
- } else {
- AttachmentManager.selectGif(this, PICK_GIF);
- }
- break;
- }
- }
-
- private ListenableFuture setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) {
- return setMedia(uri, mediaType, 0, 0);
- }
-
- private ListenableFuture setMedia(@Nullable Uri uri, @NonNull MediaType mediaType, int width, int height) {
- if (uri == null) {
- return new SettableFuture<>(false);
- }
-
- if (MediaType.VCARD.equals(mediaType)) {
- return new SettableFuture<>(false);
- } else if (MediaType.IMAGE.equals(mediaType) || MediaType.GIF.equals(mediaType) || MediaType.VIDEO.equals(mediaType)) {
- Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, Optional.absent(), Optional.absent());
- startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed()), MEDIA_SENDER);
- return new SettableFuture<>(false);
- } else {
- return attachmentManager.setMedia(glideRequests, uri, mediaType, MediaConstraints.getPushMediaConstraints(), width, height);
- }
- }
-
- private void addAttachmentContactInfo(Uri contactUri) {
- ContactAccessor contactDataList = ContactAccessor.getInstance();
- ContactData contactData = contactDataList.getContactData(this, contactUri);
-
- if (contactData.numbers.size() == 1) composeText.append(contactData.numbers.get(0).number);
- else if (contactData.numbers.size() > 1) selectContactInfo(contactData);
- }
-
- private void selectContactInfo(ContactData contactData) {
- final CharSequence[] numbers = new CharSequence[contactData.numbers.size()];
- final CharSequence[] numberItems = new CharSequence[contactData.numbers.size()];
-
- for (int i = 0; i < contactData.numbers.size(); i++) {
- numbers[i] = contactData.numbers.get(i).number;
- numberItems[i] = contactData.numbers.get(i).type + ": " + contactData.numbers.get(i).number;
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setIconAttribute(R.attr.conversation_attach_contact_info);
- builder.setTitle(R.string.ConversationActivity_select_contact_info);
-
- builder.setItems(numberItems, (dialog, which) -> composeText.append(numbers[which]));
- builder.show();
- }
-
- private Drafts getDraftsForCurrentState() {
- Drafts drafts = new Drafts();
-
- if (!org.thoughtcrime.securesms.util.Util.isEmpty(composeText)) {
- drafts.add(new Draft(Draft.TEXT, composeText.getTextTrimmed()));
- }
-
- for (Slide slide : attachmentManager.buildSlideDeck().getSlides()) {
- if (slide.hasAudio() && slide.getUri() != null) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString()));
- else if (slide.hasVideo() && slide.getUri() != null) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString()));
- else if (slide.hasImage() && slide.getUri() != null) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
- }
-
- Optional quote = inputPanel.getQuote();
-
- if (quote.isPresent()) {
- drafts.add(new Draft(Draft.QUOTE, new QuoteId(quote.get().getId(), quote.get().getAuthor()).serialize()));
- }
-
- return drafts;
- }
-
- protected ListenableFuture saveDraft() {
- final SettableFuture future = new SettableFuture<>();
-
- if (this.recipient == null) {
- future.set(threadId);
- return future;
- }
-
- final Drafts drafts = getDraftsForCurrentState();
- final long thisThreadId = this.threadId;
- final int thisDistributionType = this.distributionType;
-
- new AsyncTask() {
- @Override
- protected Long doInBackground(Long... params) {
- ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(ConversationActivity.this);
- DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
- long threadId = params[0];
-
- if (drafts.size() > 0) {
- if (threadId == -1) threadId = threadDatabase.getOrCreateThreadIdFor(getRecipient(), thisDistributionType);
-
- draftDatabase.insertDrafts(threadId, drafts);
- threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
- drafts.getUriSnippet(),
- System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true);
- } else if (threadId > 0) {
- threadDatabase.update(threadId, false);
- }
-
- return threadId;
- }
-
- @Override
- protected void onPostExecute(Long result) {
- future.set(result);
- }
-
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, thisThreadId);
-
- return future;
- }
-
- private void updateInputUI(Recipient recipient) {
- if (recipient.isGroupRecipient() && !isActiveGroup()) {
- unblockButton.setVisibility(View.GONE);
- inputPanel.setVisibility(View.GONE);
- makeDefaultSmsButton.setVisibility(View.GONE);
- } else if (recipient.isBlocked()) {
- unblockButton.setVisibility(View.VISIBLE);
- inputPanel.setVisibility(View.GONE);
- makeDefaultSmsButton.setVisibility(View.GONE);
- } else {
- inputPanel.setVisibility(View.VISIBLE);
- unblockButton.setVisibility(View.GONE);
- makeDefaultSmsButton.setVisibility(View.GONE);
- }
- }
-
- private void initializeMediaKeyboardProviders(@NonNull MediaKeyboard mediaKeyboard) {
- boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this);
- if (!isSystemEmojiPreferred) {
- mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, inputPanel));
- }
- }
-
-
- private boolean isSingleConversation() {
- return getRecipient() != null && !getRecipient().isGroupRecipient();
- }
-
- private boolean isActiveGroup() {
- if (!isGroupConversation()) return false;
-
- Optional record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString());
- return record.isPresent() && record.get().isActive();
- }
-
- @SuppressWarnings("SimplifiableIfStatement")
- private boolean isSelfConversation() {
- if (!TextSecurePreferences.isPushRegistered(this)) return false;
- if (recipient.isGroupRecipient()) return false;
-
- return Util.isOwnNumber(this, recipient.getAddress().serialize());
- }
-
- private boolean isGroupConversation() {
- return getRecipient() != null && getRecipient().isGroupRecipient();
- }
-
- private boolean isPushGroupConversation() {
- return getRecipient() != null && getRecipient().isPushGroupRecipient();
- }
-
- protected Recipient getRecipient() {
- return this.recipient;
- }
-
- protected long getThreadId() {
- return this.threadId;
- }
-
- private String getMessage() throws InvalidMessageException {
- String result = composeText.getTextTrimmed();
- if (result.length() < 1) throw new InvalidMessageException();
- for (Mention mention : mentions) {
- try {
- int startIndex = result.indexOf("@" + mention.getDisplayName());
- int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @
- result = result.substring(0, startIndex) + "@" + mention.getPublicKey() + result.substring(endIndex);
- } catch (Exception exception) {
- Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + ".");
- }
- }
- return result;
- }
-
- private Pair> getSplitMessage(String rawText, int maxPrimaryMessageSize) {
- String bodyText = rawText;
- Optional textSlide = Optional.absent();
-
- if (bodyText.length() > maxPrimaryMessageSize) {
- bodyText = rawText.substring(0, maxPrimaryMessageSize);
-
- byte[] textData = rawText.getBytes();
- String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date());
- String filename = String.format("signal-%s.txt", timestamp);
- Uri textUri = BlobProvider.getInstance()
- .forData(textData)
- .withMimeType(MediaTypes.LONG_TEXT)
- .withFileName(filename)
- .createForSingleSessionInMemory();
-
- textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length));
- }
-
- return new Pair<>(bodyText, textSlide);
- }
-
- private void markThreadAsRead() {
- Recipient recipient = this.recipient;
- new AsyncTask() {
- @Override
- protected Void doInBackground(Long... params) {
- Context context = ConversationActivity.this;
- List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false);
-
- if (!SessionMetaProtocol.shouldSendReadReceipt(recipient.getAddress())) {
- for (MarkedMessageInfo messageInfo : messageIds) {
- MarkReadReceiver.scheduleDeletion(context, messageInfo.getExpirationInfo());
- }
- } else {
- MarkReadReceiver.process(context, messageIds);
- }
- ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
- }
-
- private void markLastSeen() {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Long... params) {
- DatabaseFactory.getThreadDatabase(ConversationActivity.this).setLastSeen(params[0]);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
- }
-
- protected void sendComplete(long threadId) {
- boolean refreshFragment = (threadId != this.threadId);
- this.threadId = threadId;
-
- if (fragment == null || !fragment.isVisible() || isFinishing()) {
- return;
- }
-
- fragment.setLastSeen(0);
-
- if (refreshFragment) {
- fragment.reload(recipient, threadId);
- ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
- }
-
- fragment.scrollToBottom();
- attachmentManager.cleanup();
-
- updateLinkPreviewState();
- }
-
- private void sendMessage() {
- if (inputPanel.isRecordingInLockedMode()) {
- inputPanel.releaseRecordingLock();
- return;
- }
-
- try {
- Recipient recipient = getRecipient();
-
- if (recipient == null) {
- throw new RecipientFormattingException("Badly formatted");
- }
-
- String message = getMessage();
- boolean initiating = threadId == -1;
- boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize;
- boolean isMediaMessage = false ||
-// recipient.isGroupRecipient() ||
- inputPanel.getQuote().isPresent() ||
- linkPreviewViewModel.hasLinkPreview() ||
- LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages
- needsSplit;
-
- if (isMediaMessage) {
- sendMediaMessage(initiating);
- } else {
- sendTextMessage(initiating);
- }
- } catch (RecipientFormattingException ex) {
- Log.w(TAG, ex);
- } catch (InvalidMessageException ex) {
- Log.w(TAG, ex);
- }
-
- if (messageStatus == null && !isGroupConversation() && !(TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize()))) {
- messageStatus = "calculatingPoW";
- updateSubtitleTextView();
- updateMessageStatusProgressBar();
- }
- }
-
- private void sendMediaMessage(boolean initiating)
- throws InvalidMessageException
- {
- Log.i(TAG, "Sending media message...");
- sendMediaMessage(getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), linkPreviewViewModel.getActiveLinkPreview(), initiating);
- }
-
- private ListenableFuture sendMediaMessage(String body,
- SlideDeck slideDeck,
- QuoteModel quote,
- Optional linkPreview,
- final boolean initiating)
- {
-
- Pair> splitMessage = getSplitMessage(body, characterCalculator.calculateCharacters(body).maxPrimaryMessageSize);
- body = splitMessage.first;
-
- if (splitMessage.second.isPresent()) {
- slideDeck.addSlide(splitMessage.second.get());
- }
-
- List attachments = slideDeck.asAttachments();
-
- VisibleMessage message = new VisibleMessage();
- message.setSentTimestamp(System.currentTimeMillis());
- message.setText(body);
- OutgoingMediaMessage outgoingMessageCandidate = OutgoingMediaMessage.from(message, recipient, attachments, quote, linkPreview.orNull());
-
- final SettableFuture future = new SettableFuture<>();
- final Context context = getApplicationContext();
-
- final OutgoingMediaMessage outgoingMessage;
-
- outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessageCandidate);
- ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId);
-
- inputPanel.clearQuote();
- attachmentManager.clear();
- silentlySetComposeText("");
-
- final long id = fragment.stageOutgoingMessage(outgoingMessage);
-
- if (initiating) {
- DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
- }
-
- try {
- long allocatedThreadId = getAllocatedThreadId(context);
- message.setId(DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMessage, allocatedThreadId, false, ()->fragment.releaseOutgoingMessage(id)));
- MessageSender.send(message, recipient.getAddress(), attachments, quote, linkPreview.orNull());
- sendComplete(allocatedThreadId);
- } catch (MmsException e) {
- Log.w(TAG, e);
- sendComplete(threadId);
- }
- future.set(null);
-
- return future;
- }
-
- private void sendTextMessage(final boolean initiating)
- throws InvalidMessageException
- {
- final Context context = getApplicationContext();
- final String messageBody = getMessage();
-
- VisibleMessage message = new VisibleMessage();
- message.setSentTimestamp(System.currentTimeMillis());
- message.setText(messageBody);
- OutgoingTextMessage outgoingTextMessage = OutgoingTextMessage.from(message, recipient);
- ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId);
-
- silentlySetComposeText("");
- final long id = fragment.stageOutgoingMessage(outgoingTextMessage);
-
- if (initiating) {
- DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
- }
-
- long allocatedThreadId = getAllocatedThreadId(context);
- message.setId(DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(allocatedThreadId, outgoingTextMessage, false, message.getSentTimestamp(), ()->fragment.releaseOutgoingMessage(id)));
- MessageSender.send(message, recipient.getAddress());
-
- sendComplete(allocatedThreadId);
- }
-
- private void sendOpenGroupInvitations(String[] contactIDs) {
- final Context context = getApplicationContext();
- OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadId);
- for (String contactID : contactIDs) {
- Recipient recipient = Recipient.from(context, Address.fromSerialized(contactID), true);
- VisibleMessage message = new VisibleMessage();
- message.setSentTimestamp(System.currentTimeMillis());
- OpenGroupInvitation openGroupInvitationMessage = new OpenGroupInvitation();
- openGroupInvitationMessage.setName(openGroup.getName());
- openGroupInvitationMessage.setUrl(openGroup.getJoinURL());
- message.setOpenGroupInvitation(openGroupInvitationMessage);
- OutgoingTextMessage outgoingTextMessage = OutgoingTextMessage.fromOpenGroupInvitation(openGroupInvitationMessage, recipient, message.getSentTimestamp());
- DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(-1, outgoingTextMessage, message.getSentTimestamp());
- MessageSender.send(message, recipient.getAddress());
- }
- }
-
- private void updateToggleButtonState() {
- if (inputPanel.isRecordingInLockedMode()) {
- buttonToggle.display(sendButton);
- quickAttachmentToggle.show();
- inlineAttachmentToggle.hide();
- return;
- }
-
- if (composeText.getText().length() == 0) {
- buttonToggle.display(attachButton);
- quickAttachmentToggle.show();
- inlineAttachmentToggle.hide();
- } else {
- buttonToggle.display(sendButton);
- quickAttachmentToggle.hide();
-
- if (!linkPreviewViewModel.hasLinkPreview()) {
- inlineAttachmentToggle.show();
- } else {
- inlineAttachmentToggle.hide();
- }
- }
- }
-
- private void updateLinkPreviewState() {
- if (TextSecurePreferences.isLinkPreviewsEnabled(this)) {
- linkPreviewViewModel.onEnabled();
- linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), composeText.getSelectionStart(), composeText.getSelectionEnd());
- } else {
- linkPreviewViewModel.onUserCancel();
- }
- }
-
- @Override
- public void onRecorderPermissionRequired() {
- Permissions.with(this)
- .request(Manifest.permission.RECORD_AUDIO)
- .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
- .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
- .execute();
- }
-
- @Override
- public void onRecorderStarted() {
- Vibrator vibrator = ServiceUtil.getVibrator(this);
- vibrator.vibrate(20);
-
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-
- audioRecorder.startRecording();
-
- audioHandler = new Handler();
- stopRecordingTask = () -> inputPanel.onRecordReleased();
- audioHandler.postDelayed(stopRecordingTask, 60000);
- }
-
- @Override
- public void onRecorderLocked() {
- updateToggleButtonState();
- }
-
- @Override
- public void onRecorderFinished() {
- if (audioHandler != null && stopRecordingTask != null) {
- audioHandler.removeCallbacks(stopRecordingTask);
- }
- updateToggleButtonState();
- Vibrator vibrator = ServiceUtil.getVibrator(this);
- vibrator.vibrate(20);
-
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-
- ListenableFuture> future = audioRecorder.stopRecording();
- future.addListener(new ListenableFuture.Listener>() {
- @Override
- public void onSuccess(final @NonNull Pair result) {
- int subscriptionId = -1;
- long expiresIn = recipient.getExpireMessages() * 1000L;
- boolean initiating = threadId == -1;
- AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaTypes.AUDIO_AAC, true);
- SlideDeck slideDeck = new SlideDeck();
- slideDeck.addSlide(audioSlide);
-
- sendMediaMessage("", slideDeck, inputPanel.getQuote().orNull(), Optional.absent(), initiating).addListener(new AssertedSuccessListener() {
- @Override
- public void onSuccess(Void nothing) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- BlobProvider.getInstance().delete(ConversationActivity.this, result.first);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- });
- }
-
- @Override
- public void onFailure(ExecutionException e) {
- Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_unable_to_record_audio, Toast.LENGTH_LONG).show();
- }
- });
- }
-
- @Override
- public void onRecorderCanceled() {
- if (audioHandler != null && stopRecordingTask != null) {
- audioHandler.removeCallbacks(stopRecordingTask);
- }
- updateToggleButtonState();
- Vibrator vibrator = ServiceUtil.getVibrator(this);
- vibrator.vibrate(50);
-
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-
- ListenableFuture> future = audioRecorder.stopRecording();
- future.addListener(new ListenableFuture.Listener>() {
- @Override
- public void onSuccess(final Pair result) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- BlobProvider.getInstance().delete(ConversationActivity.this, result.first);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- @Override
- public void onFailure(ExecutionException e) {}
- });
- }
-
- @Override
- public void onEmojiToggle() {
- if (!emojiDrawerStub.resolved()) {
- initializeMediaKeyboardProviders(emojiDrawerStub.get());
-
- inputPanel.setMediaKeyboard(emojiDrawerStub.get());
- }
-
- if (container.getCurrentInput() == emojiDrawerStub.get()) {
- container.showSoftkey(composeText);
- } else {
- container.show(composeText, emojiDrawerStub.get());
- }
- }
-
- @Override
- public void onLinkPreviewCanceled() {
- linkPreviewViewModel.onUserCancel();
- }
-
- @Override
- public void onMediaSelected(@NonNull Uri uri, String contentType) {
- if (!TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif")) {
- setMedia(uri, MediaType.GIF);
- } else if (MediaUtil.isImageType(contentType)) {
- setMedia(uri, MediaType.IMAGE);
- } else if (MediaUtil.isVideoType(contentType)) {
- setMedia(uri, MediaType.VIDEO);
- } else if (MediaUtil.isAudioType(contentType)) {
- setMedia(uri, MediaType.AUDIO);
- }
- }
-
- @Override
- public void onCursorPositionChanged(int start, int end) {
- linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), start, end);
- }
-
- private void silentlySetComposeText(String text) {
- typingTextWatcher.setEnabled(false);
- composeText.setText(text);
- if (text.isEmpty()) { resetMentions(); }
- typingTextWatcher.setEnabled(true);
- }
-
- // Listeners
-
- private class AttachmentTypeListener implements AttachmentTypeSelector.AttachmentClickedListener {
- @Override
- public void onClick(int type) {
- addAttachment(type);
- }
-
- @Override
- public void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) {
- linkPreviewViewModel.onUserCancel();
- Media media = new Media(uri, mimeType, dateTaken, width, height, size, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.absent());
- startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed()), MEDIA_SENDER);
- }
- }
-
- private class QuickCameraToggleListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- Permissions.with(ConversationActivity.this)
- .request(Manifest.permission.CAMERA)
- .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48)
- .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
- .onAllGranted(() -> {
- composeText.clearFocus();
- startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER);
- overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary);
- })
- .onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show())
- .execute();
- }
- }
-
- private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener {
- @Override
- public void onClick(View v) {
- sendMessage();
- }
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_SEND) {
- sendButton.performClick();
- return true;
- }
- return false;
- }
- }
-
- private class AttachButtonListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- handleAddAttachment();
- }
- }
-
- private class AttachButtonLongClickListener implements View.OnLongClickListener {
- @Override
- public boolean onLongClick(View v) {
- return sendButton.performLongClick();
- }
- }
-
- private class ComposeKeyPressedListener implements OnKeyListener, OnClickListener, TextWatcher, OnFocusChangeListener {
-
- int beforeLength;
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (keyCode == KeyEvent.KEYCODE_ENTER) {
- if (TextSecurePreferences.isEnterSendsEnabled(ConversationActivity.this)) {
- sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
- sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public void onClick(View v) {
- container.showSoftkey(composeText);
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,int after) {
- beforeLength = composeText.getTextTrimmed().length();
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (composeText.getTextTrimmed().length() == 0 || beforeLength == 0) {
- composeText.postDelayed(ConversationActivity.this::updateToggleButtonState, 50);
- }
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,int count) {}
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {}
- }
-
- private class TypingStatusTextWatcher extends SimpleTextWatcher {
- private boolean enabled = true;
-
- @Override
- public void onTextChanged(String text) {
- if (enabled && threadId > 0) {
- ApplicationContext.getInstance(ConversationActivity.this).getTypingStatusSender().onTypingStarted(threadId);
- }
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
- }
-
- private class MentionTextWatcher extends SimpleTextWatcher {
-
- @Override
- public void onTextChanged(String text) {
- boolean isBackspace = text.length() < oldText.length();
- if (isBackspace) {
- currentMentionStartIndex = -1;
- mentionCandidateSelectionView.hide();
- mentionCandidateSelectionViewContainer.setVisibility(View.GONE);
- ArrayList mentionsToRemove = new ArrayList<>();
- for (Mention mention : mentions) {
- if (!text.contains(mention.getDisplayName())) {
- mentionsToRemove.add(mention);
- }
- }
- mentions.removeAll(mentionsToRemove);
- }
- if (text.length() > 0) {
- if (currentMentionStartIndex > text.length()) {
- resetMentions(); // Should never occur
- }
- int lastCharacterIndex = text.length() - 1;
- char lastCharacter = text.charAt(lastCharacterIndex);
- char secondToLastCharacter = ' ';
- if (lastCharacterIndex > 0) {
- secondToLastCharacter = text.charAt(lastCharacterIndex - 1);
- }
- if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) {
- List mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates("", threadId, recipient.isOpenGroupRecipient());
- currentMentionStartIndex = lastCharacterIndex;
- mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
- mentionCandidateSelectionView.show(mentionCandidates, threadId);
- } else if (Character.isWhitespace(lastCharacter)) {
- currentMentionStartIndex = -1;
- mentionCandidateSelectionView.hide();
- mentionCandidateSelectionViewContainer.setVisibility(View.GONE);
- } else {
- if (currentMentionStartIndex != -1) {
- String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @
- List mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates(query, threadId, recipient.isOpenGroupRecipient());
- mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
- mentionCandidateSelectionView.show(mentionCandidates, threadId);
- }
- }
- }
- ConversationActivity.this.oldText = text;
- }
- }
-
- private void resetMentions() {
- oldText = "";
- currentMentionStartIndex = -1;
- mentions.clear();
- }
-
- @Override
- public void setThreadId(long threadId) {
- this.threadId = threadId;
- }
-
- @Override
- public void handleReplyMessage(MessageRecord messageRecord) {
- if (recipient.isGroupRecipient() && !isActiveGroup()) { return; }
-
- Recipient author;
-
- if (messageRecord.isOutgoing()) {
- author = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), true);
- } else {
- author = messageRecord.getIndividualRecipient();
- }
-
- if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) {
- Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0);
- String displayName = ContactUtil.getDisplayName(contact);
- String body = getString(R.string.ConversationActivity_quoted_contact_message, EmojiStrings.BUST_IN_SILHOUETTE, displayName);
- SlideDeck slideDeck = new SlideDeck();
-
- if (contact.getAvatarAttachment() != null) {
- slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, contact.getAvatarAttachment()));
- }
-
- inputPanel.setQuote(GlideApp.with(this),
- messageRecord.getDateSent(),
- author,
- body,
- slideDeck,
- recipient,
- threadId);
-
- } else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) {
- LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
- SlideDeck slideDeck = new SlideDeck();
-
- if (linkPreview.getThumbnail().isPresent()) {
- slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, linkPreview.getThumbnail().get()));
- }
-
- inputPanel.setQuote(GlideApp.with(this),
- messageRecord.getDateSent(),
- author,
- messageRecord.getBody(),
- slideDeck,
- recipient,
- threadId);
- } else {
- inputPanel.setQuote(GlideApp.with(this),
- messageRecord.getDateSent(),
- author,
- messageRecord.getBody(),
- messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(),
- recipient,
- threadId);
- }
- }
-
- @Override
- public void onMessageActionToolbarOpened() {
- searchViewItem.collapseActionView();
- }
-
- @Override
- public void onForwardClicked() {
- inputPanel.clearQuote();
- }
-
- @Override
- public void onAttachmentChanged() {
- handleSecurityChange(true, isDefaultSms);
- updateToggleButtonState();
- updateLinkPreviewState();
- }
-
- private class QuoteRestorationTask extends AsyncTask {
-
- private final String serialized;
- private final SettableFuture future;
-
- QuoteRestorationTask(@NonNull String serialized, @NonNull SettableFuture future) {
- this.serialized = serialized;
- this.future = future;
- }
-
- @Override
- protected MessageRecord doInBackground(Void... voids) {
- QuoteId quoteId = QuoteId.deserialize(serialized);
-
- if (quoteId != null) {
- return DatabaseFactory.getMmsSmsDatabase(getApplicationContext()).getMessageFor(quoteId.getId(), quoteId.getAuthor());
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(MessageRecord messageRecord) {
- if (messageRecord != null) {
- handleReplyMessage(messageRecord);
- future.set(true);
- } else {
- Log.e(TAG, "Failed to restore a quote from a draft. No matching message record.");
- future.set(false);
- }
- }
- }
-
- // region Loki
- private long getAllocatedThreadId(Context context) {
- long allocatedThreadId;
- if (threadId == -1) {
- allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
- } else {
- allocatedThreadId = threadId;
- }
- return allocatedThreadId;
- }
-
- private void updateTitleTextView(Recipient recipient) {
- String userPublicKey = TextSecurePreferences.getLocalNumber(this);
- if (recipient == null) {
- titleTextView.setText(R.string.ConversationActivity_compose);
- } else if (recipient.getAddress().toString().toLowerCase().equals(userPublicKey)) {
- titleTextView.setText(R.string.note_to_self);
- } else {
- String displayName = recipient.getName(); // Uses the Contact API internally
- boolean hasName = (displayName != null);
- titleTextView.setText(hasName ? displayName : recipient.getAddress().toString());
- }
- }
-
- private void updateProfilePicture() {
- try {
- profilePictureView.glide = GlideApp.with(this);
- profilePictureView.update(recipient, threadId);
- } catch (Exception exception) {
- // Do nothing
- }
- }
-
- private void updateSubtitleTextView() {
- muteIndicatorImageView.setVisibility(View.GONE);
- subtitleTextView.setVisibility(View.VISIBLE);
- if (recipient.isMuted()) {
- muteIndicatorImageView.setVisibility(View.VISIBLE);
- subtitleTextView.setText(getString(R.string.ConversationActivity_muted_until_date,DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())));
- } else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
- OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId);
- if (openGroup != null) {
- Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(openGroup.getRoom(),openGroup.getServer());
- if (userCount == null) { userCount = 0; }
- subtitleTextView.setText(getString(R.string.ConversationActivity_member_count,userCount));
- } else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
- subtitleTextView.setText(recipient.getAddress().toString());
- } else {
- subtitleTextView.setVisibility(View.GONE);
- }
- } else {
- subtitleTextView.setVisibility(View.GONE);
- }
- titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((subtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size));
- }
-
- private void setMessageStatusProgressAnimatedIfPossible(int progress) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- messageStatusProgressBar.setProgress(progress, true);
- } else {
- messageStatusProgressBar.setProgress(progress);
- }
- }
-
- private void updateMessageStatusProgressBar() {
- if (messageStatus != null) {
- messageStatusProgressBar.setAlpha(1.0f);
- switch (messageStatus) {
- case "calculatingPoW": setMessageStatusProgressAnimatedIfPossible(25); break;
- case "contactingNetwork": setMessageStatusProgressAnimatedIfPossible(50); break;
- case "sendingMessage": setMessageStatusProgressAnimatedIfPossible(75); break;
- case "messageSent":
- setMessageStatusProgressAnimatedIfPossible(100);
- new Handler().postDelayed(() -> messageStatusProgressBar.animate().alpha(0).setDuration(250).start(), 250);
- new Handler().postDelayed(() -> messageStatusProgressBar.setProgress(0), 500);
- break;
- case "messageFailed":
- messageStatusProgressBar.animate().alpha(0).setDuration(250).start();
- new Handler().postDelayed(() -> messageStatusProgressBar.setProgress(0), 250);
- break;
- }
- }
- }
-
- private void handleMessageStatusChanged(String newMessageStatus, long timestamp) {
- if (timestamp == 0 || (TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize())) ) { return; }
- updateForNewMessageStatusIfNeeded(newMessageStatus, timestamp);
- if (newMessageStatus.equals("messageFailed") || newMessageStatus.equals("messageSent")) {
- new Handler().postDelayed(() -> clearMessageStatusIfNeeded(timestamp), 1000);
- }
- }
-
- private int precedence(String messageStatus) {
- if (messageStatus != null) {
- switch (messageStatus) {
- case "calculatingPoW": return 0;
- case "contactingNetwork": return 1;
- case "sendingMessage": return 2;
- case "messageSent": return 3;
- case "messageFailed": return 4;
- default: return -1;
- }
- } else {
- return -1;
- }
- }
-
- private void updateForNewMessageStatusIfNeeded(String newMessageStatus, long timestamp) {
- if (!DatabaseFactory.getSmsDatabase(this).isOutgoingMessage(timestamp) && !DatabaseFactory.getMmsDatabase(this).isOutgoingMessage(timestamp)) { return; }
- if (precedence(newMessageStatus) > precedence(messageStatus)) {
- messageStatus = newMessageStatus;
- updateSubtitleTextView();
- updateMessageStatusProgressBar();
- }
- }
-
- private void clearMessageStatusIfNeeded(long timestamp) {
- if (!DatabaseFactory.getSmsDatabase(this).isOutgoingMessage(timestamp) && !DatabaseFactory.getMmsDatabase(this).isOutgoingMessage(timestamp)) { return; }
- messageStatus = null;
- updateSubtitleTextView();
- updateMessageStatusProgressBar();
- }
- // endregion
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java
deleted file mode 100644
index 8c7522a3f0..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright (C) 2011 Whisper Systems
- *
- * 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 .
- */
-package org.thoughtcrime.securesms.conversation;
-
-import android.content.Context;
-import android.database.Cursor;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.recyclerview.widget.RecyclerView;
-
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.annimon.stream.Stream;
-
-import org.thoughtcrime.securesms.BindableConversationItem;
-import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.FastCursorRecyclerViewAdapter;
-import org.thoughtcrime.securesms.database.MmsSmsColumns;
-import org.thoughtcrime.securesms.database.MmsSmsDatabase;
-import org.thoughtcrime.securesms.database.model.MessageRecord;
-import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
-import org.session.libsignal.utilities.Log;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.mms.SlideDeck;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.thoughtcrime.securesms.util.DateUtils;
-import org.thoughtcrime.securesms.util.LRUCache;
-import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
-import org.session.libsignal.utilities.guava.Optional;
-
-import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
-import org.session.libsession.utilities.Conversions;
-import org.session.libsession.utilities.ViewUtil;
-import org.session.libsession.utilities.Util;
-
-import java.lang.ref.SoftReference;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import network.loki.messenger.R;
-
-/**
- * A cursor adapter for a conversation thread. Ultimately
- * used by ComposeMessageActivity to display a conversation
- * thread in a ListActivity.
- *
- * @author Moxie Marlinspike
- *
- */
-public class ConversationAdapter
- extends FastCursorRecyclerViewAdapter
- implements StickyHeaderDecoration.StickyHeaderAdapter
-{
-
- private static final int MAX_CACHE_SIZE = 1000;
- private static final String TAG = ConversationAdapter.class.getSimpleName();
- private final Map> messageRecordCache =
- Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE));
- private final SparseArray positionToCacheRef = new SparseArray<>();
-
- private static final int MESSAGE_TYPE_OUTGOING = 0;
- private static final int MESSAGE_TYPE_INCOMING = 1;
- private static final int MESSAGE_TYPE_UPDATE = 2;
- private static final int MESSAGE_TYPE_AUDIO_OUTGOING = 3;
- private static final int MESSAGE_TYPE_AUDIO_INCOMING = 4;
- private static final int MESSAGE_TYPE_THUMBNAIL_OUTGOING = 5;
- private static final int MESSAGE_TYPE_THUMBNAIL_INCOMING = 6;
- private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7;
- private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8;
- private static final int MESSAGE_TYPE_INVITATION_OUTGOING = 9;
- private static final int MESSAGE_TYPE_INVITATION_INCOMING = 10;
-
- private final Set batchSelected = Collections.synchronizedSet(new HashSet());
-
- private final @Nullable ItemClickListener clickListener;
- private final @NonNull
- GlideRequests glideRequests;
- private final @NonNull Locale locale;
- private final @NonNull Recipient recipient;
- private final @NonNull MmsSmsDatabase db;
- private final @NonNull LayoutInflater inflater;
- private final @NonNull Calendar calendar;
- private final @NonNull MessageDigest digest;
-
- private MessageRecord recordToPulseHighlight;
- private String searchQuery;
-
- protected static class ViewHolder extends RecyclerView.ViewHolder {
- public ViewHolder(final @NonNull V itemView) {
- super(itemView);
- }
-
- @SuppressWarnings("unchecked")
- public V getView() {
- return (V)itemView;
- }
- }
-
-
- static class HeaderViewHolder extends RecyclerView.ViewHolder {
- TextView textView;
-
- HeaderViewHolder(View itemView) {
- super(itemView);
- textView = ViewUtil.findById(itemView, R.id.text);
- }
-
- HeaderViewHolder(TextView textView) {
- super(textView);
- this.textView = textView;
- }
-
- public void setText(CharSequence text) {
- textView.setText(text);
- }
- }
-
-
- interface ItemClickListener extends BindableConversationItem.EventListener {
- void onItemClick(MessageRecord item);
- void onItemLongClick(MessageRecord item);
- }
-
- @SuppressWarnings("ConstantConditions")
- @VisibleForTesting
- ConversationAdapter(Context context, Cursor cursor) {
- super(context, cursor);
- try {
- this.glideRequests = null;
- this.locale = null;
- this.clickListener = null;
- this.recipient = null;
- this.inflater = null;
- this.db = null;
- this.calendar = null;
- this.digest = MessageDigest.getInstance("SHA1");
- } catch (NoSuchAlgorithmException nsae) {
- throw new AssertionError("SHA1 isn't supported!");
- }
- }
-
- public ConversationAdapter(@NonNull Context context,
- @NonNull GlideRequests glideRequests,
- @NonNull Locale locale,
- @Nullable ItemClickListener clickListener,
- @Nullable Cursor cursor,
- @NonNull Recipient recipient)
- {
- super(context, cursor);
-
- try {
- this.glideRequests = glideRequests;
- this.locale = locale;
- this.clickListener = clickListener;
- this.recipient = recipient;
- this.inflater = LayoutInflater.from(context);
- this.db = DatabaseFactory.getMmsSmsDatabase(context);
- this.calendar = Calendar.getInstance();
- this.digest = MessageDigest.getInstance("SHA1");
-
- setHasStableIds(true);
- } catch (NoSuchAlgorithmException nsae) {
- throw new AssertionError("SHA1 isn't supported!");
- }
- }
-
- @Override
- public void changeCursor(Cursor cursor) {
- messageRecordCache.clear();
- positionToCacheRef.clear();
- super.cleanFastRecords();
- super.changeCursor(cursor);
- }
-
- @Override
- protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) {
- int adapterPosition = viewHolder.getAdapterPosition();
-
- String prevCachedId = positionToCacheRef.get(adapterPosition + 1,null);
- String nextCachedId = positionToCacheRef.get(adapterPosition - 1, null);
-
- MessageRecord previousRecord = null;
- if (adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1)) {
- if (prevCachedId != null && messageRecordCache.containsKey(prevCachedId)) {
- SoftReference prevSoftRecord = messageRecordCache.get(prevCachedId);
- MessageRecord prevCachedRecord = prevSoftRecord.get();
- if (prevCachedRecord != null) {
- previousRecord = prevCachedRecord;
- } else {
- previousRecord = getRecordForPositionOrThrow(adapterPosition + 1);
- }
- } else {
- previousRecord = getRecordForPositionOrThrow(adapterPosition + 1);
- }
- }
-
- MessageRecord nextRecord = null;
- if (adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1)) {
- if (nextCachedId != null && messageRecordCache.containsKey(nextCachedId)) {
- SoftReference nextSoftRecord = messageRecordCache.get(nextCachedId);
- MessageRecord nextCachedRecord = nextSoftRecord.get();
- if (nextCachedRecord != null) {
- nextRecord = nextCachedRecord;
- } else {
- nextRecord = getRecordForPositionOrThrow(adapterPosition - 1);
- }
- } else {
- nextRecord = getRecordForPositionOrThrow(adapterPosition - 1);
- }
- }
-
- viewHolder.getView().bind(messageRecord,
- Optional.fromNullable(previousRecord),
- Optional.fromNullable(nextRecord),
- glideRequests,
- locale,
- batchSelected,
- recipient,
- searchQuery,
- messageRecord == recordToPulseHighlight);
-
- if (messageRecord == recordToPulseHighlight) {
- recordToPulseHighlight = null;
- }
- }
-
- @Override
- public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
- long start = System.currentTimeMillis();
- final V itemView = ViewUtil.inflate(inflater, parent, getLayoutForViewType(viewType));
- itemView.setOnClickListener(view -> {
- if (clickListener != null) {
- clickListener.onItemClick(itemView.getMessageRecord());
- }
- });
- itemView.setOnLongClickListener(view -> {
- if (clickListener != null) {
- clickListener.onItemLongClick(itemView.getMessageRecord());
- }
- return true;
- });
- itemView.setEventListener(clickListener);
- Log.d(TAG, "Inflate time: " + (System.currentTimeMillis() - start));
- return new ViewHolder(itemView);
- }
-
- @Override
- public void onItemViewRecycled(ViewHolder holder) {
- holder.getView().unbind();
- }
-
- private @LayoutRes int getLayoutForViewType(int viewType) {
- switch (viewType) {
- case MESSAGE_TYPE_AUDIO_OUTGOING:
- case MESSAGE_TYPE_THUMBNAIL_OUTGOING:
- case MESSAGE_TYPE_DOCUMENT_OUTGOING:
- case MESSAGE_TYPE_INVITATION_OUTGOING:
- case MESSAGE_TYPE_OUTGOING: return R.layout.conversation_item_sent;
- case MESSAGE_TYPE_AUDIO_INCOMING:
- case MESSAGE_TYPE_THUMBNAIL_INCOMING:
- case MESSAGE_TYPE_DOCUMENT_INCOMING:
- case MESSAGE_TYPE_INVITATION_INCOMING:
- case MESSAGE_TYPE_INCOMING: return R.layout.conversation_item_received;
- case MESSAGE_TYPE_UPDATE: return R.layout.conversation_item_update;
- default: throw new IllegalArgumentException("unsupported item view type given to ConversationAdapter");
- }
- }
-
- @Override
- public int getItemViewType(@NonNull MessageRecord messageRecord) {
- if (messageRecord.isUpdate()) {
- return MESSAGE_TYPE_UPDATE;
- } else if (messageRecord.isOpenGroupInvitation()) {
- if (messageRecord.isOutgoing()) return MESSAGE_TYPE_INVITATION_OUTGOING;
- else return MESSAGE_TYPE_INVITATION_INCOMING;
- } else if (hasAudio(messageRecord)) {
- if (messageRecord.isOutgoing()) return MESSAGE_TYPE_AUDIO_OUTGOING;
- else return MESSAGE_TYPE_AUDIO_INCOMING;
- } else if (hasDocument(messageRecord)) {
- if (messageRecord.isOutgoing()) return MESSAGE_TYPE_DOCUMENT_OUTGOING;
- else return MESSAGE_TYPE_DOCUMENT_INCOMING;
- } else if (hasThumbnail(messageRecord)) {
- if (messageRecord.isOutgoing()) return MESSAGE_TYPE_THUMBNAIL_OUTGOING;
- else return MESSAGE_TYPE_THUMBNAIL_INCOMING;
- } else if (messageRecord.isOutgoing()) {
- return MESSAGE_TYPE_OUTGOING;
- } else {
- return MESSAGE_TYPE_INCOMING;
- }
- }
-
- @Override
- protected boolean isRecordForId(@NonNull MessageRecord record, long id) {
- return record.getId() == id;
- }
-
- @Override
- public long getItemId(@NonNull Cursor cursor) {
- List attachments = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(cursor);
- List messageAttachments = Stream.of(attachments).filterNot(DatabaseAttachment::isQuote).toList();
-
- if (messageAttachments.size() > 0 && messageAttachments.get(0).getFastPreflightId() != null) {
- return Long.valueOf(messageAttachments.get(0).getFastPreflightId());
- }
-
- final String unique = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsColumns.UNIQUE_ROW_ID));
- final byte[] bytes = digest.digest(unique.getBytes());
- return Conversions.byteArrayToLong(bytes);
- }
-
- @Override
- protected long getItemId(@NonNull MessageRecord record) {
- if (record.isOutgoing() && record.isMms()) {
- MmsMessageRecord mmsRecord = (MmsMessageRecord) record;
- SlideDeck slideDeck = mmsRecord.getSlideDeck();
-
- if (slideDeck.getThumbnailSlide() != null && slideDeck.getThumbnailSlide().getFastPreflightId() != null) {
- return Long.valueOf(slideDeck.getThumbnailSlide().getFastPreflightId());
- }
- }
-
- return record.getId();
- }
-
- @Override
- protected MessageRecord getRecordFromCursor(@NonNull Cursor cursor) {
- long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID));
- String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
-
- final SoftReference reference = messageRecordCache.get(type + messageId);
- if (reference != null) {
- final MessageRecord record = reference.get();
- if (record != null) return record;
- }
-
- final MessageRecord messageRecord = db.readerFor(cursor).getCurrent();
- messageRecordCache.put(type + messageId, new SoftReference<>(messageRecord));
-
- return messageRecord;
- }
-
- public void close() {
- getCursor().close();
- }
-
- public int findLastSeenPosition(long lastSeen) {
- if (lastSeen <= 0) return -1;
- if (!isActiveCursor()) return -1;
-
- int count = getItemCount() - (hasFooterView() ? 1 : 0);
-
- for (int i=(hasHeaderView() ? 1 : 0);i getSelectedItems() {
- return Collections.unmodifiableSet(new HashSet<>(batchSelected));
- }
-
- public void pulseHighlightItem(int position) {
- if (position < getItemCount()) {
- recordToPulseHighlight = getRecordForPositionOrThrow(position);
- notifyItemChanged(position);
- }
- }
-
- public void onSearchQueryUpdated(@Nullable String query) {
- this.searchQuery = query;
- notifyDataSetChanged();
- }
-
- private boolean hasAudio(MessageRecord messageRecord) {
- return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null;
- }
-
- private boolean hasDocument(MessageRecord messageRecord) {
- return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null;
- }
-
- private boolean hasThumbnail(MessageRecord messageRecord) {
- return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null;
- }
-
- @Override
- public long getHeaderId(int position) {
- if (!isActiveCursor()) return -1;
- if (isHeaderPosition(position)) return -1;
- if (isFooterPosition(position)) return -1;
- if (position >= getItemCount()) return -1;
- if (position < 0) return -1;
-
- MessageRecord record = getRecordForPositionOrThrow(position);
- if (record.getRecipient().getAddress().isOpenGroup()) {
- calendar.setTime(new Date(record.getDateReceived()));
- } else {
- calendar.setTime(new Date(record.getDateSent()));
- }
- return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
- }
-
- public long getReceivedTimestamp(int position) {
- if (!isActiveCursor()) return 0;
- if (isHeaderPosition(position)) return 0;
- if (isFooterPosition(position)) return 0;
- if (position >= getItemCount()) return 0;
- if (position < 0) return 0;
-
- MessageRecord messageRecord = getRecordForPositionOrThrow(position);
-
- if (messageRecord.isOutgoing()) return 0;
- else return messageRecord.getDateReceived();
- }
-
- @Override
- public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
- return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_header, parent, false));
- }
-
- public HeaderViewHolder onCreateLastSeenViewHolder(ViewGroup parent) {
- return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_last_seen, parent, false));
- }
-
- @Override
- public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
- MessageRecord messageRecord = getRecordForPositionOrThrow(position);
- long timestamp = messageRecord.getDateReceived();
- if (recipient.getAddress().isOpenGroup()) { timestamp = messageRecord.getTimestamp(); }
- viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, timestamp));
- }
-
- public void onBindLastSeenViewHolder(HeaderViewHolder viewHolder, int position) {
- viewHolder.setText(getContext().getResources().getQuantityString(R.plurals.ConversationAdapter_n_unread_messages, (position + 1), (position + 1)));
- }
-
- static class LastSeenHeader extends StickyHeaderDecoration {
-
- private final ConversationAdapter adapter;
- private final long lastSeenTimestamp;
-
- LastSeenHeader(ConversationAdapter adapter, long lastSeenTimestamp) {
- super(adapter, false, false);
- this.adapter = adapter;
- this.lastSeenTimestamp = lastSeenTimestamp;
- }
-
- @Override
- protected boolean hasHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) {
- if (!adapter.isActiveCursor()) {
- return false;
- }
-
- if (lastSeenTimestamp <= 0) {
- return false;
- }
-
- long currentRecordTimestamp = adapter.getReceivedTimestamp(position);
- long previousRecordTimestamp = adapter.getReceivedTimestamp(position + 1);
-
- return currentRecordTimestamp > lastSeenTimestamp && previousRecordTimestamp < lastSeenTimestamp;
- }
-
- @Override
- protected int getHeaderTop(RecyclerView parent, View child, View header, int adapterPos, int layoutPos) {
- return parent.getLayoutManager().getDecoratedTop(child);
- }
-
- @Override
- protected HeaderViewHolder getHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) {
- HeaderViewHolder viewHolder = adapter.onCreateLastSeenViewHolder(parent);
- adapter.onBindLastSeenViewHolder(viewHolder, position);
-
- int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
- int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
-
- int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), viewHolder.itemView.getLayoutParams().width);
- int childHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), viewHolder.itemView.getLayoutParams().height);
-
- viewHolder.itemView.measure(childWidth, childHeight);
- viewHolder.itemView.layout(0, 0, viewHolder.itemView.getMeasuredWidth(), viewHolder.itemView.getMeasuredHeight());
-
- return viewHolder;
- }
- }
-
-}
-
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java
deleted file mode 100644
index 74a3b5eddb..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java
+++ /dev/null
@@ -1,1220 +0,0 @@
-/*
- * Copyright (C) 2015 Open Whisper Systems
- *
- * 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 .
- */
-package org.thoughtcrime.securesms.conversation;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.ClipData;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.text.ClipboardManager;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.Window;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.ViewSwitcher;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.view.ActionMode;
-import androidx.fragment.app.Fragment;
-import androidx.loader.app.LoaderManager;
-import androidx.loader.content.Loader;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
-import com.annimon.stream.Stream;
-import org.session.libsession.messaging.MessagingModuleConfiguration;
-import org.session.libsession.messaging.messages.control.DataExtractionNotification;
-import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
-import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
-import org.session.libsession.messaging.messages.visible.Quote;
-import org.session.libsession.messaging.messages.visible.VisibleMessage;
-import org.session.libsession.messaging.open_groups.OpenGroupAPIV2;
-import org.session.libsession.messaging.open_groups.OpenGroupV2;
-import org.session.libsession.messaging.sending_receiving.MessageSender;
-import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
-import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
-import org.session.libsession.utilities.Address;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.session.libsession.utilities.TextSecurePreferences;
-import org.session.libsession.utilities.Util;
-import org.session.libsession.utilities.ViewUtil;
-import org.session.libsession.utilities.concurrent.SimpleTask;
-import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
-import org.session.libsignal.utilities.guava.Optional;
-import org.session.libsignal.utilities.Log;
-import org.thoughtcrime.securesms.ApplicationContext;
-import org.thoughtcrime.securesms.MessageDetailsActivity;
-import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
-import org.thoughtcrime.securesms.ShareActivity;
-import org.thoughtcrime.securesms.components.ConversationTypingView;
-import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
-import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
-import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.MmsSmsDatabase;
-import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
-import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
-import org.thoughtcrime.securesms.database.model.MessageRecord;
-import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
-import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
-import org.thoughtcrime.securesms.mediasend.Media;
-import org.thoughtcrime.securesms.mms.GlideApp;
-import org.thoughtcrime.securesms.mms.PartAuthority;
-import org.thoughtcrime.securesms.mms.Slide;
-import org.thoughtcrime.securesms.permissions.Permissions;
-import org.thoughtcrime.securesms.util.CommunicationActions;
-import org.thoughtcrime.securesms.util.SaveAttachmentTask;
-import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-import kotlin.Unit;
-import network.loki.messenger.R;
-
-@SuppressLint("StaticFieldLeak")
-public class ConversationFragment extends Fragment
- implements LoaderManager.LoaderCallbacks
-{
- private static final String TAG = ConversationFragment.class.getSimpleName();
- private static final String KEY_LIMIT = "limit";
-
- private static final int PARTIAL_CONVERSATION_LIMIT = 500;
- private static final int SCROLL_ANIMATION_THRESHOLD = 50;
- private static final int CODE_ADD_EDIT_CONTACT = 77;
-
- private final ActionModeCallback actionModeCallback = new ActionModeCallback();
- private final ItemClickListener selectionClickListener = new ConversationFragmentItemClickListener();
-
- private ConversationFragmentListener listener;
-
- private Recipient recipient;
- private long threadId;
- private long lastSeen;
- private int startingPosition;
- private int previousOffset;
- private int activeOffset;
- private boolean firstLoad;
- private long loaderStartTime;
- private ActionMode actionMode;
- private Locale locale;
- private RecyclerView list;
- private RecyclerView.ItemDecoration lastSeenDecoration;
- private ViewSwitcher topLoadMoreView;
- private ViewSwitcher bottomLoadMoreView;
- private ConversationTypingView typingView;
- private View composeDivider;
- private View scrollToBottomButton;
- private TextView scrollDateHeader;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA);
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- final View view = inflater.inflate(R.layout.conversation_fragment, container, false);
- list = ViewUtil.findById(view, android.R.id.list);
- composeDivider = ViewUtil.findById(view, R.id.compose_divider);
- scrollToBottomButton = ViewUtil.findById(view, R.id.scroll_to_bottom_button);
- scrollDateHeader = ViewUtil.findById(view, R.id.scroll_date_header);
-
- scrollToBottomButton.setOnClickListener(v -> scrollToBottom());
-
- final LinearLayoutManager layoutManager = new SmoothScrollingLinearLayoutManager(getActivity(), true);
- list.setHasFixedSize(false);
- list.setLayoutManager(layoutManager);
- list.setItemAnimator(null);
-
- topLoadMoreView = (ViewSwitcher) inflater.inflate(R.layout.load_more_header, container, false);
- bottomLoadMoreView = (ViewSwitcher) inflater.inflate(R.layout.load_more_header, container, false);
- initializeLoadMoreView(topLoadMoreView);
- initializeLoadMoreView(bottomLoadMoreView);
-
- typingView = (ConversationTypingView) inflater.inflate(R.layout.conversation_typing_view, container, false);
-
- return view;
- }
-
- @Override
- public void onActivityCreated(Bundle bundle) {
- super.onActivityCreated(bundle);
-
- initializeResources();
- initializeListAdapter();
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- this.listener = (ConversationFragmentListener)activity;
- }
-
- @Override
- public void onStart() {
- super.onStart();
- initializeTypingObserver();
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- if (list.getAdapter() != null) {
- list.getAdapter().notifyDataSetChanged();
- }
- }
-
- @Override
- public void onStop() {
- super.onStop();
- ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypists(threadId).removeObservers(this);
- }
-
- public void onNewIntent() {
- if (actionMode != null) {
- actionMode.finish();
- }
-
- initializeResources();
- initializeListAdapter();
-
- if (threadId == -1) {
- getLoaderManager().restartLoader(0, Bundle.EMPTY, this);
- }
- }
-
- public void reloadList() {
- getLoaderManager().restartLoader(0, Bundle.EMPTY, this);
- }
-
- public void moveToLastSeen() {
- if (lastSeen <= 0) {
- Log.i(TAG, "No need to move to last seen.");
- return;
- }
-
- if (list == null || getListAdapter() == null) {
- Log.w(TAG, "Tried to move to last seen position, but we hadn't initialized the view yet.");
- return;
- }
-
- int position = getListAdapter().findLastSeenPosition(lastSeen);
- scrollToLastSeenPosition(position);
- }
-
- private void initializeResources() {
- this.recipient = Recipient.from(getActivity(), getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true);
- this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
- this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
- this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1);
- this.firstLoad = true;
-
- OnScrollListener scrollListener = new ConversationScrollListener(getActivity());
- list.addOnScrollListener(scrollListener);
- }
-
- private void initializeListAdapter() {
- if (this.recipient != null && this.threadId != -1) {
- ConversationAdapter adapter = new ConversationAdapter(getActivity(), GlideApp.with(this), locale, selectionClickListener, null, this.recipient);
- list.setAdapter(adapter);
- list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false));
-
- setLastSeen(lastSeen);
- getLoaderManager().restartLoader(0, Bundle.EMPTY, this);
- }
- }
-
- private void initializeLoadMoreView(ViewSwitcher loadMoreView) {
- loadMoreView.setOnClickListener(v -> {
- Bundle args = new Bundle();
- args.putInt(KEY_LIMIT, 0);
- getLoaderManager().restartLoader(0, args, ConversationFragment.this);
- loadMoreView.showNext();
- loadMoreView.setOnClickListener(null);
- });
- }
-
- private void initializeTypingObserver() {
- if (!TextSecurePreferences.isTypingIndicatorsEnabled(requireContext())) {
- return;
- }
-
- ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypists(threadId).observe(this, typingState -> {
- List recipients;
- boolean replacedByIncomingMessage;
-
- if (typingState != null) {
- recipients = typingState.getTypists();
- replacedByIncomingMessage = typingState.isReplacedByIncomingMessage();
- } else {
- recipients = Collections.emptyList();
- replacedByIncomingMessage = false;
- }
-
- typingView.setTypists(GlideApp.with(ConversationFragment.this), recipients, recipient.isGroupRecipient());
-
- ConversationAdapter adapter = getListAdapter();
-
- if (adapter.getHeaderView() != null && adapter.getHeaderView() != typingView) {
- Log.i(TAG, "Skipping typing indicator -- the header slot is occupied.");
- return;
- }
-
- if (recipients.size() > 0) {
- if (adapter.getHeaderView() == null && isAtBottom()) {
- list.setVerticalScrollBarEnabled(false);
- list.post(() -> getListLayoutManager().smoothScrollToPosition(requireContext(), 0, 250));
- list.postDelayed(() -> list.setVerticalScrollBarEnabled(true), 300);
- adapter.setHeaderView(typingView);
- adapter.notifyItemInserted(0);
- } else {
- if (adapter.getHeaderView() == null) {
- adapter.setHeaderView(typingView);
- adapter.notifyItemInserted(0);
- } else {
- adapter.setHeaderView(typingView);
- adapter.notifyItemChanged(0);
- }
- }
- } else {
- if (getListLayoutManager().findFirstCompletelyVisibleItemPosition() == 0 && getListLayoutManager().getItemCount() > 1 && !replacedByIncomingMessage) {
- getListLayoutManager().smoothScrollToPosition(requireContext(), 1, 250);
- list.setVerticalScrollBarEnabled(false);
- list.postDelayed(() -> {
- adapter.setHeaderView(null);
- adapter.notifyItemRemoved(0);
- list.post(() -> list.setVerticalScrollBarEnabled(true));
- }, 200);
- } else if (!replacedByIncomingMessage) {
- adapter.setHeaderView(null);
- adapter.notifyItemRemoved(0);
- } else {
- adapter.setHeaderView(null);
- }
- }
- });
- }
-
- private void setCorrectMenuVisibility(Menu menu) {
- Set messageRecords = getListAdapter().getSelectedItems();
- boolean actionMessage = false;
- boolean hasText = false;
- boolean sharedContact = false;
-
- if (actionMode != null && messageRecords.size() == 0) {
- actionMode.finish();
- return;
- }
-
- for (MessageRecord messageRecord : messageRecords) {
- if (messageRecord.isCallLog() || messageRecord.isExpirationTimerUpdate())
- {
- actionMessage = true;
- }
-
- if (messageRecord.getBody().length() > 0) {
- hasText = true;
- }
-
- if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) {
- sharedContact = true;
- }
- }
-
- if (messageRecords.size() > 1) {
- menu.findItem(R.id.menu_context_details).setVisible(false);
- menu.findItem(R.id.menu_context_reply).setVisible(false);
- menu.findItem(R.id.menu_context_save_attachment).setVisible(false);
- menu.findItem(R.id.menu_context_resend).setVisible(false);
- } else {
- MessageRecord messageRecord = messageRecords.iterator().next();
-
- menu.findItem(R.id.menu_context_details).setVisible(true);
- menu.findItem(R.id.menu_context_resend).setVisible(messageRecord.isFailed());
- menu.findItem(R.id.menu_context_save_attachment).setVisible(!actionMessage &&
- messageRecord.isMms() &&
- !messageRecord.isMmsNotification() &&
- ((MediaMmsMessageRecord)messageRecord).containsMediaSlide());
-
- menu.findItem(R.id.menu_context_reply).setVisible(!actionMessage &&
- !messageRecord.isPending() &&
- !messageRecord.isFailed());
- }
-
- menu.findItem(R.id.menu_context_copy).setVisible(!actionMessage && hasText);
-
- boolean isGroupChat = recipient.isGroupRecipient();
-
- if (isGroupChat) {
- OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId);
- boolean isPublicChat = (openGroupChat != null);
- int selectedMessageCount = messageRecords.size();
- boolean areAllSentByUser = true;
- Set uniqueUserSet = new HashSet<>();
- for (MessageRecord message : messageRecords) {
- if (!message.isOutgoing()) { areAllSentByUser = false; }
- uniqueUserSet.add(message.getRecipient().getAddress().toString());
- }
- menu.findItem(R.id.menu_context_copy_public_key).setVisible(selectedMessageCount == 1 && !areAllSentByUser);
- menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1);
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext());
- boolean userCanModerate =
- (isPublicChat && (OpenGroupAPIV2.isUserModerator(userHexEncodedPublicKey, openGroupChat.getRoom(), openGroupChat.getServer())));
- boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate);
- // allow banning if moderating a public chat and only one user's messages are selected
- boolean isBanOptionVisible = isPublicChat && userCanModerate && !areAllSentByUser && uniqueUserSet.size() == 1;
- menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible);
- menu.findItem(R.id.menu_context_ban_user).setVisible(isBanOptionVisible);
- } else {
- menu.findItem(R.id.menu_context_copy_public_key).setVisible(false);
- menu.findItem(R.id.menu_context_delete_message).setVisible(true);
- menu.findItem(R.id.menu_context_ban_user).setVisible(false);
- }
- }
-
- private ConversationAdapter getListAdapter() {
- return (ConversationAdapter) list.getAdapter();
- }
-
- private SmoothScrollingLinearLayoutManager getListLayoutManager() {
- return (SmoothScrollingLinearLayoutManager) list.getLayoutManager();
- }
-
- private MessageRecord getSelectedMessageRecord() {
- Set messageRecords = getListAdapter().getSelectedItems();
- return messageRecords.iterator().next();
- }
-
- public void reload(Recipient recipient, long threadId) {
- this.recipient = recipient;
-
- if (this.threadId != threadId) {
- this.threadId = threadId;
- initializeListAdapter();
- }
- }
-
- public void scrollToBottom() {
- if (getListLayoutManager().findFirstVisibleItemPosition() < SCROLL_ANIMATION_THRESHOLD) {
- list.smoothScrollToPosition(0);
- } else {
- list.scrollToPosition(0);
- }
- }
-
- public void setLastSeen(long lastSeen) {
- this.lastSeen = lastSeen;
- if (lastSeenDecoration != null) {
- list.removeItemDecoration(lastSeenDecoration);
- }
-
- lastSeenDecoration = new ConversationAdapter.LastSeenHeader(getListAdapter(), lastSeen);
- list.addItemDecoration(lastSeenDecoration);
- }
-
- private void handleCopyMessage(final Set messageRecords) {
- List messageList = new LinkedList<>(messageRecords);
- Collections.sort(messageList, new Comparator() {
- @Override
- public int compare(MessageRecord lhs, MessageRecord rhs) {
- if (lhs.getDateReceived() < rhs.getDateReceived()) return -1;
- else if (lhs.getDateReceived() == rhs.getDateReceived()) return 0;
- else return 1;
- }
- });
-
- StringBuilder bodyBuilder = new StringBuilder();
- ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
-
- for (MessageRecord messageRecord : messageList) {
- String body = messageRecord.getDisplayBody(requireContext()).toString();
- if (!TextUtils.isEmpty(body)) {
- bodyBuilder.append(body).append('\n');
- }
- }
- if (bodyBuilder.length() > 0 && bodyBuilder.charAt(bodyBuilder.length() - 1) == '\n') {
- bodyBuilder.deleteCharAt(bodyBuilder.length() - 1);
- }
-
- String result = bodyBuilder.toString();
-
- if (!TextUtils.isEmpty(result))
- clipboard.setText(result);
- }
-
- private void handleCopyPublicKey(MessageRecord messageRecord) {
- String sessionID = messageRecord.getRecipient().getAddress().toString();
- android.content.ClipboardManager clipboard = (android.content.ClipboardManager)requireActivity().getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("Session ID", sessionID);
- clipboard.setPrimaryClip(clip);
- Toast.makeText(getContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
- }
-
- private void handleDeleteMessages(final Set messageRecords) {
- int messagesCount = messageRecords.size();
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
- builder.setIconAttribute(R.attr.dialog_alert_icon);
- builder.setTitle(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messagesCount, messagesCount));
- builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount));
- builder.setCancelable(true);
-
- OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId);
-
- builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new ProgressDialogAsyncTask(getActivity(),
- R.string.ConversationFragment_deleting,
- R.string.ConversationFragment_deleting_messages)
- {
- @Override
- protected Void doInBackground(MessageRecord... messageRecords) {
- if (openGroupChat != null) {
- ArrayList serverIDs = new ArrayList<>();
- ArrayList ignoredMessages = new ArrayList<>();
- ArrayList failedMessages = new ArrayList<>();
- boolean isSentByUser = true;
- for (MessageRecord messageRecord : messageRecords) {
- isSentByUser = isSentByUser && messageRecord.isOutgoing();
- Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
- if (serverID != null) {
- serverIDs.add(serverID);
- } else {
- ignoredMessages.add(messageRecord.getId());
- }
- }
- if (openGroupChat != null) {
- for (Long serverId : serverIDs) {
- OpenGroupAPIV2
- .deleteMessage(serverId, openGroupChat.getRoom(), openGroupChat.getServer())
- .success(l -> {
- for (MessageRecord messageRecord : messageRecords) {
- Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
- if (serverID != null && serverID.equals(serverId)) {
- MessagingModuleConfiguration.shared.getMessageDataProvider().deleteMessage(messageRecord.id, !messageRecord.isMms());
- break;
- }
- }
- return null;
- }).fail(e->{
- Log.e("Loki", "Couldn't delete message due to error",e);
- return null;
- });
- }
- }
- } else {
- for (MessageRecord messageRecord : messageRecords) {
- if (messageRecord.isMms()) {
- DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId());
- } else {
- DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageRecord.getId());
- }
- }
- }
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, messageRecords.toArray(new MessageRecord[messageRecords.size()]));
- }
- });
-
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.show();
- }
-
- private void handleBanUser(Set messageRecords) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
- String userPublicKey = null;
- for (MessageRecord record: messageRecords) {
- String currentPublicKey = record.getRecipient().getAddress().toString();
- if (userPublicKey == null) {
- userPublicKey = currentPublicKey;
- }
- }
- final String finalPublicKey = userPublicKey;
-
- builder.setIconAttribute(R.attr.dialog_alert_icon);
- builder.setTitle(R.string.ConversationFragment_ban_selected_user);
- builder.setCancelable(true);
-
- final OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId);
-
- builder.setPositiveButton(R.string.ban, (dialog, which) -> {
- ConversationAdapter chatAdapter = getListAdapter();
- chatAdapter.clearSelection();
- chatAdapter.notifyDataSetChanged();
- new ProgressDialogAsyncTask(getActivity(),
- R.string.ConversationFragment_banning,
- R.string.ConversationFragment_banning_user) {
- @Override
- protected Void doInBackground(String... userPublicKeyParam) {
- String userPublicKey = userPublicKeyParam[0];
- if (openGroupChat != null) {
- OpenGroupAPIV2
- .ban(userPublicKey, openGroupChat.getRoom(), openGroupChat.getServer())
- .success(l -> {
- Log.d("Loki", "User banned");
- return Unit.INSTANCE;
- }).fail(e -> {
- Log.e("Loki", "Failed to ban user",e);
- return null;
- });
- } else {
- Log.d("Loki", "Tried to ban user from a non-public chat");
- }
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, finalPublicKey);
- });
-
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.show();
- }
-
- private void handleDisplayDetails(MessageRecord message) {
- Intent intent = new Intent(getActivity(), MessageDetailsActivity.class);
- intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId());
- intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId);
- intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
- intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, recipient.getAddress());
- intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, recipient.isGroupRecipient());
- startActivity(intent);
- }
-
- private void handleForwardMessage(MessageRecord message) {
- listener.onForwardClicked();
-
- SimpleTask.run(getLifecycle(), () -> {
- Intent composeIntent = new Intent(getActivity(), ShareActivity.class);
- composeIntent.putExtra(Intent.EXTRA_TEXT, message.getDisplayBody(requireContext()).toString());
-
- if (message.isMms()) {
- MmsMessageRecord mediaMessage = (MmsMessageRecord) message;
- boolean isAlbum = mediaMessage.containsMediaSlide() &&
- mediaMessage.getSlideDeck().getSlides().size() > 1 &&
- mediaMessage.getSlideDeck().getAudioSlide() == null &&
- mediaMessage.getSlideDeck().getDocumentSlide() == null;
-
- if (isAlbum) {
- ArrayList mediaList = new ArrayList<>(mediaMessage.getSlideDeck().getSlides().size());
- List attachments = Stream.of(mediaMessage.getSlideDeck().getSlides())
- .filter(s -> s.hasImage() || s.hasVideo())
- .map(Slide::asAttachment)
- .toList();
-
- for (Attachment attachment : attachments) {
- Uri uri = attachment.getDataUri() != null ? attachment.getDataUri() : attachment.getThumbnailUri();
-
- if (uri != null) {
- mediaList.add(new Media(uri,
- attachment.getContentType(),
- System.currentTimeMillis(),
- attachment.getWidth(),
- attachment.getHeight(),
- attachment.getSize(),
- Optional.absent(),
- Optional.fromNullable(attachment.getCaption())));
- }
- };
-
- if (!mediaList.isEmpty()) {
- composeIntent.putExtra(ConversationActivity.MEDIA_EXTRA, mediaList);
- }
- } else if (mediaMessage.containsMediaSlide()) {
- Slide slide = mediaMessage.getSlideDeck().getSlides().get(0);
- composeIntent.putExtra(Intent.EXTRA_STREAM, slide.getUri());
- composeIntent.setType(slide.getContentType());
- }
-
- if (mediaMessage.getSlideDeck().getTextSlide() != null && mediaMessage.getSlideDeck().getTextSlide().getUri() != null) {
- try (InputStream stream = PartAuthority.getAttachmentStream(requireContext(), mediaMessage.getSlideDeck().getTextSlide().getUri())) {
- String fullBody = Util.readFullyAsString(stream);
- composeIntent.putExtra(Intent.EXTRA_TEXT, fullBody);
- } catch (IOException e) {
- Log.w(TAG, "Failed to read long message text when forwarding.");
- }
- }
- }
-
- return composeIntent;
- }, this::startActivity);
- }
-
- private void handleResendMessage(final MessageRecord message) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(MessageRecord... messageRecords) {
- MessageRecord messageRecord = messageRecords[0];
- Recipient recipient = messageRecord.getRecipient();
- VisibleMessage message = new VisibleMessage();
- message.setId(messageRecord.getId());
- message.setText(messageRecord.getBody());
- message.setSentTimestamp(messageRecord.getTimestamp());
- if (recipient.isGroupRecipient()) {
- message.setGroupPublicKey(recipient.getAddress().toGroupString());
- } else {
- message.setRecipient(messageRecord.getRecipient().getAddress().serialize());
- }
- message.setThreadID(messageRecord.getThreadId());
- if (messageRecord.isMms()) {
- MmsMessageRecord mmsMessageRecord = (MmsMessageRecord) messageRecord;
- if (!mmsMessageRecord.getLinkPreviews().isEmpty()) {
- message.setLinkPreview(org.session.libsession.messaging.messages.visible.LinkPreview.Companion.from(mmsMessageRecord.getLinkPreviews().get(0)));
- }
- if (mmsMessageRecord.getQuote() != null) {
- message.setQuote(Quote.Companion.from(mmsMessageRecord.getQuote().getQuoteModel()));
- }
- message.addSignalAttachments(mmsMessageRecord.getSlideDeck().asAttachments());
- }
- MessageSender.send(message, recipient.getAddress());
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
- }
-
- private void handleReplyMessage(final MessageRecord message) {
- listener.handleReplyMessage(message);
- }
-
- private void handleSaveAttachment(final MediaMmsMessageRecord message) {
- SaveAttachmentTask.showWarningDialog(getActivity(), (dialog, which) -> {
- Permissions.with(this)
- .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
- .maxSdkVersion(Build.VERSION_CODES.P)
- .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
- .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
- .onAllGranted(() -> {
- List attachments =
- Stream.of(message.getSlideDeck().getSlides())
- .filter(s -> s.getUri() != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument()))
- .map(s -> new SaveAttachmentTask.Attachment(s.getUri(), s.getContentType(), message.getDateReceived(), s.getFileName().orNull()))
- .toList();
- if (!Util.isEmpty(attachments)) {
- SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity());
- saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0]));
- if (!message.isOutgoing()) {
- sendMediaSavedNotificationIfNeeded();
- }
- return;
- }
-
- Log.w(TAG, "No slide with attachable media found, failing nicely.");
- Toast.makeText(getActivity(),
- getResources().getQuantityString(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, 1),
- Toast.LENGTH_LONG).show();
- })
- .execute();
- });
- }
-
- private void sendMediaSavedNotificationIfNeeded() {
- if (recipient.isGroupRecipient()) return;
- DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
- MessageSender.send(message, recipient.getAddress());
- }
-
- @Override
- public @NonNull Loader onCreateLoader(int id, Bundle args) {
- Log.i(TAG, "onCreateLoader");
- loaderStartTime = System.currentTimeMillis();
-
- int limit = args.getInt(KEY_LIMIT, PARTIAL_CONVERSATION_LIMIT);
- int offset = 0;
- if (limit != 0 && startingPosition >= limit) {
- offset = Math.max(startingPosition - (limit / 2) + 1, 0);
- startingPosition -= offset - 1;
- }
-
- return new ConversationLoader(getActivity(), threadId, offset, limit, lastSeen);
- }
-
- @Override
- public void onLoadFinished(@NonNull Loader cursorLoader, Cursor cursor) {
- long loadTime = System.currentTimeMillis() - loaderStartTime;
- int count = cursor.getCount();
- Log.i(TAG, "onLoadFinished - took " + loadTime + " ms to load a cursor of size " + count);
- ConversationLoader loader = (ConversationLoader)cursorLoader;
-
- ConversationAdapter adapter = getListAdapter();
- if (adapter == null) {
- return;
- }
-
- if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) {
- adapter.setFooterView(topLoadMoreView);
- } else {
- adapter.setFooterView(null);
- }
-
- if (lastSeen == -1) {
- setLastSeen(loader.getLastSeen());
- }
-
- if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == Recipient.RegisteredState.REGISTERED) {
-// adapter.setHeaderView(unknownSenderView);
- } else {
- clearHeaderIfNotTyping(adapter);
- }
-
- if (loader.hasOffset()) {
- adapter.setHeaderView(bottomLoadMoreView);
- }
-
- if (firstLoad || loader.hasOffset()) {
- previousOffset = loader.getOffset();
- }
-
- activeOffset = loader.getOffset();
-
- adapter.changeCursor(cursor);
-
- int lastSeenPosition = adapter.findLastSeenPosition(lastSeen);
-
- if (adapter.getHeaderView() == typingView) {
- lastSeenPosition = Math.max(lastSeenPosition - 1, 0);
- }
-
- if (firstLoad) {
- if (startingPosition >= 0) {
- scrollToStartingPosition(startingPosition);
- } else {
- scrollToLastSeenPosition(lastSeenPosition);
- }
- firstLoad = false;
- } else if (previousOffset > 0) {
- int scrollPosition = previousOffset + getListLayoutManager().findFirstVisibleItemPosition();
- scrollPosition = Math.min(scrollPosition, count - 1);
-
- View firstView = list.getLayoutManager().getChildAt(scrollPosition);
- int pixelOffset = (firstView == null) ? 0 : (firstView.getBottom() - list.getPaddingBottom());
-
- getListLayoutManager().scrollToPositionWithOffset(scrollPosition, pixelOffset);
- previousOffset = 0;
- }
-
- if (lastSeenPosition <= 0) {
- setLastSeen(0);
- }
- }
-
- private void clearHeaderIfNotTyping(ConversationAdapter adapter) {
- if (adapter.getHeaderView() != typingView) {
- adapter.setHeaderView(null);
- }
- }
-
- @Override
- public void onLoaderReset(@NonNull Loader arg0) {
- if (list.getAdapter() != null) {
- getListAdapter().changeCursor(null);
- }
- }
-
- public long stageOutgoingMessage(OutgoingMediaMessage message) {
- MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(getContext()).readerFor(message, threadId).getCurrent();
-
- if (getListAdapter() != null) {
- clearHeaderIfNotTyping(getListAdapter());
- setLastSeen(0);
- getListAdapter().addFastRecord(messageRecord);
- }
-
- return messageRecord.getId();
- }
-
- public long stageOutgoingMessage(OutgoingTextMessage message) {
- MessageRecord messageRecord = DatabaseFactory.getSmsDatabase(getContext()).readerFor(message, threadId).getCurrent();
-
- if (getListAdapter() != null) {
- clearHeaderIfNotTyping(getListAdapter());
- setLastSeen(0);
- getListAdapter().addFastRecord(messageRecord);
- }
-
- return messageRecord.getId();
- }
-
- public void releaseOutgoingMessage(long id) {
- if (getListAdapter() != null) {
- getListAdapter().releaseFastRecord(id);
- }
- }
-
- private void scrollToStartingPosition(final int startingPosition) {
- list.post(() -> {
- list.getLayoutManager().scrollToPosition(startingPosition);
- getListAdapter().pulseHighlightItem(startingPosition);
- });
- }
-
- private void scrollToLastSeenPosition(final int lastSeenPosition) {
- if (lastSeenPosition > 0) {
- list.post(() -> getListLayoutManager().scrollToPositionWithOffset(lastSeenPosition, list.getHeight()));
- }
- }
-
- private boolean isAtBottom() {
- if (list.getChildCount() == 0) return true;
-
- int firstVisiblePosition = getListLayoutManager().findFirstVisibleItemPosition();
-
- if (getListAdapter().getHeaderView() == typingView) {
- RecyclerView.ViewHolder item1 = list.findViewHolderForAdapterPosition(1);
- return firstVisiblePosition <= 1 && item1 != null && item1.itemView.getBottom() <= list.getHeight();
- }
-
- return firstVisiblePosition == 0 && list.getChildAt(0).getBottom() <= list.getHeight();
- }
-
- public void onSearchQueryUpdated(@Nullable String query) {
- if (getListAdapter() != null) {
- getListAdapter().onSearchQueryUpdated(query);
- }
- }
-
- public void jumpToMessage(@NonNull Address author, long timestamp, @Nullable Runnable onMessageNotFound) {
- SimpleTask.run(getLifecycle(), () -> {
- return DatabaseFactory.getMmsSmsDatabase(getContext())
- .getMessagePositionInConversation(threadId, timestamp, author);
- }, p -> moveToMessagePosition(p, onMessageNotFound));
- }
-
- private void moveToMessagePosition(int position, @Nullable Runnable onMessageNotFound) {
- Log.d(TAG, "Moving to message position: " + position + " activeOffset: " + activeOffset + " cursorCount: " + getListAdapter().getCursorCount());
-
- if (position >= activeOffset && position >= 0 && position < getListAdapter().getCursorCount()) {
- int offset = activeOffset > 0 ? activeOffset - 1 : 0;
- list.scrollToPosition(position - offset);
- getListAdapter().pulseHighlightItem(position - offset);
- } else if (position < 0) {
- Log.w(TAG, "Tried to navigate to message, but it wasn't found.");
- if (onMessageNotFound != null) {
- onMessageNotFound.run();
- }
- } else {
- Log.i(TAG, "Message was outside of the loaded range. Need to restart the loader.");
-
- firstLoad = true;
- startingPosition = position;
- getLoaderManager().restartLoader(0, Bundle.EMPTY, ConversationFragment.this);
- }
- }
-
- public interface ConversationFragmentListener {
- void setThreadId(long threadId);
- void handleReplyMessage(MessageRecord messageRecord);
- void onMessageActionToolbarOpened();
- void onForwardClicked();
- }
-
- private class ConversationScrollListener extends OnScrollListener {
-
- private final Animation scrollButtonInAnimation;
- private final Animation scrollButtonOutAnimation;
- private final ConversationDateHeader conversationDateHeader;
-
- private boolean wasAtBottom = true;
- private boolean wasAtZoomScrollHeight = false;
- private long lastPositionId = -1;
-
- ConversationScrollListener(@NonNull Context context) {
- this.scrollButtonInAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_scale_in);
- this.scrollButtonOutAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_scale_out);
- this.conversationDateHeader = new ConversationDateHeader(context, scrollDateHeader);
-
- this.scrollButtonInAnimation.setDuration(100);
- this.scrollButtonOutAnimation.setDuration(50);
- }
-
- @Override
- public void onScrolled(@NonNull final RecyclerView rv, final int dx, final int dy) {
- boolean currentlyAtBottom = isAtBottom();
- boolean currentlyAtZoomScrollHeight = isAtZoomScrollHeight();
- int positionId = getHeaderPositionId();
-
- if (currentlyAtBottom && !wasAtBottom) {
- ViewUtil.fadeOut(composeDivider, 50, View.INVISIBLE);
- ViewUtil.animateOut(scrollToBottomButton, scrollButtonOutAnimation, View.INVISIBLE);
- } else if (!currentlyAtBottom && wasAtBottom) {
- ViewUtil.fadeIn(composeDivider, 500);
- }
-
- if (currentlyAtZoomScrollHeight && !wasAtZoomScrollHeight) {
- ViewUtil.animateIn(scrollToBottomButton, scrollButtonInAnimation);
- }
-
- if (positionId != lastPositionId) {
- bindScrollHeader(conversationDateHeader, positionId);
- }
-
- wasAtBottom = currentlyAtBottom;
- wasAtZoomScrollHeight = currentlyAtZoomScrollHeight;
- lastPositionId = positionId;
- }
-
- @Override
- public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
- if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
- conversationDateHeader.show();
- } else if (newState == RecyclerView.SCROLL_STATE_IDLE) {
- conversationDateHeader.hide();
- }
- }
-
- private boolean isAtZoomScrollHeight() {
- return getListLayoutManager().findFirstCompletelyVisibleItemPosition() > 4;
- }
-
- private int getHeaderPositionId() {
- return getListLayoutManager().findLastVisibleItemPosition();
- }
-
- private void bindScrollHeader(HeaderViewHolder headerViewHolder, int positionId) {
- if (((ConversationAdapter)list.getAdapter()).getHeaderId(positionId) != -1) {
- ((ConversationAdapter) list.getAdapter()).onBindHeaderViewHolder(headerViewHolder, positionId);
- }
- }
- }
-
- private class ConversationFragmentItemClickListener implements ItemClickListener {
-
- @Override
- public void onItemClick(MessageRecord messageRecord) {
- if (messageRecord.isUpdate()) return;
- if (actionMode != null) {
- ((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord);
- list.getAdapter().notifyDataSetChanged();
-
- if (getListAdapter().getSelectedItems().size() == 0) {
- actionMode.finish();
- } else {
- setCorrectMenuVisibility(actionMode.getMenu());
- actionMode.setTitle(String.valueOf(getListAdapter().getSelectedItems().size()));
- }
- }
- }
-
- @Override
- public void onItemLongClick(MessageRecord messageRecord) {
- if (messageRecord.isUpdate()) return;
- if (actionMode == null) {
- ((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord);
- list.getAdapter().notifyDataSetChanged();
-
- actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(actionModeCallback);
-
- View titleTextView = (getActivity().findViewById(R.id.action_bar_title));
- if (titleTextView != null) {
- titleTextView.setBackgroundColor(getResources().getColor(R.color.transparent));
- ViewParent titleTextViewContainerView = titleTextView.getParent();
- if (titleTextViewContainerView != null) {
- ((View)titleTextViewContainerView).setBackgroundColor(getResources().getColor(R.color.transparent));
- }
- }
- }
- }
-
- @Override
- public void onQuoteClicked(MmsMessageRecord messageRecord) {
- if (messageRecord.getQuote() == null) {
- Log.w(TAG, "Received a 'quote clicked' event, but there's no quote...");
- return;
- }
-
- if (messageRecord.getQuote().isOriginalMissing()) {
- Log.i(TAG, "Clicked on a quote whose original message we never had.");
- Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_not_found, Toast.LENGTH_SHORT).show();
- return;
- }
-
- SimpleTask.run(getLifecycle(), () -> {
- return DatabaseFactory.getMmsSmsDatabase(getContext())
- .getQuotedMessagePosition(threadId,
- messageRecord.getQuote().getId(),
- messageRecord.getQuote().getAuthor());
- }, p -> moveToMessagePosition(p, () -> {
- Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_no_longer_available, Toast.LENGTH_SHORT).show();
- }));
- }
-
- @Override
- public void onLinkPreviewClicked(@NonNull LinkPreview linkPreview) {
- if (getContext() != null && getActivity() != null) {
- CommunicationActions.openBrowserLink(getActivity(), linkPreview.getUrl());
- }
- }
-
- @Override
- public void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms) {
- if (getContext() != null && getActivity() != null) {
- startActivity(LongMessageActivity.getIntent(getContext(), conversationAddress, messageId, isMms));
- }
- }
- }
-
- private class ActionModeCallback implements ActionMode.Callback {
-
- private int statusBarColor;
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- MenuInflater inflater = mode.getMenuInflater();
- inflater.inflate(R.menu.conversation_context, menu);
-
- mode.setTitle("1");
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- Window window = getActivity().getWindow();
- statusBarColor = window.getStatusBarColor();
- }
-
- setCorrectMenuVisibility(menu);
- listener.onMessageActionToolbarOpened();
- return true;
- }
-
- @Override
- public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
- return false;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- ((ConversationAdapter)list.getAdapter()).clearSelection();
- list.getAdapter().notifyDataSetChanged();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- getActivity().getWindow().setStatusBarColor(statusBarColor);
- }
-
- actionMode = null;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- switch(item.getItemId()) {
- case R.id.menu_context_copy:
- handleCopyMessage(getListAdapter().getSelectedItems());
- actionMode.finish();
- return true;
- case R.id.menu_context_copy_public_key:
- handleCopyPublicKey((MessageRecord) getListAdapter().getSelectedItems().toArray()[0]);
- actionMode.finish();
- return true;
- case R.id.menu_context_delete_message:
- handleDeleteMessages(getListAdapter().getSelectedItems());
- actionMode.finish();
- return true;
- case R.id.menu_context_ban_user:
- handleBanUser(getListAdapter().getSelectedItems());
- return true;
- case R.id.menu_context_details:
- handleDisplayDetails(getSelectedMessageRecord());
- actionMode.finish();
- return true;
-// case R.id.menu_context_forward:
-// handleForwardMessage(getSelectedMessageRecord());
-// actionMode.finish();
-// return true;
- case R.id.menu_context_resend:
- handleResendMessage(getSelectedMessageRecord());
- actionMode.finish();
- return true;
- case R.id.menu_context_save_attachment:
- handleSaveAttachment((MediaMmsMessageRecord)getSelectedMessageRecord());
- actionMode.finish();
- return true;
- case R.id.menu_context_reply:
- handleReplyMessage(getSelectedMessageRecord());
- actionMode.finish();
- return true;
- }
-
- return false;
- }
- }
-
- private static class ConversationDateHeader extends HeaderViewHolder {
-
- private final Animation animateIn;
- private final Animation animateOut;
-
- private boolean pendingHide = false;
-
- private ConversationDateHeader(Context context, TextView textView) {
- super(textView);
- this.animateIn = AnimationUtils.loadAnimation(context, R.anim.slide_from_top);
- this.animateOut = AnimationUtils.loadAnimation(context, R.anim.slide_to_top);
-
- this.animateIn.setDuration(100);
- this.animateOut.setDuration(100);
- }
-
- public void show() {
- if (pendingHide) {
- pendingHide = false;
- } else {
- ViewUtil.animateIn(textView, animateIn);
- }
- }
-
- public void hide() {
- pendingHide = true;
-
- textView.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (pendingHide) {
- pendingHide = false;
- ViewUtil.animateOut(textView, animateOut, View.GONE);
- }
- }
- }, 400);
- }
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java
deleted file mode 100644
index aa60603074..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java
+++ /dev/null
@@ -1,1203 +0,0 @@
-/*
- * Copyright (C) 2011 Whisper Systems
- *
- * 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 .
- */
-package org.thoughtcrime.securesms.conversation;
-
-import android.annotation.SuppressLint;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.Typeface;
-import android.net.Uri;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.style.BackgroundColorSpan;
-import android.text.style.CharacterStyle;
-import android.text.style.ClickableSpan;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.URLSpan;
-import android.text.util.Linkify;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.annotation.DimenRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.annimon.stream.Stream;
-
-import org.session.libsession.messaging.contacts.Contact;
-import org.session.libsession.messaging.jobs.AttachmentDownloadJob;
-import org.session.libsession.messaging.jobs.JobQueue;
-import org.session.libsession.messaging.open_groups.OpenGroupAPIV2;
-import org.session.libsession.messaging.open_groups.OpenGroupV2;
-import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
-import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
-import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
-import org.session.libsession.messaging.utilities.UpdateMessageData;
-import org.session.libsession.utilities.Stub;
-import org.session.libsession.utilities.TextSecurePreferences;
-import org.session.libsession.utilities.ThemeUtil;
-import org.session.libsession.utilities.Util;
-import org.session.libsession.utilities.ViewUtil;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.session.libsession.utilities.recipients.RecipientModifiedListener;
-import org.session.libsignal.utilities.Log;
-import org.session.libsignal.utilities.guava.Optional;
-import org.thoughtcrime.securesms.BindableConversationItem;
-import org.thoughtcrime.securesms.MediaPreviewActivity;
-import org.thoughtcrime.securesms.MessageDetailsActivity;
-import org.thoughtcrime.securesms.components.ConversationItemAlertView;
-import org.thoughtcrime.securesms.components.ConversationItemFooter;
-import org.thoughtcrime.securesms.components.ConversationItemThumbnail;
-import org.thoughtcrime.securesms.components.DocumentView;
-import org.thoughtcrime.securesms.components.LinkPreviewView;
-import org.thoughtcrime.securesms.components.QuoteView;
-import org.thoughtcrime.securesms.components.StickerView;
-import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.MmsSmsDatabase;
-import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
-import org.thoughtcrime.securesms.database.model.MessageRecord;
-import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
-import org.thoughtcrime.securesms.database.model.Quote;
-import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
-import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
-import org.thoughtcrime.securesms.loki.views.MessageAudioView;
-import org.thoughtcrime.securesms.loki.views.OpenGroupInvitationView;
-import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.mms.ImageSlide;
-import org.thoughtcrime.securesms.mms.PartAuthority;
-import org.thoughtcrime.securesms.mms.Slide;
-import org.thoughtcrime.securesms.mms.SlideClickListener;
-import org.thoughtcrime.securesms.mms.SlidesClickedListener;
-import org.thoughtcrime.securesms.mms.TextSlide;
-import org.thoughtcrime.securesms.util.DateUtils;
-import org.thoughtcrime.securesms.util.LongClickCopySpan;
-import org.thoughtcrime.securesms.util.LongClickMovementMethod;
-import org.thoughtcrime.securesms.util.SearchUtil;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-import network.loki.messenger.R;
-
-/**
- * A view that displays an individual conversation item within a conversation
- * thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
- *
- * @author Moxie Marlinspike
- *
- */
-
-public class ConversationItem extends LinearLayout
- implements RecipientModifiedListener, BindableConversationItem
-{
- private static final String TAG = ConversationItem.class.getSimpleName();
-
- private static final int MAX_MEASURE_CALLS = 3;
- private static final int MAX_BODY_DISPLAY_LENGTH = 1000;
-
- private MessageRecord messageRecord;
- private Locale locale;
- private boolean groupThread;
- private Recipient recipient;
- private GlideRequests glideRequests;
-
- protected ViewGroup bodyBubble;
- private QuoteView quoteView;
- private EmojiTextView bodyText;
- private ConversationItemFooter footer;
- private ConversationItemFooter stickerFooter;
- private TextView groupSender;
- private TextView groupSenderProfileName;
- private View groupSenderHolder;
- private ProfilePictureView profilePictureView;
- private ImageView moderatorIconImageView;
- private ViewGroup contactPhotoHolder;
- private ConversationItemAlertView alertView;
- private ViewGroup container;
-
- private @NonNull Set batchSelected = new HashSet<>();
- private Recipient conversationRecipient;
- private Stub mediaThumbnailStub;
- private Stub audioViewStub;
- private Stub documentViewStub;
- private Stub linkPreviewStub;
- private Stub stickerStub;
- private Stub openGroupInvitationViewStub;
- private @Nullable EventListener eventListener;
-
- private int defaultBubbleColor;
- private int measureCalls;
-
- private final PassthroughClickListener passthroughClickListener = new PassthroughClickListener();
- private final AttachmentDownloadClickListener downloadClickListener = new AttachmentDownloadClickListener();
- private final SlideClickPassthroughListener singleDownloadClickListener = new SlideClickPassthroughListener(downloadClickListener);
- private final LinkPreviewClickListener linkPreviewClickListener = new LinkPreviewClickListener();
-
- private final Context context;
-
- public ConversationItem(Context context) {
- this(context, null);
- }
-
- public ConversationItem(Context context, AttributeSet attrs) {
- super(context, attrs);
- this.context = context;
- }
-
- @Override
- public void setOnClickListener(OnClickListener l) {
- super.setOnClickListener(new ClickListener(l));
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- initializeAttributes();
-
- this.bodyText = findViewById(R.id.conversation_item_body);
- this.footer = findViewById(R.id.conversation_item_footer);
- this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer);
- this.groupSender = findViewById(R.id.group_message_sender);
- this.groupSenderProfileName = findViewById(R.id.group_message_sender_profile);
- this.alertView = findViewById(R.id.indicators_parent);
- this.profilePictureView = findViewById(R.id.profilePictureView);
- this.moderatorIconImageView = findViewById(R.id.moderator_icon_image_view);
- this.contactPhotoHolder = findViewById(R.id.contact_photo_container);
- this.bodyBubble = findViewById(R.id.body_bubble);
- this.mediaThumbnailStub = new Stub<>(findViewById(R.id.image_view_stub));
- this.audioViewStub = new Stub<>(findViewById(R.id.audio_view_stub));
- this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub));
- this.linkPreviewStub = new Stub<>(findViewById(R.id.link_preview_stub));
- this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub));
- this.openGroupInvitationViewStub = new Stub<>(findViewById(R.id.open_group_invitation_stub));
- this.groupSenderHolder = findViewById(R.id.group_sender_holder);
- this.quoteView = findViewById(R.id.quote_view);
- this.container = findViewById(R.id.container);
-
- setOnClickListener(new ClickListener(null));
-
- bodyText.setOnLongClickListener(passthroughClickListener);
- bodyText.setOnClickListener(passthroughClickListener);
-
- bodyText.setMovementMethod(LongClickMovementMethod.getInstance(getContext()));
- }
-
- @Override
- public void bind(@NonNull MessageRecord messageRecord,
- @NonNull Optional previousMessageRecord,
- @NonNull Optional nextMessageRecord,
- @NonNull GlideRequests glideRequests,
- @NonNull Locale locale,
- @NonNull Set batchSelected,
- @NonNull Recipient conversationRecipient,
- @Nullable String searchQuery,
- boolean pulseHighlight)
- {
- this.messageRecord = messageRecord;
- this.locale = locale;
- this.glideRequests = glideRequests;
- this.batchSelected = batchSelected;
- this.conversationRecipient = conversationRecipient;
- this.groupThread = conversationRecipient.isGroupRecipient();
- this.recipient = messageRecord.getIndividualRecipient();
-
- this.recipient.addListener(this);
- this.conversationRecipient.addListener(this);
-
- setGutterSizes(messageRecord, groupThread);
- setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
- setMediaAttributes(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread);
- setInteractionState(messageRecord, pulseHighlight);
- setBodyText(messageRecord, searchQuery, groupThread);
- setBubbleState(messageRecord);
- setStatusIcons(messageRecord);
- setContactPhoto(recipient);
- setGroupMessageStatus(messageRecord, recipient);
- setGroupAuthorColor(messageRecord);
- setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
- setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
- setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
- setFooter(messageRecord, nextMessageRecord, locale, groupThread);
- adjustMarginsIfNeeded(messageRecord);
- }
-
- @Override
- public void setEventListener(@Nullable EventListener eventListener) {
- this.eventListener = eventListener;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (isInEditMode()) {
- return;
- }
-
- boolean needsMeasure = false;
-
- if (hasQuote(messageRecord)) {
- int quoteWidth = quoteView.getMeasuredWidth();
- int availableWidth = getAvailableMessageBubbleWidth(quoteView);
-
- if (quoteWidth != availableWidth) {
- quoteView.getLayoutParams().width = availableWidth;
- needsMeasure = true;
- }
- }
-
- if (hasThumbnail(messageRecord) && messageRecord.getDisplayBody(context).length() > 0) {
- ViewUtil.updateLayoutParams(bodyText, getAvailableMessageBubbleWidth(bodyText), ViewGroup.LayoutParams.WRAP_CONTENT);
- }
-
- ConversationItemFooter activeFooter = getActiveFooter(messageRecord);
- int availableWidth = getAvailableMessageBubbleWidth(footer);
-
- if (activeFooter.getVisibility() != GONE && activeFooter.getMeasuredWidth() != availableWidth) {
- activeFooter.getLayoutParams().width = availableWidth;
- needsMeasure = true;
- }
-
- if (needsMeasure) {
- if (measureCalls < MAX_MEASURE_CALLS) {
- measureCalls++;
- measure(widthMeasureSpec, heightMeasureSpec);
- } else {
- Log.w(TAG, "Hit measure() cap of " + MAX_MEASURE_CALLS);
- }
- } else {
- measureCalls = 0;
- }
- }
-
- private int getAvailableMessageBubbleWidth(@NonNull View forView) {
- int availableWidth;
- if (hasAudio(messageRecord)) {
- availableWidth = audioViewStub.get().getMeasuredWidth() + ViewUtil.getLeftMargin(audioViewStub.get()) + ViewUtil.getRightMargin(audioViewStub.get());
- } else if (hasThumbnail(messageRecord) || hasBigImageLinkPreview(messageRecord)) {
- availableWidth = mediaThumbnailStub.get().getMeasuredWidth();
- } else {
- availableWidth = bodyBubble.getMeasuredWidth() - bodyBubble.getPaddingLeft() - bodyBubble.getPaddingRight();
- }
-
- availableWidth -= ViewUtil.getLeftMargin(forView) + ViewUtil.getRightMargin(forView);
-
- return availableWidth;
- }
-
- private void initializeAttributes() {
- final int[] attributes = new int[] {R.attr.conversation_item_bubble_background};
- final TypedArray attrs = context.obtainStyledAttributes(attributes);
-
- defaultBubbleColor = attrs.getColor(0, Color.WHITE);
- attrs.recycle();
- }
-
- @Override
- public void unbind() {
- if (recipient != null) {
- recipient.removeListener(this);
- }
- if (profilePictureView != null) {
- profilePictureView.recycle();
- }
- }
-
- public MessageRecord getMessageRecord() {
- return messageRecord;
- }
-
- /// MessageRecord Attribute Parsers
-
- private void setBubbleState(MessageRecord messageRecord) {
- int bubbleColor = ThemeUtil.getThemedColor(getContext(), messageRecord.isOutgoing() ?
- R.attr.message_sent_background_color :
- R.attr.message_received_background_color);
- bodyBubble.getBackground().setColorFilter(bubbleColor, PorterDuff.Mode.MULTIPLY);
-
- if (audioViewStub.resolved()) {
- setAudioViewTint(messageRecord, this.conversationRecipient);
- }
- }
-
- private void setAudioViewTint(MessageRecord messageRecord, Recipient recipient) {
-// audioViewStub.get().setTint(Color.WHITE, getResources().getColor(R.color.action_bar_background));
- }
-
- private void setInteractionState(MessageRecord messageRecord, boolean pulseHighlight) {
- if (batchSelected.contains(messageRecord)) {
- setBackgroundResource(R.drawable.conversation_item_background);
- setSelected(true);
- } else if (pulseHighlight) {
- setBackgroundResource(R.drawable.conversation_item_background_animated);
- setSelected(true);
- postDelayed(() -> setSelected(false), 500);
- } else {
- setSelected(false);
- }
-
- if (mediaThumbnailStub.resolved()) {
- mediaThumbnailStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
- mediaThumbnailStub.get().setClickable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
- mediaThumbnailStub.get().setLongClickable(batchSelected.isEmpty());
- }
-
- if (audioViewStub.resolved()) {
- audioViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
- audioViewStub.get().setClickable(batchSelected.isEmpty());
- audioViewStub.get().setEnabled(batchSelected.isEmpty());
- }
-
- if (documentViewStub.resolved()) {
- documentViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
- documentViewStub.get().setClickable(batchSelected.isEmpty());
- }
- }
-
- private boolean isCaptionlessMms(MessageRecord messageRecord) {
- return TextUtils.isEmpty(messageRecord.getDisplayBody(getContext())) && messageRecord.isMms() && ((MmsMessageRecord) messageRecord).getSlideDeck().getTextSlide() == null;
- }
-
- private boolean hasAudio(MessageRecord messageRecord) {
- return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null;
- }
-
- private boolean hasThumbnail(MessageRecord messageRecord) {
- return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null;
- }
-
- private boolean hasOnlyThumbnail(MessageRecord messageRecord) {
- return hasThumbnail(messageRecord) &&
- !hasAudio(messageRecord) &&
- !hasDocument(messageRecord);
- }
-
- private boolean hasOnlyDocument(MessageRecord messageRecord) {
- return messageRecord.getBody().length() == 0 &&
- !hasThumbnail(messageRecord) &&
- !hasAudio(messageRecord) &&
- hasDocument(messageRecord) &&
- !hasQuote(messageRecord);
- }
-
- private boolean hasOnlyText(MessageRecord messageRecord) {
- return messageRecord.getBody().length() != 0 &&
- !hasThumbnail(messageRecord) &&
- !hasAudio(messageRecord) &&
- !hasDocument(messageRecord) &&
- !hasQuote(messageRecord);
- }
-
- private boolean hasDocument(MessageRecord messageRecord) {
- return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null;
- }
-
- private boolean hasExtraText(MessageRecord messageRecord) {
- boolean hasTextSlide = messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getTextSlide() != null;
- boolean hasOverflowText = messageRecord.getBody().length() > MAX_BODY_DISPLAY_LENGTH;
-
- return hasTextSlide || hasOverflowText;
- }
-
- private boolean hasQuote(MessageRecord messageRecord) {
- return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getQuote() != null;
- }
-
- private boolean hasLinkPreview(MessageRecord messageRecord) {
- return messageRecord.isMms() && !((MmsMessageRecord)messageRecord).getLinkPreviews().isEmpty();
- }
-
- private boolean hasBigImageLinkPreview(MessageRecord messageRecord) {
- if (!hasLinkPreview(messageRecord)) return false;
-
- LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
- int minWidth = getResources().getDimensionPixelSize(R.dimen.media_bubble_min_width);
-
- return linkPreview.getThumbnail().isPresent() &&
- linkPreview.getThumbnail().get().getWidth() >= minWidth;
- }
-
- private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery, boolean isGroupThread) {
- bodyText.setClickable(false);
- bodyText.setFocusable(false);
- bodyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, TextSecurePreferences.getMessageBodyTextSize(context));
- if (isCaptionlessMms(messageRecord)) {
- bodyText.setVisibility(View.GONE);
- } else {
- Spannable text = MentionUtilities.highlightMentions(linkifyMessageBody(messageRecord.getDisplayBody(context), batchSelected.isEmpty()), messageRecord.isOutgoing(), messageRecord.getThreadId(), context);
- text = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.WHITE), text, searchQuery);
- text = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), text, searchQuery);
-
- if (hasExtraText(messageRecord)) {
- bodyText.setOverflowText(getLongMessageSpan(messageRecord));
- } else {
- bodyText.setOverflowText(null);
- }
-
- if (!messageRecord.isOpenGroupInvitation())
- bodyText.setText(text);
-
- bodyText.setVisibility(View.VISIBLE);
- }
- }
-
- private void adjustMarginsIfNeeded(MessageRecord messageRecord) {
- LinearLayout.LayoutParams bodyTextLayoutParams = (LinearLayout.LayoutParams)bodyText.getLayoutParams();
- bodyTextLayoutParams.topMargin = 0;
- if (hasOnlyThumbnail(messageRecord) || hasLinkPreview(messageRecord)) {
- int topPadding = 0;
- if (groupSenderHolder.getVisibility() == VISIBLE) {
- topPadding = (int)getResources().getDimension(R.dimen.medium_spacing);
- }
- int bottomPadding = 0;
- if (messageRecord.getBody().length() > 0) {
- bodyTextLayoutParams.topMargin = (int)getResources().getDimension(R.dimen.medium_spacing);
- bottomPadding = (int)getResources().getDimension(R.dimen.medium_spacing);
- }
- bodyBubble.setPadding(0, topPadding, 0, bottomPadding);
- } else {
- bodyBubble.setPadding(0, (int)getResources().getDimension(R.dimen.medium_spacing), 0, (int)getResources().getDimension(R.dimen.medium_spacing));
- }
- bodyText.setLayoutParams(bodyTextLayoutParams);
- LinearLayout.LayoutParams senderHolderLayoutParams = (LinearLayout.LayoutParams)groupSenderHolder.getLayoutParams();
- if (groupSenderHolder.getVisibility() == VISIBLE && hasOnlyText(messageRecord)) {
- senderHolderLayoutParams.bottomMargin = (int)(getResources().getDisplayMetrics().density * 4);
- } else {
- senderHolderLayoutParams.bottomMargin = (int)getResources().getDimension(R.dimen.medium_spacing);
- }
- groupSenderHolder.setLayoutParams(senderHolderLayoutParams);
- if (documentViewStub.resolved()) {
- LinearLayout.LayoutParams documentViewLayoutParams = (LinearLayout.LayoutParams)documentViewStub.get().getLayoutParams();
- int bottomMargin = 0;
- if (hasOnlyDocument(messageRecord)) {
- if (footer.getVisibility() == VISIBLE) {
- bottomMargin = (int)(4 * getResources().getDisplayMetrics().density);
- } else {
- bottomMargin = (int)(-4 * getResources().getDisplayMetrics().density);
- }
- } else {
- bottomMargin = (int)(4 * getResources().getDisplayMetrics().density);
- }
- documentViewLayoutParams.bottomMargin = bottomMargin;
- documentViewStub.get().setLayoutParams(documentViewLayoutParams);
- }
- }
-
- private void setMediaAttributes(@NonNull MessageRecord messageRecord,
- @NonNull Optional previousRecord,
- @NonNull Optional nextRecord,
- @NonNull Recipient conversationRecipient,
- boolean isGroupThread)
- {
- boolean showControls = !messageRecord.isFailed();
-
- if (hasLinkPreview(messageRecord)) {
- linkPreviewStub.get().setVisibility(View.VISIBLE);
- if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
- if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
- if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
- if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
- if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
-
- //noinspection ConstantConditions
- LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
-
- if (hasBigImageLinkPreview(messageRecord)) {
- mediaThumbnailStub.get().setVisibility(VISIBLE);
- mediaThumbnailStub.get().setImageResource(glideRequests, Collections.singletonList(new ImageSlide(context, linkPreview.getThumbnail().get())), showControls, false);
- mediaThumbnailStub.get().setThumbnailClickListener(new LinkPreviewThumbnailClickListener());
- mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener);
- mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
-
- linkPreviewStub.get().setLinkPreview(glideRequests, linkPreview, false, false);
-
- setThumbnailCorners(messageRecord, previousRecord, nextRecord, isGroupThread);
- setLinkPreviewCorners(messageRecord, previousRecord, nextRecord, isGroupThread, true);
-
- ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- } else {
- linkPreviewStub.get().setLinkPreview(glideRequests, linkPreview, true, false);
- linkPreviewStub.get().setDownloadClickedListener(downloadClickListener);
- setLinkPreviewCorners(messageRecord, previousRecord, nextRecord, isGroupThread, false);
- ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- }
-
- linkPreviewStub.get().setOnClickListener(linkPreviewClickListener);
- linkPreviewStub.get().setOnLongClickListener(passthroughClickListener);
-
- footer.setVisibility(VISIBLE);
- } else if (hasAudio(messageRecord)) {
- audioViewStub.get().setVisibility(View.VISIBLE);
- if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
- if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
- if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
- if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
- if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
-
- //noinspection ConstantConditions
- audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls);
- audioViewStub.get().setDownloadClickListener(singleDownloadClickListener);
- audioViewStub.get().setOnLongClickListener(passthroughClickListener);
-
- ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- footer.setVisibility(VISIBLE);
- } else if (hasDocument(messageRecord)) {
- documentViewStub.get().setVisibility(View.VISIBLE);
- if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
- if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
- if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
- if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
- if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
-
- //noinspection ConstantConditions
- documentViewStub.get().setDocument(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getDocumentSlide(), showControls);
- documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
- documentViewStub.get().setDownloadClickListener(singleDownloadClickListener);
- documentViewStub.get().setOnLongClickListener(passthroughClickListener);
-
- ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- footer.setVisibility(VISIBLE);
- } else if (hasThumbnail(messageRecord)) {
- mediaThumbnailStub.get().setVisibility(View.VISIBLE);
- if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
- if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
- if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
- if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
- if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
-
- //noinspection ConstantConditions
- List thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides();
- mediaThumbnailStub.get().setImageResource(glideRequests,
- thumbnailSlides,
- showControls,
- false);
- mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener());
- mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener);
- mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
- mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
- mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getDisplayBody(getContext())) && !hasExtraText(messageRecord));
- mediaThumbnailStub.get().setConversationColor(messageRecord.isOutgoing() ? defaultBubbleColor
- : messageRecord.getRecipient().getColor().toConversationColor(context));
- mediaThumbnailStub.get().setBorderless(false);
-
- setThumbnailCorners(messageRecord, previousRecord, nextRecord, isGroupThread);
-
- ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- footer.setVisibility(VISIBLE);
- } else if (messageRecord.isOpenGroupInvitation()) {
- openGroupInvitationViewStub.get().setVisibility(View.VISIBLE);
- if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
- if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
- if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
- if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
- if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
-
- UpdateMessageData updateMessageData = UpdateMessageData.Companion.fromJSON(messageRecord.getBody());
- String name = null, url = null;
- if (updateMessageData.getKind() instanceof UpdateMessageData.Kind.OpenGroupInvitation) {
- UpdateMessageData.Kind.OpenGroupInvitation data = (UpdateMessageData.Kind.OpenGroupInvitation)updateMessageData.getKind();
- name = data.getGroupName();
- url = data.getGroupUrl();
- }
-
- openGroupInvitationViewStub.get().setOpenGroup(name, url, messageRecord.isOutgoing());
- openGroupInvitationViewStub.get().setOnLongClickListener(passthroughClickListener);
-
- bodyText.setVisibility(View.GONE);
-
- ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- } else {
- if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
- if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
- if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
- if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
- if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
-
- ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- footer.setVisibility(VISIBLE);
- }
- }
-
- private void setThumbnailCorners(@NonNull MessageRecord current,
- @NonNull Optional previous,
- @NonNull Optional next,
- boolean isGroupThread)
- {
- int defaultRadius = readDimen(R.dimen.message_corner_radius);
- int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius);
-
- int topLeft = defaultRadius;
- int topRight = defaultRadius;
- int bottomLeft = defaultRadius;
- int bottomRight = defaultRadius;
-
- if (isSingularMessage(current, previous, next, isGroupThread)) {
- topLeft = defaultRadius;
- topRight = defaultRadius;
- bottomLeft = defaultRadius;
- bottomRight = defaultRadius;
- } else if (isStartOfMessageCluster(current, previous, isGroupThread)) {
- if (current.isOutgoing()) {
- bottomRight = collapseRadius;
- } else {
- bottomLeft = collapseRadius;
- }
- } else if (isEndOfMessageCluster(current, next, isGroupThread)) {
- if (current.isOutgoing()) {
- topRight = collapseRadius;
- } else {
- topLeft = collapseRadius;
- }
- } else {
- if (current.isOutgoing()) {
- topRight = collapseRadius;
- bottomRight = collapseRadius;
- } else {
- topLeft = collapseRadius;
- bottomLeft = collapseRadius;
- }
- }
-
- if (!TextUtils.isEmpty(current.getDisplayBody(getContext()))) {
- bottomLeft = 0;
- bottomRight = 0;
- }
-
- if (isStartOfMessageCluster(current, previous, isGroupThread) && !current.isOutgoing() && isGroupThread) {
- topLeft = 0;
- topRight = 0;
- }
-
- if (hasQuote(messageRecord)) {
- topLeft = 0;
- topRight = 0;
- }
-
- if (hasLinkPreview(messageRecord) || hasExtraText(messageRecord)) {
- bottomLeft = 0;
- bottomRight = 0;
- }
-
- mediaThumbnailStub.get().setCorners(topLeft, topRight, bottomRight, bottomLeft);
- }
- private void setLinkPreviewCorners(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread, boolean bigImage) {
- int defaultRadius = readDimen(R.dimen.message_corner_radius);
- int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius);
-
- if (bigImage) {
- linkPreviewStub.get().setCorners(0, 0);
- } else if (isStartOfMessageCluster(current, previous, isGroupThread) && !current.isOutgoing() && isGroupThread) {
- linkPreviewStub.get().setCorners(0, 0);
- } else if (isSingularMessage(current, previous, next, isGroupThread) || isStartOfMessageCluster(current, previous, isGroupThread)) {
- linkPreviewStub.get().setCorners(defaultRadius, defaultRadius);
- } else if (current.isOutgoing()) {
- linkPreviewStub.get().setCorners(defaultRadius, collapseRadius);
- } else {
- linkPreviewStub.get().setCorners(collapseRadius, defaultRadius);
- }
- }
-
- private void setContactPhoto(@NonNull Recipient recipient) {
- if (messageRecord == null) { return; } // TODO: Figure out how this happens
- LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)bodyBubble.getLayoutParams();
- int groupThreadMargin = (int)((12 * getResources().getDisplayMetrics().density) + getResources().getDimension(R.dimen.small_profile_picture_size));
- int defaultMargin = 0;
- long threadID = messageRecord.getThreadId();
- Recipient r = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID);
- String threadName = r != null ? r.getName() : "";
- boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates"));
- layoutParams.setMarginStart((groupThread && !isRSSFeed) ? groupThreadMargin : defaultMargin);
- bodyBubble.setLayoutParams(layoutParams);
- if (profilePictureView == null) { return; }
- String publicKey = recipient.getAddress().toString();
- profilePictureView.setPublicKey(publicKey);
- String displayName = recipient.getName();
- profilePictureView.setDisplayName(displayName);
- profilePictureView.setAdditionalPublicKey(null);
- profilePictureView.setGlide(glideRequests);
- profilePictureView.update();
- }
-
- private SpannableString linkifyMessageBody(SpannableString messageBody, boolean shouldLinkifyAllLinks) {
- int linkPattern = Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS;
- boolean hasLinks = Linkify.addLinks(messageBody, shouldLinkifyAllLinks ? linkPattern : 0);
-
- if (hasLinks) {
- Stream.of(messageBody.getSpans(0, messageBody.length(), URLSpan.class))
- .filterNot(url -> LinkPreviewUtil.isLegalUrl(url.getURL()))
- .forEach(messageBody::removeSpan);
-
- URLSpan[] urlSpans = messageBody.getSpans(0, messageBody.length(), URLSpan.class);
-
- for (URLSpan urlSpan : urlSpans) {
- int start = messageBody.getSpanStart(urlSpan);
- int end = messageBody.getSpanEnd(urlSpan);
- messageBody.setSpan(new LongClickCopySpan(urlSpan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- return messageBody;
- }
-
- private void setStatusIcons(MessageRecord messageRecord) {
- bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
-
- if (messageRecord.isFailed()) {
- alertView.setFailed();
- } else {
- alertView.setNone();
- }
- }
-
- private void setQuote(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) {
- if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) {
- Quote quote = ((MediaMmsMessageRecord)current).getQuote();
- //noinspection ConstantConditions
- String quoteBody = MentionUtilities.highlightMentions(quote.getText(), current.getThreadId(), context);
- quoteView.setQuote(glideRequests, quote.getId(), Recipient.from(context, quote.getAuthor(), true), quoteBody, quote.isOriginalMissing(), quote.getAttachment(), conversationRecipient);
- quoteView.setVisibility(View.VISIBLE);
- quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
-
- quoteView.setOnClickListener(view -> {
- if (eventListener != null && batchSelected.isEmpty()) {
- eventListener.onQuoteClicked((MmsMessageRecord) current);
- } else {
- passthroughClickListener.onClick(view);
- }
- });
-
- quoteView.setOnLongClickListener(passthroughClickListener);
-
- if (isStartOfMessageCluster(current, previous, isGroupThread)) {
- if (current.isOutgoing()) {
- quoteView.setTopCornerSizes(true, true);
- } else if (isGroupThread) {
- quoteView.setTopCornerSizes(false, false);
- } else {
- quoteView.setTopCornerSizes(true, true);
- }
- } else if (!isSingularMessage(current, previous, next, isGroupThread)) {
- if (current.isOutgoing()) {
- quoteView.setTopCornerSizes(true, false);
- } else {
- quoteView.setTopCornerSizes(false, true);
- }
- }
-
- if (mediaThumbnailStub.resolved()) {
- ViewUtil.setTopMargin(mediaThumbnailStub.get(), readDimen(R.dimen.message_bubble_top_padding));
- }
- } else {
- quoteView.dismiss();
-
- if (mediaThumbnailStub.resolved()) {
- ViewUtil.setTopMargin(mediaThumbnailStub.get(), 0);
- }
- }
- }
-
- private void setGutterSizes(@NonNull MessageRecord current, boolean isGroupThread) {
- if (isGroupThread && current.isOutgoing()) {
- ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_group_left_gutter));
- } else if (current.isOutgoing()) {
- ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_individual_left_gutter));
- }
- }
-
- private void setFooter(@NonNull MessageRecord current, @NonNull Optional next, @NonNull Locale locale, boolean isGroupThread) {
- ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-
- footer.setVisibility(GONE);
- stickerFooter.setVisibility(GONE);
- if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().getFooter().setVisibility(GONE);
-
- boolean differentTimestamps = next.isPresent() && !DateUtils.isSameExtendedRelativeTimestamp(context, locale, next.get().getTimestamp(), current.getTimestamp());
-
- if (current.getExpiresIn() > 0 || current.isPending() ||
- current.isFailed() || differentTimestamps || isEndOfMessageCluster(current, next, isGroupThread))
- {
- ConversationItemFooter activeFooter = getActiveFooter(current);
- activeFooter.setVisibility(VISIBLE);
- activeFooter.setMessageRecord(current, locale);
- }
- }
-
- private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
- if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) {
- return mediaThumbnailStub.get().getFooter();
- } else {
- return footer;
- }
- }
-
- private int readDimen(@DimenRes int dimenId) {
- return context.getResources().getDimensionPixelOffset(dimenId);
- }
-
- private boolean shouldInterceptClicks(MessageRecord messageRecord) {
- return batchSelected.isEmpty() && (messageRecord.isFailed() && !messageRecord.isMmsNotification());
- }
-
- @SuppressLint("SetTextI18n")
- private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) {
- if (groupThread && !messageRecord.isOutgoing()) {
- String sessionID = recipient.getAddress().serialize();
- Contact contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(sessionID);
- String displayName;
- if (contact != null) {
- Contact.ContactContext context = (this.conversationRecipient.isOpenGroupRecipient()) ? Contact.ContactContext.OPEN_GROUP : Contact.ContactContext.REGULAR;
- displayName = contact.displayName(context);
- } else {
- displayName = sessionID;
- }
-
- this.groupSender.setText(displayName);
-
- if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
- this.groupSenderProfileName.setText("~" + recipient.getProfileName());
- this.groupSenderProfileName.setVisibility(View.VISIBLE);
- } else {
- this.groupSenderProfileName.setText(null);
- this.groupSenderProfileName.setVisibility(View.GONE);
- }
- }
- }
-
- private void setGroupAuthorColor(@NonNull MessageRecord messageRecord) {
- groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color));
- groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color));
- }
-
- private void setAuthor(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) {
- Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(current.getThreadId());
- String threadName = null;
- if (recipient != null) {
- threadName = recipient.getName();
- }
- boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates"));
- if (isGroupThread && !isRSSFeed && !current.isOutgoing()) {
- contactPhotoHolder.setVisibility(VISIBLE);
-
- if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()) ||
- !DateUtils.isSameDay(previous.get().getTimestamp(), current.getTimestamp()))
- {
- groupSenderHolder.setVisibility(VISIBLE);
- } else {
- groupSenderHolder.setVisibility(GONE);
- }
-
- if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress())) {
- profilePictureView.setVisibility(VISIBLE);
- int visibility = View.GONE;
-
- OpenGroupV2 openGroupV2 = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(messageRecord.getThreadId());
- if (openGroupV2 != null) {
- boolean isModerator = OpenGroupAPIV2.isUserModerator(current.getRecipient().getAddress().toString(), openGroupV2.getRoom(), openGroupV2.getServer());
- visibility = isModerator ? View.VISIBLE : View.GONE;
- }
-
- moderatorIconImageView.setVisibility(visibility);
- } else {
- profilePictureView.setVisibility(GONE);
- moderatorIconImageView.setVisibility(GONE);
-
- }
- } else {
- groupSenderHolder.setVisibility(GONE);
-
- if (contactPhotoHolder != null) {
- contactPhotoHolder.setVisibility(GONE);
- moderatorIconImageView.setVisibility(GONE);
- }
- }
- }
-
- private void setMessageShape(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) {
- int background;
- if (isSingularMessage(current, previous, next, isGroupThread)) {
- background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_alone : R.drawable.message_bubble_background_received_alone;
- } else if (isStartOfMessageCluster(current, previous, isGroupThread)) {
- background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_start : R.drawable.message_bubble_background_received_start;
- } else if (isEndOfMessageCluster(current, next, isGroupThread)) {
- background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_end : R.drawable.message_bubble_background_received_end;
- } else {
- background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_middle : R.drawable.message_bubble_background_received_middle;
- }
-
- bodyBubble.setBackgroundResource(background);
- }
-
- private boolean isStartOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional previous, boolean isGroupThread) {
- if (isGroupThread) {
- return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) ||
- !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress());
- } else {
- return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) ||
- current.isOutgoing() != previous.get().isOutgoing();
- }
- }
-
- private boolean isEndOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional next, boolean isGroupThread) {
- if (isGroupThread) {
- return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) ||
- !current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress());
- } else {
- return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) ||
- current.isOutgoing() != next.get().isOutgoing();
- }
- }
-
- private boolean isSingularMessage(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) {
- return isStartOfMessageCluster(current, previous, isGroupThread) && isEndOfMessageCluster(current, next, isGroupThread);
- }
-
- private void setMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) {
- int spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse);
- int spacingBottom = spacingTop;
-
- if (isStartOfMessageCluster(current, previous, isGroupThread)) {
- spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_default);
- }
-
- if (isEndOfMessageCluster(current, next, isGroupThread)) {
- spacingBottom = readDimen(context, R.dimen.conversation_vertical_message_spacing_default);
- }
-
- ViewUtil.setPaddingTop(this, spacingTop);
- ViewUtil.setPaddingBottom(this, spacingBottom);
- }
-
- private int readDimen(@NonNull Context context, @DimenRes int dimenId) {
- return context.getResources().getDimensionPixelOffset(dimenId);
- }
-
- /// Event handlers
-
- private Spannable getLongMessageSpan(@NonNull MessageRecord messageRecord) {
- String message;
- Runnable action;
-
- if (messageRecord.isMms()) {
- TextSlide slide = ((MmsMessageRecord) messageRecord).getSlideDeck().getTextSlide();
-
- if (slide != null && slide.asAttachment().getTransferState() == AttachmentTransferProgress.TRANSFER_PROGRESS_DONE) {
- message = getResources().getString(R.string.ConversationItem_read_more);
- action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms());
- } else if (slide != null && slide.asAttachment().getTransferState() == AttachmentTransferProgress.TRANSFER_PROGRESS_STARTED) {
- message = getResources().getString(R.string.ConversationItem_pending);
- action = () -> {};
- } else if (slide != null) {
- message = getResources().getString(R.string.ConversationItem_download_more);
- action = () -> singleDownloadClickListener.onClick(bodyText, slide);
- } else {
- message = getResources().getString(R.string.ConversationItem_read_more);
- action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms());
- }
- } else {
- message = getResources().getString(R.string.ConversationItem_read_more);
- action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms());
- }
-
- SpannableStringBuilder span = new SpannableStringBuilder(message);
- CharacterStyle style = new ClickableSpan() {
- @Override
- public void onClick(@NonNull View widget) {
- if (eventListener != null && batchSelected.isEmpty()) {
- action.run();
- }
- }
-
- @Override
- public void updateDrawState(@NonNull TextPaint ds) {
- ds.setTypeface(Typeface.DEFAULT_BOLD);
- }
- };
- span.setSpan(style, 0, span.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- return span;
- }
-
- @Override
- public void onModified(final Recipient modified) {
- Util.runOnMain(() -> {
- setBubbleState(messageRecord);
- setContactPhoto(recipient);
- setGroupMessageStatus(messageRecord, recipient);
- setAudioViewTint(messageRecord, conversationRecipient);
- });
- }
-
- private class LinkPreviewClickListener implements View.OnClickListener {
- @Override
- public void onClick(View view) {
- if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) {
- eventListener.onLinkPreviewClicked(((MmsMessageRecord) messageRecord).getLinkPreviews().get(0));
- } else {
- passthroughClickListener.onClick(view);
- }
- }
- }
-
- private class LinkPreviewThumbnailClickListener implements SlideClickListener {
- public void onClick(final View v, final Slide slide) {
- if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) {
- eventListener.onLinkPreviewClicked(((MmsMessageRecord) messageRecord).getLinkPreviews().get(0));
- } else {
- performClick();
- }
- }
- }
-
- private class AttachmentDownloadClickListener implements SlidesClickedListener {
- @Override
- public void onClick(View v, final List slides) {
- Log.i(TAG, "onClick() for attachment download");
- Log.i(TAG, "Scheduling push attachment downloads for " + slides.size() + " items");
-
- for (Slide slide : slides) {
- JobQueue.getShared().add(
- new AttachmentDownloadJob(messageRecord.getId(),
- ((DatabaseAttachment)slide.asAttachment()).getAttachmentId().getRowId())
- );
- }
- }
- }
-
- private class SlideClickPassthroughListener implements SlideClickListener {
-
- private final SlidesClickedListener original;
-
- private SlideClickPassthroughListener(@NonNull SlidesClickedListener original) {
- this.original = original;
- }
-
- @Override
- public void onClick(View v, Slide slide) {
- original.onClick(v, Collections.singletonList(slide));
- }
- }
-
- private class StickerClickListener implements SlideClickListener {
- @Override
- public void onClick(View v, Slide slide) {
- if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
- performClick();
- }
- }
- }
-
- private class ThumbnailClickListener implements SlideClickListener {
- public void onClick(final View v, final Slide slide) {
- if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
- performClick();
- } else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) {
- Intent intent = new Intent(context, MediaPreviewActivity.class);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setDataAndType(slide.getUri(), slide.getContentType());
- intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
- intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, messageRecord.isOutgoing());
- intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
- intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
- intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orNull());
- intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false);
-
- context.startActivity(intent);
- } else if (slide.getUri() != null) {
- Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType());
- Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri());
- Log.i(TAG, "Public URI: " + publicUri);
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), slide.getContentType());
- try {
- context.startActivity(intent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(TAG, "No activity existed to view the media.");
- Toast.makeText(context, R.string.ConversationItem_unable_to_open_media, Toast.LENGTH_LONG).show();
- }
- }
- }
- }
-
- private class PassthroughClickListener implements View.OnLongClickListener, View.OnClickListener {
-
- @Override
- public boolean onLongClick(View v) {
- if (bodyText.hasSelection()) {
- return false;
- }
- performLongClick();
- return true;
- }
-
- @Override
- public void onClick(View v) {
- performClick();
- }
- }
-
- private class ClickListener implements View.OnClickListener {
- private OnClickListener parent;
-
- ClickListener(@Nullable OnClickListener parent) {
- this.parent = parent;
- }
-
- public void onClick(View v) {
- if (!shouldInterceptClicks(messageRecord) && parent != null) {
- parent.onClick(v);
- } else if (messageRecord.isFailed()) {
- Intent intent = new Intent(context, MessageDetailsActivity.class);
- intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, messageRecord.getId());
- intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId());
- intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
- intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread);
- intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
- context.startActivity(intent);
- }
- }
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
deleted file mode 100644
index ece1bb784d..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package org.thoughtcrime.securesms.conversation;
-
-import android.content.Intent;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import androidx.core.app.ActivityOptionsCompat;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.WindowManager;
-
-import org.session.libsignal.utilities.Log;
-import org.session.libsignal.utilities.ListenableFuture;
-import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
-
-import java.util.concurrent.ExecutionException;
-
-import network.loki.messenger.R;
-
-public class ConversationPopupActivity extends ConversationActivity {
-
- private static final String TAG = ConversationPopupActivity.class.getSimpleName();
-
- @Override
- protected void onPreCreate() {
- super.onPreCreate();
- overridePendingTransition(R.anim.slide_from_top, R.anim.slide_to_top);
- }
-
- @Override
- protected void onCreate(Bundle bundle, boolean ready) {
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND,
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
- WindowManager.LayoutParams params = getWindow().getAttributes();
- params.alpha = 1.0f;
- params.dimAmount = 0.1f;
- params.gravity = Gravity.TOP;
- getWindow().setAttributes(params);
-
- Display display = getWindowManager().getDefaultDisplay();
- int width = display.getWidth();
- int height = display.getHeight();
-
- if (height > width) getWindow().setLayout((int) (width * .85), (int) (height * .5));
- else getWindow().setLayout((int) (width * .7), (int) (height * .75));
-
- super.onCreate(bundle, ready);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- composeText.requestFocus();
- quickAttachmentToggle.disable();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (isFinishing()) overridePendingTransition(R.anim.slide_from_top, R.anim.slide_to_top);
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuInflater inflater = this.getMenuInflater();
- menu.clear();
-
- inflater.inflate(R.menu.conversation_popup, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_expand:
- saveDraft().addListener(new ListenableFuture.Listener() {
- @Override
- public void onSuccess(Long result) {
- ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height);
- Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivityV2.class);
- intent.putExtra(ConversationActivityV2.ADDRESS, getRecipient().getAddress());
- intent.putExtra(ConversationActivityV2.THREAD_ID, result);
-
- if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
- startActivity(intent, transition.toBundle());
- } else {
- startActivity(intent);
- overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
- }
-
- finish();
- }
-
- @Override
- public void onFailure(ExecutionException e) {
- Log.w(TAG, e);
- }
- });
- return true;
- }
-
- return false;
- }
-
- @Override
- protected void initializeActionBar() {
- super.initializeActionBar();
- getSupportActionBar().setDisplayHomeAsUpEnabled(false);
- }
-
- @Override
- protected void sendComplete(long threadId) {
- super.sendComplete(threadId);
- finish();
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java
deleted file mode 100644
index d24539982d..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package org.thoughtcrime.securesms.conversation;
-
-import android.app.Application;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.LiveData;
-import android.content.Context;
-import androidx.annotation.NonNull;
-
-import org.thoughtcrime.securesms.contacts.ContactAccessor;
-import org.thoughtcrime.securesms.database.CursorList;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.search.SearchRepository;
-import org.thoughtcrime.securesms.search.model.MessageResult;
-import org.thoughtcrime.securesms.util.CloseableLiveData;
-import org.session.libsession.utilities.Debouncer;
-import org.session.libsession.utilities.Util;
-import org.session.libsession.utilities.concurrent.SignalExecutors;
-
-import java.io.Closeable;
-import java.util.List;
-
-public class ConversationSearchViewModel extends AndroidViewModel {
-
- private final SearchRepository searchRepository;
- private final CloseableLiveData result;
- private final Debouncer debouncer;
-
- private boolean firstSearch;
- private boolean searchOpen;
- private String activeQuery;
- private long activeThreadId;
-
- public ConversationSearchViewModel(@NonNull Application application) {
- super(application);
- Context context = application.getApplicationContext();
- result = new CloseableLiveData<>();
- debouncer = new Debouncer(500);
- searchRepository = new SearchRepository(context,
- DatabaseFactory.getSearchDatabase(context),
- DatabaseFactory.getThreadDatabase(context),
- ContactAccessor.getInstance(),
- SignalExecutors.SERIAL);
- }
-
- LiveData getSearchResults() {
- return result;
- }
-
- void onQueryUpdated(@NonNull String query, long threadId) {
- if (firstSearch && query.length() < 2) {
- result.postValue(new SearchResult(CursorList.emptyList(), 0));
- return;
- }
-
- if (query.equals(activeQuery)) {
- return;
- }
-
- updateQuery(query, threadId);
- }
-
- void onMissingResult() {
- if (activeQuery != null) {
- updateQuery(activeQuery, activeThreadId);
- }
- }
-
- void onMoveUp() {
- debouncer.clear();
-
- CursorList messages = (CursorList) result.getValue().getResults();
- int position = Math.min(result.getValue().getPosition() + 1, messages.size() - 1);
-
- result.setValue(new SearchResult(messages, position), false);
- }
-
- void onMoveDown() {
- debouncer.clear();
-
- CursorList messages = (CursorList) result.getValue().getResults();
- int position = Math.max(result.getValue().getPosition() - 1, 0);
-
- result.setValue(new SearchResult(messages, position), false);
- }
-
-
- void onSearchOpened() {
- searchOpen = true;
- firstSearch = true;
- }
-
- void onSearchClosed() {
- searchOpen = false;
- debouncer.clear();
- result.close();
- }
-
- @Override
- protected void onCleared() {
- super.onCleared();
- result.close();
- }
-
- private void updateQuery(@NonNull String query, long threadId) {
- activeQuery = query;
- activeThreadId = threadId;
-
- debouncer.publish(() -> {
- firstSearch = false;
-
- searchRepository.query(query, threadId, messages -> {
- Util.runOnMain(() -> {
- if (searchOpen && query.equals(activeQuery)) {
- result.setValue(new SearchResult(messages, 0));
- } else {
- messages.close();
- }
- });
- });
- });
- }
-
- static class SearchResult implements Closeable {
-
- private final CursorList results;
- private final int position;
-
- SearchResult(CursorList results, int position) {
- this.results = results;
- this.position = position;
- }
-
- public List getResults() {
- return results;
- }
-
- public int getPosition() {
- return position;
- }
-
- @Override
- public void close() {
- results.close();
- }
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
deleted file mode 100644
index 6dfd2fa887..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package org.thoughtcrime.securesms.conversation;
-
-import android.content.Context;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage;
-import org.session.libsession.utilities.ExpirationUtil;
-import org.session.libsession.utilities.Util;
-import org.session.libsession.utilities.recipients.Recipient;
-import org.session.libsession.utilities.recipients.RecipientModifiedListener;
-import org.session.libsignal.utilities.guava.Optional;
-import org.thoughtcrime.securesms.BindableConversationItem;
-import org.thoughtcrime.securesms.database.model.MessageRecord;
-import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.util.DateUtils;
-
-import java.util.Locale;
-import java.util.Set;
-
-import network.loki.messenger.R;
-
-//TODO Remove this class.
-public class ConversationUpdateItem extends LinearLayout
- implements RecipientModifiedListener, BindableConversationItem
-{
- private static final String TAG = ConversationUpdateItem.class.getSimpleName();
-
- private Set batchSelected;
-
- private ImageView icon;
- private TextView title;
- private TextView body;
- private TextView date;
- private Recipient sender;
- private MessageRecord messageRecord;
- private Locale locale;
-
- public ConversationUpdateItem(Context context) {
- super(context);
- }
-
- public ConversationUpdateItem(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
-
- this.icon = findViewById(R.id.conversation_update_icon);
- this.title = findViewById(R.id.conversation_update_title);
- this.body = findViewById(R.id.conversation_update_body);
- this.date = findViewById(R.id.conversation_update_date);
-
- this.setOnClickListener(new InternalClickListener(null));
- }
-
- @Override
- public void bind(@NonNull MessageRecord messageRecord,
- @NonNull Optional previousMessageRecord,
- @NonNull Optional nextMessageRecord,
- @NonNull GlideRequests glideRequests,
- @NonNull Locale locale,
- @NonNull Set batchSelected,
- @NonNull Recipient conversationRecipient,
- @Nullable String searchQuery,
- boolean pulseUpdate)
- {
- this.batchSelected = batchSelected;
-
- bind(messageRecord, locale);
- }
-
- @Override
- public void setEventListener(@Nullable EventListener listener) {
- // No events to report yet
- }
-
- @Override
- public MessageRecord getMessageRecord() {
- return messageRecord;
- }
-
- private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
- this.messageRecord = messageRecord;
- this.sender = messageRecord.getIndividualRecipient();
- this.locale = locale;
-
- this.sender.addListener(this);
-
- if (messageRecord.isCallLog()) setCallRecord(messageRecord);
- else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord);
- else if (messageRecord.isScreenshotNotification()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT);
- else if (messageRecord.isMediaSavedNotification()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED);
- else throw new AssertionError("Neither group nor log nor joined.");
-
- if (batchSelected.contains(messageRecord)) setSelected(true);
- else setSelected(false);
- }
-
- private void setCallRecord(MessageRecord messageRecord) {
- if (messageRecord.isIncomingCall()) icon.setImageResource(R.drawable.ic_call_received_grey600_24dp);
- else if (messageRecord.isOutgoingCall()) icon.setImageResource(R.drawable.ic_call_made_grey600_24dp);
- else icon.setImageResource(R.drawable.ic_call_missed_grey600_24dp);
-
- body.setText(messageRecord.getDisplayBody(getContext()));
- date.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, messageRecord.getDateReceived()));
-
- title.setVisibility(GONE);
- body.setVisibility(VISIBLE);
- date.setVisibility(View.VISIBLE);
- }
-
- private void setTimerRecord(final MessageRecord messageRecord) {
- @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme());
- if (messageRecord.getExpiresIn() > 0) {
- icon.setImageResource(R.drawable.ic_timer);
- icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
- } else {
- icon.setImageResource(R.drawable.ic_timer_disabled);
- icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
- }
-
- title.setText(ExpirationUtil.getExpirationDisplayValue(getContext(), (int)(messageRecord.getExpiresIn() / 1000)));
- body.setText(messageRecord.getDisplayBody(getContext()));
-
- title.setVisibility(VISIBLE);
- body.setVisibility(VISIBLE);
- date.setVisibility(GONE);
- }
-
- private void setDataExtractionRecord(final MessageRecord messageRecord, DataExtractionNotificationInfoMessage.Kind kind) {
- @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme());
- if (kind == DataExtractionNotificationInfoMessage.Kind.SCREENSHOT) {
- icon.setImageResource(R.drawable.quick_camera_dark);
- } else if (kind == DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED) {
- icon.setImageResource(R.drawable.ic_file_download_white_36dp);
- }
- icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
-
- body.setText(messageRecord.getDisplayBody(getContext()));
-
- title.setVisibility(VISIBLE);
- body.setVisibility(VISIBLE);
- date.setVisibility(GONE);
- }
-
- private void setTextMessageRecord(MessageRecord messageRecord) {
- body.setText(messageRecord.getDisplayBody(getContext()));
-
- icon.setVisibility(GONE);
- title.setVisibility(GONE);
- body.setVisibility(VISIBLE);
- date.setVisibility(GONE);
- }
-
- @Override
- public void onModified(Recipient recipient) {
- Util.runOnMain(() -> bind(messageRecord, locale));
- }
-
- @Override
- public void setOnClickListener(View.OnClickListener l) {
- super.setOnClickListener(new InternalClickListener(l));
- }
-
- @Override
- public void unbind() {
- if (sender != null) {
- sender.removeListener(this);
- }
- }
-
- private class InternalClickListener implements View.OnClickListener {
-
- @Nullable private final View.OnClickListener parent;
-
- InternalClickListener(@Nullable View.OnClickListener parent) {
- this.parent = parent;
- }
-
- @Override
- public void onClick(View v) {
-
- }
- }
-}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
index b9899c8a32..480c74d33e 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
@@ -78,7 +78,7 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.audio.AudioRecorder
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
-import org.thoughtcrime.securesms.conversation.ConversationActivity
+
import org.thoughtcrime.securesms.conversation.v2.dialogs.*
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate
@@ -902,7 +902,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
message.text = body
val quote = quotedMessage?.let {
val quotedAttachments = (it as? MmsMessageRecord)?.slideDeck?.asAttachments() ?: listOf()
- QuoteModel(it.dateSent, it.individualRecipient.address, it.body, false, quotedAttachments)
+ val sender = if (it.isOutgoing) fromSerialized(TextSecurePreferences.getLocalNumber(this)!!) else it.individualRecipient.address
+ QuoteModel(it.dateSent, sender, it.body, false, quotedAttachments)
}
val outgoingTextMessage = OutgoingMediaMessage.from(message, thread, attachments, quote, linkPreview)
// Clear the input bar
@@ -1048,10 +1049,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
val future = audioRecorder.stopRecording()
stopAudioHandler.removeCallbacks(stopVoiceMessageRecordingTask)
- future.addListener(object : ListenableFuture.Listener> {
+ future.addListener(object : ListenableFuture.Listener> {
- override fun onSuccess(result: Pair) {
- val audioSlide = AudioSlide(this@ConversationActivityV2, result.first, result.second!!, MediaTypes.AUDIO_AAC, true)
+ override fun onSuccess(result: Pair) {
+ val audioSlide = AudioSlide(this@ConversationActivityV2, result.first, result.second, MediaTypes.AUDIO_AAC, true)
val slideDeck = SlideDeck()
slideDeck.addSlide(audioSlide)
sendAttachments(slideDeck.asAttachments(), null)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt
index cfb1e38726..f310cd019b 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt
@@ -38,7 +38,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
set(value) { field = value; showOrHideInputIfNeeded() }
var text: String
- get() { return inputBarEditText.text.toString() }
+ get() { return inputBarEditText.text?.toString() ?: "" }
set(value) { inputBarEditText.setText(value) }
private val attachmentsButton by lazy { InputBarButton(context, R.drawable.ic_plus_24) }
@@ -122,7 +122,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
val maxContentWidth = (screenWidth - 2 * resources.getDimension(R.dimen.medium_spacing) - toPx(16, resources) - toPx(30, resources)).roundToInt()
val sender = if (message.isOutgoing) TextSecurePreferences.getLocalNumber(context)!! else message.individualRecipient.address.serialize()
quoteView.bind(sender, message.body, attachments,
- thread, true, maxContentWidth, message.isOpenGroupInvitation, message.threadId, glide)
+ thread, true, maxContentWidth, message.isOpenGroupInvitation, message.threadId, false, glide)
// The 6 DP below is the padding the quote view applies to itself, which isn't included in the
// intrinsic height calculation.
val quoteViewIntrinsicHeight = quoteView.getIntrinsicHeight(maxContentWidth) + toPx(6, resources)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt
index 2ab119950a..b03b4be278 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt
@@ -110,7 +110,8 @@ class QuoteView : LinearLayout {
// region Updating
fun bind(authorPublicKey: String, body: String?, attachments: SlideDeck?, thread: Recipient,
- isOutgoingMessage: Boolean, maxContentWidth: Int, isOpenGroupInvitation: Boolean, threadID: Long, glide: GlideRequests) {
+ isOutgoingMessage: Boolean, maxContentWidth: Int, isOpenGroupInvitation: Boolean, threadID: Long,
+ isOriginalMissing: Boolean, glide: GlideRequests) {
val contactDB = DatabaseFactory.getSessionContactDatabase(context)
// Reduce the max body text view line count to 2 if this is a group thread because
// we'll be showing the author text view and we don't want the overall quote view height
@@ -128,7 +129,7 @@ class QuoteView : LinearLayout {
quoteViewBodyTextView.text = if (isOpenGroupInvitation) resources.getString(R.string.open_group_invitation_view__open_group_invitation) else MentionUtilities.highlightMentions((body ?: "").toSpannable(), threadID, context);
quoteViewBodyTextView.setTextColor(getTextColor(isOutgoingMessage))
// Accent line / attachment preview
- val hasAttachments = (attachments != null && attachments.asAttachments().isNotEmpty())
+ val hasAttachments = (attachments != null && attachments.asAttachments().isNotEmpty()) && !isOriginalMissing
quoteViewAccentLine.isVisible = !hasAttachments
quoteViewAttachmentPreviewContainer.isVisible = hasAttachments
if (!hasAttachments) {
@@ -136,8 +137,7 @@ class QuoteView : LinearLayout {
accentLineLayoutParams.height = getIntrinsicContentHeight(maxContentWidth) // Match the intrinsic * content * height
quoteViewAccentLine.layoutParams = accentLineLayoutParams
quoteViewAccentLine.setBackgroundColor(getLineColor(isOutgoingMessage))
- } else {
- attachments!!
+ } else if (attachments != null) {
quoteViewAttachmentPreviewImageView.imageTintList = ColorStateList.valueOf(ResourcesCompat.getColor(resources, R.color.white, context.theme))
val backgroundColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.black else R.color.accent
val backgroundColor = ResourcesCompat.getColor(resources, backgroundColorID, context.theme)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt
index 5c02cb45a4..3f98ba5f87 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt
@@ -82,12 +82,18 @@ class VisibleMessageContentView : LinearLayout {
} else if (message is MmsMessageRecord && message.quote != null) {
val quote = message.quote!!
val quoteView = QuoteView(context, QuoteView.Mode.Regular)
- // The max content width is the max message bubble size - 2 times the horizontal padding - the
- // quote view content area's start margin. This unfortunately has to be calculated manually
+ // The max content width is the max message bubble size - 2 times the horizontal padding - 2
+ // times the horizontal margin. This unfortunately has to be calculated manually
// here to get the layout right.
- val maxContentWidth = (maxWidth - 2 * resources.getDimension(R.dimen.medium_spacing) - toPx(16, resources)).roundToInt()
- quoteView.bind(quote.author.toString(), quote.text, quote.attachment, thread,
- message.isOutgoing, maxContentWidth, message.isOpenGroupInvitation, message.threadId, glide)
+ val maxContentWidth = (maxWidth - 2 * resources.getDimension(R.dimen.medium_spacing) - 2 * toPx(16, resources)).roundToInt()
+ val quoteText = if (quote.isOriginalMissing) {
+ context.getString(R.string.QuoteView_original_missing)
+ } else {
+ quote.text
+ }
+ quoteView.bind(quote.author.toString(), quoteText, quote.attachment, thread,
+ message.isOutgoing, maxContentWidth, message.isOpenGroupInvitation, message.threadId,
+ quote.isOriginalMissing, glide)
mainContainer.addView(quoteView)
val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery)
ViewUtil.setPaddingTop(bodyTextView, 0)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt
index 8cbd904c29..d48d0b4d64 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt
@@ -4,15 +4,18 @@ import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.util.Log
-import android.view.*
+import android.view.LayoutInflater
+import android.view.View
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.view_voice_message.view.*
import network.loki.messenger.R
+import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.audio.AudioSlidePlayer
import org.thoughtcrime.securesms.components.CornerMask
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
+import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
@@ -21,6 +24,10 @@ import kotlin.math.roundToLong
class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
private val cornerMask by lazy { CornerMask(this) }
private var isPlaying = false
+ set(value) {
+ field = value
+ renderIcon()
+ }
private var progress = 0.0
private var duration = 0L
private var player: AudioSlidePlayer? = null
@@ -44,29 +51,36 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
// region Updating
fun bind(message: MmsMessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean) {
val audio = message.slideDeck.audioSlide!!
- val player = AudioSlidePlayer.createFor(context, audio, this)
- this.player = player
- isPreparing = true
- if (!audio.isPendingDownload && !audio.isInProgress) {
- player.play(0.0)
- }
voiceMessageViewLoader.isVisible = audio.isPendingDownload
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
cornerMask.setTopLeftRadius(cornerRadii[0])
cornerMask.setTopRightRadius(cornerRadii[1])
cornerMask.setBottomRightRadius(cornerRadii[2])
cornerMask.setBottomLeftRadius(cornerRadii[3])
+
+ // only process audio if downloaded
+ if (audio.isPendingDownload || audio.isInProgress) {
+ this.player = null
+ return
+ }
+
+ val player = AudioSlidePlayer.createFor(context, audio, this)
+ this.player = player
+
+ (audio.asAttachment() as? DatabaseAttachment)?.let { attachment ->
+ DatabaseFactory.getAttachmentDatabase(context).getAttachmentAudioExtras(attachment.attachmentId)?.let { audioExtras ->
+ if (audioExtras.durationMs > 0) {
+ duration = audioExtras.durationMs
+ voiceMessageViewDurationTextView.visibility = View.VISIBLE
+ voiceMessageViewDurationTextView.text = String.format("%01d:%02d",
+ TimeUnit.MILLISECONDS.toMinutes(audioExtras.durationMs),
+ TimeUnit.MILLISECONDS.toSeconds(audioExtras.durationMs))
+ }
+ }
+ }
}
- override fun onPlayerStart(player: AudioSlidePlayer) {
- if (!isPreparing) { return }
- isPreparing = false
- duration = player.duration
- voiceMessageViewDurationTextView.text = String.format("%01d:%02d",
- TimeUnit.MILLISECONDS.toMinutes(duration),
- TimeUnit.MILLISECONDS.toSeconds(duration))
- player.stop()
- }
+ override fun onPlayerStart(player: AudioSlidePlayer) {}
override fun onPlayerProgress(player: AudioSlidePlayer, progress: Double, unused: Long) {
if (progress == 1.0) {
@@ -88,20 +102,27 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
progressView.layoutParams = layoutParams
}
- override fun onPlayerStop(player: AudioSlidePlayer) { }
+ override fun onPlayerStop(player: AudioSlidePlayer) {
+ Log.d("Loki", "Player stopped")
+ isPlaying = false
+ }
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
cornerMask.mask(canvas)
}
+
+ private fun renderIcon() {
+ val iconID = if (isPlaying) R.drawable.exo_icon_pause else R.drawable.exo_icon_play
+ voiceMessagePlaybackImageView.setImageResource(iconID)
+ }
+
// endregion
// region Interaction
fun togglePlayback() {
val player = this.player ?: return
isPlaying = !isPlaying
- val iconID = if (isPlaying) R.drawable.exo_icon_pause else R.drawable.exo_icon_play
- voiceMessagePlaybackImageView.setImageResource(iconID)
if (isPlaying) {
player.play(progress)
} else {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java
index 4b20ee202e..2f85f27d52 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java
@@ -44,8 +44,8 @@ import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAt
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachmentAudioExtras;
import org.session.libsession.utilities.MediaTypes;
import org.session.libsession.utilities.Util;
-import org.session.libsignal.utilities.JsonUtil;
import org.session.libsignal.utilities.ExternalStorageUtil;
+import org.session.libsignal.utilities.JsonUtil;
import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
@@ -820,7 +820,7 @@ public class AttachmentDatabase extends Database {
* @return true if the update operation was successful.
*/
@Synchronized
- public boolean setAttachmentAudioExtras(@NonNull DatabaseAttachmentAudioExtras extras) {
+ public boolean setAttachmentAudioExtras(@NonNull DatabaseAttachmentAudioExtras extras, long threadId) {
ContentValues values = new ContentValues();
values.put(AUDIO_VISUAL_SAMPLES, extras.getVisualSamples());
values.put(AUDIO_DURATION, extras.getDurationMs());
@@ -830,9 +830,22 @@ public class AttachmentDatabase extends Database {
PART_ID_WHERE + " AND " + PART_AUDIO_ONLY_WHERE,
extras.getAttachmentId().toStrings());
+ if (threadId >= 0) {
+ notifyConversationListeners(threadId);
+ }
+
return alteredRows > 0;
}
+ /**
+ * Updates audio extra columns for the "audio/*" mime type attachments only.
+ * @return true if the update operation was successful.
+ */
+ @Synchronized
+ public boolean setAttachmentAudioExtras(@NonNull DatabaseAttachmentAudioExtras extras) {
+ return setAttachmentAudioExtras(extras, -1); // -1 for no update
+ }
+
@VisibleForTesting
class ThumbnailFetchCallable implements Callable {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java
index ce2ddba24a..87b59132d4 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -63,6 +63,7 @@ import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
+import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.Quote;
import org.thoughtcrime.securesms.mms.MmsException;
@@ -881,6 +882,20 @@ public class MmsDatabase extends MessagingDatabase {
}
}
+ public void deleteQuotedFromMessages(MessageRecord toDeleteRecord) {
+ String query = THREAD_ID + " = ?";
+ Cursor threadMmsCursor = rawQuery(query, new String[]{String.valueOf(toDeleteRecord.getThreadId())});
+ Reader reader = readerFor(threadMmsCursor);
+ MmsMessageRecord messageRecord;
+
+ while ((messageRecord = (MmsMessageRecord) reader.getNext()) != null) {
+ if (messageRecord.getQuote() != null && toDeleteRecord.getDateSent() == messageRecord.getQuote().getId()) {
+ setQuoteMissing(messageRecord.getId());
+ }
+ }
+ reader.close();
+ }
+
public boolean delete(long messageId) {
long threadId = getThreadIdForMessage(messageId);
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
@@ -889,6 +904,12 @@ public class MmsDatabase extends MessagingDatabase {
GroupReceiptDatabase groupReceiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
groupReceiptDatabase.deleteRowsForMessage(messageId);
+ MessageRecord toDelete;
+ try (Cursor messageCursor = getMessage(messageId)) {
+ toDelete = readerFor(messageCursor).getNext();
+ }
+
+ deleteQuotedFromMessages(toDelete);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false);
@@ -1066,6 +1087,14 @@ public class MmsDatabase extends MessagingDatabase {
return new OutgoingMessageReader(message, threadId);
}
+ public int setQuoteMissing(long messageId) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(QUOTE_MISSING, 1);
+ SQLiteDatabase database = databaseHelper.getWritableDatabase();
+ int rows = database.update(TABLE_NAME, contentValues, ID + " = ?", new String[]{ String.valueOf(messageId) });
+ return rows;
+ }
+
public static class Status {
public static final int DOWNLOAD_INITIALIZED = 1;
public static final int DOWNLOAD_NO_CONNECTIVITY = 2;
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java
index 6706f5fe77..f66479c41f 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java
@@ -514,6 +514,12 @@ public class SmsDatabase extends MessagingDatabase {
Log.i("MessageDatabase", "Deleting: " + messageId);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long threadId = getThreadIdForMessage(messageId);
+ try {
+ SmsMessageRecord toDelete = getMessage(messageId);
+ DatabaseFactory.getMmsDatabase(context).deleteQuotedFromMessages(toDelete);
+ } catch (NoSuchMessageException e) {
+ Log.e(TAG, "Couldn't find message record for messageId "+messageId, e);
+ }
db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
index 15b91048e2..7fe982a9e3 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
@@ -27,7 +27,6 @@ import org.session.libsignal.utilities.KeyHelper
import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
-import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase
@@ -190,7 +189,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun resumeMessageSendJobIfNeeded(messageSendJobID: String) {
val job = DatabaseFactory.getSessionJobDatabase(context).getMessageSendJob(messageSendJobID) ?: return
- JobQueue.shared.add(job)
+ JobQueue.shared.resumePendingSendMessage(job)
}
override fun isJobCanceled(job: Job): Boolean {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt
index df4f3ce614..9b40454a2b 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt
@@ -17,7 +17,7 @@ import nl.komponents.kovenant.ui.successUi
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.sending_receiving.groupSizeLimit
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
-import org.thoughtcrime.securesms.conversation.ConversationActivity
+
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.DistributionTypes
import org.thoughtcrime.securesms.database.DatabaseFactory
@@ -138,7 +138,6 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM
private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) {
val intent = Intent(context, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId)
- intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
context.startActivity(intent)
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt
index 7d429bf223..6eedd97013 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt
@@ -29,7 +29,7 @@ import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
-import org.thoughtcrime.securesms.conversation.ConversationActivity
+
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
@@ -114,11 +114,9 @@ class CreatePrivateChatActivity : PassphraseRequiredActionBarActivity(), ScanQRC
val recipient = Recipient.from(this, Address.fromSerialized(hexEncodedPublicKey), false)
val intent = Intent(this, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
- intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA))
intent.setDataAndType(getIntent().data, getIntent().type)
val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient)
intent.putExtra(ConversationActivityV2.THREAD_ID, existingThread)
- intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
startActivity(intent)
finish()
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
index 474d8036de..58eda3cc91 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt
@@ -37,7 +37,7 @@ import org.session.libsignal.utilities.toHexString
import org.session.libsignal.utilities.ThreadUtils
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
-import org.thoughtcrime.securesms.conversation.ConversationActivity
+
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt
index dc038cc0cb..4ce8bbde04 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt
@@ -32,7 +32,7 @@ import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
-import org.thoughtcrime.securesms.conversation.ConversationActivity
+
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
@@ -130,7 +130,6 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) {
val intent = Intent(context, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId)
- intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
context.startActivity(intent)
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt
index ec2a76e760..8cfa9d9e3e 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt
@@ -14,7 +14,6 @@ import androidx.fragment.app.FragmentPagerAdapter
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_link_device.*
-import kotlinx.android.synthetic.main.conversation_activity.*
import kotlinx.android.synthetic.main.fragment_recovery_phrase.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt
index 96d175ef99..2f5638d952 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt
@@ -14,7 +14,7 @@ import kotlinx.android.synthetic.main.activity_qr_code.*
import kotlinx.android.synthetic.main.fragment_view_my_qr_code.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
-import org.thoughtcrime.securesms.conversation.ConversationActivity
+
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.DistributionTypes
import org.thoughtcrime.securesms.database.DatabaseFactory
@@ -56,11 +56,9 @@ class QRCodeActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperF
val recipient = Recipient.from(this, Address.fromSerialized(hexEncodedPublicKey), false)
val intent = Intent(this, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
- intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA))
intent.setDataAndType(getIntent().data, getIntent().type)
val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient)
intent.putExtra(ConversationActivityV2.THREAD_ID, existingThread)
- intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
startActivity(intent)
finish()
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt
index ac342a707c..2078e15dc1 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt
@@ -27,14 +27,12 @@ import nl.komponents.kovenant.ui.alwaysUi
import nl.komponents.kovenant.ui.successUi
import org.session.libsession.avatars.AvatarHelper
import org.session.libsession.utilities.Address
+import org.session.libsession.utilities.ProfileKeyUtil
import org.session.libsession.utilities.ProfilePictureUtilities
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
import org.session.libsession.utilities.TextSecurePreferences
-import org.session.libsession.utilities.ProfileKeyUtil
-import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.avatar.AvatarSelection
-import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.dialogs.ChangeUiModeDialog
import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog
import org.thoughtcrime.securesms.loki.dialogs.SeedDialog
@@ -260,7 +258,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
intent.action = Intent.ACTION_SEND
intent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey)
intent.type = "text/plain"
- startActivity(intent)
+ val chooser = Intent.createChooser(intent, getString(R.string.share))
+ startActivity(chooser)
}
private fun showPrivacySettings() {
@@ -284,7 +283,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
val invitation = "Hey, I've been using Session to chat with complete privacy and security. Come join me! Download it at https://getsession.org/. My Session ID is $hexEncodedPublicKey!"
intent.putExtra(Intent.EXTRA_TEXT, invitation)
intent.type = "text/plain"
- startActivity(intent)
+ val chooser = Intent.createChooser(intent, getString(R.string.activity_settings_invite_button_title))
+ startActivity(chooser)
}
private fun helpTranslate() {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt
index 6c1a96d1de..fa92ba3d29 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt
@@ -9,10 +9,11 @@ import org.session.libsession.messaging.utilities.Data
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachmentAudioExtras
+import org.session.libsession.utilities.DecodedAudio
+import org.session.libsession.utilities.InputStreamMediaDataSource
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobs.BaseJob
-import org.thoughtcrime.securesms.loki.utilities.DecodedAudio
import org.thoughtcrime.securesms.mms.PartAuthority
import java.io.InputStream
import java.lang.IllegalStateException
@@ -133,35 +134,4 @@ class PrepareAttachmentAudioExtrasJob : BaseJob {
/** Gets dispatched once the audio extras have been updated. */
data class AudioExtrasUpdatedEvent(val attachmentId: AttachmentId)
-
- @RequiresApi(Build.VERSION_CODES.M)
- private class InputStreamMediaDataSource: MediaDataSource {
-
- private val data: ByteArray
-
- constructor(inputStream: InputStream): super() {
- this.data = inputStream.readBytes()
- }
-
- override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
- val length: Int = data.size
- if (position >= length) {
- return -1 // -1 indicates EOF
- }
- var actualSize = size
- if (position + size > length) {
- actualSize -= (position + size - length).toInt()
- }
- System.arraycopy(data, position.toInt(), buffer, offset, actualSize)
- return actualSize
- }
-
- override fun getSize(): Long {
- return data.size.toLong()
- }
-
- override fun close() {
- // We don't need to close the wrapped stream.
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt
index 86028521f1..df74bd9ca0 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt
@@ -14,7 +14,7 @@ import android.view.ViewConfiguration
import android.view.animation.DecelerateInterpolator
import androidx.core.math.MathUtils
import network.loki.messenger.R
-import org.thoughtcrime.securesms.loki.utilities.byteToNormalizedFloat
+import org.session.libsession.utilities.byteToNormalizedFloat
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java
index 0df65497c8..ce1ccb2bbe 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java
@@ -82,7 +82,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
private InputAwareLayout hud;
private View captionAndRail;
private ImageButton sendButton;
- private ComposeText composeText;
+ private ComposeText composeText;
private ViewGroup composeContainer;
private EmojiEditText captionText;
private EmojiToggle emojiToggle;
diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java
index a0c1046e07..c02663843e 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java
@@ -48,7 +48,7 @@ import org.session.libsignal.utilities.Util;
import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.contactshare.ContactUtil;
-import org.thoughtcrime.securesms.conversation.ConversationActivity;
+
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java
index c2041a84fa..991989e8da 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java
@@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.TaskStackBuilder;
-import org.thoughtcrime.securesms.conversation.ConversationActivity;
+
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.session.libsession.utilities.recipients.Recipient;
diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java
index 9330ec9e37..fe934e229f 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java
@@ -7,11 +7,10 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import org.thoughtcrime.securesms.conversation.ConversationActivity;
-import org.thoughtcrime.securesms.conversation.ConversationPopupActivity;
import org.session.libsignal.utilities.Log;
import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsession.utilities.recipients.Recipient.*;
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -167,9 +166,9 @@ public class NotificationState {
public PendingIntent getQuickReplyIntent(Context context, Recipient recipient) {
if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications! " + threads.size());
- Intent intent = new Intent(context, ConversationPopupActivity.class);
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)threads.toArray()[0]);
+ Intent intent = new Intent(context, ConversationActivityV2.class);
+ intent.putExtra(ConversationActivityV2.ADDRESS, recipient.getAddress());
+ intent.putExtra(ConversationActivityV2.THREAD_ID, (long)threads.toArray()[0]);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java
index e8dd7cd589..9bb71ad5cc 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java
@@ -11,7 +11,7 @@ import androidx.core.app.TaskStackBuilder;
import android.text.TextUtils;
import android.widget.Toast;
-import org.thoughtcrime.securesms.conversation.ConversationActivity;
+
import network.loki.messenger.R;
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
@@ -36,11 +36,6 @@ public class CommunicationActions {
Intent intent = new Intent(context, ConversationActivityV2.class);
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.getAddress());
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
- intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());
-
- if (!TextUtils.isEmpty(text)) {
- intent.putExtra(ConversationActivity.TEXT_EXTRA, text);
- }
if (backStack != null) {
backStack.addNextIntent(intent);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/Util.java b/app/src/main/java/org/thoughtcrime/securesms/util/Util.java
index 2384fe482a..41b18f0247 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/Util.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/Util.java
@@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.util;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.text.TextUtils;
diff --git a/app/src/main/res/drawable-hdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_add_white_24dp.png
deleted file mode 100644
index b48ba111a3..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_add_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_made_grey600_24dp.png
deleted file mode 100644
index 7d0807b0bf..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_call_made_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_missed_grey600_24dp.png
deleted file mode 100644
index 0241747c68..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_call_missed_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_received_grey600_24dp.png
deleted file mode 100644
index 5c9a88d126..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_call_received_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png
deleted file mode 100644
index 468ea5acd0..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_favorite_grey600_24dp.png
deleted file mode 100644
index 2bdad7d3f1..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_favorite_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_group_grey600_24dp.png
deleted file mode 100644
index 816fe2053f..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_group_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_menu_login.png b/app/src/main/res/drawable-hdpi/ic_menu_login.png
deleted file mode 100644
index 163f0aa854..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_menu_login.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png
deleted file mode 100644
index 38cd52d9d5..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_security_white_24dp.png
deleted file mode 100644
index 262800a4d8..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_security_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_timer_disabled.png b/app/src/main/res/drawable-hdpi/ic_timer_disabled.png
deleted file mode 100644
index 9e6127d54f..0000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_timer_disabled.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/quick_camera_dark.png b/app/src/main/res/drawable-hdpi/quick_camera_dark.png
deleted file mode 100644
index aa3a3e5717..0000000000
Binary files a/app/src/main/res/drawable-hdpi/quick_camera_dark.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_add_white_24dp.png
deleted file mode 100644
index eeba9f6696..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_add_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_made_grey600_24dp.png
deleted file mode 100644
index f275722ffe..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_call_made_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_missed_grey600_24dp.png
deleted file mode 100644
index 609ef52617..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_call_missed_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_received_grey600_24dp.png
deleted file mode 100644
index 685982d8e2..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_call_received_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png
deleted file mode 100644
index 6a93d69194..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_favorite_grey600_24dp.png
deleted file mode 100644
index 63f13a33b4..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_favorite_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_group_grey600_24dp.png
deleted file mode 100644
index 2c69b7cc14..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_group_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_menu_login.png b/app/src/main/res/drawable-mdpi/ic_menu_login.png
deleted file mode 100644
index 07dd6a510e..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_menu_login.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png
deleted file mode 100644
index 48e37b75c4..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_security_white_24dp.png
deleted file mode 100644
index 44ee7346e3..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_security_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_timer_disabled.png b/app/src/main/res/drawable-mdpi/ic_timer_disabled.png
deleted file mode 100644
index b2cd951a91..0000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_timer_disabled.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/quick_camera_dark.png b/app/src/main/res/drawable-mdpi/quick_camera_dark.png
deleted file mode 100644
index 4dbf6c77bc..0000000000
Binary files a/app/src/main/res/drawable-mdpi/quick_camera_dark.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png
deleted file mode 100644
index 67bb598e52..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_made_grey600_24dp.png
deleted file mode 100644
index 1e609bb5e6..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_call_made_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_missed_grey600_24dp.png
deleted file mode 100644
index fb5e2794f0..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_call_missed_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_received_grey600_24dp.png
deleted file mode 100644
index 91b5587a8a..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_call_received_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png
deleted file mode 100644
index 9868d19a42..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_favorite_grey600_24dp.png
deleted file mode 100644
index c4ad1cb303..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_favorite_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_group_grey600_24dp.png
deleted file mode 100644
index 1865da6942..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_group_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_menu_login.png b/app/src/main/res/drawable-xhdpi/ic_menu_login.png
deleted file mode 100644
index 17c2e5c39b..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_menu_login.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png
deleted file mode 100644
index 7e5c6ef194..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_security_white_24dp.png
deleted file mode 100644
index 7e306c303c..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_security_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_timer_disabled.png b/app/src/main/res/drawable-xhdpi/ic_timer_disabled.png
deleted file mode 100644
index 45165abecc..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_timer_disabled.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/quick_camera_dark.png b/app/src/main/res/drawable-xhdpi/quick_camera_dark.png
deleted file mode 100644
index 753805275b..0000000000
Binary files a/app/src/main/res/drawable-xhdpi/quick_camera_dark.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png
deleted file mode 100644
index 0fdced8fce..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_made_grey600_24dp.png
deleted file mode 100644
index 66a9ff46e8..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_call_made_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_missed_grey600_24dp.png
deleted file mode 100644
index 84c13861cb..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_call_missed_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_received_grey600_24dp.png
deleted file mode 100644
index f4be04c671..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_call_received_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png
deleted file mode 100644
index 2a7c32de61..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png
deleted file mode 100644
index 11f108dad1..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_group_grey600_24dp.png
deleted file mode 100644
index 2aa030e4cf..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_group_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png
deleted file mode 100644
index 72128fe690..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_security_white_24dp.png
deleted file mode 100644
index 7bcb2fd013..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_security_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_timer_disabled.png b/app/src/main/res/drawable-xxhdpi/ic_timer_disabled.png
deleted file mode 100644
index fabf9ffdba..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_timer_disabled.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/quick_camera_dark.png b/app/src/main/res/drawable-xxhdpi/quick_camera_dark.png
deleted file mode 100644
index c1a3549bfc..0000000000
Binary files a/app/src/main/res/drawable-xxhdpi/quick_camera_dark.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png
deleted file mode 100644
index d601ca823c..0000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png
deleted file mode 100644
index e8ee60e7e6..0000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_security_white_24dp.png
deleted file mode 100644
index b1eddbd6c3..0000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_security_white_24dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_timer_disabled.png b/app/src/main/res/drawable-xxxhdpi/ic_timer_disabled.png
deleted file mode 100644
index 6301f7bada..0000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_timer_disabled.png and /dev/null differ
diff --git a/app/src/main/res/drawable/compose_divider_background.xml b/app/src/main/res/drawable/compose_divider_background.xml
deleted file mode 100644
index 0046fe4e43..0000000000
--- a/app/src/main/res/drawable/compose_divider_background.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/drawable/contact_photo_background.xml b/app/src/main/res/drawable/contact_photo_background.xml
deleted file mode 100644
index ff1153bf2a..0000000000
--- a/app/src/main/res/drawable/contact_photo_background.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/conversation_back_button_background.xml b/app/src/main/res/drawable/conversation_back_button_background.xml
deleted file mode 100644
index f259e83e74..0000000000
--- a/app/src/main/res/drawable/conversation_back_button_background.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/conversation_home_touch_highlight.xml b/app/src/main/res/drawable/conversation_home_touch_highlight.xml
deleted file mode 100644
index 5fbf5ba7d0..0000000000
--- a/app/src/main/res/drawable/conversation_home_touch_highlight.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/conversation_item_background.xml b/app/src/main/res/drawable/conversation_item_background.xml
deleted file mode 100644
index dfae25e4f8..0000000000
--- a/app/src/main/res/drawable/conversation_item_background.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/conversation_item_background_animated.xml b/app/src/main/res/drawable/conversation_item_background_animated.xml
deleted file mode 100644
index 4206e6a57f..0000000000
--- a/app/src/main/res/drawable/conversation_item_background_animated.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/error_round.xml b/app/src/main/res/drawable/error_round.xml
deleted file mode 100644
index 56cc75291a..0000000000
--- a/app/src/main/res/drawable/error_round.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_compact_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_compact_24.xml
deleted file mode 100644
index f632160152..0000000000
--- a/app/src/main/res/drawable/ic_baseline_arrow_back_compact_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_baseline_key_24.xml b/app/src/main/res/drawable/ic_baseline_key_24.xml
deleted file mode 100644
index 2316df207c..0000000000
--- a/app/src/main/res/drawable/ic_baseline_key_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_baseline_refresh_24.xml b/app/src/main/res/drawable/ic_baseline_refresh_24.xml
deleted file mode 100644
index f2be45bab5..0000000000
--- a/app/src/main/res/drawable/ic_baseline_refresh_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_circle_plus.xml b/app/src/main/res/drawable/ic_circle_plus.xml
deleted file mode 100644
index 5e243e5296..0000000000
--- a/app/src/main/res/drawable/ic_circle_plus.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_shield.xml b/app/src/main/res/drawable/ic_shield.xml
deleted file mode 100644
index 67a9845daa..0000000000
--- a/app/src/main/res/drawable/ic_shield.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/info_round.xml b/app/src/main/res/drawable/info_round.xml
deleted file mode 100644
index 668eaf3bee..0000000000
--- a/app/src/main/res/drawable/info_round.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/message_bubble_background.xml b/app/src/main/res/drawable/message_bubble_background.xml
deleted file mode 100644
index 9c4347ae6e..0000000000
--- a/app/src/main/res/drawable/message_bubble_background.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/profile_picture_view_large_foreground.xml b/app/src/main/res/drawable/profile_picture_view_large_foreground.xml
deleted file mode 100644
index e73b68d168..0000000000
--- a/app/src/main/res/drawable/profile_picture_view_large_foreground.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/profile_picture_view_medium_foreground.xml b/app/src/main/res/drawable/profile_picture_view_medium_foreground.xml
deleted file mode 100644
index 808be8d0e4..0000000000
--- a/app/src/main/res/drawable/profile_picture_view_medium_foreground.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/profile_picture_view_rss_medium_background.xml b/app/src/main/res/drawable/profile_picture_view_rss_medium_background.xml
deleted file mode 100644
index a12f21fce5..0000000000
--- a/app/src/main/res/drawable/profile_picture_view_rss_medium_background.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/profile_picture_view_small_foreground.xml b/app/src/main/res/drawable/profile_picture_view_small_foreground.xml
deleted file mode 100644
index 6a46584bf8..0000000000
--- a/app/src/main/res/drawable/profile_picture_view_small_foreground.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/prominent_dialog_button_background.xml b/app/src/main/res/drawable/prominent_dialog_button_background.xml
deleted file mode 100644
index de3409d482..0000000000
--- a/app/src/main/res/drawable/prominent_dialog_button_background.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/scroll_to_bottom_button_background.xml b/app/src/main/res/drawable/scroll_to_bottom_button_background.xml
deleted file mode 100644
index 387c75be90..0000000000
--- a/app/src/main/res/drawable/scroll_to_bottom_button_background.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/session_logo_white.xml b/app/src/main/res/drawable/session_logo_white.xml
deleted file mode 100644
index 9927e8822f..0000000000
--- a/app/src/main/res/drawable/session_logo_white.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_backup_restore.xml b/app/src/main/res/layout/activity_backup_restore.xml
deleted file mode 100644
index af86967c77..0000000000
--- a/app/src/main/res/layout/activity_backup_restore.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_activity.xml b/app/src/main/res/layout/conversation_activity.xml
deleted file mode 100644
index 814d7d6943..0000000000
--- a/app/src/main/res/layout/conversation_activity.xml
+++ /dev/null
@@ -1,235 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/conversation_activity_attachment_editor_stub.xml b/app/src/main/res/layout/conversation_activity_attachment_editor_stub.xml
deleted file mode 100644
index 389f1e2348..0000000000
--- a/app/src/main/res/layout/conversation_activity_attachment_editor_stub.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/conversation_activity_emojidrawer_stub.xml b/app/src/main/res/layout/conversation_activity_emojidrawer_stub.xml
deleted file mode 100644
index c52ae1d333..0000000000
--- a/app/src/main/res/layout/conversation_activity_emojidrawer_stub.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_fragment.xml b/app/src/main/res/layout/conversation_fragment.xml
deleted file mode 100644
index ed9b8f5c86..0000000000
--- a/app/src/main/res/layout/conversation_fragment.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/conversation_input_panel.xml b/app/src/main/res/layout/conversation_input_panel.xml
deleted file mode 100644
index e628de7561..0000000000
--- a/app/src/main/res/layout/conversation_input_panel.xml
+++ /dev/null
@@ -1,208 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_item_header.xml b/app/src/main/res/layout/conversation_item_header.xml
deleted file mode 100644
index 588ddd64ef..0000000000
--- a/app/src/main/res/layout/conversation_item_header.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/conversation_item_last_seen.xml b/app/src/main/res/layout/conversation_item_last_seen.xml
deleted file mode 100644
index ced6a1beed..0000000000
--- a/app/src/main/res/layout/conversation_item_last_seen.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_item_received.xml b/app/src/main/res/layout/conversation_item_received.xml
deleted file mode 100644
index ade3ea2c78..0000000000
--- a/app/src/main/res/layout/conversation_item_received.xml
+++ /dev/null
@@ -1,221 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_item_received_audio.xml b/app/src/main/res/layout/conversation_item_received_audio.xml
deleted file mode 100644
index 728135ea5f..0000000000
--- a/app/src/main/res/layout/conversation_item_received_audio.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_received_document.xml b/app/src/main/res/layout/conversation_item_received_document.xml
deleted file mode 100644
index ae72a23f0a..0000000000
--- a/app/src/main/res/layout/conversation_item_received_document.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_received_link_preview.xml b/app/src/main/res/layout/conversation_item_received_link_preview.xml
deleted file mode 100644
index 3c49d6a421..0000000000
--- a/app/src/main/res/layout/conversation_item_received_link_preview.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_received_open_group_invitation.xml b/app/src/main/res/layout/conversation_item_received_open_group_invitation.xml
deleted file mode 100644
index 4ef955f1b2..0000000000
--- a/app/src/main/res/layout/conversation_item_received_open_group_invitation.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_item_received_sticker.xml b/app/src/main/res/layout/conversation_item_received_sticker.xml
deleted file mode 100644
index b162cece3b..0000000000
--- a/app/src/main/res/layout/conversation_item_received_sticker.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_received_thumbnail.xml b/app/src/main/res/layout/conversation_item_received_thumbnail.xml
deleted file mode 100644
index 3d93f459af..0000000000
--- a/app/src/main/res/layout/conversation_item_received_thumbnail.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_sent.xml b/app/src/main/res/layout/conversation_item_sent.xml
deleted file mode 100644
index b6a7ff291a..0000000000
--- a/app/src/main/res/layout/conversation_item_sent.xml
+++ /dev/null
@@ -1,176 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_item_sent_audio.xml b/app/src/main/res/layout/conversation_item_sent_audio.xml
deleted file mode 100644
index cb7889fa1f..0000000000
--- a/app/src/main/res/layout/conversation_item_sent_audio.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_sent_document.xml b/app/src/main/res/layout/conversation_item_sent_document.xml
deleted file mode 100644
index 2145cfdbf2..0000000000
--- a/app/src/main/res/layout/conversation_item_sent_document.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_sent_link_preview.xml b/app/src/main/res/layout/conversation_item_sent_link_preview.xml
deleted file mode 100644
index 3c49d6a421..0000000000
--- a/app/src/main/res/layout/conversation_item_sent_link_preview.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_sent_open_group_invitation.xml b/app/src/main/res/layout/conversation_item_sent_open_group_invitation.xml
deleted file mode 100644
index 4ef955f1b2..0000000000
--- a/app/src/main/res/layout/conversation_item_sent_open_group_invitation.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_item_sent_sticker.xml b/app/src/main/res/layout/conversation_item_sent_sticker.xml
deleted file mode 100644
index b162cece3b..0000000000
--- a/app/src/main/res/layout/conversation_item_sent_sticker.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_sent_thumbnail.xml b/app/src/main/res/layout/conversation_item_sent_thumbnail.xml
deleted file mode 100644
index fc7af1b517..0000000000
--- a/app/src/main/res/layout/conversation_item_sent_thumbnail.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/conversation_item_update.xml b/app/src/main/res/layout/conversation_item_update.xml
deleted file mode 100644
index 69a49226e3..0000000000
--- a/app/src/main/res/layout/conversation_item_update.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/conversation_search_nav.xml b/app/src/main/res/layout/conversation_search_nav.xml
deleted file mode 100644
index d0f4fb99ce..0000000000
--- a/app/src/main/res/layout/conversation_search_nav.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conversation_typing_view.xml b/app/src/main/res/layout/conversation_typing_view.xml
deleted file mode 100644
index e3ba2a3d92..0000000000
--- a/app/src/main/res/layout/conversation_typing_view.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/load_more_header.xml b/app/src/main/res/layout/load_more_header.xml
deleted file mode 100644
index 7b591b3d48..0000000000
--- a/app/src/main/res/layout/load_more_header.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/longmessage_bubble_received.xml b/app/src/main/res/layout/longmessage_bubble_received.xml
deleted file mode 100644
index f13704218b..0000000000
--- a/app/src/main/res/layout/longmessage_bubble_received.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/longmessage_bubble_sent.xml b/app/src/main/res/layout/longmessage_bubble_sent.xml
deleted file mode 100644
index 4b28d5ef78..0000000000
--- a/app/src/main/res/layout/longmessage_bubble_sent.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/message_details_activity.xml b/app/src/main/res/layout/message_details_activity.xml
deleted file mode 100644
index 88cfb1061f..0000000000
--- a/app/src/main/res/layout/message_details_activity.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/message_details_header.xml b/app/src/main/res/layout/message_details_header.xml
deleted file mode 100644
index dbd467499b..0000000000
--- a/app/src/main/res/layout/message_details_header.xml
+++ /dev/null
@@ -1,162 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/microphone_recorder_view.xml b/app/src/main/res/layout/microphone_recorder_view.xml
deleted file mode 100644
index e02cbc4c89..0000000000
--- a/app/src/main/res/layout/microphone_recorder_view.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/recording_layout.xml b/app/src/main/res/layout/recording_layout.xml
deleted file mode 100644
index 92e8077983..0000000000
--- a/app/src/main/res/layout/recording_layout.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/view_input_bar_button.xml b/app/src/main/res/layout/view_input_bar_button.xml
deleted file mode 100644
index 096c616ab6..0000000000
--- a/app/src/main/res/layout/view_input_bar_button.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_mention_candidate_selection.xml b/app/src/main/res/layout/view_mention_candidate_selection.xml
deleted file mode 100644
index 00c1439ed4..0000000000
--- a/app/src/main/res/layout/view_mention_candidate_selection.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation.xml b/app/src/main/res/menu/conversation.xml
deleted file mode 100644
index e1f59e00bb..0000000000
--- a/app/src/main/res/menu/conversation.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
diff --git a/app/src/main/res/menu/conversation_block.xml b/app/src/main/res/menu/conversation_block.xml
deleted file mode 100644
index fb4184d59a..0000000000
--- a/app/src/main/res/menu/conversation_block.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation_context.xml b/app/src/main/res/menu/conversation_context.xml
deleted file mode 100644
index 1204fedb31..0000000000
--- a/app/src/main/res/menu/conversation_context.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
diff --git a/app/src/main/res/menu/conversation_copy_session_id.xml b/app/src/main/res/menu/conversation_copy_session_id.xml
deleted file mode 100644
index e74d0b4c24..0000000000
--- a/app/src/main/res/menu/conversation_copy_session_id.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation_expiring_off.xml b/app/src/main/res/menu/conversation_expiring_off.xml
deleted file mode 100644
index 7b0ad285c6..0000000000
--- a/app/src/main/res/menu/conversation_expiring_off.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation_expiring_on.xml b/app/src/main/res/menu/conversation_expiring_on.xml
deleted file mode 100644
index 5dbf88b001..0000000000
--- a/app/src/main/res/menu/conversation_expiring_on.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation_invite_open_group.xml b/app/src/main/res/menu/conversation_invite_open_group.xml
deleted file mode 100644
index 0cef563b7c..0000000000
--- a/app/src/main/res/menu/conversation_invite_open_group.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation_mms_group_options.xml b/app/src/main/res/menu/conversation_mms_group_options.xml
deleted file mode 100644
index 49c10c3e7d..0000000000
--- a/app/src/main/res/menu/conversation_mms_group_options.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/app/src/main/res/menu/conversation_muted.xml b/app/src/main/res/menu/conversation_muted.xml
deleted file mode 100644
index 3b429c04db..0000000000
--- a/app/src/main/res/menu/conversation_muted.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation_popup.xml b/app/src/main/res/menu/conversation_popup.xml
deleted file mode 100644
index f35e1f1ae3..0000000000
--- a/app/src/main/res/menu/conversation_popup.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/conversation_push_group_options.xml b/app/src/main/res/menu/conversation_push_group_options.xml
deleted file mode 100644
index 2a7f2713da..0000000000
--- a/app/src/main/res/menu/conversation_push_group_options.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/conversation_unblock.xml b/app/src/main/res/menu/conversation_unblock.xml
deleted file mode 100644
index 6cf3bcbb27..0000000000
--- a/app/src/main/res/menu/conversation_unblock.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/conversation_unmuted.xml b/app/src/main/res/menu/conversation_unmuted.xml
deleted file mode 100644
index 8474edc5cf..0000000000
--- a/app/src/main/res/menu/conversation_unmuted.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-af/strings.xml
+++ b/app/src/main/res/values-af/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 5d7a75111c..5f7929993a 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -62,7 +62,7 @@
فشل الإرسال، انقر للرد الغير آمنلم يتم العثور على تطبيق قادر على فتح الملف.تم نسخ %s
- إقرأ المزيد
+ Read More تنزيل المزيد في الإنتظار
@@ -308,7 +308,7 @@
أنتغير مدعوم.مسودة:
- يحتاج Session إلى إذن سعة التخزين من أجل استخدام الذاكرة الخارجية ولكن تم إيقاف الإذن على نحو دائم، رجاء زيارة إعدادات التطبيق ثم \"الأذونات\"، ثم تفعيل \"سعة التخزين\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".لا يمكن الحفظ إلى الذاكرة الخارجية بدون أذوناتأَتريد حذف الرسالة ؟سوف يقوم هذا بحذف الرسالة كُليًا.
@@ -655,12 +655,14 @@
عقدة الخدمةالوجهةلمعرفة المزيد
+ Resolving…جلسة جديدةأدخِل معرّف الجلسةفحص كود QRإمسح كود QR المستخدم لبدء جلسة معه. يمكن الحصول على اكواد الـQR بالضغط على ايقونة كود الـQR في إعدادات الحساب.
- ادخِل معرّف الجلسة للمستخدم
+ Enter Session ID or ONS nameيمكن للمستخدمين مشاركة معرّف سيشن الخاص بهم بالذهاب إلى إعدادات حسابهم و الضغط على \"مشاركة معرف سيشن\"، او عبر مشاركة كود QR الخاص بهم.
+ Please check the Session ID or ONS name and try again.تحتاج الجلسة إلى الوصول إلى الكاميرا لمسح رموز QRمنح صلاحية الكاميرامجموعة مغلقة جديدة
@@ -695,6 +697,8 @@
الخصوصيةChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -740,6 +744,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -752,4 +759,20 @@
انقر للفتحEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index 0abee2dc8e..844ec229ee 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -1,14 +1,14 @@
- Session
+ SesiyaBəliXeyrSil
- Ban
+ qadağaGözləyin...Yaddaşa yazNəzərində Saxla
- Version %s
+ Versiya %sYeni mesaj
@@ -52,34 +52,34 @@
Açar mübadiləsi mesajı almısan, yerinə yetirmək üçün kliklə.%1$s qrupdan çıxdı.Göndərə bilmədik, zəmanətsiz alternativ variant üçün kliklə
- Bu medianı aça bilən aplikasiya tapa bilmirəm.
- Köçürülən %s
- Daha Ətraflı
- Daha Çox Yüklə
- Gözlənilən
+ Bu medianı açmaq üçün tətbiq tapıla bilmədi.
+ %s kopyalandı
+ Daha çox oxu
+ Daha çox endir
+ Gözlənilir
- Fayl əlavə et
+ Qoşma əlavə etƏlaqə məlumatını seç
- Üzr istəyirik, faylın yüklənməsində xəta baş verdi.
- Message
- Compose
- Muted until %1$s
- %1$d members
- Community Guidelines
- Səhv qəbul edən!
- Baş səhifəyə əlavə edildi
- Qrupdan çıxırsan?
- Bu qrupdan çıxmaq istədiyinə əminsən?
- Qrupdan çıxarkən xəta
- Bi əlaqənin kilidini açırsan?
+ Təəssüf ki, qoşmanız tənzimlənərkən xəta baş verdi.
+ Mesaj
+ Yarat
+ %1$s qədər səssizdə
+ %1$d üzv
+ Cəmiyyət Təlimatları
+ Etibarsız alıcı!
+ Əsas ekrana əlavə edildi
+ Qrupu tərk edirsiniz?
+ Bu qrupu tərk etmək istədiyinizə əminsiniz?
+ Qrupu tərk etmə xətası
+ Bu əlaqə əngəldən çıxarılsın?Bu əlaqədən yenə mesaj və zəng qəbul edəcəksən.Kilidi açGöndərdiyin mesajın növünə görə faylın ölçüsü limiti aşır.Səs yaza bilmirik!Cihazında bu linkin öhdəsindən gələ biləcək aplikasiya yoxdur.
- Add members
- Join %s
- Are you sure you want to join the %s open group?
+ Üzv əlavə et
+ %s qrupuna qoşul
+ %s açıq qrupuna qoşulmaq istədiyinizə əminsiniz?Səsli mesaj göndərə bilmək üçün Session-a icazə ver mikrofonunu istifadə edə bilsin.Səsli mesaj göndərə bilməsi üçün Session-ın Mikrofon seçimlərinə giriş icazəsi olmalıdır ki, o da hal hazırda qadağa edilmişdir. Davam edə bilmək üçün zəhmət olmasa aplikasiya parametrləri menyusuna, oradan isə \"İcazələr bölməsinə keçərək \"Mikrofon\" hissəsini aktivləşdir.Şəkil və video çəkə bilməsi üçün Session-a Kamera seçimlərinə giriş icazəsi ver.
@@ -102,7 +102,7 @@
Bu addım seçilmiş mesajı həmişəlik siləcəkdir.Bu addım seçilmiş %1$d mesajı həmişəlik siləcəkdir.
- Ban this user?
+ İstifadəçiyə qadağa qoyulsun?Yaddaşa yazırsan?Yaddaşa yazılacaq bu media fayla cihazındakı digər aplikasiyaların da giriş icazəsi olacaq.\n\nDavam edək?
@@ -126,8 +126,8 @@
SMSSilinirMesajların silinməsi...
- Banning
- Banning user…
+ Qadağa qoyulur
+ İstifadəçiyə qadağa qoyulur…Mesajın əsli tapılmadıMesajın əsli artıq mövcud deyil
@@ -236,14 +236,14 @@
%s artıq Session-dadır!Yox olan mesajlar deaktiv edildiYox olan mesajlar üçün zaman intervalı %s tıəyin edildi
- %s took a screenshot.
- Media saved by %s.
+ %s ekran şəkli çəkdi.
+ %s medianı yaddaşa yazdı.Təhlükəsizlik rəqəmi dəyişdirildi%s ilə olan təhlükəsizlik rəqəmin dəyişilmişdir.Sən təsdiqlənmiş kimi işarələnmisənSən təsdiqlənməmiş kimi işarələnmisən.
- This conversation is empty
- Open group invitation
+ Bu danışıq boşdur
+ Açıq qrup dəvətiSession yenilənməsiSession-ın yeni versiyası hazırdır, yeniləmək üçün kliklə
@@ -263,7 +263,7 @@
SənDəstəklənməyən media növüQaralama
- Xarici yaddaşa məlumat yaza bilməsi üçün Session-ın Yaddaş seçimlərinə giriş icazəsi olmalıdır ki, o da hal hazırda qadağa edilmişdir. Davam edə bilmək üçün zəhmət olmasa aplikasiya parametrləri menyusuna, oradan isə \"İcazələr bölməsinə keçərək \"Yaddaş\" hissəsini aktivləşdir.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".İcazəsiz xarici yaddaşa məlumat yaza bilmərikMesajı silirsən?Bu addım mesajı həmişəlik siləcək.
@@ -302,7 +302,7 @@
Xətalı qısa yol
- Session
+ SeansYeni ismarış
@@ -356,11 +356,11 @@
PauzaYüklə
- Join
- Open group invitation
- Pinned message
- Community guidelines
- Read
+ Qoşul
+ Açıq qrup dəvəti
+ Sancaqlanmış mesaj
+ Cəmiyyət təlimatları
+ OxuSəsVideo
@@ -429,7 +429,7 @@
Daxil et düyməsi göndərirDaxil et düymasını kliklədikdə mesaj göndəriləcəkLink ilkin baxışlarını göndər
- Previews are supported for Imgur, Instagram, Pinterest, Reddit, and YouTube links
+ Önbaxışlar, Imgur, Instagram, Pinterest, Reddit və YouTube bağlantıları üçün dəstəklənirEkran təhlükəsizliyiSon daxil olanlar siyahısında və aplikasiyanın içində ekranın şəkil kimi yadda saxlanmasını bloklaXəbərdarlıqlar
@@ -490,7 +490,7 @@
Mesajın detallarıMətni köçürMesajı sil
- Ban user
+ İstifadəçiyə qadağa qoyMesajı yenidən göndərMesajı cavabla
@@ -550,20 +550,20 @@
Ekran kilidinin qeyri aktivliyinin zamanı bitdiHeç biri
- Copy public key
+ İctimai açarı kopyala
- Continue
- Copy
- Invalid URL
- Copied to clipboard
- Next
- Share
- Invalid Session ID
- Cancel
- Your Session ID
- Your Session begins here...
- Create Session ID
- Continue Your Session
+ Davam
+ Kopyala
+ Etibarsız URL
+ Lövhəyə kopyalandı
+ Növbəti
+ Paylaş
+ Etibarsız Seans Kimliyi
+ İmtina
+ Seans Kimliyiniz
+ Seansınız burada başlayır...
+ Seans Kimliyi Yarat
+ Seansa Davam EdinWhat\'s Session?It\'s a decentralized, encrypted messaging appSo it doesn\'t collect my personal information or my conversation metadata? How does it work?
@@ -602,65 +602,69 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed GroupEnter a group name
- You don\'t have any contacts yet
- Start a Session
- Please enter a group name
- Please enter a shorter group name
- Please pick at least 1 group member
- A closed group cannot have more than 100 members
- Join Open Group
- Couldn\'t join group
- Open Group URL
- Scan QR Code
+ Hələ heç bir əlaqəniz yoxdur
+ Bir Seans Başladın
+ Zəhmət olmasa bir qrup adı daxil edin
+ Qısa qrup adı daxil edin
+ Ən azı 1 qrup üzvü seçin
+ Bağlı qrupda 100-dən çox üzv ola bilməz
+ Açıq Qrupa Qoşul
+ Qrupa qoşulmaq olmur
+ Açıq Qrup URL-si
+ QR Kodu Skan EdinScan the QR code of the open group you\'d like to joinEnter an open group URL
- Settings
- Enter a display name
- Please pick a display name
- Please pick a shorter display name
- Privacy
- Notifications
- Chats
- Devices
- Invite
- Recovery Phrase
- Clear Data
- Help us Translate Session
- Notifications
- Notification Style
- Notification Content
- Privacy
- Chats
- Notification Strategy
- Change name
- Unlink device
- Your Recovery Phrase
+ Tənzimləmələr
+ Ekran adı daxil edin
+ Bir ekran adı seçin
+ Qısa bir ekran adı seçin
+ Gizlilik
+ Bildirişlər
+ Söhbətlər
+ Cihazlar
+ Dəvət et
+ Fazanı bərpa edin
+ Verilənləri təmizləyin
+ Tərcüməyə kömək edin
+ Bildirişlər
+ Bildiriş stili
+ Bildiriş məzmunu
+ Gizlilik
+ Söhbətlər
+ Bildiriş Strategiyası
+ Sürətli rejim istifadə et
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.
+ Adı dəyişdir
+ Cihazı ayır
+ Bərpa fazanızThis is your recovery phrase. With it, you can restore or migrate your Session ID to a new device.
- Clear All Data
+ Bütün verilənləri təmizləThis will permanently delete your messages, sessions, and contacts.
- QR Code
- View My QR Code
- Scan QR Code
+ QR Kodu
+ QR Koduma bax
+ QR kodu skan etScan someone\'s QR code to start a conversation with them
- Scan Me
+ Məni skan etThis is your QR code. Other users can scan it to start a session with you.
- Share QR Code
- Contacts
- Closed Groups
- Open Groups
- You don\'t have any contacts yet
+ QR kodu paylaş
+ Əlaqələr
+ Bağlı qruplar
+ Açıq qruplar
+ Hələ heç bir əlaqəniz yoxdur
- Apply
+ Tətbiq etDoneEdit GroupEnter a new group name
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-bal/strings.xml b/app/src/main/res/values-bal/strings.xml
index 4c07665cc9..7deaf2acca 100644
--- a/app/src/main/res/values-bal/strings.xml
+++ b/app/src/main/res/values-bal/strings.xml
@@ -54,7 +54,7 @@
Gagal mengirim, ketuk untuk mengirim kembaliTidak bisa menemukan aplikasi untuk membuka media ini.Disalin %s
- Lanjut Baca
+ Read MoreUnduh Lebih BanyakTertunda
@@ -261,7 +261,7 @@ memproses pertukaran kunci korupsi.AndaTipe media tidak didukungDraf
- Session memerlukan izin akses Penyimpanan agar bisa menyimpan di penyimpanan luar, tetapi ditolak secara permanen. Silakan lanjut ke pengaturan aplikasi, pilih \"Izin\" dan aktifkan \"Penyimpanan\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Tidak bisa menyimpan ke penyimpanan luar tanpa izinHapus pesan?Pesan ini akan dihapus secara permanen.
@@ -600,12 +600,14 @@ memproses pertukaran kunci korupsi.Simpul layananTujuanPelajari lebih lanjut
+ Resolving…Session baruMasukkan Session IDPindai kode QRPindai kode QR pengguna lain untuk memulai Session. Kode QR bisa ditemukan dengan mengetukan gambar kode QR di pengaturan akun
- Masukkan Session ID penerima
+ Enter Session ID or ONS namePengguna bisa membagikan Session ID miliknya dengan masuk ke pengaturan akun dan mengetuk \"Bagikan Session ID\" atau dengan membagikan kode QR mereka
+ Please check the Session ID or ONS name and try again.Session membutuhkan akses kamera untuk memindai kode QRBerikan akses kameraGrup tertutup baru
@@ -640,6 +642,8 @@ memproses pertukaran kunci korupsi.PrivasiPercakapanStrategi notofikasi
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Ubah namaPutuskan koneksi dengan perangkatKata pemulihan anda
@@ -685,6 +689,9 @@ memproses pertukaran kunci korupsi.30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ memproses pertukaran kunci korupsi.Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index fe0e89e49a..c35708fba7 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -1,16 +1,16 @@
- Session
- Yes
- No
- Delete
- Ban
- Please wait...
- Save
+ Сэсія
+ Так
+ Не
+ Выдаліць
+ Заблакаваць
+ Пачакайце, калі ласка...
+ ЗахавацьNote to SelfVersion %s
- New message
+ Новыя паведамленні\+%d
@@ -287,7 +287,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -630,12 +630,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -670,6 +672,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -715,6 +719,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -727,4 +734,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index de816a2c83..469cac9fad 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -54,7 +54,7 @@
Неуспешно изпращане, натиснете за изпращане по несигурен начинНеуспешно откриване на приложение за отваряне на този файл.%s e копирано
- Прочети
+ Read MoreИзтегли Повече в Очакване
@@ -261,7 +261,7 @@
ТиНеподдържан медиен формат.Чернова
- Session се нуждае от достъп до вградения диск, за да може да запазва на него, но той му е отказан. Моля, отидете в настройки в менюто и изберете \"Разрешения\" и \"Дискове\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Неуспешно запазване на външен диск без нуждното разрешение за достъпИзтриване на съобщението?Това ще изтрие невъзвращаемо текущото съобщение.
@@ -600,12 +600,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -640,6 +642,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -685,6 +689,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index dca7d0c440..f0f3618c7d 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -1,6 +1,6 @@
- Sessió
+ SessionSíNoSuprimeix
@@ -54,7 +54,7 @@
Ha fallat l\'enviament. Toqueu per al mode no segur.No s\'ha trobat una aplicació que pugui obrir aquest fitxer.S\'ha copiat %s
- Més informació
+ Read More Baixa\'n més Pendent
@@ -261,7 +261,7 @@ d\'intercanvi de claus!JoTipus de fitxer no compatibleEsborrany
- El Session necessita el permís de l\'emmagatzematge per tal de desar en un emmagatzematge extern, però s\'ha denegat permanentment. Si us plau, continueu cap al menú de configuració de l\'aplicació, seleccioneu Permisos i habiliteu-hi l\'emmagatzematge.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".No es pot desar en un emmagatzematge extern sense permís.Voleu suprimir el missatge?Aquest missatge se suprimirà permanentment.
@@ -600,12 +600,14 @@ d\'intercanvi de claus!Node de serveiDestinacióAprèn-ne més
+ Resolving…Nova sessió
- Introdueix la teva ID de Session
+ Introdueix el teu ID de SessionEscaneja el codi QREscaneja el codi QR d’un usuari per a iniciar una sessió. Es poden trobar codis QR tocant la icona de codi QR a la configuració del compte.
- Introdueix la ID de Session del destinatari
- Els usuaris poden compartir el seu ID de Session accedint a la configuració del compte i tocant \'Comparteix la ID de Session\' o compartint el seu codi QR.
+ Enter Session ID or ONS name
+ Els usuaris poden compartir el seu ID de Session accedint a la configuració del compte i tocant \'Comparteix l\'ID de Session\' o compartint el seu codi QR.
+ Please check the Session ID or ONS name and try again.Session necessita accés a la càmera per escanejar codis QRPermet accés a la càmeraNou grup tancat
@@ -640,10 +642,12 @@ d\'intercanvi de claus!
PrivadesaXatsEstratègia de les notificacions
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Canvia el nomDesenllaça el dispositiuLa teva frase de recuperació
- Aquesta és la teva frase de recuperació. Pots restaurar-ne o migrar-ne la teva ID de Session cap a un nou dispositiu.
+ Aquesta és la teva frase de recuperació. Pots restaurar-ne o migrar-ne el teu ID de Session cap a un nou dispositiu.Esborra totes les dadesAixò esborrarà tots els missatges, sessions i contactes permanentment.Codi QR
@@ -674,7 +678,7 @@ d\'intercanvi de claus!
DiaNitPer defecte del sistema
- Copia la ID de Session
+ Copia l\'ID de SessionAdjuntMissatge de veuDetalls
@@ -685,6 +689,9 @@ d\'intercanvi de claus!
Frase de pas de 30 dígitsAixò triga un xic, t\'ho vols saltar?Enllaça un dispositiu
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.O uneix-te a alguns d\'aquests…Notificacions de missatgeHi ha dues maneres per les quals Session et pot notificar els missatges nous.
@@ -697,4 +704,20 @@ d\'intercanvi de claus!
Toca per a desblocarIntrodueix una sobrenomClau pública invàlida
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 314c53218a..9d1128fa98 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -58,7 +58,7 @@
Odeslání se nezdařilo, klikněte pro odeslání nezabepečeným způsobemNemohu nalézt aplikaci pro otevření tohoto typu dat.Zkopírováno %s
- Číst dál
+ Read MoreStáhnout dalšíProbíhá
@@ -284,7 +284,7 @@
VyNepodporovaný typ dat.Koncept
- Signál potřebuje oprávnění pro přístup k úložišti aby mohl zapisovat do externího úložiště, ale toto oprávnění je nyní zakázáno. Prosím pokračujte do menu nastavení aplikací, vyberte \"Oprávnění\" a povolte \"Úložiště\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Nelze ukládat do externího úložiště bez oprávnění.Smazat zprávu?Tímto trvale smažete tuto zprávu.
@@ -627,12 +627,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -667,6 +669,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -712,6 +716,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -724,4 +731,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml
index 1b1daa7fb9..29b94bdd79 100644
--- a/app/src/main/res/values-cy/strings.xml
+++ b/app/src/main/res/values-cy/strings.xml
@@ -62,7 +62,7 @@
Methodd yr anfon, tapiwch am gam nôl anniogelMethu canfod app sy\'n gallu agor y cyfryngau hwn.Copïwyd %s
- Darllen Rhagor
+ Read MoreLlwytho Rhagor i LawrYn aros
@@ -309,7 +309,7 @@
ChiMath o gyfrwng heb ei gynnalDrafft
- Mae ar Session angen caniatâd Storio er mwyn cadw i storfa allanol, ond fe\'i rwystrwyd yn barhaol. Ewch i ddewislen gosodiadau\'r ap, dewis \"Caniatâd\", a galluogi \"Storfa\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Methu cadw i storfa allanol heb ganiatâdDileu neges?Bydd hyn yn dileu yn barhaol y neges hon.
@@ -656,12 +656,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -696,6 +698,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -741,6 +745,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -753,4 +760,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 71426bf4a7..a65d355f40 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -54,7 +54,7 @@
Fejl ved afsendelse, tap for at sende usikretKan ikke finde en app, der kan åbne medietKopieret %s
- Læs mere
+ Read More Download mere Afventer
@@ -261,7 +261,7 @@ udveksel besked!
DigMultimedie type ikke understøttetKladde
- Session kræver tilladelse til at tilgå din hukommelse, for at kunne gemme til ekstern placering, hvilket det er blevet nægtet. Gå venligst via appens menu til Indstillinger, vælg \"Tilladelser\" og tilvælg \"Hukommelse\"
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Ikke muligt at gemme til ekstern placering, uden tilladelseSlet besked?Dette sletter beskeden permanent
@@ -600,12 +600,14 @@ udveksel besked!
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -640,6 +642,8 @@ udveksel besked!
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -685,6 +689,9 @@ udveksel besked!
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ udveksel besked!
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index af861a6a4d..c8529aab48 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -54,7 +54,7 @@
Senden gescheitert. Für Rückgriff auf unsicheren Versand antippen.Keine App zum Öffnen dieser Medieninhalte gefunden.%s kopiert
- Mehr lesen
+ Read More Mehr herunterladen Ausstehend
@@ -126,7 +126,7 @@
SMSLöschenNachrichten werden gelöscht …
- Banning
+ SperrenSperre nutzer…Originalnachricht nicht gefundenOriginalnachricht nicht mehr verfügbar
@@ -260,7 +260,7 @@
DuNicht unterstützter MedieninhaltetypEntwurf
- Session benötigt die Berechtigung »Speicher« für das Speichern in den Gerätespeicher, diese wurde jedoch dauerhaft abgelehnt. Bitte öffne die App-Einstellungen, wähle »Berechtigungen« und aktiviere »Speicher«.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Speichern in den Gerätespeicher ohne Berechtigungen nicht möglich.Nachricht löschen?Dies wird diese Nachricht unwiderruflich löschen.
@@ -588,7 +588,7 @@
Das ist Ihr Wiederherstellungssatz.Ihr Wiederherstellungssatz ist der Hauptschlüssel für Ihre Session ID. Mit diesem Satz können Sie Ihre Session ID wiederherstellen, wenn Sie den Zugriff auf Ihr Gerät verlieren. Bewahren Sie Ihren Wiederherstellungssatz an einem sicheren Ort auf und geben Sie ihn an niemandem weiter.Zur Anzeige gedrückt halten
- Du bist fast fertig! 80%
+ Du bist fast fertgi! 80%Sichern Sie Ihr Konto, indem Sie Ihren Wiederherstellungssatz speichernTippen und halten Sie die verborgenen Wörter, um Ihren Wiederherstellungssatz anzuzeigen, und speichern Sie ihn dann sicher, um Ihre Session ID zu sichern.Bewahren Sie Ihren Wiederherstellungssatz an einem sicheren Ort auf.
@@ -599,12 +599,14 @@
DienstknotenZielMehr erfahren
+ Auflösung…Neue SessionSession ID eingebenQR-Code scannenScannen 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.
- Geben Sie eine Session ID ein.
+ Sitzungs-ID oder ONS-Name eingebenBenutzer können ihre Session ID freigeben, indem sie in ihren Einstellungen auf \"Session ID freigeben\" tippen oder ihren QR-Code freigeben.
+ Bitte überprüfe die Session-ID oder den ONS-Namen und versuche es erneut.Session benötigt Kamerazugriff, um die QR-Codes scannen zu können.Kamerazugriff gewährenNeue geschlossene Gruppe
@@ -639,6 +641,8 @@
DatenschutzKonversationenBenachrichtigungsstrategie
+ Schnellen Modus verwenden
+ Über neue Nachrichten wirst du zuverlässig und sofort von Googles Benachrichtigungsservern informiert.Namen ändernGerät trennenIhr Wiederherstellungssatz
@@ -684,6 +688,9 @@
30-stellige PassphraseDies dauert eine Weile, möchtest du überspringen?Gerät verbinden
+ Wiederherstellungssatz
+ QR-Code scannen
+ Navigiere zu Einstellungen → Wiederherstellungssatz auf deinem anderen Gerät, um deinen QR-Code anzuzeigen.Oder tritt diesen bei…Benachrichtigung - Neue NachrichtenSession kann dich auf zwei Wegen über neue Nachrichten informieren.
@@ -696,4 +703,20 @@
Tippe zum entsperrenSpitznamen eingebenUngültiger öffentlicher Schlüssel
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index b25d5df018..2080c4bfaf 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -54,7 +54,7 @@
Η αποστολή απέτυχε, πατήστε για μη ασφαλή εναλλακτική αποστολήΔεν μπορεί να βρεθεί κατάλληλη εφαρμογή για το άνοιγμα αυτού του πολυμέσου.Αντιγράφτηκε: %s
- Διαβάστε περισσότερα
+ Read More Κατεβάστε περισσότερα Εκκρεμεί
@@ -262,7 +262,7 @@
ΕσείςΜη υποστηριζόμενος τύπος πολυμέσωνΠρόχειρο
- Το Session χρειάζεται τα δικαιώματα Αποθηκευτικού Χώρου για να μπορούμε να αποθηκεύσουμε στην εξωτερική μνήμη, αλλά αυτά δεν έχουν δοθεί μόνιμα. Παρακαλώ πηγαίνετε στις ρυθμίσεις εφαρμογών, επιλέξτε τα \"Δικαιώματα\", και ενεργοποιήστε το \"Αποθηκευτικός Χώρος\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Αδυναμία αποθήκευσης στην εξωτερική μνήμη χωρίς δικαιώματαΔιαγραφή μηνύματος;Αυτή η επιλογή θα διαγράψει οριστικά αυτό το μήνυμα.
@@ -601,12 +601,14 @@
Κόμβος ΥπηρεσίαςΠροορισμόςΜάθετε περισσότερα
+ Resolving…Νέα συνομιλίαΕισαγωγή Session ΤαυτότηταςΣαρώστε τον κωδικό QRΣαρώστε τον κωδικό QR ενός χρήστη για να ξεκινήσετε μια συνομιλία. Οι κωδικοί QR μπορούν να βρεθούν πατώντας το εικονίδιο QR κώδικα στις ρυθμίσεις λογαριασμού.
- Εισάγετε τη ταυτότητα του παραλήπτη
+ Enter Session ID or ONS nameΟι χρήστες μπορούν να μοιραστούν τη ταυτότητα τους πηγαίνοντας στις ρυθμίσεις του λογαριασμού τους και πατώντας το \"Διαμοιρασμό Session ταυτότητας\" ή κοινοποιώντας τον κωδικό QR.
+ Please check the Session ID or ONS name and try again.Το Session χρειάζεται πρόσβαση στην κάμερα για σάρωση κωδικών QRΧορήγηση Πρόσβασης ΚάμεραςΝέα Κλειστή Ομάδα
@@ -641,6 +643,8 @@
ΑπόρρητοΣυνομιλίεςΠεριεχόμενο Ειδοποιήσεων
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Αλλαγή ονόματοςΑποσύνδεση ΣυσκευήςΗ φράση ανάκτησής σας
@@ -686,6 +690,9 @@
φράση 30-ψηφίωνΑυτό παίρνει λίγο χρόνο, θα θέλατε να παραλείψετε;Σύνδεση Συσκευής
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Ή συνδεθείτε σε ένα από αυτά…Ειδοποίησεις μηνυμάτωνΥπάρχουν δύο τρόποι με τους οποίους το Session μπορεί να σας ειδοποιήσει για νέα μηνύματα.
@@ -698,4 +705,20 @@
Πατήστε για ξεκλείδωμαΕισαγωγή ψευδώνυμουΜη έγκυρο δημόσιο κλειδί
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index ff0f853206..68f2b85baf 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -54,7 +54,7 @@
Sendado malsukcesis, tuŝeti por nesekura retropaŝoNe povas trovi aplikaĵon, kiu kapablas malfermi ĉi tiun aŭdvidaĵon.Kopiinta %s
- Legi pli
+ Read More Elŝuti pli Pritraktota
@@ -262,7 +262,7 @@
ViNesubtenata tipo de aŭdvidaĵoMalneto
- Session bezonas la permeson aliri al konservejo por konservi al ekstera konservejo, sed ĝi estis porĉiame malakceptita. Bonvolu iri al la aplikaĵaj agordoj, elekti „Permesoj“, kaj ŝalti „Konservejo“.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Ne eblas konservi en la ekstera konservejo sen permesojĈu forviŝi mesaĝon?Tio daŭre forviŝos ĉi tiun mesaĝon.
@@ -601,12 +601,14 @@
Serva NodoCeloLerni pli
+ Resolving…Nova SesioEntajpu Session ID-onSkani QR-KodonSkanu QR-kodon de uzanto por komenci sesion. QR-kodoj povas esti trovitaj per tuŝeti la QR-kodan bildeton en la konta agordo.
- Entajpu la Session ID-on de la ricevanto
+ Enter Session ID or ONS nameUzantoj povas kunhavigi sian Session ID-on per irante en sia konta agordo kaj tuŝeti \"Kunhavigi Session ID-on\", aŭ per kunhavigi sian QR-kodon.
+ Please check the Session ID or ONS name and try again.Sesio bezonas fotilan aliron por skani QR-kodojnPermesi Fotilan AlironNova Ferma Grupo
@@ -641,6 +643,8 @@
PrivatecoInterparolojSciiga Strategio
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Ŝanĝi nomonMalligi aparatonVia Ripara Frazo
@@ -686,6 +690,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Ligi Aparaton
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Sciigoj pri MesaĝojThere are two ways Session can notify you of new messages.
@@ -698,4 +705,20 @@
Tuŝetu por MalŝlosiEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 1e2ea59731..4b2135040e 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -54,7 +54,7 @@
Fallo al enviar. Toca para enviar sin cifrarNo se pudo encontrar la aplicación para mostrar este contenido.%s copiado(s)
- Leer más
+ Leer Más Descargar más Pendiente
@@ -261,7 +261,7 @@ de intercambio de claves!
TúFormato de contenido incompatibleBorrador
- Session necesita acceso al almacenamiento del teléfono para guardar adjuntos si éste es externo. Por favor, ve al menú de configuración de la aplicación, selecciona «Permisos» y activa «Almacenamiento».
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".No se puede guardar en una unidad de almacenamiento externo si Session no tiene acceso¿Eliminar mensaje?Este mensaje se eliminará permanentemente.
@@ -600,12 +600,14 @@ de intercambio de claves!
Nodo de ServicioDestinoSaber Más
+ Resolviendo…Nueva SessionSession IDEscanear código QREscanea el código QR de un usuario para empezar una sesión. Los códigos QR se pueden encontrar tocando el icono del código QR en los ajustes de la cuenta.
- Ingresa la ID de Session del destinatario
+ Introduzca el ID de Session o el nombre ONSLos usuarios pueden compartir su ID de Session yendo a los ajustes de su cuenta y pulsando en Compartir ID de Session o compartiendo su código QR
+ Por favor, comprueba el ID de Session o el nombre ONS e inténtela de nuevo.Session necesita acceso a la cámara para escanear códigos QRPermitir acceso a cámaraNuevo grupo cerrado
@@ -640,6 +642,8 @@ de intercambio de claves!
PrivacidadChatsEstrategia de notificación
+ Usar Modo Rápido
+ Se te notificará de nuevos mensajes de forma fiable e inmediata usando los servidores de notificaciones de Google.Cambiar nombreDesvincular dispositivoTu frase de recuperación
@@ -685,6 +689,9 @@ de intercambio de claves!
Clave de acceso de 30 dígitosEsto está tomando un tiempo, ¿te gustaría saltarlo?Vincular un Dispositivo
+ Frase de Recuperación
+ Escanear Código QR
+ Navega a Ajustes → Frase de Recuperación en tu otro dispositivo para mostrar tu código QR.O únete a uno de estos…Notificaciones de MensajesHay dos maneras en las que Session puede notificarle de nuevos mensajes.
@@ -697,4 +704,20 @@ de intercambio de claves!
Toca para DesbloquearEscriba un apodoClave pública no válida
+ Documento
+ ¿Desbloquear a %s?
+ ¿Estás seguro de que quieres desbloquear a %s?
+ ¿Unirse a %s?
+ ¿Estás seguro de que quieres unirte al grupo abierto %s?
+ ¿Abrir URL?
+ ¿Estás seguro de que quieres abrir %s?
+ Abrir
+ ¿Habilitar Previsualización de Enlaces?
+ Activar la vista previa de enlaces mostrará las vistas previas de las URL que envíes y recibas. Esto puede ser útil, pero Session tendrá que ponerse en contacto con los sitios web enlazados para generar vistas previas. Siempre puedes desactivar las vistas previas de enlaces en la configuración de Session.
+ Activar
+ ¿Confiar en %s?
+ ¿Estás seguro que quieres descargar el medio enviado por %s?
+ Descargar
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml
index 69046a06ad..2980a0c566 100644
--- a/app/src/main/res/values-et/strings.xml
+++ b/app/src/main/res/values-et/strings.xml
@@ -54,7 +54,7 @@
Saatmine ebaõnnestus, koputa, et saata turvamata sõnumEi leia rakendust, mis oleks võimeline seda meediafaili avama.%s kopeeritud
- Loe rohkem
+ Read More Laadi rohkem alla Ootel
@@ -263,7 +263,7 @@
SinaMittetoetatud meediatüüpMustand
- Session vajab mäluruumi luba, et salvestada välismällu, ent see on püsivalt keelatud. Palun jätka rakenduse seadetes, vali \"Load\" ja luba \"Mäluruum\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Õiguste puudumise tõttu pole võimalik välisele andmekandjale salvestada.Kustutad sõnumi?See kustutab püsivalt valitud sõnumi.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 658f872e27..17dfce8939 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -1,11 +1,11 @@
- نشست
+ Sessionبلهخیرحذفمسدود
- لطفاً شکیبا باشید...
+ لطفاً صبر کنید...ذخیرهیادداشت به خودنگارش %s
@@ -44,7 +44,7 @@
این هفتهاین ماه
- هیچ مرورگر وبی یافت نشد.
+ هیچ مرورگری یافت نشد.گروه ها
@@ -54,7 +54,7 @@
ارسال نشد, برای روش جایگزین ناامن لمس کنیدبرنامه ای برای بازکردن این رسانه وجود ندارد.%s کپی شد
- بیشتر بخوانید
+ Read More بیشتر دانلود کنید در انتظار
@@ -120,9 +120,9 @@
در حال ذخیره %1$d پیوست در حافظه...در حال ذخیره %1$d پیوست در حافظه...
- در انتظار ...
- داده ها (Session)
- پیام چند رسانه ای
+ درحال انتظار...
+ دادهها (Session)
+ پیام چندرسانهایپیامکدر حال حذف کردنحذف پیام ها...
@@ -206,7 +206,7 @@
انسداد این تماس؟شما ديگر تماس يا پيامی از این مخاطب دريافت نخواهید کرد.
- بلوک
+ مسدودرفع انسداد این تماس؟شما مجددا قادر به دریافت پیام ها و تماس ها از این مخاطب هستید.رفع انسداد
@@ -261,7 +261,7 @@
شمانوع رسانه پشتیبانی نشدهپیش نویس
- Session برای ذخیره در حافظهی خارجی نیاز به دسترسی به حافظه دارد اما این دسترسی به طور همیشگی رد شده است. لطفا به بخش برنامه در تنظیمات تلفن همراه خود رفته و پس از یافتن Session وارد بخش دسترسی ها شده و گزینه حافظه را فعال کنید.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".نمیتوان بدون دسترسیها در حافظهی خارجی ذخیرهسازی نمودحذف پیام؟این باعث حذف این پیام به صورت دائم خواهد شد.
@@ -379,7 +379,7 @@
ارسال دوباره
- بلوک
+ مسدودبرخی از مسائل نیاز به توجه شما دارند.فرستاده
@@ -429,7 +429,7 @@
ارسال پیش نمایش لینک هاپیش نمایش ها برای Imgur، Instagram، Pinterest، Reddit، و لینک های Youtube پشتیبانی می شودمحافظ صفحه نمایش
- بلوک کردن عکس گرفتن از صفحه در لیست تازه و در داخل برنامه
+ اجازه ندادن برای عکس از صفحه در داخل نرمافزار و لیستاخیراطلاعیههارنگ چراغ الایدیناشناخته
@@ -560,7 +560,7 @@
لغوشناسهی Session شماSession شما از اینجا شروع میشود...
- شناسهی Session را ایجاد کنید
+ ساخت شناسهی SessionSession خود را ادامه دهیدSession چیست؟Session یک برنامه پیامرسان غیرمتمرکز و رمزگذاری شده است.
@@ -600,12 +600,14 @@
گره سرویسمقصدبیشتر بدانید
+ Resolving…Session جدیدشناسهی Session را وارد کنیدکد QR را اسکن کنیدبرای شروع Session، کد QR کاربر را اسکن کنید. با ضربه زدن روی نماد کد QR در تنظیمات حساب کاربری، کدهای QR را میتوان یافت.
- شناسه گیرنده را وارد کنید
+ Enter Session ID or ONS nameکاربران میتوانند شناسهی Session خود را با رفتن به تنظیمات حساب خود و ضربه زدن به Share Session ID یا با به اشتراک گذاشتن کد QR خود، با دیگران به اشتراک بگذارند.
+ Please check the Session ID or ONS name and try again.اپ Session برای اسکن کدهای QR احتیاج دارد به دوربین دسترسی داشته باشداجازه دسترسی به دوربینگروه خصوصی جدید
@@ -640,6 +642,8 @@
حریم خصوصیگفتوگوهااستراتژی اعلان
+ استفاده از حالت سریع
+ با استفاده از سرورهای گوگل، شما به صورت سریع و مطمئن از پیامهای جدید مطلع میشوید.تغییر نامقطع اتصال دستگاهعبارت بازیابی شما
@@ -674,7 +678,7 @@
روزشبپیش فرض سیستم
- رونوشت شناسه نشست شما
+ کپی شناسهی Sessionپیوستپیام صوتیجزئيات
@@ -685,6 +689,9 @@
گذرمتن ۳۰رقمیاین مقداری طول میکشد، دوست دارید ردش کنید؟اتصال به یک دستگاه
+ عبارت بازیابی
+ اسکن کد QR
+ برای دیدن کد QR خود، در دستگاه دیگرتان به «تنظیمات ← عبارت بازیابی» مراجعه کنید.یا به یکی از اینها بپیوندید…اعلانهای پیامدو راه برای اطلاع رسانی شما از پیامهای جدید در نشست وجود دارد.
@@ -697,4 +704,20 @@
ﺑﺮﺍﯼ ﮔﺸﻮﺩﻥ ﺿﺮﺑﻪ ﺑﺰﻧﻴﺪیک نام مستعار وارد کنیدکلید عمومی نامعتبر
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 3e40c1e4f0..fe5fda99bf 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -54,7 +54,7 @@
Lähetys epäonnistui, napauta lähettääksesi suojaamattomanaMedian avaamiseen ei löytynyt sovellusta.Kopioitu: %s
- Lue lisää
+ Read More Lataa lisää Käsiteltävänä
@@ -261,7 +261,7 @@ on viallinen!
SinäMedian tyyppiä ei tuetaLuonnos
- Signal tarvitsee lupaa käyttää laitteesi tallennustilaa tallentaakseen sinne, mutta tämä käyttöoikeus on pysyvästi evätty Sessionilta. Voit muuttaa tätä menemällä sovellusten asetuksiin, valitsemalla \"Sovelluksen käyttöoikeudet\" ja laittamalla päälle \"Tallennustila\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Tallennus ulkoiseen tallennustilaan ei onnistu ilman käyttöoikeutta.Poistetaanko viesti?Tämä poistaa pysyvästi tämän viestin.
@@ -600,12 +600,14 @@ on viallinen!
Palvelu SolmuPäämääräLisätietoja
+ Resolving…Uusi IstuntoAnna Session IDLue QR-koodiSkannaa käyttäjän QR-koodi aloittaaksesi istunnon. QR-koodit löytyvät napauttamalla QR-koodin kuvaketta tilin asetuksista.
- Syötä vastaanottajan Session ID
+ Enter Session ID or ONS nameKäyttäjät voivat jakaa Session tunnuksensa menemällä tiliasetuksiinsa ja painamalla \"Jaa Session ID\" tai jakamalla QR-koodin.
+ Please check the Session ID or ONS name and try again.Session tarvitsee kameran käyttöoikeuden skannatakseensa QR-kooditMyönnä Kameran KäyttöoikeusUusi Suljettu Ryhmä
@@ -640,6 +642,8 @@ on viallinen!
YksityisyysKeskustelutIlmoitusten Strategia
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Vaihda nimiPoista laitteen linkitysPalautuslauseke
@@ -685,6 +689,9 @@ on viallinen!
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ on viallinen!
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index ac7a0a3985..ae55c18763 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -4,7 +4,7 @@
OuiNonSupprimer
- Ban
+ BannirVeuillez patienter…EnregistrerNote à mon intention
@@ -51,21 +51,21 @@
Échec d’envoi, touchez pour obtenir des détailsVous avez reçu un message d’échange de clés. Touchez pour le traiter.%1$s a quitté le groupe.
- Échec d’envoi, touchez pour utiliser la solution de rechange non sécurisée
+ Échec de l’envoi, touchez pour utiliser la solution de rechange non sécuriséeImpossible de trouver une appli pour ouvrir ce média.Copié %s
- En savoir plus
- Télécharger plus
- En attente
+ Read More
+ Télécharger plus
+ En attenteAjouter une pièce jointeSélectionner les renseignements du contactUne erreur est survenue lors du chargement de la pièce jointe.Message
- Compose
- Muted until %1$s
- %1$d members
- Community Guidelines
+ Rédiger
+ Son désactivé jusqu\'à %1$s
+ %1$d membres
+ Règles de la communautéLe destinataire est invalide !Ajouté à l’écran d’accueilQuitter le groupe ?
@@ -75,12 +75,12 @@
Vous pourrez de nouveau recevoir les messages et appels de ce contact.DébloquerLa pièce jointe dépasse la limite de taille autorisée.
- Impossible d’enregistrer l’audio !
+ Impossible d’enregistrer du son !Il n’y a aucune appli pour gérer ce lien sur votre appareil.
- Add members
- Join %s
- Are you sure you want to join the %s open group?
- Pour envoyer des messages audio, autorisez Session à accéder à votre microphone.
+ Ajouter des membres
+ Rejoindre %s
+ Êtes-vous sûr de vouloir rejoindre le groupe public %s ?
+ Pour envoyer des messages audio, Session a besoin d’accéder à votre microphone.Session exige l’autorisation Microphone afin d’envoyer des messages audio, mais elle a été refusée définitivement. Veuillez accéder au menu des paramètres des applis, sélectionner « Autorisations » et activer « Microphone ».Pour capturer des photos et des vidéos, autorisez Session à accéder à l’appareil photo.Session a besoin de l’autorisation Appareil photo afin de prendre des photos ou des vidéos, mais elle a été refusée définitivement. Veuillez accéder au menu des paramètres des applis, sélectionner Autorisations et activer Appareil photo.
@@ -100,10 +100,10 @@
Le message sélectionné sera irrémédiablement supprimé.
- Les %1$d messages sélectionnés seront irrémédiablement supprimés
+ Les %1$d messages sélectionnés seront irrémédiablement supprimés.
- Ban this user?
- Enregistrer dans la mémoire ?
+ Bannir cet utilisateur ?
+ Enregistrer dans la mémoire du téléphone ?La sauvegarde du média dans l’espace de stockage autorisera d’autres applications à y accéder.\n\nContinuer ?L’enregistrement des %1$d médias dans la mémoire permettra à n’importe quelles autres applis de votre appareil d’y accéder.\n\nContinuer ?
@@ -122,12 +122,12 @@
En attente…Données (Session)
- Messagerie multimédia
- Textos
+ Messagerie multimédia (MMS)
+ Textos (SMS)SuppressionSuppression des messages…
- Banning
- Banning user…
+ Bannissement
+ Bannissement de l\'utilisateur…Le message original est introuvableLe message original n’est plus disponible
@@ -194,7 +194,7 @@
Tous les médiasVous avez reçu un message chiffré avec une ancienne version de Signal qui n’est plus prise en charge. Veuillez demander à l’expéditeur de mettre Session à jour vers la version la plus récente et de renvoyer son message.
- Vous avez quitté le groupe
+ Vous avez quitté le groupe.Vous avez mis le groupe à jour.%s a mis le groupe à jour.
@@ -233,14 +233,14 @@
%s est sur Session !Les messages éphémères sont désactivésL’expiration des messages éphémères a été définie à %s
- %s took a screenshot.
- Media saved by %s.
+ %s a effectué une capture d\'écran.
+ Média enregistré par %s.Le numéro de sécurité a changé
- Votre numéro de sécurité avec %s a changé
+ Votre numéro de sécurité avec %s a changé.Vous avez marqué comme vérifiéVous avez marqué comme non vérifié
- This conversation is empty
- Open group invitation
+ Cette conversation est vide
+ Invitation à un groupe publicMise à jour de SessionUne nouvelle version de Session est proposée. Touchez pour lancer la mise à jour
@@ -253,14 +253,14 @@
Notifications en sourdine
- Touchez pour ouvrir
+ Touchez pour ouvrir.Session est déverrouilléVerrouiller SessionVousType de médias non pris en chargeBrouillon
- Session exige l’autorisation Stockage afin d’enregistrer sur la mémoire externe, mais elle a été refusée définitivement. Veuillez accéder au menu des paramètres des applis, sélectionner Autorisations et activer Stockage.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Impossible d’enregistrer sur la mémoire externe sans autorisationSupprimer le message ?Ce message sera supprimé définitivement.
@@ -353,11 +353,11 @@
Mettre en pauseTélécharger
- Join
- Open group invitation
- Pinned message
- Community guidelines
- Read
+ Rejoindre
+ Invitation à un groupe public
+ Message épinglé
+ Règles de la communauté
+ LireContenu audioVidéo
@@ -380,7 +380,7 @@
Bloquer
- Des problèmes demandent votre attention
+ Des problèmes demandent votre attention.EnvoyéReçuDisparaît dans
@@ -487,7 +487,7 @@
Détails du messageCopier le texteSupprimer le message
- Ban user
+ Bannir l\'utilisateurRenvoyer le messageRépondre au message
@@ -588,23 +588,25 @@
Voici votre phrase de récupérationVotre phrase de récupération est la clé principale de votre Session ID - vous pouvez l\'utiliser pour restaurer votre Session ID si vous perdez l\'accès à votre appareil. Conservez la dans un endroit sûr et ne la donnez à personne.Appuyer pour révéler
- You\'re almost finished! 80%
+ Vous avez presque terminé ! 80%Sécurisez votre compte en sauvegardant votre phrase de récupérationAppuyez et maintenez les mots masqués pour révéler votre phrase de récupération, puis stockez-la en toute sécurité pour sécuriser votre Session ID.Assurez-vous de conserver votre phrase de récupération dans un endroit sûrCheminSession occulte votre adresse IP en envoyant vos messages via plusieurs nœuds de service dans le réseau décentralisé de Session. Voici les pays par le biais desquels votre connexion est actuellement envoyée :Vous
- Noeud d’entrée
- Noeud de service
+ Nœud d’entrée
+ Nœud de serviceDestinationEn savoir plus
+ Contact en cours…Nouvelle SessionSaisir un Session IDScanner un Code QR
- Scannez le code QR d\'un utilisateur pour démarrer une session. Les codes QR peuvent se trouver en appuyant sur l\'icône du code QR dans les paramètres du compte.
- Saisissez le Session ID du destinataire
+ Scannez le code QR d\'un utilisateur pour démarrer une session. Les codes QR peuvent se trouver en touchant l\'icône du code QR dans les paramètres du compte.
+ Entrer un Session ID ou un nom ONSLes utilisateurs peuvent partager leur Session ID depuis les paramètres du compte ou en utilisant le code QR.
+ Veuillez vérifier le Session ID ou le nom ONS et réessayer.Session a besoin d\'accéder à l\'appareil photo pour scanner les codes QRAutoriser l\'accèsNouveau groupe privé
@@ -614,7 +616,7 @@
Veuillez saisir un nom de groupeVeuillez saisir un nom de groupe plus courtVeuillez choisir au moins un membre du groupe
- A closed group cannot have more than 100 members
+ Un groupe privé ne peut avoir plus de 100 membresJoindre un groupe publicImpossible de rejoindre le groupeURL du groupe public
@@ -629,16 +631,18 @@
NotificationsConversationsAppareils reliés
- Invite
+ InviterPhrase de récupérationEffacer les données
- Help us Translate Session
+ Aidez-nous à traduire SessionNotificationsStyle de notificationContenu de notificationConfidentialitéConversationsStratégie de notification
+ Utiliser le Mode Rapide
+ Vous serez averti de nouveaux messages de manière fiable et immédiate en utilisant les serveurs de notification de Google.Modifier le nomDéconnecter l\'appareilVotre phrase de récupération
@@ -649,13 +653,13 @@
Afficher mon code QRScanner le code QRScannez le code QR d\'un autre utilisateur pour démarrer une session
- Scan Me
+ Scannez-moiCeci est votre code QR. Les autres utilisateurs peuvent le scanner pour démarrer une session avec vous.Partager le code QRContactsGroupes privésGroupes publics
- You don\'t have any contacts yet
+ Vous n\'avez pas encore de contactsAppliquerTerminé
@@ -678,22 +682,41 @@
Message vocalDétailsÉchec de l\'activation des sauvegardes. Veuillez réessayer ou contacter le support.
- Restore backup
- Select a file
- Select a backup file and enter the passphrase it was created with.
- 30-digit passphrase
- This is taking a while, would you like to skip?
- Link a Device
- Or join one of these…
- Message Notifications
- There are two ways Session can notify you of new messages.
- Fast Mode
- Slow Mode
- You’ll be notified of new messages reliably and immediately using Google’s notification servers.
- Session will occasionally check for new messages in the background.
- Recovery Phrase
- Session is Locked
- Tap to Unlock
- Enter a nickname
- Invalid public key
+ Restaurer une sauvegarde
+ Sélectionnez un fichier
+ Sélectionnez un fichier de sauvegarde et entrez la phrase de passe avec laquelle il a été créé.
+ Phrase de passe de 30 caractères
+ Cela prend un certain temps, voulez-vous passer ?
+ Relier un appareil
+ Phrase de récupération
+ Scannez le code QR
+ Allez dans Paramètres → Phrase de récupération sur votre autre appareil pour afficher votre code QR.
+ Ou rejoignez l\'un(e) de ceux-ci…
+ Notifications de message
+ Session peut vous avertir de la présence de nouveaux messages de deux façons.
+ Mode rapide
+ Mode lent
+ Vous serez averti de nouveaux messages de manière fiable et immédiate en utilisant les serveurs de notification de Google.
+ Session vérifiera occasionnellement la présence de nouveaux messages en tâche de fond.
+ Phrase de récupération
+ Session est Verrouillé
+ Toucher pour déverrouiller
+ Saisissez un nom
+ Clé publique non valide
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index b63b1a6535..b6d4f836dd 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -54,7 +54,7 @@
Erro ao enviar, toca para volver a un modo non seguroNon se atopa unha aplicación con que abrir este contido multimedia.Copiouse %s
- Ler máis
+ Read MoreDescargar máisPendente
@@ -263,7 +263,7 @@
TiTipo multimedia non compatibleBorrador
- Session necesita permiso para acceder ao almacenamento externo e poder gardar nel, pero foi denegado de forma permanente. Vai aos axustes da aplicación, selecciona \"Permisos\" e activa \"Almacenamento\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Non é posible gardar no almacenamento externo sen permisoBorrar mensaxe?Isto borrará de xeito permanente esta mensaxe.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ha/strings.xml b/app/src/main/res/values-ha/strings.xml
index cba1fadb09..5fa3a7f53c 100644
--- a/app/src/main/res/values-ha/strings.xml
+++ b/app/src/main/res/values-ha/strings.xml
@@ -54,7 +54,7 @@
Send failed, tap for unsecured fallbackSepena vê medyayê veke nehat dîtin.%s ji ber girtî
- Bêhtir Bixwîne
+ Read MoreBêhtir Daxîne Hilawîstiye
@@ -263,7 +263,7 @@
TuCureyê medyayê ya nepesartîReşnivîs
- Session pêdivî destûrê ye ku li bîrbara derveyî tomar bike. Lê ev mayinde hatiye redkirî. Ji kerema xwe ji \'Sazkarî\' > \'Destûr\' > \'Bîrbar\' destûr bide.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Bê destûr nabe ku li bîrbara derveyî tomar bibePeyamê jê bibe?Ev dê bi temamî vê peyamê jê bibe.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 0bf9272cae..0bc5e8331e 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -1,14 +1,14 @@
- Session
+ ישיבהכןלאמחק
- Ban
+ הסראנא המתן...שמורהערה לעצמי
- Version %s
+ גרסה %sהודעה חדשה
@@ -58,14 +58,14 @@
שליחה נכשלה, הקש לנסיגה בלתי מאובטחתלא ניתן למצוא יישום המסוגל לפתוח מדיה זו.הועתק %s
- קרא עוד
+ Read More הורד עוד ממתיןהוסף צרופהבחר מידע של איש קשרסליחה, אירעה שגיאה בהוספת הצרופה שלך.
- Message
+ הודעהComposeMuted until %1$s%1$d members
@@ -285,7 +285,7 @@
אתהסוג מדיה בלתי נתמךטיוטה
- Session צריך את הרשאת האחסון על מנת לשמור באחסון חיצוני, אבל היא נדחתה לצמיתות. אנא המשך אל הגדרות היישום, בחר \"הרשאות\" ואפשר את \"אחסון\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".לא היה ניתן לשמור באחסון חיצוני ללא הרשאותלמחוק הודעה?זה ימחק לצמיתות הודעה זו.
@@ -628,12 +628,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -668,6 +670,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -713,6 +717,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -725,4 +732,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index d1ba2ee827..65a4a0f270 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -54,7 +54,7 @@
भेजने में असफल रहा, असुरक्षित फ़ॉलबैक के लिए टैप करेंइस मीडिया को खोलने में सक्षम कोई ऐप नहीं मिल सकता है।%s अनुकरण
- और पढो
+ Read Moreऔर डाउनलोड करेंबचा हुआ
@@ -261,7 +261,7 @@
आपअसमर्थित मीडिया प्रकारप्रारूप
- बाहरी भंडारण को सहेजने के लिए Session को स्टोरेज अनुमति की आवश्यकता होती है, लेकिन इसे स्थायी रूप से अस्वीकार कर दिया गया है। कृपया ऐप सेटिंग्स जारी रखें, \"अनुमतियां\" चुनें, और \"संग्रहण\" सक्षम करें।
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".अनुमति के बिना बाहरी भंडारण में सहेजने में असमर्थचयनित संदेश हटाएं?यह इस संदेश को स्थायी रूप से हटा देगा।
@@ -600,12 +600,14 @@
सर्विस नोडगंतव्यअधिक जानें
+ Resolving…नया सेशनसेशन आईडी डालेंQR कोड को स्कैन करेंसेशन शुरू करने के लिए यूजर के क्यूआर कोड को स्कैन करें। क्यूआर कोड को अकाउंट सेटिंग में क्यूआर कोड आइकन पर टैप करके पाया जा सकता है।
- रेसिपिएंट का Session आईडी दर्ज करें
+ Enter Session ID or ONS nameयूजर अपनी अकाउंट सेटिंग में जाकर और \"शेयर Session आईडी\" टैप करके, या अपना क्यूआर कोड शेयर कर सकते है।
+ Please check the Session ID or ONS name and try again.क्यूआर कोड स्कैन करने के लिए Session को कैमरा एक्सेस की आवश्यकता हैकैमरा केउपयोग को प्रदान करेंनया Closed Group
@@ -640,6 +642,8 @@
गोपनियताचैटसूचना रणनीति
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.नाम बदलेंडिवाइस को अनलिंक करेंआपका पुनर्प्राप्ति वाक्यांश
@@ -685,6 +689,9 @@
30 अंक का पासफ्रेसयह समय ले रहा है, इसे छोड़ दें?डिवाइस को लिंक करें
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.या इनमें से एक को जोड़ें...संदेश सूचनाएंसेशन आपको नए संदेश के बारे में दो तरीकों से बता सकता है
@@ -697,4 +704,20 @@
अनलॉक करने के लिए टैप करेंउपनाम चुनेंअमान्य सार्वजनिक कुंजी
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 9857557145..f79f8b4786 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -275,7 +275,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -616,12 +616,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -656,6 +658,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -701,6 +705,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -713,4 +720,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 7813eaadf4..de9f8becc7 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -54,7 +54,7 @@
Sikertelen küldés, koppints a titkosítatlan üzenetküldéshezNem található olyan alkalmazás, ami képes lenne megnyitni ezt a médiafájlt.%s másolva
- Tovább
+ Read More További letöltése Függőben
@@ -263,7 +263,7 @@
TeNem támogatott médiatípusPiszkozat
- A Session-nak szüksége van a Tárhely engedélyre ahhoz, hogy külső tárolóra menthessen, de ez jelenleg nincs megadva. Kérlek menj az alkalmazásbeállításokhoz, válaszd az \"Engedélyek\"-et és engedélyezd a \"Tárhely\"-t.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Nem lehet engedély nélkül menteni a külső tárolóraTörlöd az üzenetet?Ez véglegesen törölni fogja ezt az üzenetet.
@@ -490,7 +490,7 @@
Üzenet részleteiSzöveg másolásaÜzenet törlése
- Ban user
+ Felhasználó kitiltásaÜzenet újraküldéseVálasz az üzenetre
@@ -550,7 +550,7 @@
Képernyő lezárása ennyi tétlenséget követőenEgyik sem
- Copy public key
+ Nyilvános kulcs másolásaFolytatásMásolás
@@ -561,18 +561,18 @@
Érvénytelen Session azonosítóMégseAz ön Session azonosítója
- Your Session begins here...
+ Az ön Session-ja itt kezdődik...Session azonosító létrehozásaContinue Your Session
- What\'s Session?
- It\'s a decentralized, encrypted messaging app
- So it doesn\'t collect my personal information or my conversation metadata? How does it work?
- Using a combination of advanced anonymous routing and end-to-end encryption technologies.
- Friends don\'t let friends use compromised messengers. You\'re welcome.
+ Mi az a Session?
+ Ez egy decentralizált, titkosított üzenetküldő alkalmazás
+ Tehát nem gyűjti a személyes adataimat vagy a beszélgetés metaadatait? Hogyan működik?
+ Fejlett névtelen útválasztási és end-to-end titkosítási technológiák kombinációjának használata.
+ A barátok nem engedik, hogy a barátok kompromittált hírnököket használjanak. Szívesen.Say hello to your Session IDYour Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.Restore your account
- Enter the recovery phrase that was given to you when you signed up to restore your account.
+ Írja be azt a helyreállítási kifejezést, amelyet a fiók visszaállításához regisztrálásakor kapott.Enter your recovery phraseVálassza ki a megjelenítendő nevetEz lesz az ön neve, amikor használja a Session alkalmazást. Ez lehet a valódi neve, álneve, vagy bármi más, ami tetszik.
@@ -584,14 +584,14 @@
You don\'t have any contacts yetStart a SessionAre you sure you want to leave this group?
- "Couldn't leave group"
+ "Nem sikerült kilépni a csoportból"Are you sure you want to delete this conversation?Conversation deletedYour Recovery PhraseMeet your recovery phrase
- Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\'t give it to anyone.
+ A helyreállítási kifejezés a Session azonosító főkulcsa - használhatja a Session azonosító visszaállítására, ha elveszíti a hozzáférését eszközéhez. Tárolja helyreállítási mondatát biztonságos helyen, és ne adja át senkinek.Hold to reveal
- You\'re almost finished! 80%
+ Már majdnem kész! 80%Secure your account by saving your recovery phraseTap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.Make sure to store your recovery phrase in a safe place
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
AdatvédelemBeszélgetésekNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Név módosításaUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Koppintson a feloldáshozÍrjon be egy becenevetÉrvénytelen nyilvános kulcs
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-hy-rAM/strings.xml b/app/src/main/res/values-hy-rAM/strings.xml
index 10860fd30d..1da16ac9ba 100644
--- a/app/src/main/res/values-hy-rAM/strings.xml
+++ b/app/src/main/res/values-hy-rAM/strings.xml
@@ -54,7 +54,7 @@
Չի ուղարկվում, սեղմեք անապահով այլընտրանքի համարՉհաջողվեց գտնել հավելված, որը ճանաչում է այս մեդիայի տեսակը։«%s»֊ը պատճենվել է
- Տեսնել Ավելին
+ Read More Բեռնել Ավելին Հերթագրված է
@@ -263,7 +263,7 @@
ԴուքUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Ծառայության ՀանգույցՆպատակակետԻմանալ Ավելին
+ Resolving…Նոր ՍեանսՄուտքագրեք Սեանսի ինքնությունըՍկանավորել QR ԿոդՍկանավորեք մեկի QR կոդը, որ սկսեք սեանս։ QR կոդը կարող եք գտնել հաշվի կարգավորումների մեջ՝ QR կոդի նշանի վրա սեղմելուց հետո։
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessՆոր Փակ Խումբ
@@ -642,6 +644,8 @@
ԳաղտնիությունԶրույցներNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index caa35aac26..704a82624b 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -251,7 +251,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -588,12 +588,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -628,6 +630,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -673,6 +677,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -685,4 +692,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 3711d8fc69..a752f35c8a 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1,6 +1,6 @@
- Session
+ جلسةSìNoCancella
@@ -8,7 +8,7 @@
Attendere...SalvaNote personali
- Version %s
+ Versione %sNuovo messaggio
@@ -54,7 +54,7 @@
Invio fallito, tocca per invio non sicuroImpossibile trovare un\'app per aprire il file.Copiato %s
- Leggi tutto
+ Read More Scarica altro In attesa
@@ -62,9 +62,9 @@
Seleziona informazioni dei contattiAttenzione, c\'è stato un errore nell\'inviare il tuo allegato.Messaggio
- Compose
- Muted until %1$s
- %1$d members
+ Componi
+ Mutato fino a %1$s
+ %1$d membriCommunity GuidelinesDestinatario non valido!Aggiunto alla schermata principale
@@ -261,7 +261,7 @@ scambia un altro messaggio!
TuTipo di media non supportatoBozza
- Session richiede l\'autorizzazione per salvare in memoria, ma è stata negata in modo permanente. Si prega di continuare con le impostazioni dell\'app, selezionare \"Autorizzazioni\", quindi abilitare \"Archiviazione\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Impossibile salvare sulla memoria senza l\'autorizzazioneEliminare il messaggio?Questo messaggio verrà eliminato definitivamente.
@@ -600,12 +600,14 @@ scambia un altro messaggio!
Nodo di servizioDestinazionePer saperne di più
+ Resolving…Nuova sessioneInserisci la Sessione IDScansiona il codice QRScansiona il codice QR di un utente per avviare una sessione. Puoi trovare i codici QR toccando l\'icona Codice QR nelle impostazioni dell\'account.
- Inserisci la Sessione ID del destinatario
+ Enter Session ID or ONS nameGli utenti possono condividere la propria Sessione ID accedendo alle impostazioni del proprio account e toccando Condividi la Sessione ID o condividendo il proprio codice QR.
+ Please check the Session ID or ONS name and try again.La Sessione richiede l\'accesso alla fotocamera per scansionare i codici QRConsenti Accesso FotocameraNuovo gruppo chiuso
@@ -640,6 +642,8 @@ scambia un altro messaggio!
PrivacyChatStrategia di notifica
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Cambia nomeScollega dispositivoFrase di recupero
@@ -685,6 +689,9 @@ scambia un altro messaggio!
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ scambia un altro messaggio!
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 1706896554..3184f03d86 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -52,7 +52,7 @@
送信失敗、タップして安全でない通信を行うこのメディアを開けるアプリが見つかりません。%sをコピーしました
- 詳細を読む
+ Read More 更にダウンロード 保留中
@@ -248,7 +248,7 @@
あなたサポートされないメディア種別下書き
- Sessionが外部ストレージにデータを保存するには、ストレージへのアクセスを許可する必要がありますが、無効になっています。アプリ設定メニューから、『アプリの権限』を選び、『ストレージ』へのアクセス許可を有効にしてください。
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".外部ストレージに保存できません。ストレージへのアクセス許可が無効になっています。メッセージを削除しますか?このメッセージを完全に削除します。
@@ -585,12 +585,14 @@
サービスノード目的先詳細
+ Resolving…新しい SessionSession ID を入力してくださいQR コードをスキャンするユーザーの QR コードをスキャンして、Session を開始します。QR コードは、アカウント設定の QR コードアイコンをタップすると見つかります。
- 受信者の Session ID を入力してください
+ Enter Session ID or ONS nameユーザーは、アカウント設定に移動して [Session ID を共有] をタップするか、QR コードを共有することで、Session ID を共有できます。
+ Please check the Session ID or ONS name and try again.Session で QR コードをスキャンするにはカメラへのアクセスが必要ですカメラへのアクセスを許可する新しいクローズドグループ
@@ -625,6 +627,8 @@
プライバシーチャット通知戦略
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.名前を変更するデバイスのリンクを解除するあなたのリカバリーフレーズ
@@ -670,6 +674,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -682,4 +689,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-ka/strings.xml
+++ b/app/src/main/res/values-ka/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml
index f9c64d2c77..88cafdc1f2 100644
--- a/app/src/main/res/values-km/strings.xml
+++ b/app/src/main/res/values-km/strings.xml
@@ -52,7 +52,7 @@
ផ្ញើបរាជ័យ ចុច សម្រាប់ជំនួសគ្មានសុវត្ថិភាពមិនអាចស្វែករកកម្មវិធី ដើម្បីបើកព័ត៌មាននេះទេ។បានចម្លង %s
- អានបន្ថែម
+ Read More ទាញយកបន្ថែម កំពុងរង់ចាំ
@@ -249,7 +249,7 @@
អ្នកប្រភេទឯកសារមិនគាំទ្រព្រាង
- Sessionត្រូវការសិទ្ធិប្រើប្រាស់អង្គរក្សាទុកដើម្បីរក្សាឯកសារក្នុងអង្គរក្សាទុកខាងក្រៅ ប៉ុន្តែវាត្រូវបានបដិសេធរហូត។ សូមបន្តទៅកាន់ ការកំណត់ ជ្រើសរើស \"ការអនុញ្ញាត\" និងបើក \"អង្គរក្សាទុក\"។
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".មិនអាចរក្សាទុកក្នុងអង្គរក្សាទុកខាងក្រៅ ដោយគ្មានការអនុញ្ញាតលុបសារ?សារនេះនឹងលុប ជារៀងរហូត។
@@ -586,12 +586,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -626,6 +628,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -671,6 +675,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -683,4 +690,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index a3f5897d11..2c8294a12a 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -248,7 +248,7 @@
나지원되지 않는 미디어 형식Draft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -585,12 +585,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -625,6 +627,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -670,6 +674,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -682,4 +689,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml
index 8bf4df6ea1..ef4c12b123 100644
--- a/app/src/main/res/values-lo/strings.xml
+++ b/app/src/main/res/values-lo/strings.xml
@@ -251,7 +251,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -588,12 +588,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -628,6 +630,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -673,6 +677,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -685,4 +692,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index f4ca3d7a48..b37020bec7 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -58,7 +58,7 @@
Siuntimas nepavyko, bakstelėkite, norėdami sugrįžti nesaugaus surogatoNepavyksta rasti programėlės, galinčios atverti šią mediją.%s nukopijuota
- Skaityti daugiau
+ Read More Atsisiųsti daugiau Laukiama
@@ -287,7 +287,7 @@
JūsNepalaikomas medijos tipasJuodraštis
- Norint įrašyti į išorinę saugyklą, Session reikia saugyklos leidimo, tačiau jis buvo visam laikui uždraustas. Pereikite į programėlės nustatymus, pasirinkite \"Leidimai\" ir įjunkite \"Saugyklą\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Nepavyksta įrašyti į išorinę saugyklą be leidimųIštrinti žinutę?Tai visiems laikams ištrins šią žinutę.
@@ -630,12 +630,14 @@
Service NodeDestinationSužinoti daugiau
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesSuteikti prieigą prie kamerosNew Closed Group
@@ -670,6 +672,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Keisti pavadinimąAtsieti įrenginįYour Recovery Phrase
@@ -715,6 +719,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -727,4 +734,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index 6c98843645..aaf955a0ef 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -275,7 +275,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -616,12 +616,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -656,6 +658,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -701,6 +705,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -713,4 +720,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 1f116600ff..860302fa9e 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -260,7 +260,7 @@
သင်ပံ့ပိုးမှုမပြုနိုင်သော ရုပ်၊သံ၊ပုံ ဖိုင်များမူကြမ်း
- သင်၏ ပြင်ပသိုလှောင်ရာနေရာတွင် သိမ်းရန် သိုလှောင်မှုခွင့်ပြုချက်အား Session ကို ပေးရန်လိုအပ်ပါသည်။ သို့သော် လုံးဝခွင့်မပြုပါ ဟုရွေးထားပြီး ဖြစ်နေသဖြင့် အပ်ပလီကေးရှင်း အပြင်အဆင်သို့ သွား၍ ခွင့်ပြုချက်များကို ရွေးချယ်ကာ သိုလှောင်မှုခွင့်ပြုချက်ကို အသုံးပြုနိုင်အောင် ပြုလုပ်ပါ။
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".ခွင့်ပြုချက်မပေးထားပါက အပြင်ဘက်တွင် သိမ်းဆည်း၍မရပါစာကို ဖျက်မှှှှှှှာလား? ဤစာများကို အပြီးတိုင်ဖျက်မည်။
@@ -599,12 +599,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -639,6 +641,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -684,6 +688,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -696,4 +703,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-mn/strings.xml b/app/src/main/res/values-mn/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-mn/strings.xml
+++ b/app/src/main/res/values-mn/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index 8bf4df6ea1..ef4c12b123 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -251,7 +251,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -588,12 +588,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -628,6 +630,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -673,6 +677,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -685,4 +692,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index 79c938fe28..60ae97c1be 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -54,7 +54,7 @@
Sending mislyktes, trykk for usikret falle tilbakeFant ingen programmer som kan åpne dette innholdet.Kopiert %s
- Les Mer
+ Read More Last ned mer Venter
@@ -261,7 +261,7 @@ nøkkelutvekslingsmelding.
DegUstøttet medietypeUtkast
- Session krever tillatelse fra systemet for å kunne skrive til ekstern lagringsenhet, men du har valgt å avslå dette permanent. Gå til «Apper»-menyen på systemet og slå på tillatelsen «Lagring».
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Du kan ikke lagre på ekstern lagringsenhet uten å slå på tillatelse i systemet førstVil du slette denne meldinga?Meldinga blir slettet for godt.
@@ -600,12 +600,14 @@ nøkkelutvekslingsmelding.
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -640,6 +642,8 @@ nøkkelutvekslingsmelding.
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -685,6 +689,9 @@ nøkkelutvekslingsmelding.
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ nøkkelutvekslingsmelding.
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index df9217802c..985100c30f 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -1,14 +1,14 @@
- Session
+ SessieJaNeeWissen
- Ban
+ VerbannenEven geduld…OpslaanNotitie aan mezelf
- Version %s
+ Versie %sNieuw bericht
@@ -23,17 +23,17 @@
Dit zal onmiddellijk alle gesprekken inkorten tot het laatste bericht.Dit zal onmiddellijk alle gesprekken inkorten tot de laatste %d berichten.
- Wissen
+ VerwijderenAanUit(afbeelding)
- (audio)
+ (geluid)(video)(reactie)Geen app gevonden om media te selecteren.
- Session heeft toegang nodig tot de externe opslagruimte om foto\'s, video\'s of audio te kunnen verzenden, maar deze toestemming is pertinent geweigerd. Ga naar de instellingen, selecteer ‘Toestemmingen’ en schakel ‘Opslagruimte’ in.
+ Session heeft toegang nodig tot de externe opslagruimte om foto\'s, video\'s of audio te kunnen verzenden, maar deze toestemming is permanent geweigerd. Ga naar de instellingen, selecteer \"Toestemmingen\" en schakel \"Opslagruimte\" in.Signal heeft toegang nodig tot de contacten om contactinformatie in Session weer te geven, maar deze toestemming is pertinent geweigerd. Ga naar de instellingen, selecteer ‘Toestemmingen’ en schakel ‘Contacten’ in.Session heeft toegang nodig tot de camera om foto\'s te kunnen maken, maar deze toegang is pertinent geweigerd. Ga naar de instellingen, selecteer ‘Toestemmingen’ en schakel ‘Camera’ in.
@@ -54,18 +54,18 @@
Verzenden is mislukt, tik om onbeveiligd te verzendenGeen app gevonden waarmee dit bestand geopend kan openen.%s gekopieerd
- Lees meer
+ Read More Download meer In afwachtingBijlage toevoegenSelecteer contactinformatieSorry, er trad een fout op bij het plaatsen van je bijlage.
- Message
- Compose
- Muted until %1$s
- %1$d members
- Community Guidelines
+ Bericht
+ Opstellen
+ Gedempt tot %1$s
+ %1$d leden
+ Community RichtlijnenOngeldige ontvanger!Aan thuisscherm toegevoegdGroep verlaten?
@@ -77,9 +77,9 @@
Bijlage overschrijdt de maximale grootte voor het type bericht dat je wilt verzenden.Kan audio niet opnemen!Er is geen app beschikbaar op je apparaat om deze koppeling te openen.
- Add members
- Join %s
- Are you sure you want to join the %s open group?
+ Leden toevoegen
+ Deelnemen %s
+ Weet u zeker dat u zich bij de %s open groep wilt aansluiten?Om audioberichten op te nemen, moet je Session toegang geven tot je microfoon.Session heeft toegang nodig tot de microfoon om audioberichten te kunnen opnemen, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, selecteer ‘Machtigingen’ en schakel ‘Microfoon’ in.Geef Session toegang tot de camera om foto\'s en video\'s te maken.
@@ -102,7 +102,7 @@
Dit zal het geselecteerde bericht onherroepelijk wissen.Dit zal alle %1$d geselecteerde berichten onherroepelijk wissen.
- Ban this user?
+ Deze gebruiker verbannen?Opslaan naar SD-kaart?Als je dit bestand opslaat op de SD-kaart wordt het niet versleuteld opgeslagen en hebben alle andere apps op je apparaat toegang tot dit bestand.\n\nDoorgaan?
@@ -126,8 +126,8 @@
Sms (onbeveiligd)Aan het wissenBerichten aan het wissen…
- Banning
- Banning user…
+ Aan het verbannen
+ Gebruiker aan het verbannen... Oorspronkelijk bericht niet gevondenHet oorspronkelijke bericht is niet langer beschikbaar
@@ -140,7 +140,7 @@
GeenNu
- %d min
+ %d minutenVandaagGisteren
@@ -212,7 +212,7 @@
DeblokkerenAfbeelding
- Audio
+ GeluidVideoBeschadigd sleuteluitwisselingsbericht
@@ -236,14 +236,14 @@
%s maakt gebruik van Session!Zelf-wissende berichten uitgeschakeldBerichten zullen zichzelf wissen na %s
- %s took a screenshot.
- Media saved by %s.
+ %s heeft een schermafbeelding genomen.
+ Media opgeslagen door %s.Veiligheidsnummer veranderdJe veiligheidsnummer met %s is veranderd.Door jou als geverifieerd gemarkeerdDoor jou als niet geverifieerd gemarkeerd
- This conversation is empty
- Open group invitation
+ Dit gesprek is leeg
+ Openbare groepsuitnodigingNieuwe versie van SessionEr is een nieuwe versie van Session beschikbaar, tik om bij te werken
@@ -263,7 +263,7 @@
JijNiet-ondersteund mediatypeConcept
- Session heeft toegang tot externe opslagruimte nodig om iets op te slaan op de externe opslag, maar deze toegang is pertinent geweigerd. Ga naar de instellingen voor deze app, selecteer ‘Machtigingen’ en schakel ‘Opslagruimte’ in.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Kan niet opslaan naar externe opslag zonder machtigingBericht wissen?Dit bericht zal onherroepelijk gewist worden.
@@ -302,7 +302,7 @@
Ongeldige snelkoppeling
- Session
+ SessieNieuw bericht
@@ -312,8 +312,8 @@
Fout bij afspelen van video
- Audio
- Audio
+ Geluid
+ GeluidContactpersoonContactpersoonCamera
@@ -356,13 +356,13 @@
PauzerenDownloaden
- Join
- Open group invitation
- Pinned message
- Community guidelines
- Read
+ Aansluiten
+ Openbare groepsuitnodiging
+ Vastgezet bericht
+ Community richtlijnen
+ Gelezen
- Audio
+ GeluidVideoFotoJij
@@ -490,7 +490,7 @@
BerichtdetailsTekst kopiërenBericht wissen
- Ban user
+ Verban gebruikerBericht opnieuw verzendenReageren
@@ -550,153 +550,176 @@
Inactiviteitsduur voor schermvergrendelingGeen
- Copy public key
+ Publieke sleutel kopiëren
- Continue
- Copy
- Invalid URL
- Copied to clipboard
- Next
- Share
- Invalid Session ID
- Cancel
- Your Session ID
- Your Session begins here...
- Create Session ID
- Continue Your Session
- What\'s Session?
- It\'s a decentralized, encrypted messaging app
- So it doesn\'t collect my personal information or my conversation metadata? How does it work?
- Using a combination of advanced anonymous routing and end-to-end encryption technologies.
- Friends don\'t let friends use compromised messengers. You\'re welcome.
- Say hello to your Session ID
- Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.
- Restore your account
- Enter the recovery phrase that was given to you when you signed up to restore your account.
- Enter your recovery phrase
- Pick your display name
- This will be your name when you use Session. It can be your real name, an alias, or anything else you like.
- Enter a display name
- Please pick a display name
- Please pick a shorter display name
- Recommended
- Please Pick an Option
- You don\'t have any contacts yet
- Start a Session
- Are you sure you want to leave this group?
- "Couldn't leave group"
- Are you sure you want to delete this conversation?
- Conversation deleted
- Your Recovery Phrase
- Meet your recovery phrase
- Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\'t give it to anyone.
- Hold to reveal
- You\'re almost finished! 80%
- Secure your account by saving your recovery phrase
- Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.
- Make sure to store your recovery phrase in a safe place
- Path
- Session hides your IP by bouncing your messages through several Service Nodes in Session\'s decentralized network. These are the countries your connection is currently being bounced through:
- You
- Entry Node
- Service Node
- Destination
- Learn More
- New Session
- Enter Session ID
- Scan QR Code
- Scan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
- Users can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
- Session needs camera access to scan QR codes
- Grant Camera Access
- New Closed Group
- Enter a group name
- You don\'t have any contacts yet
- Start a Session
- Please enter a group name
- Please enter a shorter group name
- Please pick at least 1 group member
- A closed group cannot have more than 100 members
- Join Open Group
- Couldn\'t join group
- Open Group URL
- Scan QR Code
- Scan the QR code of the open group you\'d like to join
- Enter an open group URL
- Settings
- Enter a display name
- Please pick a display name
- Please pick a shorter display name
+ Doorgaan
+ Kopiëren
+ Ongeldige URL
+ Gekopieerd naar klembord
+ Volgende
+ Delen
+ Ongeldige Sessie-ID
+ Annuleren
+ Uw Sessie-ID
+ Uw sessie begint hier...
+ Sessie-ID aanmaken
+ Doorgaan met uw sessie
+ Wat is Sessie?
+ Het is een gedecentraliseerde, versleutelde berichten-app
+ Dus het verzamelt geen persoonlijke informatie of de metagegevens van mijn gesprek? Hoe werkt het?
+ Met behulp van een combinatie van geavanceerde anonieme routing en end-to-end encryptietechnologieën.
+ Vrienden laten vrienden geen gecomprimeerde berichten apps gebruiken. Graag gedaan.
+ Zeg hallo tegen uw Sessie-ID
+ Uw Sessie-ID is het unieke adres dat mensen kunnen gebruiken om contact met u op te nemen via Sessie. Zonder verbinding met je echte identiteit, is je Sessie-ID volledig anoniem en privé.
+ Uw account herstellen
+ Voer de herstel zin in die u hebt gekregen toen u uwzelf hebt aangemeld om uw account te herstellen.
+ Voer uw herstel zin in
+ Kies je weergavenaam
+ Dit is je naam wanneer je Sessie gebruikt. Het kan je echte naam zijn, een alias, of wat je maar wilt.
+ Voer een weergavenaam in
+ Kies a.u.b. een weergavenaam
+ Kies a.u.b. een kortere weergavenaam
+ Aanbevolen
+ Gelieve een optie te selecteren
+ U heeft nog geen contactpersonen
+ Sessie starten
+ Weet u zeker dat u deze groep wilt verlaten?
+ "Kon de groep niet verlaten"
+ Weet u zeker dat u deze groep wilt verlaten?
+ Gesprek verwijderd
+ Uw herstel zin
+ Maak kennis met uw herstel zin
+ Uw herstel zin is de hoofdsleutel van uw Sessie-ID - u kunt deze gebruiken om uw Sessie-ID te herstellen als u de toegang tot uw apparaat verliest. Sla uw herstel zin op op een veilige plaats op en geef het aan niemand anders.
+ Ingedrukt houden om te onthullen
+ Je bent bijna klaar! 80%
+ Beveilig uw account door uw herstel zin op te slaan
+ Hou de herschreven woorden ingedrukt om uw herstelzin te onthullen, en sla het vervolgens veilig op om uw Sessie-ID te beveiligen.
+ Zorg ervoor dat u uw herstel zin op een veilige plek opslaat
+ Locatie
+ Session verbergt uw IP door uw berichten te routeren via meerdere Service Nodes in het gedecentraliseerde netwerk van Sessie. Dit zijn de landen die uw verbinding momenteel doorvoert:
+ U
+ Invoer node
+ Service node
+ Bestemming
+ Kom meer te weten
+ Resolving…
+ Nieuwe sessie
+ Uw Sessie-ID
+ Scan QR-code
+ 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.
+ Enter Session ID or ONS name
+ 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.
+ Please check the Session ID or ONS name and try again.
+ Sessie heeft cameratoegang nodig om QR-codes te scannen
+ Toegang tot camera verlenen
+ Nieuwe gesloten groep
+ Vul een groepsnaam in
+ U heeft nog geen contactpersonen
+ Sessie starten
+ Vul een groepsnaam in
+ Vul a.u.b. een kortere groepsnaam in
+ Kies ten minste één groepslid
+ Een gesloten groep kan niet meer dan 100 leden hebben
+ Deelnemen aan open groep
+ Kon niet deelnemen aan de groep
+ Groep-URL openen
+ Scan QR-code
+ Scan de QR-code van de open groep waar u zich bij wilt aansluiten
+ Voer een open groep URL in
+ Instellingen
+ Voer een weergavenaam in
+ Kies a.u.b. een weergavenaam
+ Kies a.u.b. een kortere weergavenaamPrivacy
- Notifications
- Chats
- Devices
- Invite
- Recovery Phrase
- Clear Data
- Help us Translate Session
- Notifications
- Notification Style
- Notification Content
+ Meldingen
+ Gesprekken
+ Apparaten
+ Uitnodigen
+ Herstel zin
+ Gegevens wissen
+ Help ons om Sessie the vertalen
+ Meldingen
+ Notificatie stijl
+ Notificatie inhoudPrivacy
- Chats
- Notification Strategy
- Change name
- Unlink device
- Your Recovery Phrase
- This is your recovery phrase. With it, you can restore or migrate your Session ID to a new device.
- Clear All Data
- This will permanently delete your messages, sessions, and contacts.
- QR Code
- View My QR Code
- Scan QR Code
- Scan someone\'s QR code to start a conversation with them
- Scan Me
- This is your QR code. Other users can scan it to start a session with you.
- Share QR Code
- Contacts
- Closed Groups
- Open Groups
- You don\'t have any contacts yet
+ Gesprekken
+ Notificatie strategie
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.
+ Naam wijzigen
+ Apparaat ontkoppelen
+ Uw herstel zin
+ Dit is uw herstel zin, Hiermee kun je je Sessie-ID herstellen of migreren naar een nieuw apparaat.
+ Wis alle gegevens
+ Hiermee worden uw berichten, sessies en contacten permanent verwijderd.
+ QR-code
+ Bekijk mijn QR-code
+ Scan QR-code
+ Scan iemands QR-code om een gesprek te beginnen
+ Scan mij
+ Dit is uw QR-code. Andere gebruikers kunnen deze scannen om een sessie met u te starten.
+ QR-code delen
+ Contacten
+ Gesloten groepen
+ Open groepen
+ U heeft nog geen contactpersonen
- Apply
- Done
- Edit Group
- Enter a new group name
- Members
- Add members
- Group name can\'t be empty
- Please enter a shorter group name
- Groups must have at least 1 group member
- Remove user from group
- Select Contacts
- Secure session reset done
- Theme
- Day
- Night
- System default
- Copy Session ID
- Attachment
- Voice Message
+ Toepassen
+ Klaar
+ Groep bewerken
+ Vul een nieuw groepsnaam in
+ Leden
+ Leden toevoegen
+ De groepsnaam mag niet leeg zijn
+ Vul a.u.b. een kortere groepsnaam in
+ Groepen moeten ten minste één groepslid hebben
+ Gebruiker uit de groep verwijderen
+ Contacten selecteren
+ Beveiligde sessie opnieuw ingesteld
+ Thema
+ Dag
+ Nacht
+ Systeemstandaard
+ Sessie-ID kopiëren
+ Bijlage
+ SpraakberichtDetails
- Failed to activate backups. Please try again or contact support.
- Restore backup
- Select a file
- Select a backup file and enter the passphrase it was created with.
- 30-digit passphrase
- This is taking a while, would you like to skip?
- Link a Device
- Or join one of these…
- Message Notifications
- There are two ways Session can notify you of new messages.
- Fast Mode
- Slow Mode
- You’ll be notified of new messages reliably and immediately using Google’s notification servers.
- Session will occasionally check for new messages in the background.
- Recovery Phrase
- Session is Locked
- Tap to Unlock
- Enter a nickname
- Invalid public key
+ Activeren van back-ups mislukt. Probeer het opnieuw of neem contact op met het ondersteuningsteam.
+ Back-up terugzetten
+ Selecteer een bestand
+ Selecteer een backup-bestand en voer de wachtwoordzin in waarmee het is gemaakt.
+ Wachtwoordzin met 30 tekens
+ Dit duurt een tijdje, wilt u het overslaan?
+ Koppel een apparaat
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.
+ Of neem deel aan een van deze...
+ Berichtmeldingen
+ Er zijn twee manieren waarop de Sessie u op de hoogte kan stellen van nieuwe berichten.
+ Snelle modus
+ Langzame modus
+ U wordt op een betrouwbare en directe manier op de hoogte gebracht van nieuwe berichten via Googles notificatieservers.
+ Sessie controleert af en toe op nieuwe berichten op de achtergrond.
+ Herstel zin
+ Sessie is vergrendeld
+ Tik om te ontgrendelen
+ Bijnaam invoeren
+ Ongeldige publieke sleutel
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml
index 88338b2f55..43fc42a8bb 100644
--- a/app/src/main/res/values-nn-rNO/strings.xml
+++ b/app/src/main/res/values-nn-rNO/strings.xml
@@ -54,7 +54,7 @@
Feil ved sending, trykk for å senda med utrygg alternativ metodeFann inga program som kan opna dette innhaldet.Kopierte %s
- Les meir
+ Read More Last ned meir Ventar
@@ -261,7 +261,7 @@ nøkkelutvekslingsmelding.
DegUstøtta medietypeKladd
- Session treng tilgang til lagring for å lagra til eksterne minnekort, men tilgangen er permanent avslått. Opna app-innstillingsmenyen og vel «Tilgang» – eventuelt «Tillatelser» – og skru på «Lagring».
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Kan ikkje lagra til eksterne minnekort utan tilgangSlett melding?Dette vil sletta denne meldinga for godt.
@@ -600,12 +600,14 @@ nøkkelutvekslingsmelding.
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -640,6 +642,8 @@ nøkkelutvekslingsmelding.
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -685,6 +689,9 @@ nøkkelutvekslingsmelding.
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ nøkkelutvekslingsmelding.
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-no/strings.xml
+++ b/app/src/main/res/values-no/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-notnight-v21/colors.xml b/app/src/main/res/values-notnight-v21/colors.xml
index 84b91b29dd..e1f6dcef95 100644
--- a/app/src/main/res/values-notnight-v21/colors.xml
+++ b/app/src/main/res/values-notnight-v21/colors.xml
@@ -5,7 +5,6 @@
#000000#f26f55#606060
- #23000000#B0B0B0#FCFCFC#DFDFDF
diff --git a/app/src/main/res/values-ny/strings.xml b/app/src/main/res/values-ny/strings.xml
index 062860dcc1..74e45903fc 100644
--- a/app/src/main/res/values-ny/strings.xml
+++ b/app/src/main/res/values-ny/strings.xml
@@ -261,7 +261,7 @@ ranti pakalla yupaykunamanta
KikinTiyashkakunapa imashinakayka mana chaypurachu. Pichak
- Session ari nichun munanmi kanllapi allichina ukuman (SDcard, USB) yaykushpa allichinkapak, shinapash mana arinirishkachu. \"Menú de configuración\" ukuman yaykushpa \"Permisos\" nikukta llapipay, \"Almacenamiento\" paskarichun.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Kanllapi allichita mana ushanchikchu kikin mana arinikpika.Chaskita pichanayanchuKayka kay chaskita pichankapachami.
@@ -600,12 +600,14 @@ ranti pakalla yupaykunamanta
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -640,6 +642,8 @@ ranti pakalla yupaykunamanta
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -685,6 +689,9 @@ ranti pakalla yupaykunamanta
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ ranti pakalla yupaykunamanta
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-pa-rIN/strings.xml b/app/src/main/res/values-pa-rIN/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-pa-rIN/strings.xml
+++ b/app/src/main/res/values-pa-rIN/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 74180d2232..89bc16fb1c 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -4,7 +4,7 @@
TakNieUsuń
- Ban
+ BanujProszę czekać...ZapiszMoje notatki
@@ -58,18 +58,18 @@
Wysyłanie nie powiodło się, dotknij w celu użycia niezabezpieczonej alternatywyNie można znaleźć aplikacji, aby otworzyć tę zawartość.Skopiowano %s
- Czytaj dalej
+ Przeczytaj więcej Pobierz więcej OczekiwanieDodaj załącznikWybierz dane kontaktowePrzepraszamy, wystąpił błąd podczas dodawania załącznika.
- Message
- Compose
- Muted until %1$s
- %1$d members
- Community Guidelines
+ Wiadomość
+ Napisz
+ Wyciszono do %1$s
+ %1$d członków
+ Wskazówki społecznościNieprawidłowy adresat!Dodano do ekranu głównegoOpuścić grupę?
@@ -81,9 +81,9 @@
Rozmiar tego załącznika przekracza limit wiadomości.Nie udało się nagrać dźwięku!Brak aplikacji do obsługi tego linku na Twoim urządzeniu.
- Add members
- Join %s
- Are you sure you want to join the %s open group?
+ Dodaj członków
+ Dołącz do %s
+ Czy na pewno chcesz dołączyć do otwartej grupy %s , ?Aby wysyłać wiadomości głosowe, zezwól Session na dostęp do mikrofonu.Session wymaga pozwolenia na dostęp do mikrofonu w celu umożliwienia wysyłania wiadomości głosowych, ale zostało one na stałe odrzucone. Przejdź do ustawień aplikacji, wybierz \"Uprawnienia\" i włącz \"Mikrofon\".Aby móc robić zdjęcia i nagrywać wideo, zezwól Session na dostęp do aparatu.
@@ -112,7 +112,7 @@
Spowoduje to trwałe usunięcie wszystkich %1$d zaznaczonych wiadomości.Spowoduje to trwałe usunięcie wszystkich %1$d wybranych wiadomości.
- Ban this user?
+ Zbanować tego użytkownika?Zapisać na dysk?Zapisanie tego załącznika na dysku pozwoli na dostęp do nich innym aplikacjom na Twoim urządzeniu.\n\nKontynuuj?
@@ -141,11 +141,11 @@
Oczekiwanie...Dane (Session)MMS
- SMS
+ رسالة نصيةUsuwanieUsuwanie wiadomości...
- Banning
- Banning user…
+ Blokowanie
+ Blokowanie użytkownika…Nie znaleziono oryginalnej wiadomościOryginalna wiadomość nie jest już dostępna
@@ -205,11 +205,11 @@
Wyślij do %sDodaj podpis...
- Pozycja została usunięta, ponieważ przekroczyła limit wielkości
- Aparat niedostępny.
+ Element został usunięty, ponieważ przekroczył limit rozmiaru
+ Aparat jest niedostępny.Wiadomość do %s
- Nie można udostępnić więcej niż %d plik multimediów.
+ Nie można udostępnić więcej niż %d pliku multimediów.Nie można udostępnić więcej niż %d multimediów.Nie można udostępnić więcej niż %d multimediów.Nie można udostępnić więcej niż %d multimediów.
@@ -217,14 +217,14 @@
Wszystkie multimedia
- Otrzymano szyfrowaną wiadomość wysłaną przy użyciu starej wersji Session, która nie jest już wspierana. Poproś nadawcę tej wiadomości o zaktualizowanie aplikacji do najnowszej wersji i przesłanie wiadomości ponownie.
+ Otrzymano zaszyfrowaną wiadomość wysłaną przy użyciu starej wersji Session, która nie jest już wspierana. Poproś nadawcę tej wiadomości o zaktualizowanie aplikacji do najnowszej wersji i przesłanie wiadomości ponownie.Opuściłeś(aś) grupę.Zaktualizowałeś(aś) grupę.%s zaktualizował(a) grupę.Znikające wiadomościTwoje wiadomości nie wygasną.
- Wiadomości wysłane i odebrane w tej rozmowie znikną %s po odczytaniu.
+ Wiadomości wysłane i odebrane w tej rozmowie znikną po %s od odczytania.Podaj hasło
@@ -236,7 +236,7 @@
OdblokujObraz
- Audio
+ DźwiękWideoOtrzymano uszkodzony klucz!
@@ -258,13 +258,13 @@
Znikające wiadomości zostały wyłączoneCzas znikania wiadomości ustawiony na %s%s wykonał zrzut ekranu.
- Media saved by %s.
+ Media zapisane przez %s.Numer bezpieczeństwa zmienił sięTwój numer bezpieczeństwa z %s zmienił się.Oznaczyłeś(aś) jako zweryfikowanyOznaczyłeś(aś) jako niezweryfikowanyTen wątek jest pusty
- Open group invitation
+ Otwórz zaproszenie do grupyAktualizacja SessionDostępna jest nowa wersja Session. Dotknij, aby zaktualizować
@@ -278,7 +278,7 @@
Wycisz powiadomieniaDotknij, aby otworzyć.
- Session jest niezablokowany
+ Session jest odblokowanyZablokuj SessionTy
@@ -335,7 +335,7 @@
Błąd przy odtwarzaniu filmu
- Audio
+ DźwiękDźwiękKontaktKontakt
@@ -349,7 +349,7 @@
PlikGaleriaPlik
- Ukryj ekran załączników
+ Przełącz listę załącznikówŁadowanie listy kontaktów...
@@ -369,7 +369,7 @@
Bezpieczna wiadomośćNie udało się wysłać
- Oczekuję akceptacji
+ Oczekuje akceptacjiDostarczonoWiadomość odczytana
@@ -379,11 +379,11 @@
PauzaPobierz
- Join
- Open group invitation
- Pinned message
- Community guidelines
- Read
+ Dołącz
+ Otwórz zaproszenie do grupy
+ Przypięta wiadomość
+ Wskazówki społeczności
+ CzytajDźwiękWideo
@@ -391,7 +391,7 @@
TyNie znaleziono oryginalnej wiadomości
- Przewiń do dołu
+ Przewiń do końcaSzukaj gifów i naklejek
@@ -433,7 +433,7 @@
Tylko imieBrak imienia lub wiadomościZdjęcia
- Audio
+ DźwiękWideoDokumentyMały
@@ -470,7 +470,7 @@
Trzy razyPięć razyDziesięć razy
- Wibracja
+ WibracjeZielonyCzerwonyNiebieski
@@ -482,7 +482,7 @@
SzybkoNormalnieWolno
- Automatycznie usuń starsze wiadomości po przekroczeniu określonej długości konwersacji
+ Automatycznie usuwaj starsze wiadomości po przekroczeniu określonej długości konwersacjiUsuń stare wiadomościLimit długości konwersacjiPrzytnij wszystkie konwersacje teraz
@@ -515,7 +515,7 @@
Szczegóły wiadomościKopiuj tekstUsuń wiadomość
- Ban user
+ Zablokuj użytkownikaWyślij ponownie wiadomośćOdpowiedz na wiadomość
@@ -593,7 +593,7 @@
To zdecentralizowana, szyfrowana aplikacja do przesyłania wiadomościWięc nie zbiera moich danych osobowych ani metadanych z mojej rozmowy? Jak to działa?Wykorzystując połączenie zaawansowanych anonimowych tras i technologii szyfrowania end-to-end.
- Znajomi nie pozwalają znajomym korzystać z zainfekowanych komunikatorów. Nie ma za co.
+ Przyjaciele nie pozwalają przyjaciołom korzystać z niezabezpieczonych komunikatorów. Nie ma za co.Przywitaj się z identyfikatorem SessionTwój identyfikator Session to unikalny adres, za pomocą którego można się z Tobą kontaktować w Session. Bez połączenia z twoją prawdziwą tożsamością, identyfikator Session jest z założenia całkowicie anonimowy i prywatny.Przywróć swoje konto
@@ -616,7 +616,7 @@
Poznaj swoją frazę odzyskiwaniaTwoja fraza odzyskiwania jest głównym kluczem do identyfikatora Session - możesz jej użyć do przywrócenia identyfikatora Session, jeśli stracisz dostęp do urządzenia. Przechowuj swoją frazę odzyskiwania w bezpiecznym miejscu i nikomu jej nie udostępniaj.Przytrzymaj, aby odsłonić
- You\'re almost finished! 80%
+ Prawie skończyłeś! 80%Zabezpiecz swoje konto, zapisując frazę odzyskiwaniaStuknij i przytrzymaj zredagowane słowa, aby odsłonić frazę odzyskiwania, a następnie przechowuj ją w bezpiecznym miejscu, aby zabezpieczyć identyfikator Session.Pamiętaj, aby przechowywać frazę odzyskiwania w bezpiecznym miejscu
@@ -627,12 +627,14 @@
Węzeł serwisowyMiejsce doceloweDowiedz się więcej
+ Resolving…Nowa sesjaWpisz identyfikator SessionSkanowania QR codeZeskanuj kod QR użytkownika, aby rozpocząć sesję. Kody QR można znaleźć, dotykając ikony kodu QR w ustawieniach konta.
- Wprowadź identyfikator Session odbiorcy
+ Wprowadź ID sesji lub nazwę ONSUżytkownicy mogą udostępnić swój identyfikator Session, przechodząc do ustawień konta i klikając opcję Udostępnij identyfikator Session lub udostępniając kod QR.
+ Proszę sprawdzić ID sesji lub nazwę ONS i spróbować ponownie.Session wymaga dostępu do kamery, aby skanować kody QRUdziel dostępu do kameryNowa grupa zamknięta
@@ -641,8 +643,8 @@
Rozpocznij sesjęWpisz nazwę grupyWprowadź krótszą nazwę grupy
- Please pick at least 1 group member
- A closed group cannot have more than 100 members
+ Proszę wybrać co najmniej 1 członka grupy
+ Zamknięta grupa nie może mieć więcej niż 100 członkówDołącz do otwartej grupyNie można dołączyć do grupyOtwórz adres URL grupy
@@ -667,6 +669,8 @@
PrywatnośćCzatyStrategia powiadomień
+ Użyj trybu szybkiego
+ Zostaniesz natychmiast powiadomiony o nowych wiadomościach w niezawodny sposób przy użyciu serwerów powiadomień Google.Zmień nazwęOdłącz urządzenieTwoja fraza odzyskiwania
@@ -677,13 +681,13 @@
Wyświetl mój kod QRSkanowania QR codeZeskanuj czyjś kod QR, aby rozpocząć z nim rozmowę
- Scan Me
+ Zeskanuj mnieTo jest twój kod QR. Inni użytkownicy mogą go zeskanować, aby rozpocząć z tobą sesję.Udostępnij kod QRŁącznośćGrupy zamknięteOtwórz grupy
- You don\'t have any contacts yet
+ Nie masz jeszcze żadnych kontaktówZastosujGotowe
@@ -692,36 +696,55 @@
CzłonkowieDodaj członkówNazwa grupy nie może być pusta
- Please enter a shorter group name
- Groups must have at least 1 group member
- Remove user from group
- Select Contacts
- Secure session reset done
+ Proszę wprowadzić krótszą nazwę grupy
+ Grupy muszą mieć co najmniej 1 członka
+ Usuń użytkownika z grupy
+ Wybierz kontakty
+ Wykonano bezpieczne zresetowanie sesjiMotyw
- Day
- Night
- System default
- Copy Session ID
+ Dzień
+ Noc
+ Domyślny dla systemu
+ Skopiuj ID sesjiZałącznikWiadomość głosowaSzczegóły
- Failed to activate backups. Please try again or contact support.
- Restore backup
- Select a file
- Select a backup file and enter the passphrase it was created with.
- 30-digit passphrase
+ Nie udało się aktywować kopii zapasowych. Spróbuj ponownie lub skontaktuj się z pomocą techniczną.
+ Przywróć kopię zapasową
+ Wybierz plik
+ Wybierz plik kopii zapasowej i wpisz hasło, z którym został utworzony.
+ Hasło złożone z 30 znakówTo zajmuje chwilę, czy chcesz pominąć?
- Link a Device
- Or join one of these…
- Message Notifications
- There are two ways Session can notify you of new messages.
- Fast Mode
- Slow Mode
- You’ll be notified of new messages reliably and immediately using Google’s notification servers.
- Session will occasionally check for new messages in the background.
- Recovery Phrase
- Session is Locked
+ Połącz urządzenie
+ Fraza odzyskiwania
+ Skanuj kod QR
+ Przejdź do Ustawień → Fraza odzyskiwania na innym urządzeniu, aby wyświetlić kod QR.
+ Lub dołącz do jednego z tych…
+ Powiadomienia o wiadomości
+ Istnieją dwa sposoby powiadamiania o nowych wiadomościach.
+ Tryb szybki
+ Tryb wolny
+ Zostaniesz natychmiast powiadomiony o nowych wiadomościach w niezawodny sposób przy użyciu serwerów powiadomień Google.
+ Session będzie czasami sprawdzać w tle czy są nowe wiadomości.
+ Fraza odzyskiwania
+ Session jest zablokowanyDotknij by odblokować
- Enter a nickname
- Invalid public key
+ Wpisz pseudonim
+ Nieprawidłowy klucz publiczny
+ Dokument
+ Odblokować %s?
+ Czy na pewno chcesz odblokować %s?
+ Dołącz do %s?
+ Czy na pewno chcesz dołączyć do otwartej grupy %s?
+ Otworzyć URL?
+ Czy na pewno chcesz otworzyć %s?
+ Otwórz
+ Włączyć podgląd linków?
+ Włączanie podglądów linków wyświetli podgląd dla adresów URL które wysyłasz i otrzymujesz. Może to być użyteczne, ale Session będzie musiał się połączyć ze stronami których linki wysyłasz aby wygenerować podgląd. Możesz zawsze wyłączyć podgląd linków w ustawieniach Session.
+ Włącz
+ Zaufać %s?
+ Czy na pewno chcesz pobrać multimedia wysłane przez %s?
+ Pobierz
+ %s jest zablokowany. Odblokować?
+ Nie udało się przygotować załącznika do wysłania.
diff --git a/app/src/main/res/values-ps/strings.xml b/app/src/main/res/values-ps/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-ps/strings.xml
+++ b/app/src/main/res/values-ps/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 3b6c1799f0..3c591304ac 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -54,7 +54,7 @@
Falha no envio, toque para fallback inseguroNão é possível encontrar um app para abrir esta mídia.Copiou %s
- Ler Mais
+ Read More Fazer Download de Mais Pendendo
@@ -263,7 +263,7 @@
VocêTipo de mídia não-suportadoRascunho
- Session precisa da permissão Armazenamento a fim de salvar em armazenamento externo, mas ela foi permanentemente negada. Por favor continue para configurações do app, selecione \"Permissões\", e habilite \"Armazenamento\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Não é possível salvar em armazenamento externo sem permissõesDeletar mensagem?Isto vai permanentemente deletar esta mensagem.
@@ -602,12 +602,14 @@
Nó de ServiçoDestinoSaber mais
+ Resolving…Nova SessãoDigite o ID SessionEscanear código QREscaneie o código QR de um usuário para iniciar uma sessão. Os códigos QR podem ser encontrados tocando no ícone de código QR nas configurações da conta.
- Digite o ID Session do destinatário
+ Enter Session ID or ONS nameOs usuários podem compartilhar seus IDs Session acessando as configurações da conta e tocando em Compartilhar ID Session, ou compartilhando o código QR.
+ Please check the Session ID or ONS name and try again.O Session precisa de acesso à câmera para escanear códigos QRConceder acesso à câmeraNovo grupo fechado
@@ -642,6 +644,8 @@
PrivacidadeChatsEstratégia de Notificação
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Mudar nomeDeslinkar dispositivoSua Frase de Recuperação
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index ec967880c1..897ed5809a 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -54,7 +54,7 @@
Falha ao enviar, pressione para enviar pelo modo inseguroNão foi possível encontrar uma aplicação capaz de abrir este ficheiro.Copiado %s
- Ler mais
+ Read More Descarregar mais Pendente
@@ -261,7 +261,7 @@ foi recebida está corrompida!
VocêTipo de multimédia não suportadoRascunho
- O Session requer permissão de acesso ao armazenamento para escrever no suporte de armazenamento externo, mas esta foi negada permanentemente. Por favor, aceda às definições da aplicação, seleccione \"Permissões\" e active \"Armazenamento\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Não é possível guardar para o armazenamento externo sem permissões.Apagar mensagem?Isto irá eliminar permanentemente esta mensagem.
@@ -600,12 +600,14 @@ foi recebida está corrompida!
Nó de ServiçoDestinoSaber mais
+ Resolving…Nova SessãoDigite o ID SessionEscanear código QREscaneie o código QR de um usuário para iniciar uma sessão. Os códigos QR podem ser encontrados tocando no ícone de código QR nas configurações da conta.
- Digite o ID Session do destinatário
+ Enter Session ID or ONS nameOs usuários podem compartilhar seus IDs Session acessando as configurações da conta e tocando em Compartilhar ID Session, ou compartilhando o código QR.
+ Please check the Session ID or ONS name and try again.O Session precisa de acesso à câmera para escanear códigos QRConceder acesso à câmeraNovo grupo fechado
@@ -640,6 +642,8 @@ foi recebida está corrompida!
PrivacidadeBate-paposEstratégia de notificação
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Mudar o nomeDessincronizar dispositivoSua frase de recuperação
@@ -685,6 +689,9 @@ foi recebida está corrompida!
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ foi recebida está corrompida!
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 2b69ebb0a4..78536f0515 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -56,7 +56,7 @@
Trimitere eșuată, apăsați pentru varianta nesecurizatăNu a fost găsită nici o aplicație pentru a deschide acest tip media.Copiat %s
- Citiți mai multe
+ Read More Descărcați mai multe În curs
@@ -273,7 +273,7 @@ schimbul de chei este corupt
TuTip media nesuportatCiornă
- Session are nevoie de permisiunea pentru spațiul de stocare extern pentru a salva pe spațiul de stocare extern, dar i-a fost refuzat accesul permanent. Vă rog navigați în meniul de setări al aplicației, selectați \"Permisiuni\" și activați \"Spațiu de stocare\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Nu se poate salva pe spațiu de stocare extern fără permisiuneȘterg mesajul?Această acțiune va șterge permanent mesajul.
@@ -614,12 +614,14 @@ schimbul de chei este corupt
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -654,6 +656,8 @@ schimbul de chei este corupt
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -699,6 +703,9 @@ schimbul de chei este corupt
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -711,4 +718,20 @@ schimbul de chei este corupt
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 7ddbc99f4c..85b12e4397 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -4,7 +4,7 @@
ДаНетУдалить
- Запретить доступ
+ ЗаблокироватьПожалуйста, подождите...СохранитьЗаметка для себя
@@ -37,7 +37,7 @@
(ответ)Не найдено приложение для выбора медиа-файлов.
- Signal требуется разрешение на доступ к хранилищу для возможности прирепления фото, видео и аудио, но оно было вами отклонено. Чтобы предоставить разрешение вручную, перейдите Настройки, выберите Приложения, найдите Session, затем выберите Разрешения и включите Хранилище.
+ Session требуется разрешение на доступ к хранилищу для возможности прирепления фото, видео и аудио, но оно было вами отклонено. Чтобы предоставить разрешение вручную, перейдите Настройки, выберите Приложения, найдите Session, затем выберите Разрешения и включите Хранилище.Для прикрепления контактной информации Signal требуется разрешение на доступ к контактам, но оно было вами отклонено. Чтобы предоставить разрешение вручную, перейдите Настройки, выберите Приложения, найдите Session, затем выберите Разрешения и включите Контакты.Чтобы сделать фото требуется разрешение на доступ к камере, но оно было вами отклонено. Чтобы предоставить разрешение вручную, перейдите Настройки, выберите Приложения, найдите Session, затем выберите Разрешения и включите Камеру.
@@ -58,7 +58,7 @@
Отправка не удалась, нажмите для негарантированного откатаНе найдено приложение, которое может открыть этот медиа-файл.Скопировано: %s
- Читать далее
+ Read More Скачать больше Ожидание
@@ -285,7 +285,7 @@
ВыТип медиа-файла не поддерживаетсяЧерновик
- Signal требуется разрешение на доступ к хранилищу для сохранения вложений, но оно было вами отклонено. Чтобы предоставить разрешение вручную, перейдите Настройки, выберите Приложения, найдите Session, затем выберите Разрешения и включите Хранилище.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Не удается сохранить во внешнем хранилище без разрешенийУдалить сообщение?Это сообщение будет навсегда удалено.
@@ -628,12 +628,14 @@
Служебный узелМесто назначенияУзнать больше
+ Resolving…Новый ДиалогВведите Session IDСканировать QR-кодСканируйте QR-код пользователя, чтобы начать сессию. QR-коды можно найти, нажав значок QR-кода в настройках учетной записи.
- Введите Session ID получателя
+ Enter Session ID or ONS nameПользователи могут поделиться своим Session ID, зайдя в настройки своей учетной записи и нажав «Отправить Session ID», или поделившись своим QR-кодом.
+ Please check the Session ID or ONS name and try again.Session нужен доступ к камере для сканирования QR-кодовПредоставить доступ к камереНовая закрытая группа
@@ -668,6 +670,8 @@
КонфиденциальностьЧатыМетод уведомлений
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Сменить имяОтключить устройствоВаша секретная фраза
@@ -713,16 +717,35 @@
Фраза из 30 цифрЭто занимает некоторое время, хотите пропустить?Привязать устройство
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Или присоединитесь к одной из этих...Уведомления о СообщенияхСуществует два способа оповещения о новых сообщениях.Ускоренный режимМедленный режимВы будете получать уведомления о новых сообщениях надежно и сразу же с помощью серверов уведомлений Google.
- Время от времени (в фоновом режиме) будут проверяться новые сообщения.
+ Session время от времени в фоновом режиме будeт проверять и получать новые сообщения.Секретная фразаSession заблокированРазблокироватьВведите никНеверный публичный ключ
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 58667c4e50..cefc33afb8 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -8,7 +8,7 @@
Čakajte prosím...UložiťPoznámka pre seba
- Version %s
+ Verzia %sNová správa
@@ -58,18 +58,18 @@
Odosielanie zlyhalo, ťuknutím pošlete nezabezpečenú správuNepodarilo sa nájst aplikáciu schopnú otvoriť tento typ súboru.Skopírovaných %s
- Čítať ďalej
+ Read More Stiahnuť viac Čaká saPridať prílohuVyberte kontaktné informácie Nastala chyba pri vytváraní prílohy.
- Message
- Compose
- Muted until %1$s
- %1$d members
- Community Guidelines
+ Správa
+ Napísať správu
+ Stlmené upozornenia do %1$s
+ %1$d členov
+ Usmernenia SpoločenstvaNeplatný príjemcaPridané na plochuOpustiť skupinu?
@@ -83,7 +83,7 @@
Vo vašom zariadení nie je aplikácia schopná otvoriť tento odkaz.Pridať členovJoin %s
- Are you sure you want to join the %s open group?
+ Naozaj sa chcete pripojiť k otvorenej skupine %s?Pre posielanie zvukových správ potrebuje Session prístup k mikrofónu.Session potrebuje prístup k mikrofónu aby mohol posielať zvukové správy, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Mikrofón\".Pre fotenie a nahrávanie videa potrebuje Session prístup k fotoaparátu.
@@ -112,7 +112,7 @@
Týmto natrvalo odstránite %1$d vybraných správ.Týmto natrvalo odstránite %1$d vybraných správ.
- Ban this user?
+ Zablokovať tohto používateľa?Uložiť na úložisko?Uložením tohto média na úložisko k nemu umožníte prístup ostatným aplikáciám v zariadení.\n\nPokračovať?
@@ -259,13 +259,13 @@ výmeny kľúčov.
Miznúce správy vypnutéČas miznúcich správ bol nastavený na %s%s urobil snímok obrazovky.
- Media saved by %s.
+ Médiá uložené používateľom %s.Bezpečnostné číslo sa zmeniloVaše bezpečnostné číslo s %s sa zmenilo.Označili ste ako overenéOznačili ste ako neoverené
- This conversation is empty
- Open group invitation
+ Táto konverzácia je prázdna
+ Otvoriť skupinovú pozvánkuAktualizácia SessionuK dispozícii je nová verzia Sessionu, ťuknutím aktualizujte
@@ -285,7 +285,7 @@ výmeny kľúčov.
VyNepodporovaný typ súboruKoncept
- Session potrebuje prístup k úložisku aby mohol ukladať dáta na externé úložisko, ale prístup bol natrvalo zakázaný. Prosím v nastaveniach aplikácií zvoľte \"Oprávnenia\", a povoľte \"Úložisko\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Bez povolenia sa nedá zapisovať na externé úložiskoVymazať správu?Týmto natrvalo odstánite túto správu.
@@ -381,10 +381,10 @@ výmeny kľúčov.
StiahnuťPripojiť sa
- Open group invitation
- Pinned message
- Community guidelines
- Read
+ Otvoriť pozvánku do skupiny
+ Pripnutá správa
+ Pravidlá komunity
+ PrečítanéZvukVideo
@@ -576,7 +576,7 @@ výmeny kľúčov.
Časový limit neaktivity pred uzamknutímŽiadny
- Copy public key
+ Kopírovať verejný kľúčPokračovaťKopírovať
@@ -584,140 +584,147 @@ výmeny kľúčov.
Skopírované do schránkyĎalejZdieľať
- Invalid Session ID
+ Neplatné Session IDZrušiť
- Your Session ID
+ Vaše Session IDYour Session begins here...
- Create Session ID
+ Vytvoriť Session IDContinue Your Session
- What\'s Session?
- It\'s a decentralized, encrypted messaging app
- So it doesn\'t collect my personal information or my conversation metadata? How does it work?
- Using a combination of advanced anonymous routing and end-to-end encryption technologies.
- Friends don\'t let friends use compromised messengers. You\'re welcome.
- Say hello to your Session ID
- Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.
- Restore your account
- Enter the recovery phrase that was given to you when you signed up to restore your account.
- Enter your recovery phrase
- Pick your display name
- This will be your name when you use Session. It can be your real name, an alias, or anything else you like.
- Enter a display name
- Please pick a display name
- Please pick a shorter display name
- Recommended
- Please Pick an Option
- You don\'t have any contacts yet
+ Čo je Session?
+ Je to decentralizovaná, šifrovaná aplikácia na posielanie správ
+ Takže nezbiera moje osobné informácie alebo metadáta mojich konverzácií? Ako to funguje?
+ Použitím kombinácie pokročilých technológií anonymného smerovania a end-to-end šifrovania.
+ Priatelia nedovolia priateľom používať skompromitované chatové aplikácie. Nieje za čo.
+ Povedzte ahoj svojmu Session ID
+ Vaše Session ID je jedinečná adresa, ktorú môžu ľudia použiť aby sa s vami skontaktovali v Session. Pretože Session ID nemá žiadne spojenie s vašou skutočnou identitou, je Session ID úplne anonymné a súkromé.
+ Obnoviť účet
+ Zadajte frázu na obnovenie, ktorá vám bola poskytnutá pri registrácii na obnovenie účtu.
+ Zadajte vašu obnovovaciu frázu
+ Vyberte svoje zobrazované meno
+ Toto bude vaše meno pri používaní Session. Môže to byť vaše skutočné meno, prezývka alebo čokoľvek čo chcete.
+ Zadajte zobrazované meno
+ Zvoľte prosím zobrazované meno
+ Zvoľte prosím kratšie zobrazované meno
+ Odporúčané
+ Prosím, zvoľte možnosť
+ Zatiaľ nemáte žiadne kontaktyStart a Session
- Are you sure you want to leave this group?
- "Couldn't leave group"
- Are you sure you want to delete this conversation?
- Conversation deleted
- Your Recovery Phrase
- Meet your recovery phrase
- Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\'t give it to anyone.
- Hold to reveal
- You\'re almost finished! 80%
- Secure your account by saving your recovery phrase
+ Ste si istí že chcete opustiť túto skupinu?
+ "Skupinu sa nepodarilo opustiť"
+ Naozaj chcete odstrániť túto konverzáciu?
+ Konverzácia úspešne zmazaná
+ Vaša fráza pre obnovenie
+ Toto je vaša fráza pre obnovenie
+ Vaša fráza na obnovenie je hlavným kľúčom k vášmu Session ID - môžete ju použiť na obnovenie svojho Session ID ak stratíte prístup k zariadeniu. Uložte frázu na obnovenie na bezpečnom mieste a nikomu ju nedávajte.
+ Podržaním odhaľte
+ Ste takmer hotoví! 80%
+ Zabezpečte svoj účet uložením frázy obnoveniaTap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.
- Make sure to store your recovery phrase in a safe place
- Path
- Session hides your IP by bouncing your messages through several Service Nodes in Session\'s decentralized network. These are the countries your connection is currently being bounced through:
- You
- Entry Node
- Service Node
- Destination
- Learn More
+ Uistite sa, že frázu na obnovenie máte uloženú na bezpečnom mieste
+ Cesta
+ Session skrýva vašu IP presmerovaním vašich správ cez viacero servisných uzlov v decentralizovanej Session sieti. Toto sú krajiny cez ktoré je momentálne presmerované vaše spojenie:
+ Vy
+ Vstupný uzol
+ Servisný uzol
+ Cieľ
+ Viac informácií
+ Resolving…New Session
- Enter Session ID
- Scan QR Code
- Scan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
- Users can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
- Session needs camera access to scan QR codes
- Grant Camera Access
- New Closed Group
- Enter a group name
- You don\'t have any contacts yet
+ Zadajte Session ID
+ Skenovať QR kód
+ Začnite stretnutie naskenovaním QR kódu iného používateľa. QR kód nájdete ťuknutím na ikonu QR kódu v nastaveniach účtu.
+ Enter Session ID or ONS name
+ Používatelia môžu zdieľať svoje Session ID tak, že prejdú do nastavení svojho účtu a klepnú na možnosť „Zdieľať Session ID“ alebo zdieľajú svoj QR kód.
+ Please check the Session ID or ONS name and try again.
+ Session potrebuje prístup k fotoaparátu na skenovanie QR kódov
+ Udeliť prístup k fotoaparátu
+ Nová uzatvorená skupina
+ Zadajte názov skupiny
+ Zatiaľ nemáte žiadne kontaktyStart a Session
- Please enter a group name
- Please enter a shorter group name
- Please pick at least 1 group member
- A closed group cannot have more than 100 members
- Join Open Group
- Couldn\'t join group
- Open Group URL
- Scan QR Code
- Scan the QR code of the open group you\'d like to join
- Enter an open group URL
- Settings
- Enter a display name
- Please pick a display name
- Please pick a shorter display name
- Privacy
- Notifications
+ Zadajte prosím názov skupiny
+ Zadajte prosím kratší názov skupiny
+ Prosím vyberte aspoň 1 člena skupiny
+ Uzatvorená skupina nemôže mať viac ako 100 členov
+ Pripojiť sa k otvorenej skupine
+ Nepodarilo sa pripojiť ku skupine
+ Otvoriť URL skupiny
+ Skenovať QR kód
+ Oskenujte QR kód skupiny ku ktorej sa chcete pripojiť
+ Zadajte URL otvorenej skupiny
+ Nastavenia
+ Zadajte zobrazované meno
+ Zvoľte prosím zobrazované meno
+ Zvoľte prosím kratšie zobrazované meno
+ Súkromie
+ UpozorneniaChats
- Devices
- Invite
- Recovery Phrase
- Clear Data
- Help us Translate Session
- Notifications
- Notification Style
- Notification Content
- Privacy
+ Zariadenia
+ Pozvať
+ Fráza pre obnovenie
+ Odstrániť dáta
+ Pomôžte nám preložiť Session
+ Upozornenia
+ Štýl upozornenia
+ Obsah upozornenia
+ SúkromieChatsNotification Strategy
- Change name
- Unlink device
- Your Recovery Phrase
- This is your recovery phrase. With it, you can restore or migrate your Session ID to a new device.
- Clear All Data
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.
+ Zmeniť meno
+ Odpojenie zariadenia
+ Vaša fráza pre obnovenie
+ Toto je vaša fráza pre obnovenie. S jej pomocou môžete obnoviť alebo presunúť svoje Session ID na nové zariadenie.
+ Odstrániť všetky dátaThis will permanently delete your messages, sessions, and contacts.
- QR Code
- View My QR Code
- Scan QR Code
- Scan someone\'s QR code to start a conversation with them
- Scan Me
+ QR kód
+ Zobraziť môj QR kód
+ Skenovať QR kód
+ Oskenujte niečí QR kód pre začatie konverzácie
+ Naskenuj maThis is your QR code. Other users can scan it to start a session with you.
- Share QR Code
- Contacts
- Closed Groups
- Open Groups
- You don\'t have any contacts yet
+ Zdieľať QR kód
+ Kontakty
+ Uzatvorené skupiny
+ Otvorené skupiny
+ Zatiaľ nemáte žiadne kontakty
- Apply
- Done
- Edit Group
- Enter a new group name
- Members
- Add members
- Group name can\'t be empty
- Please enter a shorter group name
- Groups must have at least 1 group member
- Remove user from group
- Select Contacts
+ Použiť
+ Dokončené
+ Upraviť skupinu
+ Zadajte názov skupiny
+ Členovia
+ Pridať členov
+ Názov skupiny nemôže byť prázdny
+ Zadajte prosím kratší názov skupiny
+ Skupiny musia mať najmenej 1 člena skupiny
+ Odstrániť užívateľa zo skupiny
+ Vyberte kontaktySecure session reset doneTheme
- Day
- Night
- System default
- Copy Session ID
- Attachment
- Voice Message
- Details
- Failed to activate backups. Please try again or contact support.
- Restore backup
- Select a file
- Select a backup file and enter the passphrase it was created with.
- 30-digit passphrase
+ Deň
+ Nočný režim
+ Predvolené systémom
+ Kopírovať Session ID
+ Príloha
+ Hlasová správa
+ Podrobnosti
+ Nepodarilo sa aktivovať zálohy. Skúste to znova alebo kontaktujte podporu.
+ Obnoviť zálohu
+ Vybrať súbor
+ Vyberte súbor zálohy a zadajte prístupovú frázu, pomocou ktorej bol vytvorený.
+ 30-znaková prístupová frázaThis is taking a while, would you like to skip?
- Link a Device
- Or join one of these…
- Message Notifications
+ Pripojiť zariadenie
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.
+ Alebo sa pripojte k jednej z týchto…
+ Upozornenia na správyThere are two ways Session can notify you of new messages.
- Fast Mode
- Slow Mode
+ Rýchly režim
+ Pomalý režimYou’ll be notified of new messages reliably and immediately using Google’s notification servers.Session will occasionally check for new messages in the background.Recovery Phrase
@@ -725,4 +732,20 @@ výmeny kľúčov.
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
index 5c4e3ad9ad..ea265bb3f8 100644
--- a/app/src/main/res/values-sl/strings.xml
+++ b/app/src/main/res/values-sl/strings.xml
@@ -287,7 +287,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -630,12 +630,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -670,6 +672,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -715,6 +719,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -727,4 +734,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 21608de72b..8c44a990eb 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -54,7 +54,7 @@
Dërgimi dështoi, prekeni për dërgim të pasigurtS\’gjendet dot një aplikacion i aftë për hapjen e kësaj medie.U kopjua %s
- Lexoni Më Tepër
+ Read More Shkarkoni Më Tepër Pezull
@@ -261,7 +261,7 @@
JuLloj media i pambuluarSkicë:
- Për të ruajtur gjëra në depozitë të jashtme, Session-i lyp leje Depozitimi, por kjo i është mohuar. Ju lutemi, kaloni te rregullimet e aplikacionit, përzgjidhni \"Leje\", dhe aktivizoni \"Depozitim\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".S\’bëhet dot ruajtje në depozitë të jashtme pa lejeTë fshihet mesazhi?Kjo do ta fshijë përgjithmonë këtë mesazh.
@@ -600,12 +600,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -640,6 +642,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -685,6 +689,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 921cc70706..9737640850 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -273,7 +273,7 @@
ВиНеподржан тип медијумаНацрт
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsОбрисати поруку?Ово ће трајно да обрише ову поруку.
@@ -614,12 +614,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -654,6 +656,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -699,6 +703,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -711,4 +718,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml
index e54b4ab035..4cf8673489 100644
--- a/app/src/main/res/values-sv-rSE/strings.xml
+++ b/app/src/main/res/values-sv-rSE/strings.xml
@@ -54,7 +54,7 @@
Sändningen misslyckades, tryck för oskyddad fallbackKan inte hitta app som kan öppna denna fil.Kopierade %s
- Läs mer
+ Read More Hämta mer Väntar
@@ -261,7 +261,7 @@ för nyckelutbyte!
DuMediatypen stöds ejUtkast
- Session behöver behörigheten Lagring för att spara till extern lagring men har nekats den permanent. Fortsätt till inställningsmenyn för Appar och aviseringar, välj \"Behörigheter\" och aktivera \"Lagring\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Kan inte spara till externt utrymme utan behörighetRadera meddelande?Detta kommer permanent radera detta meddelande.
@@ -600,12 +600,14 @@ för nyckelutbyte!
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -640,6 +642,8 @@ för nyckelutbyte!
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -685,6 +689,9 @@ för nyckelutbyte!
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ för nyckelutbyte!
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml
index 190197852b..b85d33e8a9 100644
--- a/app/src/main/res/values-sw/strings.xml
+++ b/app/src/main/res/values-sw/strings.xml
@@ -260,7 +260,7 @@
Weweaina ya vyombo vya habari ambazo hazijaungwa mkonorasimu
- Session inahitaji ruhusa ya Hifadhi ili kuokoa kwenye hifadhi ya nje, lakini imekataliwa kabisa. Tafadhali endelea kwenye mipangilio ya programu, chagua \"Ruhusa\", na uwezesha \"Uhifadhi\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".haiwezekani kuhifadhi kwenye external storage bila ruhusafuta ujumbe?hii itafuta ujumbe kwa kudumu
@@ -599,12 +599,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -639,6 +641,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -684,6 +688,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -696,4 +703,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml
index 154c18c0e1..64abf3ab8d 100644
--- a/app/src/main/res/values-te/strings.xml
+++ b/app/src/main/res/values-te/strings.xml
@@ -264,7 +264,7 @@
మీరుమద్ధతు లేనటువంటి ప్రసార మాధ్యమంచిత్తు పత్రం
- బాహ్య నిల్వకు సేవ్ చేయడానికి Sessionకు నిల్వ అనుమతి అవసరం, కానీ ఇది శాశ్వతంగా తిరస్కరించబడింది. దయచేసి అనువర్తనం సెట్టింగ్లకు కొనసాగించండి, \"అనుమతులు\" ఎంచుకోండి, ఆపై \"నిల్వ\" ను ప్రారంభించండి.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".అనుమతులు లేకుండా బాహ్య నిల్వకి సేవ్ చేయడం సాధ్యపడలేదుసందేశాన్ని తొలగించాలా?ఇది శాశ్వతంగా ఈ సందేశాన్ని తొలగిస్తుంది.
@@ -603,12 +603,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -643,6 +645,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -688,6 +692,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -700,4 +707,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml
index f1299b9162..5165fdc041 100644
--- a/app/src/main/res/values-th/strings.xml
+++ b/app/src/main/res/values-th/strings.xml
@@ -251,7 +251,7 @@
คุณชนิดสื่อที่ไม่รองรับร่าง
- เพื่อที่จะบันทึกข้อมูลลงที่เก็บข้อมูลภายนอกได้ Session ต้องได้รับอนุญาตให้เข้าถึงที่เก็บข้อมูล แต่คำขอนั้นถูกปฏิเสธอย่างถาวร กรุณาไปที่เมนูตั้งค่าแอป เลือก \"การอนุญาต\" และเปิดใช้งาน \"ที่เก็บข้อมูล\"
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".ไม่สามารถบันทึกลงที่เก็บข้อมูลภายนอกได้ หากไม่ได้รับอนุญาตลบข้อความหรือไม่นี่จะเป็นการลบข้อความนี้โดยถาวร
@@ -588,12 +588,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -628,6 +630,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -673,6 +677,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -685,4 +692,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index a0a8366393..d50623f3c8 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -4,11 +4,11 @@
EvetHayırSil
- Ban
+ YasaklaLütfen bekleyin...KaydetKendime Not
- Version %s
+ Sürüm %sYeni ileti
@@ -54,18 +54,18 @@
Gönderme başarısız oldu, şifresiz göndermek için dokununBu medyayı açabilen bir uygulama bulunamadı.%s kopyalandı
- Dahasını Okuyun
+ Devamını Oku Dahasını İndirin Beklemede Eklenti ekleKişi bilgisi seçÜzgünüz, eklentiniz ayarlanırken hata oluştu.
- Message
- Compose
- Muted until %1$s
- %1$d members
- Community Guidelines
+ İleti
+ Oluştur
+ %1$s\'e kadar sessiz
+ %1$d üye
+ Topluluk İlkeleriGeçersiz alıcı!Ana ekrana eklendiGruptan ayrıl?
@@ -77,9 +77,9 @@
Eklenti gönderdiğiniz ileti türü için olan boyut limitini aşıyor.Ses kaydedilemedi!Cihazınızda bu bağlantıyı işleyebilecek uygulama bulunmamaktadır.
- Add members
- Join %s
- Are you sure you want to join the %s open group?
+ Üye ekle
+ %s\'e katıl
+ %s açık gurubuna katılmak istediğinize emin misiniz?Sesli ileti göndermek için Session\'in mikrofonunuza erişmesine izin verin.Session, sesli iletiler göndermek için Mikrofon iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Mikrofon\"u etkinleştirin.Fotoğraf veya video çekmek için, Session\'in kameraya erişmesine izin verin.
@@ -102,7 +102,7 @@
Bu işlem seçtiğiniz iletiyi kalıcı olarak silecektir.Bu işlem seçtiğiniz %1$d iletiyi kalıcı olarak silecektir.
- Ban this user?
+ Bu kullanıcıyı yasakla?Depolama alanına kaydedilsin mi?Bu medyayı depolama alanına kaydetmek, telefonunuzdaki diğer uygulamaların erişimine izin verecektir.\n\nDevam edilsin mi?
@@ -234,14 +234,14 @@ iletisi alındı!
%s Session\'e katıldı!Kaybolan iletiler devre dışıKaybolan ileti süresi %s olarak ayarlandı
- %s took a screenshot.
+ %s ekran görüntüsü aldı.Media saved by %s.Güvenlik numarası değişti%s ile güvenlik numaranız değişti.Doğrulanmış olarak işaretlediniz.Doğrulanmamış olarak işaretlediniz.
- This conversation is empty
- Open group invitation
+ Bu konuşma boş
+ Açık grup davetiSession güncellemesiSession\'in yeni sürümü mevcut, güncellemek için dokunun
@@ -261,7 +261,7 @@ iletisi alındı!
SizDesteklenmeyen medya türüTaslak
- Session, harici depolamaya kaydedebilmek için Depolama iznine ihtiyaç duyar, fakat bu izin kalıcı olarak reddedilmiş. Lütfen uygulama ayarları menüsüne girip \"İzinler\" kısmını seçin, ve \"Depolama\"yı etkinleştirin.
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".İzinler olmadan harici depolamaya kaydedilemiyorİletiyi sil?Bu eylem bu iletiyi kalıcı olarak silecektir.
@@ -354,11 +354,11 @@ iletisi alındı!
Durdurİndir
- Join
- Open group invitation
- Pinned message
- Community guidelines
- Read
+ Katıl
+ Açık grup daveti
+ Sabitlenen mesaj
+ Topluluk İlkeleri
+ OkunduSesVideo
@@ -488,7 +488,7 @@ iletisi alındı!
İleti ayrıntılarıMetni kopyalaİletiyi sil
- Ban user
+ Kullanıcıyı engelleİletiyi tekrar gönderİletiyi yanıtla
@@ -548,17 +548,17 @@ iletisi alındı!
Ekran kilidi zaman aşımıHiçbiri
- Copy public key
+ Genel Anahtarı Kopyala
- Continue
- Copy
- Invalid URL
- Copied to clipboard
- Next
- Share
- Invalid Session ID
- Cancel
- Your Session ID
+ Devam Et
+ Kopyala
+ Geçersiz URL
+ Panoya kopyalandı
+ İleri
+ Paylaş
+ Geçersiz Session ID\'si
+ İptal
+ Session ID\'nizYour Session begins here...Create Session IDContinue Your Session
@@ -569,7 +569,7 @@ iletisi alındı!
Friends don\'t let friends use compromised messengers. You\'re welcome.Say hello to your Session IDYour Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.
- Restore your account
+ Hesabınızı geri yükleyinEnter the recovery phrase that was given to you when you signed up to restore your account.Enter your recovery phrasePick your display name
@@ -577,8 +577,8 @@ iletisi alındı!
Enter a display namePlease pick a display namePlease pick a shorter display name
- Recommended
- Please Pick an Option
+ Önerilenler
+ Lütfen Bir Seçenek SeçinYou don\'t have any contacts yetStart a SessionAre you sure you want to leave this group?
@@ -593,19 +593,21 @@ iletisi alındı!
Secure your account by saving your recovery phraseTap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.Make sure to store your recovery phrase in a safe place
- Path
+ YolSession hides your IP by bouncing your messages through several Service Nodes in Session\'s decentralized network. These are the countries your connection is currently being bounced through:
- You
- Entry Node
- Service Node
- Destination
- Learn More
+ Siz
+ Giriş Düğüm
+ Hizmet Düğüm
+ Hedef Konum
+ Fazlasını Öğrenin
+ Çözümleniyor…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -622,24 +624,26 @@ iletisi alındı!
Scan QR CodeScan the QR code of the open group you\'d like to joinEnter an open group URL
- Settings
+ AyarlarEnter a display namePlease pick a display namePlease pick a shorter display name
- Privacy
- Notifications
+ Gizlilik
+ BildirimlerChatsDevicesInviteRecovery PhraseClear DataHelp us Translate Session
- Notifications
+ BildirimlerNotification StyleNotification Content
- Privacy
+ GizlilikChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -652,13 +656,13 @@ iletisi alındı!
Scan someone\'s QR code to start a conversation with themScan MeThis is your QR code. Other users can scan it to start a session with you.
- Share QR Code
- Contacts
- Closed Groups
- Open Groups
+ QR kodu paylaş
+ Kişiler
+ Özel Gruplar
+ Genel GruplarYou don\'t have any contacts yet
- Apply
+ UygulaDoneEdit GroupEnter a new group name
@@ -670,14 +674,14 @@ iletisi alındı!
Remove user from groupSelect ContactsSecure session reset done
- Theme
- Day
- Night
- System default
+ Tema
+ Gün
+ Gece
+ Sistem varsayılanıCopy Session IDAttachmentVoice Message
- Details
+ AyrıntılarFailed to activate backups. Please try again or contact support.Restore backupSelect a file
@@ -685,6 +689,9 @@ iletisi alındı!
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -697,4 +704,20 @@ iletisi alındı!
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ İndir
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 71c8d0fad3..df92f97cef 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -58,7 +58,7 @@
Відправити не вдалося, натисніть для незахищеного запасного варіантуНеможливо знайти програму для відкриття цього медіа.Скопійовано %s
- Читати ще
+ Read More Завантажити ще Очікування
@@ -287,7 +287,7 @@
ВиНепідтримуваний тип медіаЧернетка
- Session потребує дозволу \"Зберігання\", щоб зберігати файли, але наразі доступу немає. Будь ласка, перейдіть до налаштувань додатку, оберіть \"Дозволи\", та увімкніть \"Зберігання\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Без доступу до файлової системи зберегти файли неможливоВидалити повідомлення?Це назавжди видалить це повідомлення.
@@ -632,12 +632,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -672,6 +674,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -717,6 +721,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -729,4 +736,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-ur-rIN/strings.xml b/app/src/main/res/values-ur-rIN/strings.xml
index 3ad6ee8682..62328c1fcc 100644
--- a/app/src/main/res/values-ur-rIN/strings.xml
+++ b/app/src/main/res/values-ur-rIN/strings.xml
@@ -263,7 +263,7 @@
YouUnsupported media typeDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -602,12 +602,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -642,6 +644,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -687,6 +691,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -699,4 +706,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index d0c79ba701..3ec3654f81 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -249,7 +249,7 @@ trao đổi chìa khóa bị hỏng!
BạnKhông hỗ trợ dạng truyền thông nàyDraft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -586,12 +586,14 @@ trao đổi chìa khóa bị hỏng!
Nút Dịch vụĐiểm đếnTìm hiểu thêm
+ Resolving…Session mớiNhập Session IDQuét mã QRQuét mã QR của người dùng để bắt đầu session. Tìm mã QR bằng cách chạm vào biểu tượng mã QR trong mục cài đặt tài khoản.
- Nhập Session ID của người nhận
+ Enter Session ID or ONS nameNgười dùng có thể chia sẻ Session ID của mình bằng cách tới mục cài đặt tài khoản và chạm vào “Chia sẻ Session ID”, hoặc bằng cách chia sẻ mã QR của họ.
+ Please check the Session ID or ONS name and try again.Session cần truy cập máy ảnh để quét mã QR Cho phép truy cập máy ảnhNhóm kín mới
@@ -626,6 +628,8 @@ trao đổi chìa khóa bị hỏng!
Riêng tưyTrò chuyệnChiến lược thông báo
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Đổi tênHủy liên kết thiết bịCụm từ khôi phục của bạn
@@ -671,6 +675,9 @@ trao đổi chìa khóa bị hỏng!
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -683,4 +690,20 @@ trao đổi chìa khóa bị hỏng!
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-xh/strings.xml b/app/src/main/res/values-xh/strings.xml
index 895259bbba..66084a58fd 100644
--- a/app/src/main/res/values-xh/strings.xml
+++ b/app/src/main/res/values-xh/strings.xml
@@ -262,7 +262,7 @@
Gwe ekika kino ekya Tekiwaniriddwa Draft
- Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".Unable to save to external storage without permissionsDelete message?This will permanently delete this message.
@@ -601,12 +601,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -641,6 +643,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -686,6 +690,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -698,4 +705,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 9688f2f63e..8930c3a8d5 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -52,7 +52,7 @@
发送失败,点击使用不安全的方式发送无法找到能打开该媒体的应用。已复制 %s
- 查看更多
+ Read More 下载更多 等待中
@@ -249,7 +249,7 @@
您不支持的媒体类型。草稿
- Session 需要存储权限以写入外部存储,但是该权限已经被永久拒绝。请进入应用程序设置,点击权限,并启用“存储”。
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".无法在没有写入到外部存储的权限时保存到外部存储删除消息吗?这将会永久删除消息。
@@ -586,12 +586,14 @@
服务节点目的地了解更多
+ Resolving…新建私人聊天输入Session ID扫描二维码扫描其他用户的二维码来发起对话。您可以在帐号设置中点击二维码图标找到二维码。
- 输入对方的Session ID
+ Enter Session ID or ONS name用户可以通过进入帐号设置并点击“共享Session ID”来分享自己的Session ID,或通过共享其二维码来分享其Session ID。
+ Please check the Session ID or ONS name and try again.Session需要摄像头访问权限才能扫描二维码授予摄像头访问权限创建私密群组
@@ -626,6 +628,8 @@
隐私会话通知选项
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.更换名称断开设备关联您的恢复口令
@@ -671,6 +675,9 @@
30-digit passphrase这需要一点时间,您想要跳过吗?关联设备
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.或加入下列群组…消息通知我们有两种方式来向您发送消息通知。
@@ -683,4 +690,20 @@
点击解锁输入昵称无效公钥
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 9bdbcc5070..21e7f3a12b 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -52,7 +52,7 @@
傳送失敗,點擊不安全的訊息退回找不到合適的應用程式來開啟媒體檔案。已複製 %s
- 讀取更多
+ Read More下載更多等待中
@@ -250,7 +250,7 @@
您不支援的媒體類型草稿
- Session 需要儲存的權限以儲存外接儲存空間,但是現在被設定為永久拒絕存取。請到應用程式設定中,選取「權限」,並啟用「儲存空間」的權限。
+ Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".因為沒有儲存的權限,無法儲存到外部儲存空間刪除訊息?這將永久刪除此訊息。
@@ -587,12 +587,14 @@
Service NodeDestinationLearn More
+ Resolving…New SessionEnter Session IDScan QR CodeScan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.
- Enter Session ID of recipient
+ Enter Session ID or ONS nameUsers can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.
+ Please check the Session ID or ONS name and try again.Session needs camera access to scan QR codesGrant Camera AccessNew Closed Group
@@ -627,6 +629,8 @@
PrivacyChatsNotification Strategy
+ Use Fast Mode
+ You’ll be notified of new messages reliably and immediately using Google’s notification servers.Change nameUnlink deviceYour Recovery Phrase
@@ -672,6 +676,9 @@
30-digit passphraseThis is taking a while, would you like to skip?Link a Device
+ Recovery Phrase
+ Scan QR Code
+ Navigate to Settings → Recovery Phrase on your other device to show your QR code.Or join one of these…Message NotificationsThere are two ways Session can notify you of new messages.
@@ -684,4 +691,20 @@
Tap to UnlockEnter a nicknameInvalid public key
+ Document
+ Unblock %s?
+ Are you sure you want to unblock %s?
+ Join %s?
+ Are you sure you want to join the %s open group?
+ Open URL?
+ Are you sure you want to open %s?
+ Open
+ Enable Link Previews?
+ Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.
+ Enable
+ Trust %s?
+ Are you sure you want to download media sent by %s?
+ Download
+ %s is blocked. Unblock them?
+ Failed to prepare attachment for sending.
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 5f8fa46a6b..feb1432278 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -5,9 +5,7 @@
#FFFFFF#FF453A#D8D8D8
- #23FFFFFF#353535
- #979797#1B1B1B#0C0C0C#171717
@@ -51,7 +49,6 @@
@color/accent@color/accent
- #882090ea@color/signal_primary@color/signal_primary_dark
@@ -76,8 +73,6 @@
#40ffffff#aaffffff
- #32000000
-
@color/gray65#22000000
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 0afc835242..5e5661c5e2 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -20,7 +20,6 @@
76dp14dp1dp
- 1dp60dp72dp36dp
@@ -76,8 +75,6 @@
2dp1.5dp@dimen/medium_spacing
- @dimen/medium_spacing
- @dimen/small_spacing@dimen/medium_spacing24dp24dp
@@ -86,7 +83,6 @@
240dp100dp320dp
- 128dp175dp85dp
@@ -99,18 +95,13 @@
4dp40dp
- @dimen/large_spacing
- 60dp8dp1dp
- 36dp
-
2dp16dp3
- 10dp10dp
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 1b561891f7..b6c72f867d 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -213,11 +213,6 @@
@dimen/small_font_size
-
-
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 110ac1dfe2..3c81efc7ee 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -280,9 +280,4 @@
true
-
-
diff --git a/app/src/test/java/org/thoughtcrime/securesms/conversation/ConversationAdapterTest.java b/app/src/test/java/org/thoughtcrime/securesms/conversation/ConversationAdapterTest.java
deleted file mode 100644
index 898ae811c6..0000000000
--- a/app/src/test/java/org/thoughtcrime/securesms/conversation/ConversationAdapterTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.thoughtcrime.securesms.conversation;
-
-import android.database.Cursor;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.thoughtcrime.securesms.BaseUnitTest;
-import org.thoughtcrime.securesms.conversation.ConversationAdapter;
-
-import static org.junit.Assert.*;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.when;
-
-public class ConversationAdapterTest extends BaseUnitTest {
- private Cursor cursor = mock(Cursor.class);
- private ConversationAdapter adapter;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- adapter = new ConversationAdapter(context, cursor);
- when(cursor.getColumnIndexOrThrow(anyString())).thenReturn(0);
- }
-
- @Test
- @Ignore("TODO: Fix test")
- public void testGetItemIdEquals() throws Exception {
- when(cursor.getString(anyInt())).thenReturn(null).thenReturn("SMS::1::1");
- long firstId = adapter.getItemId(cursor);
- when(cursor.getString(anyInt())).thenReturn(null).thenReturn("MMS::1::1");
- long secondId = adapter.getItemId(cursor);
- assertNotEquals(firstId, secondId);
- when(cursor.getString(anyInt())).thenReturn(null).thenReturn("MMS::2::1");
- long thirdId = adapter.getItemId(cursor);
- assertNotEquals(secondId, thirdId);
- }
-}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 3343145c60..884e6ed085 100644
--- a/build.gradle
+++ b/build.gradle
@@ -50,6 +50,7 @@ allprojects {
}
project.ext {
+ androidMinimumSdkVersion = 23
androidCompileSdkVersion = 30
}
}
\ No newline at end of file
diff --git a/libsession/build.gradle b/libsession/build.gradle
index 6763e0cff5..8bd46532eb 100644
--- a/libsession/build.gradle
+++ b/libsession/build.gradle
@@ -6,6 +6,10 @@ plugins {
android {
compileSdkVersion androidCompileSdkVersion
+ defaultConfig {
+ minSdkVersion androidMinimumSdkVersion
+ }
+
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
diff --git a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt
index 4cf93a429d..39ca4854ce 100644
--- a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt
+++ b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt
@@ -20,6 +20,7 @@ interface MessageDataProvider {
fun getSignalAttachmentPointer(attachmentId: Long): SignalServiceAttachmentPointer?
fun setAttachmentState(attachmentState: AttachmentState, attachmentId: Long, messageID: Long)
fun insertAttachment(messageId: Long, attachmentId: AttachmentId, stream : InputStream)
+ fun updateAudioAttachmentDuration(attachmentId: AttachmentId, durationMs: Long, threadId: Long)
fun isOutgoingMessage(timestamp: Long): Boolean
fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult)
fun handleFailedAttachmentUpload(attachmentId: Long)
diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt
index a5aaeaa429..d0a09dc771 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt
@@ -4,13 +4,18 @@ import okhttp3.HttpUrl
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
+import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.messaging.utilities.Data
+import org.session.libsession.snode.OnionRequestAPI
+import org.session.libsession.utilities.DecodedAudio
import org.session.libsession.utilities.DownloadUtilities
+import org.session.libsession.utilities.InputStreamMediaDataSource
import org.session.libsignal.streams.AttachmentCipherInputStream
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Log
import java.io.File
import java.io.FileInputStream
+import java.io.InputStream
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job {
override var delegate: JobDelegate? = null
@@ -37,46 +42,68 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
val storage = MessagingModuleConfiguration.shared.storage
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
val handleFailure: (java.lang.Exception) -> Unit = { exception ->
- if (exception == Error.NoAttachment) {
+ if (exception == Error.NoAttachment
+ || (exception is OnionRequestAPI.HTTPRequestFailedAtDestinationException && exception.statusCode == 400)) {
messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
this.handlePermanentFailure(exception)
} else {
this.handleFailure(exception)
}
}
+ var tempFile: File? = null
try {
val attachment = messageDataProvider.getDatabaseAttachment(attachmentID)
?: return handleFailure(Error.NoAttachment)
messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachmentID, this.databaseMessageID)
- val tempFile = createTempFile()
+ tempFile = createTempFile()
val threadID = storage.getThreadIdForMms(databaseMessageID)
val openGroupV2 = storage.getV2OpenGroup(threadID)
- val inputStream = if (openGroupV2 == null) {
+ if (openGroupV2 == null) {
DownloadUtilities.downloadFile(tempFile, attachment.url)
- // Assume we're retrieving an attachment for an open group server if the digest is not set
- if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) {
- FileInputStream(tempFile)
- } else {
- AttachmentCipherInputStream.createForAttachment(tempFile, attachment.size, Base64.decode(attachment.key), attachment.digest)
- }
} else {
val url = HttpUrl.parse(attachment.url)!!
val fileID = url.pathSegments().last()
OpenGroupAPIV2.download(fileID.toLong(), openGroupV2.room, openGroupV2.server).get().let {
tempFile.writeBytes(it)
}
- FileInputStream(tempFile)
}
+ val inputStream = getInputStream(tempFile, attachment)
+
messageDataProvider.insertAttachment(databaseMessageID, attachment.attachmentId, inputStream)
+ if (attachment.contentType.startsWith("audio/")) {
+ // process the duration
+ try {
+ InputStreamMediaDataSource(getInputStream(tempFile, attachment)).use { mediaDataSource ->
+ val durationMs = (DecodedAudio.create(mediaDataSource).totalDuration / 1000.0).toLong()
+ messageDataProvider.updateAudioAttachmentDuration(
+ attachment.attachmentId,
+ durationMs,
+ threadID
+ )
+ }
+ } catch (e: Exception) {
+ Log.e("Loki", "Couldn't process audio attachment", e)
+ }
+ }
tempFile.delete()
handleSuccess()
} catch (e: Exception) {
+ tempFile?.delete()
return handleFailure(e)
}
}
+ private fun getInputStream(tempFile: File, attachment: DatabaseAttachment): InputStream {
+ // Assume we're retrieving an attachment for an open group server if the digest is not set
+ return if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) {
+ FileInputStream(tempFile)
+ } else {
+ AttachmentCipherInputStream.createForAttachment(tempFile, attachment.size, Base64.decode(attachment.key), attachment.digest)
+ }
+ }
+
private fun handleSuccess() {
- Log.w(AttachmentUploadJob.TAG, "Attachment downloaded successfully.")
+ Log.w("AttachmentDownloadJob", "Attachment downloaded successfully.")
delegate?.handleJobSucceeded(this)
}
diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt
index e4cc76974f..02475c39cb 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt
@@ -11,16 +11,14 @@ import org.session.libsession.messaging.messages.Message
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.utilities.Data
+import org.session.libsession.utilities.DecodedAudio
+import org.session.libsession.utilities.InputStreamMediaDataSource
import org.session.libsession.utilities.UploadResult
-import org.session.libsignal.streams.AttachmentCipherOutputStream
import org.session.libsignal.messages.SignalServiceAttachmentStream
-import org.session.libsignal.streams.PaddingInputStream
-import org.session.libsignal.utilities.PushAttachmentData
-import org.session.libsignal.streams.AttachmentCipherOutputStreamFactory
-import org.session.libsignal.streams.DigestingRequestBody
-import org.session.libsignal.utilities.Util
-import org.session.libsignal.streams.PlaintextOutputStreamFactory
+import org.session.libsignal.streams.*
import org.session.libsignal.utilities.Log
+import org.session.libsignal.utilities.PushAttachmentData
+import org.session.libsignal.utilities.Util
class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val message: Message, val messageSendJobID: String) : Job {
override var delegate: JobDelegate? = null
@@ -108,7 +106,22 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult) {
Log.d(TAG, "Attachment uploaded successfully.")
delegate?.handleJobSucceeded(this)
- MessagingModuleConfiguration.shared.messageDataProvider.handleSuccessfulAttachmentUpload(attachmentID, attachment, attachmentKey, uploadResult)
+ val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
+ messageDataProvider.handleSuccessfulAttachmentUpload(attachmentID, attachment, attachmentKey, uploadResult)
+ if (attachment.contentType.startsWith("audio/")) {
+ // process the duration
+ try {
+ val inputStream = messageDataProvider.getAttachmentStream(attachmentID)!!.inputStream!!
+ InputStreamMediaDataSource(inputStream).use { mediaDataSource ->
+ val durationMs = (DecodedAudio.create(mediaDataSource).totalDuration / 1000.0).toLong()
+ messageDataProvider.getDatabaseAttachment(attachmentID)?.attachmentId?.let { attachmentId ->
+ messageDataProvider.updateAudioAttachmentDuration(attachmentId, durationMs, threadID.toLong())
+ }
+ }
+ } catch (e: Exception) {
+ Log.e("Loki", "Couldn't process audio attachment", e)
+ }
+ }
MessagingModuleConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID)
}
@@ -140,13 +153,13 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
val kryo = Kryo()
kryo.isRegistrationRequired = false
val serializedMessage = ByteArray(4096)
- val output = Output(serializedMessage)
- kryo.writeObject(output, message)
+ val output = Output(serializedMessage, Job.MAX_BUFFER_SIZE)
+ kryo.writeClassAndObject(output, message)
output.close()
return Data.Builder()
.putLong(ATTACHMENT_ID_KEY, attachmentID)
.putString(THREAD_ID_KEY, threadID)
- .putByteArray(MESSAGE_KEY, serializedMessage)
+ .putByteArray(MESSAGE_KEY, output.toBytes())
.putString(MESSAGE_SEND_JOB_ID_KEY, messageSendJobID)
.build()
}
@@ -157,18 +170,24 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
class Factory: Job.Factory {
- override fun create(data: Data): AttachmentUploadJob {
+ override fun create(data: Data): AttachmentUploadJob? {
val serializedMessage = data.getByteArray(MESSAGE_KEY)
val kryo = Kryo()
kryo.isRegistrationRequired = false
val input = Input(serializedMessage)
- val message = kryo.readObject(input, Message::class.java)
+ val message: Message
+ try {
+ message = kryo.readClassAndObject(input) as Message
+ } catch (e: Exception) {
+ Log.e("Loki","Couldn't serialize the AttachmentUploadJob", e)
+ return null
+ }
input.close()
return AttachmentUploadJob(
- data.getLong(ATTACHMENT_ID_KEY),
- data.getString(THREAD_ID_KEY)!!,
- message,
- data.getString(MESSAGE_SEND_JOB_ID_KEY)!!
+ data.getLong(ATTACHMENT_ID_KEY),
+ data.getString(THREAD_ID_KEY)!!,
+ message,
+ data.getString(MESSAGE_SEND_JOB_ID_KEY)!!
)
}
}
diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt
index e6803e89dc..9ced85b110 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt
@@ -23,6 +23,7 @@ class JobQueue : JobDelegate {
private val attachmentDispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher()
private val scope = GlobalScope + SupervisorJob()
private val queue = Channel(UNLIMITED)
+ private val pendingJobIds = mutableSetOf()
val timer = Timer()
@@ -86,6 +87,19 @@ class JobQueue : JobDelegate {
MessagingModuleConfiguration.shared.storage.persistJob(job)
}
+ fun resumePendingSendMessage(job: Job) {
+ val id = job.id ?: run {
+ Log.e("Loki", "tried to resume pending send job with no ID")
+ return
+ }
+ if (!pendingJobIds.add(id)) {
+ Log.e("Loki","tried to re-queue pending/in-progress job")
+ return
+ }
+ queue.offer(job)
+ Log.d("Loki", "resumed pending send message $id")
+ }
+
fun resumePendingJobs() {
if (hasResumedPendingJobs) {
Log.d("Loki", "resumePendingJobs() should only be called once.")
@@ -120,6 +134,7 @@ class JobQueue : JobDelegate {
override fun handleJobSucceeded(job: Job) {
val jobId = job.id ?: return
MessagingModuleConfiguration.shared.storage.markJobAsSucceeded(jobId)
+ pendingJobIds.remove(jobId)
}
override fun handleJobFailed(job: Job, error: Exception) {
@@ -169,4 +184,7 @@ class JobQueue : JobDelegate {
val maxBackoff = (10 * 60).toDouble() // 10 minutes
return (1000 * 0.25 * min(maxBackoff, (2.0).pow(job.failureCount))).roundToLong()
}
+
+ private fun Job.isSend() = this is MessageSendJob || this is AttachmentUploadJob
+
}
\ No newline at end of file
diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt
index bd20346f99..f4cfd9202c 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt
@@ -34,7 +34,7 @@ class ClosedGroupControlMessage() : ControlMessage() {
is Kind.New -> {
!kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair?.publicKey != null
&& kind.encryptionKeyPair?.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
- && kind.expireTimer >= 0
+ && kind.expirationTimer >= 0
}
is Kind.EncryptionKeyPair -> true
is Kind.NameChange -> kind.name.isNotEmpty()
@@ -45,7 +45,7 @@ class ClosedGroupControlMessage() : ControlMessage() {
}
sealed class Kind {
- class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List, var admins: List, var expireTimer: Int) : Kind() {
+ class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List, var admins: List, var expirationTimer: Int) : Kind() {
internal constructor() : this(ByteString.EMPTY, "", null, listOf(), listOf(), 0)
}
/** An encryption key pair encrypted for each member individually.
@@ -89,11 +89,11 @@ class ClosedGroupControlMessage() : ControlMessage() {
val publicKey = closedGroupControlMessageProto.publicKey ?: return null
val name = closedGroupControlMessageProto.name ?: return null
val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null
- val expireTimer = closedGroupControlMessageProto.expireTimer
+ val expirationTimer = closedGroupControlMessageProto.expirationTimer
try {
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray()),
DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
- kind = Kind.New(publicKey, name, encryptionKeyPair, closedGroupControlMessageProto.membersList, closedGroupControlMessageProto.adminsList, expireTimer)
+ kind = Kind.New(publicKey, name, encryptionKeyPair, closedGroupControlMessageProto.membersList, closedGroupControlMessageProto.adminsList, expirationTimer)
} catch (e: Exception) {
Log.w(TAG, "Couldn't parse key pair from proto: $encryptionKeyPairAsProto.")
return null
@@ -145,7 +145,7 @@ class ClosedGroupControlMessage() : ControlMessage() {
closedGroupControlMessage.encryptionKeyPair = encryptionKeyPair.build()
closedGroupControlMessage.addAllMembers(kind.members)
closedGroupControlMessage.addAllAdmins(kind.admins)
- closedGroupControlMessage.expireTimer = kind.expireTimer
+ closedGroupControlMessage.expirationTimer = kind.expirationTimer
}
is Kind.EncryptionKeyPair -> {
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR
@@ -173,10 +173,6 @@ class ClosedGroupControlMessage() : ControlMessage() {
dataMessageProto.closedGroupControlMessage = closedGroupControlMessage.build()
// Group context
setGroupContext(dataMessageProto)
- // Expiration timer
- // TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
- // if it receives a message without the current expiration timer value attached to it...
- dataMessageProto.expireTimer = Recipient.from(MessagingModuleConfiguration.shared.context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
contentProto.dataMessage = dataMessageProto.build()
return contentProto.build()
} catch (e: Exception) {
diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt
index cc49cec65b..cb355aa15e 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt
@@ -6,6 +6,7 @@ import org.session.libsession.utilities.Address
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.ProfileKeyUtil
+import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
import org.session.libsignal.crypto.ecc.DjbECPublicKey
import org.session.libsignal.crypto.ecc.ECKeyPair
@@ -19,10 +20,10 @@ class ConfigurationMessage(var closedGroups: List, var openGroups:
override val isSelfSendValid: Boolean = true
- class ClosedGroup(var publicKey: String, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List, var admins: List) {
+ class ClosedGroup(var publicKey: String, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List, var admins: List, var expirationTimer: Int) {
val isValid: Boolean get() = members.isNotEmpty() && admins.isNotEmpty()
- internal constructor() : this("", "", null, listOf(), listOf())
+ internal constructor() : this("", "", null, listOf(), listOf(), 0)
override fun toString(): String {
return name
@@ -39,7 +40,8 @@ class ConfigurationMessage(var closedGroups: List, var openGroups:
DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
val members = proto.membersList.map { it.toByteArray().toHexString() }
val admins = proto.adminsList.map { it.toByteArray().toHexString() }
- return ClosedGroup(publicKey, name, encryptionKeyPair, members, admins)
+ val expirationTimer = proto.expirationTimer
+ return ClosedGroup(publicKey, name, encryptionKeyPair, members, admins, expirationTimer)
}
}
@@ -53,6 +55,7 @@ class ConfigurationMessage(var closedGroups: List, var openGroups:
result.encryptionKeyPair = encryptionKeyPairAsProto.build()
result.addAllMembers(members.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
result.addAllAdmins(admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
+ result.expirationTimer = expirationTimer
return result.build()
}
}
@@ -110,7 +113,8 @@ class ConfigurationMessage(var closedGroups: List, var openGroups:
if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue
val groupPublicKey = GroupUtil.doubleDecodeGroupID(group.encodedId).toHexString()
val encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: continue
- val closedGroup = ClosedGroup(groupPublicKey, group.title, encryptionKeyPair, group.members.map { it.serialize() }, group.admins.map { it.serialize() })
+ val recipient = Recipient.from(context, Address.fromSerialized(group.encodedId), false)
+ val closedGroup = ClosedGroup(groupPublicKey, group.title, encryptionKeyPair, group.members.map { it.serialize() }, group.admins.map { it.serialize() }, recipient.expireMessages)
closedGroups.add(closedGroup)
}
if (group.isOpenGroup) {
diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt
index 27bd608f3c..cfbd5605bd 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt
@@ -121,7 +121,7 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) {
for (closedGroup in message.closedGroups) {
if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) continue
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, closedGroup.publicKey, closedGroup.name,
- closedGroup.encryptionKeyPair!!, closedGroup.members, closedGroup.admins, message.sentTimestamp!!, 0)
+ closedGroup.encryptionKeyPair!!, closedGroup.members, closedGroup.admins, message.sentTimestamp!!, closedGroup.expirationTimer)
}
val allV2OpenGroups = storage.getAllV2OpenGroups().map { it.value.joinURL }
for (openGroup in message.openGroups) {
@@ -256,7 +256,7 @@ private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMess
val groupPublicKey = kind.publicKey.toByteArray().toHexString()
val members = kind.members.map { it.toByteArray().toHexString() }
val admins = kind.admins.map { it.toByteArray().toHexString() }
- val expireTimer = kind.expireTimer
+ val expireTimer = kind.expirationTimer
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!, expireTimer)
}
diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt
index 7cfc44801f..b08fa66f34 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt
@@ -94,7 +94,9 @@ class OpenGroupPollerV2(private val server: String, private val executorService:
if (actualMax > 0) {
storage.setLastMessageServerID(room, server, actualMax)
}
- JobQueue.shared.add(TrimThreadJob(threadId))
+ if (messages.isNotEmpty()) {
+ JobQueue.shared.add(TrimThreadJob(threadId))
+ }
}
private fun handleDeletedMessages(room: String, openGroupID: String, deletions: List) {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DecodedAudio.kt b/libsession/src/main/java/org/session/libsession/utilities/DecodedAudio.kt
similarity index 94%
rename from app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DecodedAudio.kt
rename to libsession/src/main/java/org/session/libsession/utilities/DecodedAudio.kt
index 77f7f88983..ef21abe4c0 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DecodedAudio.kt
+++ b/libsession/src/main/java/org/session/libsession/utilities/DecodedAudio.kt
@@ -1,4 +1,4 @@
-package org.thoughtcrime.securesms.loki.utilities
+package org.session.libsession.utilities
import android.media.AudioFormat
import android.media.MediaCodec
@@ -11,6 +11,7 @@ import androidx.annotation.RequiresApi
import java.io.FileDescriptor
import java.io.IOException
+import java.io.InputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.ShortBuffer
@@ -365,4 +366,34 @@ inline fun byteToNormalizedFloat(value: Byte): Float {
/** Turns a [0..1] float into a signed byte. */
inline fun normalizedFloatToByte(value: Float): Byte {
return (255f * value - 128f).roundToInt().toByte()
+}
+
+class InputStreamMediaDataSource: MediaDataSource {
+
+ private val data: ByteArray
+
+ constructor(inputStream: InputStream): super() {
+ this.data = inputStream.readBytes()
+ }
+
+ override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
+ val length: Int = data.size
+ if (position >= length) {
+ return -1 // -1 indicates EOF
+ }
+ var actualSize = size
+ if (position + size > length) {
+ actualSize -= (position + size - length).toInt()
+ }
+ System.arraycopy(data, position.toInt(), buffer, offset, actualSize)
+ return actualSize
+ }
+
+ override fun getSize(): Long {
+ return data.size.toLong()
+ }
+
+ override fun close() {
+ // We don't need to close the wrapped stream.
+ }
}
\ No newline at end of file
diff --git a/libsession/src/main/res/drawable/ic_person_large.xml b/libsession/src/main/res/drawable/ic_person_large.xml
deleted file mode 100644
index c3324be1de..0000000000
--- a/libsession/src/main/res/drawable/ic_person_large.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
diff --git a/libsession/src/main/res/values/arrays.xml b/libsession/src/main/res/values/arrays.xml
index 6e75269b4c..385c0711f5 100644
--- a/libsession/src/main/res/values/arrays.xml
+++ b/libsession/src/main/res/values/arrays.xml
@@ -119,17 +119,7 @@
vi
-
- @string/preferences__light_theme
- @string/preferences__dark_theme
-
-
-
- light
- dark
-
-
-
+ @string/preferences__green@string/preferences__red@string/preferences__blue
@@ -200,19 +190,7 @@
@string/arrays__mute_for_one_year
-
- @string/arrays__settings_default
- @string/arrays__enabled
- @string/arrays__disabled
-
-
-
- 0
- 1
- 2
-
-
-
+ @string/arrays__name_and_message@string/arrays__name_only@string/arrays__no_name_or_message
@@ -225,21 +203,8 @@
-
- image
- audio
- video
- documents
-
-
- @string/arrays__images
- @string/arrays__audio
- @string/arrays__video
- @string/arrays__documents
-
-
-
+ imageaudio
@@ -282,20 +247,6 @@
#000000
-
- @string/arrays__small
- @string/arrays__normal
- @string/arrays__large
- @string/arrays__extra_large
-
-
-
- 13
- 16
- 20
- 30
-
-
@string/arrays__default@string/arrays__high
diff --git a/libsession/src/main/res/values/colors.xml b/libsession/src/main/res/values/colors.xml
index b4d8bc2aae..de9d41f309 100644
--- a/libsession/src/main/res/values/colors.xml
+++ b/libsession/src/main/res/values/colors.xml
@@ -5,20 +5,14 @@
#FFFFFF#FF453A#D8D8D8
- #23FFFFFF#353535
- #979797#1B1B1B#0C0C0C#171717
- #121212#36383C#323232#101011
- #212121@color/unimportant_button_background
- #3F4146
- #000000#333132#0AFFFFFF#1B1B1B
@@ -37,21 +31,16 @@
#fcac5a
- #78be20
- #419B41#0a0a0a@color/accent@color/accent
- #882090ea@color/signal_primary@color/signal_primary_dark#ffffffff#ff000000
- #ffeeeeee
- #ffdddddd#ffe0e0e0#ffababab#ffcccccc
@@ -60,28 +49,16 @@
#ff595959#ff4d4d4d#ff383838
- #ff111111
- #05000000
- #10000000
- #20000000#30000000#40000000#70000000#90000000
- #05ffffff
- #10ffffff
- #20ffffff#30ffffff#40ffffff
- #60ffffff
- #70ffffff#aaffffff#bbffffff
- #ddffffff
-
- #32000000@color/gray65#22000000
@@ -89,24 +66,11 @@
#ffffffff#ff333333
- #ffeeeeee
- #ff333333
- #ffd5d5d5
- #ff222222
- #400099cc
- #40ffffff
-
- @color/conversation_crimson
- @color/core_blue
-
- #99ffffff#00FFFFFF#00000000#88000000
- #44ff2d00
-
@color/transparent_black_90#121212
diff --git a/libsession/src/main/res/values/core_colors.xml b/libsession/src/main/res/values/core_colors.xml
index 9622208f1f..e99bedcc97 100644
--- a/libsession/src/main/res/values/core_colors.xml
+++ b/libsession/src/main/res/values/core_colors.xml
@@ -1,9 +1,7 @@
#5bca5b
- #4caf50#f44336
- #ef5350#ffffff#000000
diff --git a/libsession/src/main/res/values/dimens.xml b/libsession/src/main/res/values/dimens.xml
index 0ffa369252..8ca4c0aa43 100644
--- a/libsession/src/main/res/values/dimens.xml
+++ b/libsession/src/main/res/values/dimens.xml
@@ -22,7 +22,6 @@
76dp14dp1dp
- 1dp60dp72dp36dp
@@ -55,18 +54,10 @@
220dp110dp170dp
- 50dp
- 5dp
- 1.5dp
- 5dp48dp16sp12sp
- 200sp
- 2dp
- 2dp64dp
- 50dp210dp105dp
@@ -85,9 +76,6 @@
2dp1.5dp@dimen/medium_spacing
- @dimen/medium_spacing
- @dimen/small_spacing
- 32dp@dimen/medium_spacing24dp24dp
@@ -96,7 +84,6 @@
240dp100dp320dp
- 128dp175dp85dp
@@ -109,39 +96,17 @@
4dp40dp
- @dimen/large_spacing
- @dimen/large_spacing
- 60dp8dp1dp
- 36dp
-
- 10dp2dp16dp3
- 10dp
- 52dp
-
- 8dp
- 88dp
- 8dp
- 96dp
-
- 16dp
-
- 8dp
-
- 150dp
- 70dp
- 16dp10dp13sp
- 26sp
@@ -154,18 +119,10 @@
holding the phone, *before* moving it up to your face and having
the prox sensor kick in.) -->
- 34sp
- 20sp
-
- 2dp
-
- 16dp
-
14dp-96dp
- 16dp24dp16sp16dp
diff --git a/libsession/src/main/res/values/google-playstore-strings.xml b/libsession/src/main/res/values/google-playstore-strings.xml
index 4057c0298d..9054f40731 100644
--- a/libsession/src/main/res/values/google-playstore-strings.xml
+++ b/libsession/src/main/res/values/google-playstore-strings.xml
@@ -15,22 +15,6 @@
- TextSecure Private Messenger
- TextSecure is a messaging app that allows you to take back your privacy while easily communicating with friends.
-
- Using TextSecure, you can communicate instantly while avoiding SMS fees, create groups so that you can chat in real time with all your friends at once, and share media or attachments all with complete privacy. The server never has access to any of your communication and never stores any of your data.
-
- * Private. TextSecure uses an advanced end to end encryption protocol that provides privacy for every message every time.
- * Open Source. TextSecure is Free and Open Source, enabling anyone to verify its security by auditing the code. TextSecure is the only private messenger that uses open source peer-reviewed cryptographic protocols to keep your messages safe.
- * Group Chat. TextSecure allows you to create encrypted groups so you can have private conversations with all your friends at once. Not only are the messages encrypted, but the TextSecure server never has access to any group metadata such as the membership list, group title, or group icon.
- * Fast. The TextSecure protocol is designed to operate in the most constrained environment possible. Using TextSecure, messages are instantly delivered to friends.
-
- Please file any bugs, issues, or feature requests at:
- https://github.com/signalapp/textsecure/issues
-
- More details:
- http://www.whispersystems.org/#encrypted_texts
-
-
+
diff --git a/libsession/src/main/res/values/material_colors.xml b/libsession/src/main/res/values/material_colors.xml
index 7b674a467d..660f6c1718 100644
--- a/libsession/src/main/res/values/material_colors.xml
+++ b/libsession/src/main/res/values/material_colors.xml
@@ -1,275 +1,30 @@
- #FFEBEE
- #FFCDD2
- #EF9A9A
- #E57373#EF5350#F44336
- #E53935
- #D32F2F
- #C62828
- #B71C1C
- #FF8A80
- #FF5252
- #FF1744
- #D50000
- #EDE7F6
- #D1C4E9
- #B39DDB
- #9575CD
- #7E57C2
- #673AB7
- #5E35B1
- #512DA8
- #4527A0
- #311B92
- #B388FF
- #7C4DFF
- #651FFF
- #6200EA
-
- #E1F5FE
- #B3E5FC
- #81D4FA
- #4FC3F7
- #29B6F6
- #03A9F4
- #039BE5
- #0288D1
- #0277BD
- #01579B
- #80D8FF
- #40C4FF
- #00B0FF
- #0091EA
-
- #E8F5E9
- #C8E6C9
- #A5D6A7
- #81C784#66BB6A#4CAF50
- #43A047
- #388E3C
- #2E7D32
- #1B5E20
- #B9F6CA
- #69F0AE
- #00E676
- #00C853
- #FFFDE7
- #FFF9C4
- #FFF59D
- #FFF176
- #FFEE58#FFEB3B
- #FDD835
- #FBC02D
- #F9A825
- #F57F17
- #FFFF8D
- #FFFF00
- #FFEA00
- #FFD600
- #FBE9E7
- #FFCCBC
- #FFAB91
- #FF8A65
- #FF7043
- #FF5722
- #F4511E
- #E64A19
- #D84315
- #BF360C
- #FF9E80
- #FF6E40
- #FF3D00
- #DD2C00
-
- #ECEFF1
- #CFD8DC
- #B0BEC5
- #90A4AE#78909C
- #607D8B
- #546E7A
- #455A64
- #37474F
- #263238
- #FCE4EC
- #F8BBD0
- #F48FB1
- #F06292
- #EC407A#E91E63
- #D81B60
- #C2185B
- #AD1457
- #880E4F
- #FF80AB
- #FF4081
- #F50057
- #C51162
- #E8EAF6
- #C5CAE9
- #9FA8DA
- #7986CB
- #5C6BC0
- #3F51B5
- #3949AB
- #303F9F
- #283593
- #1A237E
- #8C9EFF
- #536DFE
- #3D5AFE
- #304FFE
-
- #E0F7FA
- #B2EBF2
- #80DEEA
- #4DD0E1#26C6DA#00BCD4
- #00ACC1
- #0097A7
- #00838F
- #006064
- #84FFFF
- #18FFFF
- #00E5FF
- #00B8D4
- #F1F8E9
- #DCEDC8
- #C5E1A5
- #AED581
- #9CCC65
- #8BC34A
- #7CB342
- #689F38
- #558B2F
- #33691E
- #CCFF90
- #B2FF59
- #76FF03
- #64DD17
-
- #FFF8E1
- #FFECB3
- #FFE082
- #FFD54F
- #FFCA28
- #FFC107
- #FFB300
- #FFA000
- #FF8F00
- #FF6F00
- #FFE57F
- #FFD740
- #FFC400
- #FFAB00
-
- #EFEBE9
- #D7CCC8
- #BCAAA4
- #A1887F
- #8D6E63
- #795548
- #6D4C41
- #5D4037
- #4E342E
- #3E2723
-
- #F3E5F5
- #E1BEE7
- #CE93D8
- #BA68C8#AB47BC
- #9C27B0
- #8E24AA
- #7B1FA2
- #6A1B9A
- #4A148C
- #EA80FC
- #E040FB
- #D500F9
- #AA00FF
- #E3F2FD
- #BBDEFB
- #90CAF9
- #64B5F6#42A5F5#2196F3
- #1E88E5
- #1976D2
- #1565C0
- #0D47A1
- #82B1FF
- #448AFF
- #2979FF
- #2962FF
- #E0F2F1
- #B2DFDB
- #80CBC4
- #4DB6AC
- #26A69A
- #009688
- #00897B
- #00796B
- #00695C
- #004D40
- #A7FFEB
- #64FFDA
- #1DE9B6
- #00BFA5
-
- #F9FBE7
- #F0F4C3
- #E6EE9C
- #DCE775
- #D4E157
- #CDDC39
- #C0CA33
- #AFB42B
- #9E9D24
- #827717
- #F4FF81
- #EEFF41
- #C6FF00
- #AEEA00
-
- #FFF3E0
- #FFE0B2
- #FFCC80
- #FFB74D#FFA726
- #FF9800
- #FB8C00
- #F57C00
- #EF6C00
- #E65100
- #FFD180
- #FFAB40
- #FF9100
- #FF6D00
- #FAFAFA#F5F5F5
- #EEEEEE
- #E0E0E0#BDBDBD
- #9E9E9E#757575
- #616161#424242#212121
diff --git a/libsession/src/main/res/values/text_styles.xml b/libsession/src/main/res/values/text_styles.xml
index 544cd22c75..5639c60147 100644
--- a/libsession/src/main/res/values/text_styles.xml
+++ b/libsession/src/main/res/values/text_styles.xml
@@ -1,15 +1,5 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libsignal/protobuf/SignalService.proto b/libsignal/protobuf/SignalService.proto
index 7c44fce228..7ee8dc9946 100644
--- a/libsignal/protobuf/SignalService.proto
+++ b/libsignal/protobuf/SignalService.proto
@@ -135,7 +135,7 @@ message DataMessage {
repeated bytes members = 5;
repeated bytes admins = 6;
repeated KeyPairWrapper wrappers = 7;
- optional uint32 expireTimer = 8;
+ optional uint32 expirationTimer = 8;
}
optional string body = 1;
@@ -161,6 +161,7 @@ message ConfigurationMessage {
optional KeyPair encryptionKeyPair = 3;
repeated bytes members = 4;
repeated bytes admins = 5;
+ optional uint32 expirationTimer = 6;
}
message Contact {
diff --git a/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java b/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java
index 8f7921dcb2..0ca46d3a9f 100644
--- a/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java
+++ b/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java
@@ -9222,15 +9222,15 @@ public final class SignalServiceProtos {
org.session.libsignal.protos.SignalServiceProtos.DataMessage.ClosedGroupControlMessage.KeyPairWrapperOrBuilder getWrappersOrBuilder(
int index);
- // optional uint32 expireTimer = 8;
+ // optional uint32 expirationTimer = 8;
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- boolean hasExpireTimer();
+ boolean hasExpirationTimer();
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- int getExpireTimer();
+ int getExpirationTimer();
}
/**
* Protobuf type {@code signalservice.DataMessage.ClosedGroupControlMessage}
@@ -9343,7 +9343,7 @@ public final class SignalServiceProtos {
}
case 64: {
bitField0_ |= 0x00000010;
- expireTimer_ = input.readUInt32();
+ expirationTimer_ = input.readUInt32();
break;
}
}
@@ -10303,20 +10303,20 @@ public final class SignalServiceProtos {
return wrappers_.get(index);
}
- // optional uint32 expireTimer = 8;
- public static final int EXPIRETIMER_FIELD_NUMBER = 8;
- private int expireTimer_;
+ // optional uint32 expirationTimer = 8;
+ public static final int EXPIRATIONTIMER_FIELD_NUMBER = 8;
+ private int expirationTimer_;
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- public boolean hasExpireTimer() {
+ public boolean hasExpirationTimer() {
return ((bitField0_ & 0x00000010) == 0x00000010);
}
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- public int getExpireTimer() {
- return expireTimer_;
+ public int getExpirationTimer() {
+ return expirationTimer_;
}
private void initFields() {
@@ -10327,7 +10327,7 @@ public final class SignalServiceProtos {
members_ = java.util.Collections.emptyList();
admins_ = java.util.Collections.emptyList();
wrappers_ = java.util.Collections.emptyList();
- expireTimer_ = 0;
+ expirationTimer_ = 0;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -10379,7 +10379,7 @@ public final class SignalServiceProtos {
output.writeMessage(7, wrappers_.get(i));
}
if (((bitField0_ & 0x00000010) == 0x00000010)) {
- output.writeUInt32(8, expireTimer_);
+ output.writeUInt32(8, expirationTimer_);
}
getUnknownFields().writeTo(output);
}
@@ -10430,7 +10430,7 @@ public final class SignalServiceProtos {
}
if (((bitField0_ & 0x00000010) == 0x00000010)) {
size += com.google.protobuf.CodedOutputStream
- .computeUInt32Size(8, expireTimer_);
+ .computeUInt32Size(8, expirationTimer_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
@@ -10572,7 +10572,7 @@ public final class SignalServiceProtos {
} else {
wrappersBuilder_.clear();
}
- expireTimer_ = 0;
+ expirationTimer_ = 0;
bitField0_ = (bitField0_ & ~0x00000080);
return this;
}
@@ -10644,7 +10644,7 @@ public final class SignalServiceProtos {
if (((from_bitField0_ & 0x00000080) == 0x00000080)) {
to_bitField0_ |= 0x00000010;
}
- result.expireTimer_ = expireTimer_;
+ result.expirationTimer_ = expirationTimer_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -10721,8 +10721,8 @@ public final class SignalServiceProtos {
}
}
}
- if (other.hasExpireTimer()) {
- setExpireTimer(other.getExpireTimer());
+ if (other.hasExpirationTimer()) {
+ setExpirationTimer(other.getExpirationTimer());
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
@@ -11430,35 +11430,35 @@ public final class SignalServiceProtos {
return wrappersBuilder_;
}
- // optional uint32 expireTimer = 8;
- private int expireTimer_ ;
+ // optional uint32 expirationTimer = 8;
+ private int expirationTimer_ ;
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- public boolean hasExpireTimer() {
+ public boolean hasExpirationTimer() {
return ((bitField0_ & 0x00000080) == 0x00000080);
}
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- public int getExpireTimer() {
- return expireTimer_;
+ public int getExpirationTimer() {
+ return expirationTimer_;
}
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- public Builder setExpireTimer(int value) {
+ public Builder setExpirationTimer(int value) {
bitField0_ |= 0x00000080;
- expireTimer_ = value;
+ expirationTimer_ = value;
onChanged();
return this;
}
/**
- * optional uint32 expireTimer = 8;
+ * optional uint32 expirationTimer = 8;
*/
- public Builder clearExpireTimer() {
+ public Builder clearExpirationTimer() {
bitField0_ = (bitField0_ & ~0x00000080);
- expireTimer_ = 0;
+ expirationTimer_ = 0;
onChanged();
return this;
}
@@ -14104,6 +14104,16 @@ public final class SignalServiceProtos {
* repeated bytes admins = 5;
*/
com.google.protobuf.ByteString getAdmins(int index);
+
+ // optional uint32 expirationTimer = 6;
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ boolean hasExpirationTimer();
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ int getExpirationTimer();
}
/**
* Protobuf type {@code signalservice.ConfigurationMessage.ClosedGroup}
@@ -14195,6 +14205,11 @@ public final class SignalServiceProtos {
admins_.add(input.readBytes());
break;
}
+ case 48: {
+ bitField0_ |= 0x00000008;
+ expirationTimer_ = input.readUInt32();
+ break;
+ }
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -14368,12 +14383,29 @@ public final class SignalServiceProtos {
return admins_.get(index);
}
+ // optional uint32 expirationTimer = 6;
+ public static final int EXPIRATIONTIMER_FIELD_NUMBER = 6;
+ private int expirationTimer_;
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ public boolean hasExpirationTimer() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ public int getExpirationTimer() {
+ return expirationTimer_;
+ }
+
private void initFields() {
publicKey_ = com.google.protobuf.ByteString.EMPTY;
name_ = "";
encryptionKeyPair_ = org.session.libsignal.protos.SignalServiceProtos.KeyPair.getDefaultInstance();
members_ = java.util.Collections.emptyList();
admins_ = java.util.Collections.emptyList();
+ expirationTimer_ = 0;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -14408,6 +14440,9 @@ public final class SignalServiceProtos {
for (int i = 0; i < admins_.size(); i++) {
output.writeBytes(5, admins_.get(i));
}
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ output.writeUInt32(6, expirationTimer_);
+ }
getUnknownFields().writeTo(output);
}
@@ -14447,6 +14482,10 @@ public final class SignalServiceProtos {
size += dataSize;
size += 1 * getAdminsList().size();
}
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeUInt32Size(6, expirationTimer_);
+ }
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -14578,6 +14617,8 @@ public final class SignalServiceProtos {
bitField0_ = (bitField0_ & ~0x00000008);
admins_ = java.util.Collections.emptyList();
bitField0_ = (bitField0_ & ~0x00000010);
+ expirationTimer_ = 0;
+ bitField0_ = (bitField0_ & ~0x00000020);
return this;
}
@@ -14632,6 +14673,10 @@ public final class SignalServiceProtos {
bitField0_ = (bitField0_ & ~0x00000010);
}
result.admins_ = admins_;
+ if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+ to_bitField0_ |= 0x00000008;
+ }
+ result.expirationTimer_ = expirationTimer_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -14679,6 +14724,9 @@ public final class SignalServiceProtos {
}
onChanged();
}
+ if (other.hasExpirationTimer()) {
+ setExpirationTimer(other.getExpirationTimer());
+ }
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -15083,6 +15131,39 @@ public final class SignalServiceProtos {
return this;
}
+ // optional uint32 expirationTimer = 6;
+ private int expirationTimer_ ;
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ public boolean hasExpirationTimer() {
+ return ((bitField0_ & 0x00000020) == 0x00000020);
+ }
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ public int getExpirationTimer() {
+ return expirationTimer_;
+ }
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ public Builder setExpirationTimer(int value) {
+ bitField0_ |= 0x00000020;
+ expirationTimer_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional uint32 expirationTimer = 6;
+ */
+ public Builder clearExpirationTimer() {
+ bitField0_ = (bitField0_ & ~0x00000020);
+ expirationTimer_ = 0;
+ onChanged();
+ return this;
+ }
+
// @@protoc_insertion_point(builder_scope:signalservice.ConfigurationMessage.ClosedGroup)
}
@@ -21289,7 +21370,7 @@ public final class SignalServiceProtos {
"fication\022<\n\004type\030\001 \002(\0162..signalservice.D",
"ataExtractionNotification.Type\022\021\n\ttimest" +
"amp\030\002 \001(\004\"\'\n\004Type\022\016\n\nSCREENSHOT\020\001\022\017\n\013MED" +
- "IA_SAVED\020\002\"\241\014\n\013DataMessage\022\014\n\004body\030\001 \001(\t" +
+ "IA_SAVED\020\002\"\245\014\n\013DataMessage\022\014\n\004body\030\001 \001(\t" +
"\0225\n\013attachments\030\002 \003(\0132 .signalservice.At" +
"tachmentPointer\022*\n\005group\030\003 \001(\0132\033.signals" +
"ervice.GroupContext\022\r\n\005flags\030\004 \001(\r\022\023\n\013ex" +
@@ -21314,7 +21395,7 @@ public final class SignalServiceProtos {
"\n\005image\030\003 \001(\0132 .signalservice.Attachment" +
"Pointer\032:\n\013LokiProfile\022\023\n\013displayName\030\001 " +
"\001(\t\022\026\n\016profilePicture\030\002 \001(\t\0320\n\023OpenGroup" +
- "Invitation\022\013\n\003url\030\001 \002(\t\022\014\n\004name\030\003 \002(\t\032\370\003" +
+ "Invitation\022\013\n\003url\030\001 \002(\t\022\014\n\004name\030\003 \002(\t\032\374\003" +
"\n\031ClosedGroupControlMessage\022G\n\004type\030\001 \002(" +
"\01629.signalservice.DataMessage.ClosedGrou",
"pControlMessage.Type\022\021\n\tpublicKey\030\002 \001(\014\022" +
@@ -21322,41 +21403,42 @@ public final class SignalServiceProtos {
"2\026.signalservice.KeyPair\022\017\n\007members\030\005 \003(" +
"\014\022\016\n\006admins\030\006 \003(\014\022U\n\010wrappers\030\007 \003(\0132C.si" +
"gnalservice.DataMessage.ClosedGroupContr" +
- "olMessage.KeyPairWrapper\022\023\n\013expireTimer\030" +
- "\010 \001(\r\032=\n\016KeyPairWrapper\022\021\n\tpublicKey\030\001 \002" +
- "(\014\022\030\n\020encryptedKeyPair\030\002 \002(\014\"r\n\004Type\022\007\n\003" +
- "NEW\020\001\022\027\n\023ENCRYPTION_KEY_PAIR\020\003\022\017\n\013NAME_C" +
- "HANGE\020\004\022\021\n\rMEMBERS_ADDED\020\005\022\023\n\017MEMBERS_RE",
- "MOVED\020\006\022\017\n\013MEMBER_LEFT\020\007\"$\n\005Flags\022\033\n\027EXP" +
- "IRATION_TIMER_UPDATE\020\002\"\316\003\n\024Configuration" +
- "Message\022E\n\014closedGroups\030\001 \003(\0132/.signalse" +
- "rvice.ConfigurationMessage.ClosedGroup\022\022" +
- "\n\nopenGroups\030\002 \003(\t\022\023\n\013displayName\030\003 \001(\t\022" +
- "\026\n\016profilePicture\030\004 \001(\t\022\022\n\nprofileKey\030\005 " +
- "\001(\014\022=\n\010contacts\030\006 \003(\0132+.signalservice.Co" +
- "nfigurationMessage.Contact\032\202\001\n\013ClosedGro" +
- "up\022\021\n\tpublicKey\030\001 \001(\014\022\014\n\004name\030\002 \001(\t\0221\n\021e" +
- "ncryptionKeyPair\030\003 \001(\0132\026.signalservice.K",
- "eyPair\022\017\n\007members\030\004 \003(\014\022\016\n\006admins\030\005 \003(\014\032" +
- "V\n\007Contact\022\021\n\tpublicKey\030\001 \002(\014\022\014\n\004name\030\002 " +
- "\002(\t\022\026\n\016profilePicture\030\003 \001(\t\022\022\n\nprofileKe" +
- "y\030\004 \001(\014\"u\n\016ReceiptMessage\0220\n\004type\030\001 \002(\0162" +
- "\".signalservice.ReceiptMessage.Type\022\021\n\tt" +
- "imestamp\030\002 \003(\004\"\036\n\004Type\022\014\n\010DELIVERY\020\000\022\010\n\004" +
- "READ\020\001\"\354\001\n\021AttachmentPointer\022\n\n\002id\030\001 \002(\006" +
- "\022\023\n\013contentType\030\002 \001(\t\022\013\n\003key\030\003 \001(\014\022\014\n\004si" +
- "ze\030\004 \001(\r\022\021\n\tthumbnail\030\005 \001(\014\022\016\n\006digest\030\006 " +
- "\001(\014\022\020\n\010fileName\030\007 \001(\t\022\r\n\005flags\030\010 \001(\r\022\r\n\005",
- "width\030\t \001(\r\022\016\n\006height\030\n \001(\r\022\017\n\007caption\030\013" +
- " \001(\t\022\013\n\003url\030e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MESS" +
- "AGE\020\001\"\365\001\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022.\n\004ty" +
- "pe\030\002 \001(\0162 .signalservice.GroupContext.Ty" +
- "pe\022\014\n\004name\030\003 \001(\t\022\017\n\007members\030\004 \003(\t\0220\n\006ava" +
- "tar\030\005 \001(\0132 .signalservice.AttachmentPoin" +
- "ter\022\016\n\006admins\030\006 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020\000" +
- "\022\n\n\006UPDATE\020\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014R" +
- "EQUEST_INFO\020\004B3\n\034org.session.libsignal.p" +
- "rotosB\023SignalServiceProtos"
+ "olMessage.KeyPairWrapper\022\027\n\017expirationTi" +
+ "mer\030\010 \001(\r\032=\n\016KeyPairWrapper\022\021\n\tpublicKey" +
+ "\030\001 \002(\014\022\030\n\020encryptedKeyPair\030\002 \002(\014\"r\n\004Type" +
+ "\022\007\n\003NEW\020\001\022\027\n\023ENCRYPTION_KEY_PAIR\020\003\022\017\n\013NA" +
+ "ME_CHANGE\020\004\022\021\n\rMEMBERS_ADDED\020\005\022\023\n\017MEMBER",
+ "S_REMOVED\020\006\022\017\n\013MEMBER_LEFT\020\007\"$\n\005Flags\022\033\n" +
+ "\027EXPIRATION_TIMER_UPDATE\020\002\"\347\003\n\024Configura" +
+ "tionMessage\022E\n\014closedGroups\030\001 \003(\0132/.sign" +
+ "alservice.ConfigurationMessage.ClosedGro" +
+ "up\022\022\n\nopenGroups\030\002 \003(\t\022\023\n\013displayName\030\003 " +
+ "\001(\t\022\026\n\016profilePicture\030\004 \001(\t\022\022\n\nprofileKe" +
+ "y\030\005 \001(\014\022=\n\010contacts\030\006 \003(\0132+.signalservic" +
+ "e.ConfigurationMessage.Contact\032\233\001\n\013Close" +
+ "dGroup\022\021\n\tpublicKey\030\001 \001(\014\022\014\n\004name\030\002 \001(\t\022" +
+ "1\n\021encryptionKeyPair\030\003 \001(\0132\026.signalservi",
+ "ce.KeyPair\022\017\n\007members\030\004 \003(\014\022\016\n\006admins\030\005 " +
+ "\003(\014\022\027\n\017expirationTimer\030\006 \001(\r\032V\n\007Contact\022" +
+ "\021\n\tpublicKey\030\001 \002(\014\022\014\n\004name\030\002 \002(\t\022\026\n\016prof" +
+ "ilePicture\030\003 \001(\t\022\022\n\nprofileKey\030\004 \001(\014\"u\n\016" +
+ "ReceiptMessage\0220\n\004type\030\001 \002(\0162\".signalser" +
+ "vice.ReceiptMessage.Type\022\021\n\ttimestamp\030\002 " +
+ "\003(\004\"\036\n\004Type\022\014\n\010DELIVERY\020\000\022\010\n\004READ\020\001\"\354\001\n\021" +
+ "AttachmentPointer\022\n\n\002id\030\001 \002(\006\022\023\n\013content" +
+ "Type\030\002 \001(\t\022\013\n\003key\030\003 \001(\014\022\014\n\004size\030\004 \001(\r\022\021\n" +
+ "\tthumbnail\030\005 \001(\014\022\016\n\006digest\030\006 \001(\014\022\020\n\010file",
+ "Name\030\007 \001(\t\022\r\n\005flags\030\010 \001(\r\022\r\n\005width\030\t \001(\r" +
+ "\022\016\n\006height\030\n \001(\r\022\017\n\007caption\030\013 \001(\t\022\013\n\003url" +
+ "\030e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MESSAGE\020\001\"\365\001\n\014G" +
+ "roupContext\022\n\n\002id\030\001 \001(\014\022.\n\004type\030\002 \001(\0162 ." +
+ "signalservice.GroupContext.Type\022\014\n\004name\030" +
+ "\003 \001(\t\022\017\n\007members\030\004 \003(\t\0220\n\006avatar\030\005 \001(\0132 " +
+ ".signalservice.AttachmentPointer\022\016\n\006admi" +
+ "ns\030\006 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020" +
+ "\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014REQUEST_INFO" +
+ "\020\004B3\n\034org.session.libsignal.protosB\023Sign",
+ "alServiceProtos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -21434,7 +21516,7 @@ public final class SignalServiceProtos {
internal_static_signalservice_DataMessage_ClosedGroupControlMessage_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_signalservice_DataMessage_ClosedGroupControlMessage_descriptor,
- new java.lang.String[] { "Type", "PublicKey", "Name", "EncryptionKeyPair", "Members", "Admins", "Wrappers", "ExpireTimer", });
+ new java.lang.String[] { "Type", "PublicKey", "Name", "EncryptionKeyPair", "Members", "Admins", "Wrappers", "ExpirationTimer", });
internal_static_signalservice_DataMessage_ClosedGroupControlMessage_KeyPairWrapper_descriptor =
internal_static_signalservice_DataMessage_ClosedGroupControlMessage_descriptor.getNestedTypes().get(0);
internal_static_signalservice_DataMessage_ClosedGroupControlMessage_KeyPairWrapper_fieldAccessorTable = new
@@ -21452,7 +21534,7 @@ public final class SignalServiceProtos {
internal_static_signalservice_ConfigurationMessage_ClosedGroup_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_signalservice_ConfigurationMessage_ClosedGroup_descriptor,
- new java.lang.String[] { "PublicKey", "Name", "EncryptionKeyPair", "Members", "Admins", });
+ new java.lang.String[] { "PublicKey", "Name", "EncryptionKeyPair", "Members", "Admins", "ExpirationTimer", });
internal_static_signalservice_ConfigurationMessage_Contact_descriptor =
internal_static_signalservice_ConfigurationMessage_descriptor.getNestedTypes().get(1);
internal_static_signalservice_ConfigurationMessage_Contact_fieldAccessorTable = new