diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a95a9baebe..bc9e379d11 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -206,18 +206,6 @@
android:resource="@mipmap/ic_launcher" />
-
-
-
-
-
.
- */
-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/components/ConversationSearchBottomBar.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java
index 603c4869a1..409acd7639 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java
@@ -11,7 +11,7 @@ import android.widget.TextView;
import network.loki.messenger.R;
/**
- * Bottom navigation bar shown in the {@link org.thoughtcrime.securesms.conversation.ConversationActivity}
+ * Bottom navigation bar shown in the ConversationActivity
* when the user is searching within a conversation. Shows details about the results and allows the
* user to move between them.
*/
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 075caf5f6e..eeb9deacab 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
@@ -75,7 +75,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
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/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/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/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_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_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/message_details_activity.xml b/app/src/main/res/layout/message_details_activity.xml
index 88cfb1061f..aa4bac9814 100644
--- a/app/src/main/res/layout/message_details_activity.xml
+++ b/app/src/main/res/layout/message_details_activity.xml
@@ -6,9 +6,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
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