Remove old Message Details.

This commit is contained in:
Cody Henthorne
2020-06-16 18:12:14 -04:00
committed by Greyson Parrelli
parent 027453bbd2
commit 1a09e70a04
18 changed files with 208 additions and 1253 deletions

View File

@@ -1,451 +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 <http://www.gnu.org/licenses/>.
*/
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.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.Loader;
import org.thoughtcrime.securesms.conversation.ConversationItem;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.Log;
import android.os.Parcelable;
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 org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
import org.thoughtcrime.securesms.color.MaterialColor;
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.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicDarkActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.util.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;
/**
* @author Jake McGinty
*/
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor> {
private final static String TAG = MessageDetailsActivity.class.getSimpleName();
public static final String MESSAGE_ID_EXTRA = "message_id";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String IS_PUSH_GROUP_EXTRA = "is_push_group";
public static final String TYPE_EXTRA = "type";
public static final String RECIPIENT_EXTRA = "recipient_id";
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 ListView recipientsList;
private LayoutInflater inflater;
private DynamicTheme dynamicTheme = new DynamicDarkActionBarTheme();
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
private boolean running;
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
public void onCreate(Bundle bundle, boolean ready) {
setContentView(R.layout.message_details_activity);
running = true;
initializeResources();
initializeActionBar();
getSupportLoaderManager().initLoader(0, null, this);
}
@Override
protected void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
assert getSupportActionBar() != null;
getSupportActionBar().setTitle(R.string.AndroidManifest__message_details);
ApplicationDependencies.getMessageNotifier().setVisibleThread(threadId);
}
@Override
protected void onPause() {
super.onPause();
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
}
@Override
protected void onDestroy() {
super.onDestroy();
running = false;
}
private void initializeActionBar() {
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
LiveRecipient recipient = Recipient.live(getIntent().getParcelableExtra(RECIPIENT_EXTRA));
recipient.observe(this, r -> setActionBarColor(r.getColor()));
setActionBarColor(recipient.get().getColor());
}
private void setActionBarColor(MaterialColor color) {
assert getSupportActionBar() != null;
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(color.toStatusBarColor(this));
}
}
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);
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.isPush()) {
transportText = getString(R.string.ConversationFragment_push);
} 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 = dynamicLanguage.getCurrentLocale();
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<RecipientDeliveryStatus> recipients) {
final int toFromRes;
if (messageRecord.isMms() && !messageRecord.isPush() && !messageRecord.isOutgoing()) {
toFromRes = R.string.message_details_header__with;
} else if (messageRecord.isOutgoing()) {
toFromRes = R.string.message_details_header__to;
} else {
toFromRes = R.string.message_details_header__from;
}
toFrom.setText(toFromRes);
conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient, null, false);
Parcelable state = recipientsList.onSaveInstanceState();
recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup));
recipientsList.onRestoreInstanceState(state);
}
private void inflateMessageViewIfAbsent(MessageRecord messageRecord) {
if (conversationItem == null) {
if (messageRecord.isGroupAction()) {
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_update, itemParent, false);
} else if (messageRecord.isOutgoing()) {
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_sent_multimedia, itemParent, false);
} else {
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_received_multimedia, 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<Cursor> 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<Cursor> 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<Cursor> 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<Void,Void,List<RecipientDeliveryStatus>> {
private final WeakReference<Context> 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<RecipientDeliveryStatus> doInBackground(Void... voids) {
Context context = getContext();
if (context == null) {
Log.w(TAG, "associated context is destroyed, finishing early");
return null;
}
List<RecipientDeliveryStatus> recipients = new LinkedList<>();
if (!messageRecord.getRecipient().isGroup()) {
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), messageRecord.isUnidentified(), -1));
} else {
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
if (receiptInfoList.isEmpty()) {
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
for (Recipient recipient : group) {
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
}
} else {
for (GroupReceiptInfo info : receiptInfoList) {
recipients.add(new RecipientDeliveryStatus(Recipient.resolved(info.getRecipientId()),
getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()),
info.isUnidentified(),
info.getTimestamp()));
}
}
}
return recipients;
}
@Override
public void onPostExecute(List<RecipientDeliveryStatus> 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 && messageRecord.getIdentityKeyMismatches().isEmpty();
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) {
resendButton.setVisibility(View.GONE);
SignalExecutors.BOUNDED.execute(() -> MessageSender.resend(MessageDetailsActivity.this, messageRecord));
}
}
}

View File

@@ -1,112 +0,0 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
private final Context context;
private final GlideRequests glideRequests;
private final MessageRecord record;
private final List<RecipientDeliveryStatus> members;
private final boolean isPushGroup;
private final StableIdGenerator<RecipientId> idGenerator;
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
boolean isPushGroup)
{
this.context = context;
this.glideRequests = glideRequests;
this.record = record;
this.isPushGroup = isPushGroup;
this.members = members;
this.idGenerator = new StableIdGenerator<>();
}
@Override
public int getCount() {
return members.size();
}
@Override
public Object getItem(int position) {
return members.get(position);
}
@Override
public long getItemId(int position) {
return idGenerator.getId(members.get(position).recipient.getId());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.message_recipient_list_item, parent, false);
}
RecipientDeliveryStatus member = members.get(position);
((MessageRecipientListItem)convertView).set(glideRequests, record, member, isPushGroup);
return convertView;
}
@Override
public void onMovedToScrapHeap(View view) {
((MessageRecipientListItem)view).unbind();
}
static class RecipientDeliveryStatus {
enum Status {
UNKNOWN, PENDING, SENT, DELIVERED, READ
}
private final Recipient recipient;
private final Status deliveryStatus;
private final boolean isUnidentified;
private final long timestamp;
RecipientDeliveryStatus(Recipient recipient, Status deliveryStatus, boolean isUnidentified, long timestamp) {
this.recipient = recipient;
this.deliveryStatus = deliveryStatus;
this.isUnidentified = isUnidentified;
this.timestamp = timestamp;
}
Status getDeliveryStatus() {
return deliveryStatus;
}
boolean isUnidentified() {
return isUnidentified;
}
public long getTimestamp() {
return timestamp;
}
public Recipient getRecipient() {
return recipient;
}
}
}

View File

@@ -1,200 +0,0 @@
/*
* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
/**
* A simple view to show the recipients of a message
*
* @author Jake McGinty
*/
public class MessageRecipientListItem extends RelativeLayout
implements RecipientForeverObserver
{
@SuppressWarnings("unused")
private final static String TAG = MessageRecipientListItem.class.getSimpleName();
private RecipientDeliveryStatus member;
private GlideRequests glideRequests;
private FromTextView fromView;
private TextView errorDescription;
private TextView actionDescription;
private Button conflictButton;
private AvatarImageView contactPhotoImage;
private ImageView unidentifiedDeliveryIcon;
private DeliveryStatusView deliveryStatusView;
public MessageRecipientListItem(Context context) {
super(context);
}
public MessageRecipientListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
this.fromView = findViewById(R.id.from);
this.errorDescription = findViewById(R.id.error_description);
this.actionDescription = findViewById(R.id.action_description);
this.contactPhotoImage = findViewById(R.id.contact_photo_image);
this.conflictButton = findViewById(R.id.conflict_button);
this.unidentifiedDeliveryIcon = findViewById(R.id.ud_indicator);
this.deliveryStatusView = findViewById(R.id.delivery_status);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
observeMember();
}
@Override
protected void onDetachedFromWindow() {
unsubscribeFromMember();
super.onDetachedFromWindow();
}
public void set(final GlideRequests glideRequests,
final MessageRecord record,
final RecipientDeliveryStatus member,
final boolean isPushGroup)
{
unsubscribeFromMember();
this.glideRequests = glideRequests;
this.member = member;
observeMember();
fromView.setText(member.getRecipient());
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
setIssueIndicators(record, isPushGroup);
unidentifiedDeliveryIcon.setVisibility(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()) && member.isUnidentified() ? VISIBLE : GONE);
}
private void observeMember() {
if (isAttachedToWindow() && member != null && member.getRecipient() != null) {
member.getRecipient().live().observeForever(this);
}
}
private void unsubscribeFromMember() {
if (member != null && member.getRecipient() != null) member.getRecipient().live().removeForeverObserver(this);
}
private void setIssueIndicators(final MessageRecord record,
final boolean isPushGroup)
{
final NetworkFailure networkFailure = getNetworkFailure(record);
final IdentityKeyMismatch keyMismatch = networkFailure == null ? getKeyMismatch(record) : null;
String errorText = "";
if (keyMismatch != null) {
conflictButton.setVisibility(View.VISIBLE);
errorText = getContext().getString(R.string.MessageDetailsRecipient_new_safety_number);
conflictButton.setOnClickListener(v -> new ConfirmIdentityDialog(getContext(), record, keyMismatch).show());
} else if ((networkFailure != null && !record.isPending()) || (!isPushGroup && record.isFailed())) {
conflictButton.setVisibility(View.GONE);
errorText = getContext().getString(R.string.MessageDetailsRecipient_failed_to_send);
} else {
if (record.isOutgoing()) {
if (member.getDeliveryStatus() == RecipientDeliveryStatus.Status.PENDING || member.getDeliveryStatus() == RecipientDeliveryStatus.Status.UNKNOWN) {
deliveryStatusView.setVisibility(View.GONE);
} else if (member.getDeliveryStatus() == RecipientDeliveryStatus.Status.READ) {
deliveryStatusView.setRead();
deliveryStatusView.setVisibility(View.VISIBLE);
} else if (member.getDeliveryStatus() == RecipientDeliveryStatus.Status.DELIVERED) {
deliveryStatusView.setDelivered();
deliveryStatusView.setVisibility(View.VISIBLE);
} else if (member.getDeliveryStatus() == RecipientDeliveryStatus.Status.SENT) {
deliveryStatusView.setSent();
deliveryStatusView.setVisibility(View.VISIBLE);
}
} else {
deliveryStatusView.setVisibility(View.GONE);
}
conflictButton.setVisibility(View.GONE);
}
errorDescription.setText(errorText);
errorDescription.setVisibility(TextUtils.isEmpty(errorText) ? View.GONE : View.VISIBLE);
}
private NetworkFailure getNetworkFailure(final MessageRecord record) {
if (record.hasNetworkFailures()) {
for (final NetworkFailure failure : record.getNetworkFailures()) {
if (failure.getRecipientId(getContext()).equals(member.getRecipient().getId())) {
return failure;
}
}
}
return null;
}
private IdentityKeyMismatch getKeyMismatch(final MessageRecord record) {
if (record.isIdentityMismatchFailure()) {
for (final IdentityKeyMismatch mismatch : record.getIdentityKeyMismatches()) {
if (mismatch.getRecipientId(getContext()).equals(member.getRecipient().getId())) {
return mismatch;
}
}
}
return null;
}
public void unbind() {
unsubscribeFromMember();
}
@Override
public void onRecipientChanged(@NonNull Recipient recipient) {
if (this.member != null && this.member.getRecipient().equals(recipient)) {
Log.d(TAG, "onRecipientChanged -- valid");
fromView.setText(recipient);
contactPhotoImage.setAvatar(glideRequests, recipient, false);
} else {
Log.d(TAG, "onRecipientChanged -- invalid");
}
}
}

View File

@@ -60,7 +60,6 @@ import com.annimon.stream.Stream;
import com.google.android.collect.Sets;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.MessageDetailsActivity;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
@@ -74,7 +73,6 @@ import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickList
import org.thoughtcrime.securesms.conversation.ConversationAdapter.StickyHeaderViewHolder;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -87,6 +85,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.messagedetails.MessageDetailsActivity;
import org.thoughtcrime.securesms.messagerequests.MessageRequestViewModel;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
@@ -690,13 +689,7 @@ public class ConversationFragment extends Fragment {
private void handleDisplayDetails(MessageRecord message) {
Intent intent = new Intent(getActivity(), org.thoughtcrime.securesms.messagedetails.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.RECIPIENT_EXTRA, recipient.getId());
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, recipient.get().isGroup() && message.isPush());
startActivity(intent);
startActivity(MessageDetailsActivity.getIntentForMessageDetails(requireContext(), message, recipient.getId(), threadId));
}
private void handleForwardMessage(MessageRecord message) {

View File

@@ -58,7 +58,6 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.ConfirmIdentityDialog;
import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.MessageDetailsActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.components.AlertView;
@@ -77,7 +76,6 @@ import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
@@ -92,6 +90,7 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.messagedetails.MessageDetailsActivity;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority;
@@ -109,7 +108,6 @@ import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.stickers.StickerUrl;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.LongClickCopySpan;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.SearchUtil;
@@ -1377,13 +1375,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
if (!shouldInterceptClicks(messageRecord) && parent != null) {
parent.onClick(v);
} else if (messageRecord.isFailed()) {
Intent intent = new Intent(context, org.thoughtcrime.securesms.messagedetails.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 && messageRecord.isPush());
intent.putExtra(MessageDetailsActivity.RECIPIENT_EXTRA, conversationRecipient.getId());
context.startActivity(intent);
context.startActivity(MessageDetailsActivity.getIntentForMessageDetails(context, messageRecord, conversationRecipient.getId(), messageRecord.getThreadId()));
} else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) {
handleApproveIdentity();
} else if (messageRecord.isPendingInsecureSmsFallback()) {

View File

@@ -1,16 +1,21 @@
package org.thoughtcrime.securesms.messagedetails;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.messagedetails.MessageDetailsAdapter.MessageDetailsViewState;
import org.thoughtcrime.securesms.messagedetails.MessageDetailsViewModel.Factory;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -25,10 +30,10 @@ import java.util.List;
public final class MessageDetailsActivity extends PassphraseRequiredActionBarActivity {
public static final String MESSAGE_ID_EXTRA = "message_id";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String TYPE_EXTRA = "type";
public static final String RECIPIENT_EXTRA = "recipient_id";
private static final String MESSAGE_ID_EXTRA = "message_id";
private static final String THREAD_ID_EXTRA = "thread_id";
private static final String TYPE_EXTRA = "type";
private static final String RECIPIENT_EXTRA = "recipient_id";
private GlideRequests glideRequests;
private MessageDetailsViewModel viewModel;
@@ -36,6 +41,15 @@ public final class MessageDetailsActivity extends PassphraseRequiredActionBarAct
private DynamicTheme dynamicTheme = new DynamicDarkActionBarTheme();
public static @NonNull Intent getIntentForMessageDetails(@NonNull Context context, @NonNull MessageRecord message, @NonNull RecipientId recipientId, long threadId) {
Intent intent = new Intent(context, MessageDetailsActivity.class);
intent.putExtra(MESSAGE_ID_EXTRA, message.getId());
intent.putExtra(THREAD_ID_EXTRA, threadId);
intent.putExtra(TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
intent.putExtra(RECIPIENT_EXTRA, recipientId);
return intent;
}
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
@@ -44,7 +58,7 @@ public final class MessageDetailsActivity extends PassphraseRequiredActionBarAct
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.message_details_2_activity);
setContentView(R.layout.message_details_activity);
glideRequests = GlideApp.with(this);

View File

@@ -32,11 +32,11 @@ final class MessageDetailsAdapter extends ListAdapter<MessageDetailsAdapter.Mess
public @NonNull RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case MessageDetailsViewState.MESSAGE_HEADER:
return new MessageHeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.message_details_2_header, parent, false), glideRequests);
return new MessageHeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.message_details_header, parent, false), glideRequests);
case MessageDetailsViewState.RECIPIENT_HEADER:
return new RecipientHeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.message_details_2_recipient_header, parent, false));
return new RecipientHeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.message_details_recipient_header, parent, false));
case MessageDetailsViewState.RECIPIENT:
return new RecipientViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.message_details_2_recipient, parent, false));
return new RecipientViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.message_details_recipient, parent, false));
default:
throw new AssertionError("unknown view type");
}

View File

@@ -48,18 +48,18 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder {
super(itemView);
this.glideRequests = glideRequests;
sentDate = itemView.findViewById(R.id.sent_time);
receivedDate = itemView.findViewById(R.id.received_time);
receivedGroup = itemView.findViewById(R.id.received_group);
expiresIn = itemView.findViewById(R.id.expires_in);
expiresGroup = itemView.findViewById(R.id.expires_group);
transport = itemView.findViewById(R.id.transport);
errorText = itemView.findViewById(R.id.error_text);
resendButton = itemView.findViewById(R.id.resend_button);
messageMetadata = itemView.findViewById(R.id.message_metadata);
updateStub = itemView.findViewById(R.id.message_view_update);
sentStub = itemView.findViewById(R.id.message_view_sent_multimedia);
receivedStub = itemView.findViewById(R.id.message_view_received_multimedia);
sentDate = itemView.findViewById(R.id.message_details_header_sent_time);
receivedDate = itemView.findViewById(R.id.message_details_header_received_time);
receivedGroup = itemView.findViewById(R.id.message_details_header_received_group);
expiresIn = itemView.findViewById(R.id.message_details_header_expires_in);
expiresGroup = itemView.findViewById(R.id.message_details_header_expires_group);
transport = itemView.findViewById(R.id.message_details_header_transport);
errorText = itemView.findViewById(R.id.message_details_header_error_text);
resendButton = itemView.findViewById(R.id.message_details_header_resend_button);
messageMetadata = itemView.findViewById(R.id.message_details_header_message_metadata);
updateStub = itemView.findViewById(R.id.message_details_header_message_view_update);
sentStub = itemView.findViewById(R.id.message_details_header_message_view_sent_multimedia);
receivedStub = itemView.findViewById(R.id.message_details_header_message_view_received_multimedia);
}
void bind(MessageRecord messageRecord, boolean running) {

View File

@@ -9,12 +9,9 @@ import org.thoughtcrime.securesms.ConfirmIdentityDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Locale;
final class RecipientViewHolder extends RecyclerView.ViewHolder {
@@ -28,12 +25,12 @@ final class RecipientViewHolder extends RecyclerView.ViewHolder {
RecipientViewHolder(View itemView) {
super(itemView);
fromView = itemView.findViewById(R.id.recipient_name);
avatar = itemView.findViewById(R.id.recipient_avatar);
timestamp = itemView.findViewById(R.id.recipient_timestamp);
error = itemView.findViewById(R.id.error_description);
conflictButton = itemView.findViewById(R.id.conflict_button);
unidentifiedDeliveryIcon = itemView.findViewById(R.id.ud_indicator);
fromView = itemView.findViewById(R.id.message_details_recipient_name);
avatar = itemView.findViewById(R.id.message_details_recipient_avatar);
timestamp = itemView.findViewById(R.id.message_details_recipient_timestamp);
error = itemView.findViewById(R.id.message_details_recipient_error_description);
conflictButton = itemView.findViewById(R.id.message_details_recipient_conflict_button);
unidentifiedDeliveryIcon = itemView.findViewById(R.id.message_details_recipient_ud_indicator);
}
void bind(RecipientDeliveryStatus data) {
@@ -45,13 +42,13 @@ final class RecipientViewHolder extends RecyclerView.ViewHolder {
timestamp.setVisibility(View.GONE);
error.setVisibility(View.VISIBLE);
conflictButton.setVisibility(View.VISIBLE);
error.setText(itemView.getContext().getString(R.string.MessageDetailsRecipient_new_safety_number));
error.setText(itemView.getContext().getString(R.string.message_details_recipient__new_safety_number));
conflictButton.setOnClickListener(unused -> new ConfirmIdentityDialog(itemView.getContext(), data.getMessageRecord(), data.getKeyMismatchFailure()).show());
} else if ((data.getNetworkFailure() != null && !data.getMessageRecord().isPending()) || (!data.getMessageRecord().getRecipient().isPushGroup() && data.getMessageRecord().isFailed())) {
timestamp.setVisibility(View.GONE);
error.setVisibility(View.VISIBLE);
conflictButton.setVisibility(View.GONE);
error.setText(itemView.getContext().getString(R.string.MessageDetailsRecipient_failed_to_send));
error.setText(itemView.getContext().getString(R.string.message_details_recipient__failed_to_send));
} else {
timestamp.setVisibility(View.VISIBLE);
error.setVisibility(View.GONE);