mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Merge pull request #40 from deltachat/conversation2
[WIP] Conversation2
This commit is contained in:
@@ -227,12 +227,6 @@
|
||||
android:theme="@style/TextSecure.LightTheme.Popup"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
|
||||
|
||||
<activity android:name=".MessageDetailsActivity"
|
||||
android:label="@string/AndroidManifest__message_details"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".GroupCreateActivity"
|
||||
android:windowSoftInputMode="stateVisible"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
@@ -23,6 +23,16 @@
|
||||
package com.b44t.messenger;
|
||||
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.model.Quote;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class DcMsg {
|
||||
|
||||
public final static int DC_MSG_UNDEFINED = 0;
|
||||
@@ -53,12 +63,105 @@ public class DcMsg {
|
||||
this.msgCPtr = msgCPtr;
|
||||
}
|
||||
|
||||
@Override protected void finalize() throws Throwable {
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
unrefMsgCPtr();
|
||||
msgCPtr = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null || !(other instanceof DcMsg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DcMsg that = (DcMsg) other;
|
||||
return this.getId()==that.getId() && this.getId()!=0;
|
||||
}
|
||||
|
||||
public boolean isOutgoing() {
|
||||
return getFromId() != DcContact.DC_CONTACT_ID_SELF;
|
||||
}
|
||||
|
||||
public boolean isGroupAction() {
|
||||
// TODO: check DcChat
|
||||
return false;
|
||||
}
|
||||
|
||||
// the following are probably system messages in delta-land
|
||||
public boolean isCallLog() { return false; }
|
||||
public boolean isIncomingCall() { return false; }
|
||||
public boolean isOutgoingCall() { return false; }
|
||||
public boolean isJoined() { return false; }
|
||||
public boolean isExpirationTimerUpdate() { return false; }
|
||||
public boolean isEndSession() { return false; }
|
||||
public boolean isIdentityUpdate() { return false; }
|
||||
public boolean isIdentityVerified() { return false; }
|
||||
public boolean isIdentityDefault() { return false; }
|
||||
|
||||
// aliases
|
||||
public String getDisplayBody() { return getText(); }
|
||||
public String getBody() { return getText(); }
|
||||
public long getDateReceived() { return getTimestamp(); }
|
||||
|
||||
public boolean isFailed() {
|
||||
return getState() == DC_STATE_OUT_ERROR;
|
||||
}
|
||||
|
||||
public long getExpiresIn() {
|
||||
return -1; // never.
|
||||
}
|
||||
|
||||
public long getExpireStarted() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return showPadlock()!=0;
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return getState() == DC_STATE_OUT_PENDING;
|
||||
}
|
||||
|
||||
public boolean isMediaPending() {
|
||||
return isPending();
|
||||
}
|
||||
|
||||
public boolean isDelivered() {
|
||||
return getState() == DC_STATE_OUT_DELIVERED;
|
||||
}
|
||||
|
||||
public boolean isRemoteRead() {
|
||||
return getState() == DC_STATE_OUT_MDN_RCVD;
|
||||
}
|
||||
|
||||
public boolean isUpdate() {
|
||||
// I'm uncertain as what is meant by 'update' here, compare MessageRecord
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getSubscriptionId() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean isMms() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Quote getQuote() {
|
||||
// TODO: remove the dependency to org.thoughtcrime.securesms.database.model.Quote
|
||||
return null;
|
||||
}
|
||||
|
||||
public Recipient getIndividualRecipient() {
|
||||
// TODO: remove the dependency to org.thoughtcrime.securesms.recipients.Recipient
|
||||
RecipientProvider.RecipientDetails recipientDetails = new RecipientProvider.RecipientDetails("individualRecipient", null, false, null, null);
|
||||
return new Recipient(Address.fromContact(getFromId()), recipientDetails);
|
||||
}
|
||||
|
||||
public native int getId();
|
||||
public native String getText();
|
||||
public native long getTimestamp();
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
@@ -16,21 +18,21 @@ import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public interface BindableConversationItem extends Unbindable {
|
||||
void bind(@NonNull MessageRecord messageRecord,
|
||||
@NonNull Optional<MessageRecord> previousMessageRecord,
|
||||
@NonNull Optional<MessageRecord> nextMessageRecord,
|
||||
void bind(@NonNull DcMsg messageRecord,
|
||||
@NonNull Optional<DcMsg> previousMessageRecord,
|
||||
@NonNull Optional<DcMsg> nextMessageRecord,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<MessageRecord> batchSelected,
|
||||
@NonNull Set<DcMsg> batchSelected,
|
||||
@NonNull Recipient recipients,
|
||||
boolean pulseHighlight);
|
||||
|
||||
MessageRecord getMessageRecord();
|
||||
DcMsg getMessageRecord();
|
||||
|
||||
void setEventListener(@Nullable EventListener listener);
|
||||
|
||||
interface EventListener {
|
||||
void onQuoteClicked(MmsMessageRecord messageRecord);
|
||||
void onQuoteClicked(DcMsg messageRecord);
|
||||
void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView);
|
||||
void onAddToContactsClicked(@NonNull Contact contact);
|
||||
void onMessageSharedContactClicked(@NonNull List<Recipient> choices);
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.VerifySpan;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
||||
|
||||
public class ConfirmIdentityDialog extends AlertDialog {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = ConfirmIdentityDialog.class.getSimpleName();
|
||||
|
||||
private OnClickListener callback;
|
||||
|
||||
public ConfirmIdentityDialog(Context context,
|
||||
MessageRecord messageRecord,
|
||||
IdentityKeyMismatch mismatch)
|
||||
{
|
||||
super(context);
|
||||
|
||||
Recipient recipient = Recipient.from(context, mismatch.getAddress(), false);
|
||||
String name = recipient.toShortString();
|
||||
String introduction = String.format(context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed), name, name);
|
||||
SpannableString spannableString = new SpannableString(introduction + " " +
|
||||
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact));
|
||||
|
||||
spannableString.setSpan(new VerifySpan(context, mismatch),
|
||||
introduction.length()+1, spannableString.length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
setTitle(name);
|
||||
setMessage(spannableString);
|
||||
|
||||
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getAddress()));
|
||||
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
((TextView)this.findViewById(android.R.id.message))
|
||||
.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
public void setCallback(OnClickListener callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
private class AcceptListener implements OnClickListener {
|
||||
|
||||
private final MessageRecord messageRecord;
|
||||
private final IdentityKeyMismatch mismatch;
|
||||
private final Address address;
|
||||
|
||||
private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) {
|
||||
this.messageRecord = messageRecord;
|
||||
this.mismatch = mismatch;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
new AsyncTask<Void, Void, Void>()
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1);
|
||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
||||
|
||||
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
|
||||
}
|
||||
|
||||
processMessageRecord(messageRecord);
|
||||
processPendingMessageRecords(messageRecord.getThreadId(), mismatch);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void processMessageRecord(MessageRecord messageRecord) {
|
||||
if (messageRecord.isOutgoing()) processOutgoingMessageRecord(messageRecord);
|
||||
else processIncomingMessageRecord(messageRecord);
|
||||
}
|
||||
|
||||
private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) {
|
||||
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext());
|
||||
Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId);
|
||||
MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor);
|
||||
MessageRecord record;
|
||||
|
||||
try {
|
||||
while ((record = reader.getNext()) != null) {
|
||||
for (IdentityKeyMismatch recordMismatch : record.getIdentityKeyMismatches()) {
|
||||
if (mismatch.equals(recordMismatch)) {
|
||||
processMessageRecord(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (reader != null)
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void processOutgoingMessageRecord(MessageRecord messageRecord) {
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
|
||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext());
|
||||
|
||||
if (messageRecord.isMms()) {
|
||||
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
||||
mismatch.getAddress(),
|
||||
mismatch.getIdentityKey());
|
||||
|
||||
if (messageRecord.getRecipient().isPushGroupRecipient()) {
|
||||
MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress());
|
||||
} else {
|
||||
MessageSender.resend(getContext(), messageRecord);
|
||||
}
|
||||
} else {
|
||||
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
||||
mismatch.getAddress(),
|
||||
mismatch.getIdentityKey());
|
||||
|
||||
MessageSender.resend(getContext(), messageRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private void processIncomingMessageRecord(MessageRecord messageRecord) {
|
||||
try {
|
||||
PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext());
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
|
||||
|
||||
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
||||
mismatch.getAddress(),
|
||||
mismatch.getIdentityKey());
|
||||
|
||||
boolean legacy = !messageRecord.isContentBundleKeyExchange();
|
||||
|
||||
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
||||
messageRecord.getIndividualRecipient().getAddress().toPhoneString(),
|
||||
messageRecord.getRecipientDeviceId(), "",
|
||||
messageRecord.getDateSent(),
|
||||
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
||||
!legacy ? Base64.decode(messageRecord.getBody()) : null);
|
||||
|
||||
long pushId = pushDatabase.insert(envelope);
|
||||
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new PushDecryptJob(getContext(), pushId, messageRecord.getId()));
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
|
||||
if (callback != null) callback.onClick(null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class CancelListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (callback != null) callback.onClick(null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,13 +30,15 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
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;
|
||||
@@ -65,22 +67,22 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A cursor adapter for a conversation thread. Ultimately
|
||||
* used by ComposeMessageActivity to display a conversation
|
||||
* A DC adapter for a conversation thread. Ultimately
|
||||
* used by ConversationActivity to display a conversation
|
||||
* thread in a ListActivity.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
extends FastCursorRecyclerViewAdapter<ConversationAdapter.ViewHolder, MessageRecord> // TODO: base this on RecyclerView.Adapter (see ConversationListAdapter)
|
||||
extends RecyclerView.Adapter
|
||||
implements StickyHeaderDecoration.StickyHeaderAdapter<HeaderViewHolder>
|
||||
{
|
||||
|
||||
private static final int MAX_CACHE_SIZE = 40;
|
||||
private static final String TAG = ConversationAdapter.class.getSimpleName();
|
||||
private final Map<String,SoftReference<MessageRecord>> messageRecordCache =
|
||||
Collections.synchronizedMap(new LRUCache<String, SoftReference<MessageRecord>>(MAX_CACHE_SIZE));
|
||||
private final Map<Integer,SoftReference<DcMsg>> messageRecordCache =
|
||||
Collections.synchronizedMap(new LRUCache<Integer,SoftReference<DcMsg>>(MAX_CACHE_SIZE)); // TODO: use the cache
|
||||
|
||||
private static final int MESSAGE_TYPE_OUTGOING = 0;
|
||||
private static final int MESSAGE_TYPE_INCOMING = 1;
|
||||
@@ -92,21 +94,20 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7;
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8;
|
||||
|
||||
private final Set<MessageRecord> batchSelected = Collections.synchronizedSet(new HashSet<MessageRecord>());
|
||||
private final Set<DcMsg> batchSelected = Collections.synchronizedSet(new HashSet<DcMsg>());
|
||||
|
||||
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 Context context;
|
||||
private final @NonNull Calendar calendar;
|
||||
private final @NonNull MessageDigest digest;
|
||||
|
||||
private MessageRecord recordToPulseHighlight;
|
||||
|
||||
private ApplicationDcContext dcContext;
|
||||
private int[] dcMsgList = new int[0];
|
||||
private @NonNull int[] dcMsgList = new int[0];
|
||||
private int recordToPulseHighlight;
|
||||
|
||||
protected static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public <V extends View & BindableConversationItem> ViewHolder(final @NonNull V itemView) {
|
||||
@@ -117,8 +118,20 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
public <V extends View & BindableConversationItem> V getView() {
|
||||
return (V)itemView;
|
||||
}
|
||||
|
||||
public BindableConversationItem getItem() {
|
||||
return getView();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return dcMsgList.length;
|
||||
}
|
||||
|
||||
private DcMsg getMsg(int position) {
|
||||
return dcContext.getMsg(dcMsgList[position]);
|
||||
}
|
||||
|
||||
static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView textView;
|
||||
@@ -140,22 +153,21 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
|
||||
|
||||
interface ItemClickListener extends BindableConversationItem.EventListener {
|
||||
void onItemClick(MessageRecord item);
|
||||
void onItemLongClick(MessageRecord item);
|
||||
void onItemClick(DcMsg item);
|
||||
void onItemLongClick(DcMsg item);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@VisibleForTesting
|
||||
ConversationAdapter(Context context, Cursor cursor) {
|
||||
super(context, cursor);
|
||||
ConversationAdapter(Context context) {
|
||||
try {
|
||||
this.glideRequests = null;
|
||||
this.locale = null;
|
||||
this.clickListener = null;
|
||||
this.recipient = null;
|
||||
this.inflater = null;
|
||||
this.db = null;
|
||||
this.calendar = null;
|
||||
this.context = context;
|
||||
this.digest = MessageDigest.getInstance("SHA1");
|
||||
this.dcContext = DcHelper.getContext(context);
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
@@ -167,20 +179,17 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@Nullable ItemClickListener clickListener,
|
||||
@Nullable Cursor cursor,
|
||||
@NonNull Recipient recipient)
|
||||
{
|
||||
super(context, cursor);
|
||||
|
||||
@NonNull Recipient recipient) {
|
||||
try {
|
||||
this.glideRequests = glideRequests;
|
||||
this.locale = locale;
|
||||
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");
|
||||
this.recipient = recipient;
|
||||
this.context = context;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.calendar = Calendar.getInstance();
|
||||
this.digest = MessageDigest.getInstance("SHA1");
|
||||
this.dcContext = DcHelper.getContext(context);
|
||||
|
||||
setHasStableIds(true);
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
@@ -189,37 +198,16 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeCursor(Cursor cursor) { // TOOD: this should take a int[] instead of a cursor; the int[] is saved in dcMsgList then
|
||||
messageRecordCache.clear();
|
||||
super.cleanFastRecords();
|
||||
super.changeCursor(cursor);
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
|
||||
ConversationAdapter.ViewHolder holder = (ConversationAdapter.ViewHolder)viewHolder;
|
||||
Optional<DcMsg> previous = position <= 0? Optional.absent() : Optional.of(getMsg(position-1));
|
||||
Optional<DcMsg> next = position >= dcMsgList.length-1? Optional.absent() : Optional.of(getMsg(position+1));
|
||||
boolean pulseHighlight = dcMsgList[position] == recordToPulseHighlight;
|
||||
holder.getItem().bind(getMsg(position), previous, next, glideRequests, locale, batchSelected, recipient, pulseHighlight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) {
|
||||
long start = System.currentTimeMillis();
|
||||
int adapterPosition = viewHolder.getAdapterPosition();
|
||||
MessageRecord previousRecord = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getRecordForPositionOrThrow(adapterPosition + 1) : null;
|
||||
MessageRecord nextRecord = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getRecordForPositionOrThrow(adapterPosition - 1) : null;
|
||||
|
||||
viewHolder.getView().bind(messageRecord,
|
||||
Optional.fromNullable(previousRecord),
|
||||
Optional.fromNullable(nextRecord),
|
||||
glideRequests,
|
||||
locale,
|
||||
batchSelected,
|
||||
recipient,
|
||||
messageRecord == recordToPulseHighlight);
|
||||
|
||||
if (messageRecord == recordToPulseHighlight) {
|
||||
recordToPulseHighlight = null;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
long start = System.currentTimeMillis();
|
||||
final V itemView = ViewUtil.inflate(inflater, parent, getLayoutForViewType(viewType));
|
||||
itemView.setOnClickListener(view -> {
|
||||
@@ -238,11 +226,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
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:
|
||||
@@ -259,16 +242,18 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(@NonNull MessageRecord messageRecord) {
|
||||
public int getItemViewType(int i) {
|
||||
DcMsg messageRecord = getMsg(i);
|
||||
int type = messageRecord.getType();
|
||||
if (messageRecord.isUpdate()) {
|
||||
return MESSAGE_TYPE_UPDATE;
|
||||
} else if (hasAudio(messageRecord)) {
|
||||
} else if (type==DcMsg.DC_MSG_AUDIO || type==DcMsg.DC_MSG_VOICE) {
|
||||
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_AUDIO_OUTGOING;
|
||||
else return MESSAGE_TYPE_AUDIO_INCOMING;
|
||||
} else if (hasDocument(messageRecord)) {
|
||||
} else if (type==DcMsg.DC_MSG_FILE) {
|
||||
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_DOCUMENT_OUTGOING;
|
||||
else return MESSAGE_TYPE_DOCUMENT_INCOMING;
|
||||
} else if (hasThumbnail(messageRecord)) {
|
||||
} else if (type==DcMsg.DC_MSG_IMAGE || type==DcMsg.DC_MSG_GIF || type==DcMsg.DC_MSG_VIDEO) {
|
||||
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_THUMBNAIL_OUTGOING;
|
||||
else return MESSAGE_TYPE_THUMBNAIL_INCOMING;
|
||||
} else if (messageRecord.isOutgoing()) {
|
||||
@@ -278,77 +263,25 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRecordForId(@NonNull MessageRecord record, long id) {
|
||||
return record.getId() == id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(@NonNull Cursor cursor) {
|
||||
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(cursor);
|
||||
List<DatabaseAttachment> 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()) {
|
||||
SlideDeck slideDeck = ((MmsMessageRecord)record).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<MessageRecord> 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) {
|
||||
/* TODO -- we shoud do this without loading all messages in the chat
|
||||
if (lastSeen <= 0) return -1;
|
||||
if (!isActiveCursor()) return -1;
|
||||
if (isActive()) return -1;
|
||||
|
||||
int count = getItemCount() - (hasFooterView() ? 1 : 0);
|
||||
int count = getItemCount();
|
||||
|
||||
for (int i=(hasHeaderView() ? 1 : 0);i<count;i++) {
|
||||
MessageRecord messageRecord = getRecordForPositionOrThrow(i);
|
||||
|
||||
if (messageRecord.isOutgoing() || messageRecord.getDateReceived() <= lastSeen) {
|
||||
for (int i = 0;i<count;i++) {
|
||||
DcMsg msg = getMsg(i);
|
||||
if (msg.isOutgoing() || msg.getTimestamp() <= lastSeen) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void toggleSelection(MessageRecord messageRecord) {
|
||||
public void toggleSelection(DcMsg messageRecord) {
|
||||
if (!batchSelected.remove(messageRecord)) {
|
||||
batchSelected.add(messageRecord);
|
||||
}
|
||||
@@ -358,54 +291,60 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
batchSelected.clear();
|
||||
}
|
||||
|
||||
public Set<MessageRecord> getSelectedItems() {
|
||||
public Set<DcMsg> getSelectedItems() {
|
||||
return Collections.unmodifiableSet(new HashSet<>(batchSelected));
|
||||
}
|
||||
|
||||
public void pulseHighlightItem(int position) {
|
||||
if (position < getItemCount()) {
|
||||
recordToPulseHighlight = getRecordForPositionOrThrow(position);
|
||||
recordToPulseHighlight = position;
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAudio(MessageRecord messageRecord) {
|
||||
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null;
|
||||
protected boolean isFooterPosition(int position) {
|
||||
// return hasFooterView() && position == getItemCount() - 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
protected boolean isHeaderPosition(int position) {
|
||||
// return hasHeaderView() && position == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
@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;
|
||||
return -1; // no header Id.
|
||||
// if (isActive()) 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);
|
||||
//
|
||||
// calendar.setTime(new Date(record.getDateSent()));
|
||||
// return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
|
||||
}
|
||||
|
||||
MessageRecord record = getRecordForPositionOrThrow(position);
|
||||
|
||||
calendar.setTime(new Date(record.getDateSent()));
|
||||
return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
|
||||
public boolean isActive() {
|
||||
return dcMsgList.length > 0;
|
||||
}
|
||||
|
||||
public long getReceivedTimestamp(int position) {
|
||||
if (!isActiveCursor()) return 0;
|
||||
if (isActive()) 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);
|
||||
DcMsg msg = getMsg(position);
|
||||
return msg.getTimestamp();
|
||||
}
|
||||
|
||||
if (messageRecord.isOutgoing()) return 0;
|
||||
else return messageRecord.getDateReceived();
|
||||
@NonNull
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -419,8 +358,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
|
||||
@Override
|
||||
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||
MessageRecord messageRecord = getRecordForPositionOrThrow(position);
|
||||
viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, messageRecord.getDateReceived()));
|
||||
DcMsg msg = getMsg(position);
|
||||
viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, msg.getTimestamp()));
|
||||
}
|
||||
|
||||
public void onBindLastSeenViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||
@@ -440,7 +379,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
|
||||
@Override
|
||||
protected boolean hasHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) {
|
||||
if (!adapter.isActiveCursor()) {
|
||||
if (!adapter.isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -477,5 +416,22 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
}
|
||||
}
|
||||
|
||||
public void changeData(@Nullable int[] dcMsgList) {
|
||||
this.dcMsgList = dcMsgList==null? new int[0] : dcMsgList;
|
||||
messageRecordCache.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setHeaderView(@Nullable View headerView) {
|
||||
// TODO: must not be implemented, remove calls to this function
|
||||
}
|
||||
|
||||
public void addFastRecord(@NonNull MessageRecord record) {
|
||||
// TODO: i think this is not need, we simply should reload the view
|
||||
}
|
||||
|
||||
public void releaseFastRecord(long id) {
|
||||
// TODO: i think this is not need, we simply should reload the view
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,11 +54,13 @@ import android.widget.Toast;
|
||||
import android.widget.ViewSwitcher;
|
||||
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
|
||||
import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener;
|
||||
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.connect.DcMsgListLoader;
|
||||
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
||||
import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
@@ -93,7 +95,7 @@ import java.util.Set;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class ConversationFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
implements LoaderManager.LoaderCallbacks<int[]>
|
||||
{
|
||||
private static final String TAG = ConversationFragment.class.getSimpleName();
|
||||
private static final String KEY_LIMIT = "limit";
|
||||
@@ -226,7 +228,7 @@ public class ConversationFragment extends Fragment
|
||||
|
||||
private void initializeListAdapter() {
|
||||
if (this.recipient != null && this.threadId != -1) {
|
||||
ConversationAdapter adapter = new ConversationAdapter(getActivity(), GlideApp.with(this), locale, selectionClickListener, null, this.recipient);
|
||||
ConversationAdapter adapter = new ConversationAdapter(getActivity(), GlideApp.with(this), locale, selectionClickListener, this.recipient);
|
||||
list.setAdapter(adapter);
|
||||
list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false));
|
||||
|
||||
@@ -408,13 +410,7 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
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() && message.isPush());
|
||||
startActivity(intent);
|
||||
// TODO: show message info
|
||||
}
|
||||
|
||||
private void handleForwardMessage(MessageRecord message) {
|
||||
@@ -466,54 +462,29 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
public Loader<int[]> onCreateLoader(int id, Bundle args) {
|
||||
Log.w(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);
|
||||
return new DcMsgListLoader(getActivity(), (int)threadId, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
public void onLoadFinished(Loader<int[]> arg0, int[] dcMsgList) {
|
||||
long loadTime = System.currentTimeMillis() - loaderStartTime;
|
||||
int count = cursor.getCount();
|
||||
Log.w(TAG, "onLoadFinished - took " + loadTime + " ms to load a cursor of size " + count);
|
||||
ConversationLoader loader = (ConversationLoader)cursorLoader;
|
||||
int count = dcMsgList.length;
|
||||
Log.w(TAG, "onLoadFinished - took " + loadTime + " ms to load a message list of size " + count);
|
||||
|
||||
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());
|
||||
//setLastSeen(loader.getLastSeen()); -- TODO
|
||||
}
|
||||
|
||||
if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
adapter.setHeaderView(unknownSenderView);
|
||||
} else {
|
||||
adapter.setHeaderView(null);
|
||||
}
|
||||
|
||||
if (loader.hasOffset()) {
|
||||
adapter.setHeaderView(bottomLoadMoreView);
|
||||
previousOffset = loader.getOffset();
|
||||
}
|
||||
|
||||
adapter.changeCursor(cursor);
|
||||
adapter.changeData(dcMsgList);
|
||||
|
||||
int lastSeenPosition = adapter.findLastSeenPosition(lastSeen);
|
||||
|
||||
@@ -541,9 +512,9 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> arg0) {
|
||||
public void onLoaderReset(Loader<int[]> arg0) {
|
||||
if (list.getAdapter() != null) {
|
||||
getListAdapter().changeCursor(null);
|
||||
getListAdapter().changeData(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +648,7 @@ public class ConversationFragment extends Fragment
|
||||
private class ConversationFragmentItemClickListener implements ItemClickListener {
|
||||
|
||||
@Override
|
||||
public void onItemClick(MessageRecord messageRecord) {
|
||||
public void onItemClick(DcMsg messageRecord) {
|
||||
if (actionMode != null) {
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord);
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
@@ -692,7 +663,7 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemLongClick(MessageRecord messageRecord) {
|
||||
public void onItemLongClick(DcMsg messageRecord) {
|
||||
if (actionMode == null) {
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord);
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
@@ -702,7 +673,7 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuoteClicked(MmsMessageRecord messageRecord) {
|
||||
public void onQuoteClicked(DcMsg messageRecord) {
|
||||
if (messageRecord.getQuote() == null) {
|
||||
Log.w(TAG, "Received a 'quote clicked' event, but there's no quote...");
|
||||
return;
|
||||
|
||||
@@ -42,6 +42,9 @@ import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.components.AlertView;
|
||||
@@ -52,6 +55,8 @@ import org.thoughtcrime.securesms.components.ConversationItemThumbnail;
|
||||
import org.thoughtcrime.securesms.components.DocumentView;
|
||||
import org.thoughtcrime.securesms.components.QuoteView;
|
||||
import org.thoughtcrime.securesms.components.SharedContactView;
|
||||
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@@ -59,9 +64,6 @@ 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;
|
||||
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.jobs.AttachmentDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.MmsDownloadJob;
|
||||
@@ -103,7 +105,7 @@ public class ConversationItem extends LinearLayout
|
||||
|
||||
private static final int MAX_MEASURE_CALLS = 3;
|
||||
|
||||
private MessageRecord messageRecord;
|
||||
private DcMsg messageRecord;
|
||||
private Locale locale;
|
||||
private boolean groupThread;
|
||||
private Recipient recipient;
|
||||
@@ -121,7 +123,7 @@ public class ConversationItem extends LinearLayout
|
||||
private AlertView alertView;
|
||||
private ViewGroup container;
|
||||
|
||||
private @NonNull Set<MessageRecord> batchSelected = new HashSet<>();
|
||||
private @NonNull Set<DcMsg> batchSelected = new HashSet<>();
|
||||
private @NonNull Recipient conversationRecipient;
|
||||
private @NonNull Stub<ConversationItemThumbnail> mediaThumbnailStub;
|
||||
private @NonNull Stub<AudioView> audioViewStub;
|
||||
@@ -138,6 +140,7 @@ public class ConversationItem extends LinearLayout
|
||||
private final SharedContactClickListener sharedContactClickListener = new SharedContactClickListener();
|
||||
|
||||
private final Context context;
|
||||
private final ApplicationDcContext dcContext;
|
||||
|
||||
public ConversationItem(Context context) {
|
||||
this(context, null);
|
||||
@@ -146,6 +149,7 @@ public class ConversationItem extends LinearLayout
|
||||
public ConversationItem(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.context = context;
|
||||
this.dcContext = DcHelper.getContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,39 +188,39 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull MessageRecord messageRecord,
|
||||
@NonNull Optional<MessageRecord> previousMessageRecord,
|
||||
@NonNull Optional<MessageRecord> nextMessageRecord,
|
||||
public void bind(@NonNull DcMsg messageRecord,
|
||||
@NonNull Optional<DcMsg> previousDcMsg,
|
||||
@NonNull Optional<DcMsg> nextDcMsg,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<MessageRecord> batchSelected,
|
||||
@NonNull Recipient conversationRecipient,
|
||||
boolean pulseHighlight)
|
||||
@NonNull Set<DcMsg> batchSelected,
|
||||
@NonNull Recipient recipients,
|
||||
boolean pulseHighlight)
|
||||
{
|
||||
this.messageRecord = messageRecord;
|
||||
this.locale = locale;
|
||||
this.glideRequests = glideRequests;
|
||||
this.batchSelected = batchSelected;
|
||||
this.conversationRecipient = conversationRecipient;
|
||||
this.conversationRecipient = recipients;
|
||||
this.groupThread = conversationRecipient.isGroupRecipient();
|
||||
this.recipient = messageRecord.getIndividualRecipient();
|
||||
this.recipient = dcContext.getRecipient(dcContext.getChat(messageRecord.getChatId()));
|
||||
|
||||
this.recipient.addListener(this);
|
||||
this.conversationRecipient.addListener(this);
|
||||
|
||||
setGutterSizes(messageRecord, groupThread);
|
||||
setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
|
||||
setMediaAttributes(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread);
|
||||
setMessageShape(messageRecord, previousDcMsg, nextDcMsg, groupThread);
|
||||
setMediaAttributes(messageRecord, previousDcMsg, nextDcMsg, conversationRecipient, groupThread);
|
||||
setInteractionState(messageRecord, pulseHighlight);
|
||||
setBodyText(messageRecord);
|
||||
setBubbleState(messageRecord);
|
||||
setStatusIcons(messageRecord);
|
||||
setContactPhoto(recipient);
|
||||
setGroupMessageStatus(messageRecord, recipient);
|
||||
setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
|
||||
setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
|
||||
setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
|
||||
setFooter(messageRecord, nextMessageRecord, locale, groupThread);
|
||||
setAuthor(messageRecord, previousDcMsg, nextDcMsg, groupThread);
|
||||
setQuote(messageRecord, previousDcMsg, nextDcMsg, groupThread);
|
||||
setMessageSpacing(context, messageRecord, previousDcMsg, nextDcMsg, groupThread);
|
||||
setFooter(messageRecord, nextDcMsg, locale, groupThread);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -294,18 +298,18 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
public MessageRecord getMessageRecord() {
|
||||
public DcMsg getMessageRecord() {
|
||||
return messageRecord;
|
||||
}
|
||||
|
||||
/// MessageRecord Attribute Parsers
|
||||
/// DcMsg Attribute Parsers
|
||||
|
||||
private void setBubbleState(MessageRecord messageRecord) {
|
||||
private void setBubbleState(DcMsg messageRecord) {
|
||||
if (messageRecord.isOutgoing()) {
|
||||
bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY);
|
||||
} else {
|
||||
bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY);
|
||||
bodyBubble.getBackground().setColorFilter(messageRecord.getRecipient().getColor().toConversationColor(context), PorterDuff.Mode.MULTIPLY);
|
||||
bodyBubble.getBackground().setColorFilter(dcContext.getRecipient(messageRecord.getChatId()).getColor().toConversationColor(context), PorterDuff.Mode.MULTIPLY);
|
||||
}
|
||||
|
||||
if (audioViewStub.resolved()) {
|
||||
@@ -313,7 +317,7 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private void setAudioViewTint(MessageRecord messageRecord, Recipient recipient) {
|
||||
private void setAudioViewTint(DcMsg messageRecord, Recipient recipient) {
|
||||
if (messageRecord.isOutgoing()) {
|
||||
if (DynamicTheme.LIGHT.equals(TextSecurePreferences.getTheme(context))) {
|
||||
audioViewStub.get().setTint(getContext().getResources().getColor(R.color.core_light_60), defaultBubbleColor);
|
||||
@@ -325,7 +329,7 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private void setInteractionState(MessageRecord messageRecord, boolean pulseHighlight) {
|
||||
private void setInteractionState(DcMsg messageRecord, boolean pulseHighlight) {
|
||||
if (batchSelected.contains(messageRecord)) {
|
||||
setBackgroundResource(R.drawable.conversation_item_background);
|
||||
setSelected(true);
|
||||
@@ -355,35 +359,41 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCaptionlessMms(MessageRecord messageRecord) {
|
||||
return TextUtils.isEmpty(messageRecord.getDisplayBody()) && messageRecord.isMms();
|
||||
private boolean isCaptionlessMms(DcMsg messageRecord) {
|
||||
return false;
|
||||
// return TextUtils.isEmpty(messageRecord.getText()) && messageRecord.isMms();
|
||||
}
|
||||
|
||||
private boolean hasAudio(MessageRecord messageRecord) {
|
||||
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null;
|
||||
private boolean hasAudio(DcMsg messageRecord) {
|
||||
return false;
|
||||
// return messageRecord.isMms() && ((MmsDcMsg)messageRecord).getSlideDeck().getAudioSlide() != null;
|
||||
}
|
||||
|
||||
private boolean hasThumbnail(MessageRecord messageRecord) {
|
||||
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null;
|
||||
private boolean hasThumbnail(DcMsg messageRecord) {
|
||||
return false;
|
||||
// return messageRecord.isMms() && ((MmsDcMsg)messageRecord).getSlideDeck().getThumbnailSlide() != null;
|
||||
}
|
||||
|
||||
private boolean hasOnlyThumbnail(MessageRecord messageRecord) {
|
||||
private boolean hasOnlyThumbnail(DcMsg messageRecord) {
|
||||
return hasThumbnail(messageRecord) && !hasAudio(messageRecord) && !hasDocument(messageRecord) && !hasSharedContact(messageRecord);
|
||||
}
|
||||
|
||||
private boolean hasDocument(MessageRecord messageRecord) {
|
||||
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null;
|
||||
private boolean hasDocument(DcMsg messageRecord) {
|
||||
return false;
|
||||
// return messageRecord.isMms() && ((MmsDcMsg)messageRecord).getSlideDeck().getDocumentSlide() != null;
|
||||
}
|
||||
|
||||
private boolean hasQuote(MessageRecord messageRecord) {
|
||||
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getQuote() != null;
|
||||
private boolean hasQuote(DcMsg messageRecord) {
|
||||
return false;
|
||||
// return messageRecord.isMms() && ((MmsDcMsg)messageRecord).getQuote() != null;
|
||||
}
|
||||
|
||||
private boolean hasSharedContact(MessageRecord messageRecord) {
|
||||
return messageRecord.isMms() && !((MmsMessageRecord)messageRecord).getSharedContacts().isEmpty();
|
||||
private boolean hasSharedContact(DcMsg messageRecord) {
|
||||
return false;
|
||||
// return messageRecord.isMms() && !((MmsDcMsg)messageRecord).getSharedContacts().isEmpty();
|
||||
}
|
||||
|
||||
private void setBodyText(MessageRecord messageRecord) {
|
||||
private void setBodyText(DcMsg messageRecord) {
|
||||
bodyText.setClickable(false);
|
||||
bodyText.setFocusable(false);
|
||||
bodyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, TextSecurePreferences.getMessageBodyTextSize(context));
|
||||
@@ -391,91 +401,91 @@ public class ConversationItem extends LinearLayout
|
||||
if (isCaptionlessMms(messageRecord)) {
|
||||
bodyText.setVisibility(View.GONE);
|
||||
} else {
|
||||
bodyText.setText(linkifyMessageBody(messageRecord.getDisplayBody(), batchSelected.isEmpty()));
|
||||
bodyText.setText(linkifyMessageBody(new SpannableString(messageRecord.getText()), batchSelected.isEmpty()));
|
||||
bodyText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setMediaAttributes(@NonNull MessageRecord messageRecord,
|
||||
@NonNull Optional<MessageRecord> previousRecord,
|
||||
@NonNull Optional<MessageRecord> nextRecord,
|
||||
private void setMediaAttributes(@NonNull DcMsg messageRecord,
|
||||
@NonNull Optional<DcMsg> previousRecord,
|
||||
@NonNull Optional<DcMsg> nextRecord,
|
||||
@NonNull Recipient conversationRecipient,
|
||||
boolean isGroupThread)
|
||||
{
|
||||
boolean showControls = !messageRecord.isFailed() && !Util.isOwnNumber(context, conversationRecipient.getAddress());
|
||||
|
||||
if (hasSharedContact(messageRecord)) {
|
||||
sharedContactStub.get().setVisibility(VISIBLE);
|
||||
if (audioViewStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
sharedContactStub.get().setContact(((MediaMmsMessageRecord) messageRecord).getSharedContacts().get(0), glideRequests, locale);
|
||||
sharedContactStub.get().setEventListener(sharedContactEventListener);
|
||||
sharedContactStub.get().setOnClickListener(sharedContactClickListener);
|
||||
sharedContactStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
|
||||
setSharedContactCorners(messageRecord, previousRecord, nextRecord, isGroupThread);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
footer.setVisibility(GONE);
|
||||
} 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 (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls);
|
||||
audioViewStub.get().setDownloadClickListener(downloadClickListener);
|
||||
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 (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
documentViewStub.get().setDocument(((MediaMmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide(), showControls);
|
||||
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
|
||||
documentViewStub.get().setDownloadClickListener(downloadClickListener);
|
||||
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 (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
Slide thumbnailSlide = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlide();
|
||||
Attachment attachment = thumbnailSlide.asAttachment();
|
||||
mediaThumbnailStub.get().setImageResource(glideRequests,
|
||||
thumbnailSlide,
|
||||
showControls,
|
||||
false,
|
||||
attachment.getWidth(),
|
||||
attachment.getHeight());
|
||||
mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener());
|
||||
mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener);
|
||||
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
|
||||
mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getDisplayBody()));
|
||||
|
||||
setThumbnailOutlineCorners(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 (hasSharedContact(messageRecord)) {
|
||||
// sharedContactStub.get().setVisibility(VISIBLE);
|
||||
// if (audioViewStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
// if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
// if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
//
|
||||
// sharedContactStub.get().setContact(((MediaMmsDcMsg) messageRecord).getSharedContacts().get(0), glideRequests, locale);
|
||||
// sharedContactStub.get().setEventListener(sharedContactEventListener);
|
||||
// sharedContactStub.get().setOnClickListener(sharedContactClickListener);
|
||||
// sharedContactStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
//
|
||||
// setSharedContactCorners(messageRecord, previousRecord, nextRecord, isGroupThread);
|
||||
//
|
||||
// ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
// ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
// footer.setVisibility(GONE);
|
||||
// } 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 (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
|
||||
//
|
||||
// //noinspection ConstantConditions
|
||||
// audioViewStub.get().setAudio(((MediaMmsDcMsg) messageRecord).getSlideDeck().getAudioSlide(), showControls);
|
||||
// audioViewStub.get().setDownloadClickListener(downloadClickListener);
|
||||
// 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 (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
|
||||
//
|
||||
// //noinspection ConstantConditions
|
||||
// documentViewStub.get().setDocument(((MediaMmsDcMsg)messageRecord).getSlideDeck().getDocumentSlide(), showControls);
|
||||
// documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
|
||||
// documentViewStub.get().setDownloadClickListener(downloadClickListener);
|
||||
// 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 (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
|
||||
//
|
||||
// //noinspection ConstantConditions
|
||||
// Slide thumbnailSlide = ((MmsDcMsg) messageRecord).getSlideDeck().getThumbnailSlide();
|
||||
// Attachment attachment = thumbnailSlide.asAttachment();
|
||||
// mediaThumbnailStub.get().setImageResource(glideRequests,
|
||||
// thumbnailSlide,
|
||||
// showControls,
|
||||
// false,
|
||||
// attachment.getWidth(),
|
||||
// attachment.getHeight());
|
||||
// mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener());
|
||||
// mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener);
|
||||
// mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
// mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
|
||||
// mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getText()));
|
||||
//
|
||||
// setThumbnailOutlineCorners(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 (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
@@ -484,12 +494,12 @@ public class ConversationItem extends LinearLayout
|
||||
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 setThumbnailOutlineCorners(@NonNull MessageRecord current,
|
||||
@NonNull Optional<MessageRecord> previous,
|
||||
@NonNull Optional<MessageRecord> next,
|
||||
private void setThumbnailOutlineCorners(@NonNull DcMsg current,
|
||||
@NonNull Optional<DcMsg> previous,
|
||||
@NonNull Optional<DcMsg> next,
|
||||
boolean isGroupThread)
|
||||
{
|
||||
int defaultRadius = readDimen(R.dimen.message_corner_radius);
|
||||
@@ -527,7 +537,7 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(current.getDisplayBody())) {
|
||||
if (!TextUtils.isEmpty(current.getText())) {
|
||||
bottomLeft = 0;
|
||||
bottomRight = 0;
|
||||
}
|
||||
@@ -545,7 +555,7 @@ public class ConversationItem extends LinearLayout
|
||||
mediaThumbnailStub.get().setOutlineCorners(topLeft, topRight, bottomRight, bottomLeft);
|
||||
}
|
||||
|
||||
private void setSharedContactCorners(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
||||
private void setSharedContactCorners(@NonNull DcMsg current, @NonNull Optional<DcMsg> previous, @NonNull Optional<DcMsg> next, boolean isGroupThread) {
|
||||
if (isSingularMessage(current, previous, next, isGroupThread) || isEndOfMessageCluster(current, next, isGroupThread)) {
|
||||
sharedContactStub.get().setSingularStyle();
|
||||
} else {
|
||||
@@ -582,65 +592,63 @@ public class ConversationItem extends LinearLayout
|
||||
return messageBody;
|
||||
}
|
||||
|
||||
private void setStatusIcons(MessageRecord messageRecord) {
|
||||
bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isKeyExchange() ? R.drawable.ic_menu_login : 0, 0);
|
||||
private void setStatusIcons(DcMsg messageRecord) {
|
||||
bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isSetupMessage() ? R.drawable.ic_menu_login : 0, 0);
|
||||
|
||||
if (messageRecord.isFailed()) {
|
||||
alertView.setFailed();
|
||||
} else if (messageRecord.isPendingInsecureSmsFallback()) {
|
||||
alertView.setPendingApproval();
|
||||
} else {
|
||||
alertView.setNone();
|
||||
}
|
||||
}
|
||||
|
||||
private void setQuote(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
||||
if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) {
|
||||
Quote quote = ((MediaMmsMessageRecord)current).getQuote();
|
||||
assert quote != null;
|
||||
quoteView.setQuote(glideRequests, quote.getId(), Recipient.from(context, quote.getAuthor(), true), quote.getText(), quote.getAttachment());
|
||||
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 setQuote(@NonNull DcMsg current, @NonNull Optional<DcMsg> previous, @NonNull Optional<DcMsg> next, boolean isGroupThread) {
|
||||
// if (current.isMms() && !current.isMmsNotification() && ((MediaMmsDcMsg)current).getQuote() != null) {
|
||||
// Quote quote = ((MediaMmsDcMsg)current).getQuote();
|
||||
// assert quote != null;
|
||||
// quoteView.setQuote(glideRequests, quote.getId(), Recipient.from(context, quote.getAuthor(), true), quote.getText(), quote.getAttachment());
|
||||
// quoteView.setVisibility(View.VISIBLE);
|
||||
// quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
//
|
||||
// quoteView.setOnClickListener(view -> {
|
||||
// if (eventListener != null && batchIsEmpty()) {
|
||||
// eventListener.onQuoteClicked((MmsDcMsg) 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) {
|
||||
private void setGutterSizes(@NonNull DcMsg current, boolean isGroupThread) {
|
||||
if (isGroupThread && current.isOutgoing()) {
|
||||
ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_group_left_gutter));
|
||||
} else if (current.isOutgoing()) {
|
||||
@@ -648,7 +656,7 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private void setFooter(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> next, @NonNull Locale locale, boolean isGroupThread) {
|
||||
private void setFooter(@NonNull DcMsg current, @NonNull Optional<DcMsg> next, @NonNull Locale locale, boolean isGroupThread) {
|
||||
ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
|
||||
footer.setVisibility(GONE);
|
||||
@@ -657,7 +665,7 @@ public class ConversationItem extends LinearLayout
|
||||
|
||||
boolean differentMinutes = next.isPresent() && !DateUtils.isSameBriefRelativeTimestamp(context, locale, next.get().getTimestamp(), current.getTimestamp());
|
||||
|
||||
if (current.getExpiresIn() > 0 || !current.isSecure() || current.isPending() || current.isPendingInsecureSmsFallback() ||
|
||||
if (current.getExpiresIn() > 0 || !current.isSecure() || current.isPending() ||
|
||||
current.isFailed() || differentMinutes || isEndOfMessageCluster(current, next, isGroupThread))
|
||||
{
|
||||
ConversationItemFooter activeFooter = getActiveFooter(current);
|
||||
@@ -666,10 +674,10 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
|
||||
private ConversationItemFooter getActiveFooter(@NonNull DcMsg messageRecord) {
|
||||
if (hasSharedContact(messageRecord)) {
|
||||
return sharedContactStub.get().getFooter();
|
||||
} else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody())) {
|
||||
} else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getText())) {
|
||||
return mediaThumbnailStub.get().getFooter();
|
||||
} else {
|
||||
return footer;
|
||||
@@ -680,15 +688,13 @@ public class ConversationItem extends LinearLayout
|
||||
return context.getResources().getDimensionPixelOffset(dimenId);
|
||||
}
|
||||
|
||||
private boolean shouldInterceptClicks(MessageRecord messageRecord) {
|
||||
return batchSelected.isEmpty() &&
|
||||
((messageRecord.isFailed() && !messageRecord.isMmsNotification()) ||
|
||||
messageRecord.isPendingInsecureSmsFallback() ||
|
||||
messageRecord.isBundleKeyExchange());
|
||||
private boolean shouldInterceptClicks(DcMsg messageRecord) {
|
||||
return batchSelected.isEmpty() && (messageRecord.isFailed() ||
|
||||
messageRecord.isSetupMessage());
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) {
|
||||
private void setGroupMessageStatus(DcMsg messageRecord, Recipient recipient) {
|
||||
if (groupThread && !messageRecord.isOutgoing()) {
|
||||
this.groupSender.setText(recipient.toShortString());
|
||||
|
||||
@@ -702,11 +708,11 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private void setAuthor(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
||||
private void setAuthor(@NonNull DcMsg current, @NonNull Optional<DcMsg> previous, @NonNull Optional<DcMsg> next, boolean isGroupThread) {
|
||||
if (isGroupThread && !current.isOutgoing()) {
|
||||
contactPhotoHolder.setVisibility(VISIBLE);
|
||||
|
||||
if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()) ||
|
||||
if (!previous.isPresent() || previous.get().isUpdate() || current.getFromId() != previous.get().getFromId() ||
|
||||
!DateUtils.isSameDay(previous.get().getTimestamp(), current.getTimestamp()))
|
||||
{
|
||||
groupSenderHolder.setVisibility(VISIBLE);
|
||||
@@ -714,7 +720,7 @@ public class ConversationItem extends LinearLayout
|
||||
groupSenderHolder.setVisibility(GONE);
|
||||
}
|
||||
|
||||
if (!next.isPresent() || next.get().isUpdate() || !current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress())) {
|
||||
if (!next.isPresent() || next.get().isUpdate() || current.getFromId() != previous.get().getFromId()) {
|
||||
contactPhoto.setVisibility(VISIBLE);
|
||||
} else {
|
||||
contactPhoto.setVisibility(GONE);
|
||||
@@ -728,7 +734,7 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private void setMessageShape(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
||||
private void setMessageShape(@NonNull DcMsg current, @NonNull Optional<DcMsg> previous, @NonNull Optional<DcMsg> next, boolean isGroupThread) {
|
||||
int background;
|
||||
if (isSingularMessage(current, previous, next, isGroupThread)) {
|
||||
background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_alone
|
||||
@@ -747,31 +753,31 @@ public class ConversationItem extends LinearLayout
|
||||
bodyBubble.setBackgroundResource(background);
|
||||
}
|
||||
|
||||
private boolean isStartOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, boolean isGroupThread) {
|
||||
private boolean isStartOfMessageCluster(@NonNull DcMsg current, @NonNull Optional<DcMsg> 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());
|
||||
current.getFromId() != previous.get().getFromId();
|
||||
} 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<MessageRecord> next, boolean isGroupThread) {
|
||||
private boolean isEndOfMessageCluster(@NonNull DcMsg current, @NonNull Optional<DcMsg> 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());
|
||||
current.getFromId() != next.get().getFromId();
|
||||
} 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<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
||||
private boolean isSingularMessage(@NonNull DcMsg current, @NonNull Optional<DcMsg> previous, @NonNull Optional<DcMsg> next, boolean isGroupThread) {
|
||||
return isStartOfMessageCluster(current, previous, isGroupThread) && isEndOfMessageCluster(current, next, isGroupThread);
|
||||
}
|
||||
|
||||
private void setMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
|
||||
private void setMessageSpacing(@NonNull Context context, @NonNull DcMsg current, @NonNull Optional<DcMsg> previous, @NonNull Optional<DcMsg> next, boolean isGroupThread) {
|
||||
int spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse);
|
||||
int spacingBottom = spacingTop;
|
||||
|
||||
@@ -793,16 +799,6 @@ public class ConversationItem extends LinearLayout
|
||||
|
||||
/// Event handlers
|
||||
|
||||
private void handleApproveIdentity() {
|
||||
List<IdentityKeyMismatch> mismatches = messageRecord.getIdentityKeyMismatches();
|
||||
|
||||
if (mismatches.size() != 1) {
|
||||
throw new AssertionError("Identity mismatch count: " + mismatches.size());
|
||||
}
|
||||
|
||||
new ConfirmIdentityDialog(context, messageRecord, mismatches.get(0)).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient modified) {
|
||||
Util.runOnMain(() -> {
|
||||
@@ -845,23 +841,23 @@ public class ConversationItem extends LinearLayout
|
||||
private class SharedContactClickListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) {
|
||||
eventListener.onSharedContactDetailsClicked(((MmsMessageRecord) messageRecord).getSharedContacts().get(0), sharedContactStub.get().getAvatarView());
|
||||
} else {
|
||||
// if (eventListener != null && batchIsEmpty() && messageRecord.isMms() && !((MmsDcMsg) messageRecord).getSharedContacts().isEmpty()) {
|
||||
// eventListener.onSharedContactDetailsClicked(((MmsDcMsg) messageRecord).getSharedContacts().get(0), sharedContactStub.get().getAvatarView());
|
||||
// } else {
|
||||
passthroughClickListener.onClick(view);
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private class AttachmentDownloadClickListener implements SlideClickListener {
|
||||
@Override
|
||||
public void onClick(View v, final Slide slide) {
|
||||
if (messageRecord.isMmsNotification()) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MmsDownloadJob(context, messageRecord.getId(),
|
||||
messageRecord.getThreadId(), false));
|
||||
} else {
|
||||
// if (messageRecord.isMmsNotification()) {
|
||||
// ApplicationContext.getInstance(context)
|
||||
// .getJobManager()
|
||||
// .add(new MmsDownloadJob(context, messageRecord.getId(),
|
||||
// messageRecord.getThreadId(), false));
|
||||
// } else {
|
||||
DatabaseFactory.getAttachmentDatabase(context).setTransferState(messageRecord.getId(),
|
||||
slide.asAttachment(),
|
||||
AttachmentDatabase.TRANSFER_PROGRESS_STARTED);
|
||||
@@ -870,7 +866,7 @@ public class ConversationItem extends LinearLayout
|
||||
.getJobManager()
|
||||
.add(new AttachmentDownloadJob(context, messageRecord.getId(),
|
||||
((DatabaseAttachment)slide.asAttachment()).getAttachmentId(), true));
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,66 +929,7 @@ public class ConversationItem extends LinearLayout
|
||||
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 && messageRecord.isPush());
|
||||
intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
|
||||
context.startActivity(intent);
|
||||
} else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) {
|
||||
handleApproveIdentity();
|
||||
} else if (messageRecord.isPendingInsecureSmsFallback()) {
|
||||
handleMessageApproval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMessageApproval() {
|
||||
final int title;
|
||||
final int message;
|
||||
|
||||
if (messageRecord.isMms()) title = R.string.ConversationItem_click_to_approve_unencrypted_mms_dialog_title;
|
||||
else title = R.string.ConversationItem_click_to_approve_unencrypted_sms_dialog_title;
|
||||
|
||||
message = R.string.ConversationItem_click_to_approve_unencrypted_dialog_message;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(title);
|
||||
|
||||
if (message > -1) builder.setMessage(message);
|
||||
|
||||
builder.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
|
||||
if (messageRecord.isMms()) {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
database.markAsInsecure(messageRecord.getId());
|
||||
database.markAsOutbox(messageRecord.getId());
|
||||
database.markAsForcedSms(messageRecord.getId());
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MmsSendJob(context, messageRecord.getId()));
|
||||
} else {
|
||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||
database.markAsInsecure(messageRecord.getId());
|
||||
database.markAsOutbox(messageRecord.getId());
|
||||
database.markAsForcedSms(messageRecord.getId());
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new SmsSendJob(context, messageRecord.getId(),
|
||||
messageRecord.getIndividualRecipient().getAddress().serialize()));
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNegativeButton(R.string.no, (dialogInterface, i) -> {
|
||||
if (messageRecord.isMms()) {
|
||||
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId());
|
||||
} else {
|
||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId());
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,8 +182,8 @@ class ConversationListAdapter extends RecyclerView.Adapter {
|
||||
void onSwitchToArchive();
|
||||
}
|
||||
|
||||
void changeData(DcChatlist chatlist) {
|
||||
dcChatlist = chatlist;
|
||||
void changeData(@Nullable DcChatlist chatlist) {
|
||||
dcChatlist = chatlist==null? new DcChatlist(0) : chatlist;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
@@ -285,9 +285,6 @@ public class ConversationListItem extends RelativeLayout
|
||||
} else if (thread.isFailed()) {
|
||||
deliveryStatusIndicator.setNone();
|
||||
alertView.setFailed();
|
||||
} else if (thread.isPendingInsecureSmsFallback()) {
|
||||
deliveryStatusIndicator.setNone();
|
||||
alertView.setPendingApproval();
|
||||
} else {
|
||||
alertView.setNone();
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
@@ -38,14 +40,14 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
{
|
||||
private static final String TAG = ConversationUpdateItem.class.getSimpleName();
|
||||
|
||||
private Set<MessageRecord> batchSelected;
|
||||
private Set<DcMsg> batchSelected;
|
||||
|
||||
private ImageView icon;
|
||||
private TextView title;
|
||||
private TextView body;
|
||||
private TextView date;
|
||||
private Recipient sender;
|
||||
private MessageRecord messageRecord;
|
||||
private DcMsg messageRecord;
|
||||
private Locale locale;
|
||||
|
||||
public ConversationUpdateItem(Context context) {
|
||||
@@ -69,12 +71,12 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull MessageRecord messageRecord,
|
||||
@NonNull Optional<MessageRecord> previousMessageRecord,
|
||||
@NonNull Optional<MessageRecord> nextMessageRecord,
|
||||
public void bind(@NonNull DcMsg messageRecord,
|
||||
@NonNull Optional<DcMsg> previousMessageRecord,
|
||||
@NonNull Optional<DcMsg> nextMessageRecord,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<MessageRecord> batchSelected,
|
||||
@NonNull Set<DcMsg> batchSelected,
|
||||
@NonNull Recipient conversationRecipient,
|
||||
boolean pulseUpdate)
|
||||
{
|
||||
@@ -89,11 +91,11 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageRecord getMessageRecord() {
|
||||
public DcMsg getMessageRecord() {
|
||||
return messageRecord;
|
||||
}
|
||||
|
||||
private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
|
||||
private void bind(@NonNull DcMsg messageRecord, @NonNull Locale locale) {
|
||||
this.messageRecord = messageRecord;
|
||||
this.sender = messageRecord.getIndividualRecipient();
|
||||
this.locale = locale;
|
||||
@@ -114,7 +116,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
else setSelected(false);
|
||||
}
|
||||
|
||||
private void setCallRecord(MessageRecord messageRecord) {
|
||||
private void setCallRecord(DcMsg 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);
|
||||
@@ -127,7 +129,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
date.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void setTimerRecord(final MessageRecord messageRecord) {
|
||||
private void setTimerRecord(final DcMsg messageRecord) {
|
||||
if (messageRecord.getExpiresIn() > 0) {
|
||||
icon.setImageResource(R.drawable.ic_timer);
|
||||
icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY));
|
||||
@@ -144,7 +146,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
date.setVisibility(GONE);
|
||||
}
|
||||
|
||||
private void setIdentityRecord(final MessageRecord messageRecord) {
|
||||
private void setIdentityRecord(final DcMsg messageRecord) {
|
||||
icon.setImageResource(R.drawable.ic_security_white_24dp);
|
||||
icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY));
|
||||
body.setText(messageRecord.getDisplayBody());
|
||||
@@ -154,7 +156,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
date.setVisibility(GONE);
|
||||
}
|
||||
|
||||
private void setIdentityVerifyUpdate(final MessageRecord messageRecord) {
|
||||
private void setIdentityVerifyUpdate(final DcMsg messageRecord) {
|
||||
if (messageRecord.isIdentityVerified()) icon.setImageResource(R.drawable.ic_check_white_24dp);
|
||||
else icon.setImageResource(R.drawable.ic_info_outline_white_24dp);
|
||||
|
||||
@@ -166,7 +168,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
date.setVisibility(GONE);
|
||||
}
|
||||
|
||||
private void setGroupRecord(MessageRecord messageRecord) {
|
||||
private void setGroupRecord(DcMsg messageRecord) {
|
||||
icon.setImageResource(R.drawable.ic_group_grey600_24dp);
|
||||
icon.clearColorFilter();
|
||||
|
||||
@@ -178,7 +180,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
date.setVisibility(GONE);
|
||||
}
|
||||
|
||||
private void setJoinedRecord(MessageRecord messageRecord) {
|
||||
private void setJoinedRecord(DcMsg messageRecord) {
|
||||
icon.setImageResource(R.drawable.ic_favorite_grey600_24dp);
|
||||
icon.clearColorFilter();
|
||||
body.setText(messageRecord.getDisplayBody());
|
||||
@@ -188,7 +190,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
date.setVisibility(GONE);
|
||||
}
|
||||
|
||||
private void setEndSessionRecord(MessageRecord messageRecord) {
|
||||
private void setEndSessionRecord(DcMsg messageRecord) {
|
||||
icon.setImageResource(R.drawable.ic_refresh_white_24dp);
|
||||
icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY));
|
||||
body.setText(messageRecord.getDisplayBody());
|
||||
|
||||
@@ -1,410 +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.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.util.Log;
|
||||
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.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
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.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>, 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 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 DynamicTheme();
|
||||
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);
|
||||
|
||||
MessageNotifier.setVisibleThread(threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
MessageNotifier.setVisibleThread(-1L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
running = false;
|
||||
}
|
||||
|
||||
private void initializeActionBar() {
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
Recipient recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
|
||||
recipient.addListener(this);
|
||||
|
||||
setActionBarColor(recipient.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));
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
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) {
|
||||
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())));
|
||||
|
||||
if (messageRecord.getDateReceived() != messageRecord.getDateSent() && !messageRecord.isOutgoing()) {
|
||||
receivedDate.setText(dateFormatter.format(new Date(messageRecord.getDateReceived())));
|
||||
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, false);
|
||||
recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup));
|
||||
}
|
||||
|
||||
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, 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public 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(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(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().isGroupRecipient()) {
|
||||
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), -1));
|
||||
} else {
|
||||
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
|
||||
|
||||
if (receiptInfoList.isEmpty()) {
|
||||
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false);
|
||||
|
||||
for (Recipient recipient : group) {
|
||||
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, -1));
|
||||
}
|
||||
} else {
|
||||
for (GroupReceiptInfo info : receiptInfoList) {
|
||||
recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true),
|
||||
getStatusFor(info.getStatus(), messageRecord.isPending()),
|
||||
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);
|
||||
if (messageRecord.isFailed()) {
|
||||
errorText.setVisibility(View.VISIBLE);
|
||||
metadataContainer.setVisibility(View.GONE);
|
||||
} else {
|
||||
updateTransport(messageRecord);
|
||||
updateTime(messageRecord);
|
||||
updateExpirationTime(messageRecord);
|
||||
errorText.setVisibility(View.GONE);
|
||||
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) {
|
||||
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 && !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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.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.util.Conversions;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return members.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return members.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
try {
|
||||
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(members.get(position).recipient.getAddress().serialize().getBytes()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@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 long timestamp;
|
||||
|
||||
RecipientDeliveryStatus(Recipient recipient, Status deliveryStatus, long timestamp) {
|
||||
this.recipient = recipient;
|
||||
this.deliveryStatus = deliveryStatus;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
Status getDeliveryStatus() {
|
||||
return deliveryStatus;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public Recipient getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,213 +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.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
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.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
/**
|
||||
* A simple view to show the recipients of a message
|
||||
*
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class MessageRecipientListItem extends RelativeLayout
|
||||
implements RecipientModifiedListener
|
||||
{
|
||||
@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 Button resendButton;
|
||||
private AvatarImageView contactPhotoImage;
|
||||
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.resendButton = findViewById(R.id.resend_button);
|
||||
this.deliveryStatusView = findViewById(R.id.delivery_status);
|
||||
}
|
||||
|
||||
public void set(final GlideRequests glideRequests,
|
||||
final MessageRecord record,
|
||||
final RecipientDeliveryStatus member,
|
||||
final boolean isPushGroup)
|
||||
{
|
||||
this.glideRequests = glideRequests;
|
||||
this.member = member;
|
||||
|
||||
member.getRecipient().addListener(this);
|
||||
fromView.setText(member.getRecipient());
|
||||
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
|
||||
setIssueIndicators(record, isPushGroup);
|
||||
}
|
||||
|
||||
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) {
|
||||
resendButton.setVisibility(View.GONE);
|
||||
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 || (!isPushGroup && record.isFailed())) {
|
||||
resendButton.setVisibility(View.VISIBLE);
|
||||
resendButton.setEnabled(true);
|
||||
resendButton.requestFocus();
|
||||
conflictButton.setVisibility(View.GONE);
|
||||
|
||||
errorText = getContext().getString(R.string.MessageDetailsRecipient_failed_to_send);
|
||||
resendButton.setOnClickListener(v -> {
|
||||
resendButton.setVisibility(View.GONE);
|
||||
errorDescription.setVisibility(View.GONE);
|
||||
actionDescription.setVisibility(View.VISIBLE);
|
||||
actionDescription.setText(R.string.message_recipients_list_item__resending);
|
||||
new ResendAsyncTask(record, networkFailure).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
});
|
||||
} 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);
|
||||
}
|
||||
|
||||
resendButton.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.getAddress().equals(member.getRecipient().getAddress())) {
|
||||
return failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private IdentityKeyMismatch getKeyMismatch(final MessageRecord record) {
|
||||
if (record.isIdentityMismatchFailure()) {
|
||||
for (final IdentityKeyMismatch mismatch : record.getIdentityKeyMismatches()) {
|
||||
if (mismatch.getAddress().equals(member.getRecipient().getAddress())) {
|
||||
return mismatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
if (this.member != null && this.member.getRecipient() != null) this.member.getRecipient().removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> {
|
||||
fromView.setText(recipient);
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, false);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class ResendAsyncTask extends AsyncTask<Void,Void,Void> {
|
||||
private final Context context;
|
||||
private final MessageRecord record;
|
||||
private final NetworkFailure failure;
|
||||
|
||||
ResendAsyncTask(MessageRecord record, NetworkFailure failure) {
|
||||
this.context = getContext().getApplicationContext();
|
||||
this.record = record;
|
||||
this.failure = failure;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
mmsDatabase.removeFailure(record.getId(), failure);
|
||||
|
||||
if (record.getRecipient().isPushGroupRecipient()) {
|
||||
MessageSender.resendGroupMessage(context, record, failure.getAddress());
|
||||
} else {
|
||||
MessageSender.resend(context, record);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@@ -72,7 +74,7 @@ public class ConversationItemFooter extends LinearLayout {
|
||||
timerView.stopAnimation();
|
||||
}
|
||||
|
||||
public void setMessageRecord(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
|
||||
public void setMessageRecord(@NonNull DcMsg messageRecord, @NonNull Locale locale) {
|
||||
presentDate(messageRecord, locale);
|
||||
presentSimInfo(messageRecord);
|
||||
presentTimer(messageRecord);
|
||||
@@ -91,19 +93,17 @@ public class ConversationItemFooter extends LinearLayout {
|
||||
deliveryStatusView.setTint(color);
|
||||
}
|
||||
|
||||
private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
|
||||
private void presentDate(@NonNull DcMsg messageRecord, @NonNull Locale locale) {
|
||||
dateView.forceLayout();
|
||||
|
||||
if (messageRecord.isFailed()) {
|
||||
dateView.setText(R.string.ConversationItem_error_not_delivered);
|
||||
} else if (messageRecord.isPendingInsecureSmsFallback()) {
|
||||
dateView.setText(R.string.ConversationItem_click_to_approve_unencrypted);
|
||||
} else {
|
||||
dateView.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, messageRecord.getTimestamp()));
|
||||
}
|
||||
}
|
||||
|
||||
private void presentSimInfo(@NonNull MessageRecord messageRecord) {
|
||||
private void presentSimInfo(@NonNull DcMsg messageRecord) {
|
||||
SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(getContext());
|
||||
|
||||
if (messageRecord.getSubscriptionId() == -1 || !Permissions.hasAll(getContext(), Manifest.permission.READ_PHONE_STATE) || subscriptionManager.getActiveSubscriptionInfoList().size() < 2) {
|
||||
@@ -124,7 +124,7 @@ public class ConversationItemFooter extends LinearLayout {
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void presentTimer(@NonNull final MessageRecord messageRecord) {
|
||||
private void presentTimer(@NonNull final DcMsg messageRecord) {
|
||||
if (messageRecord.getExpiresIn() > 0 && !messageRecord.isPending()) {
|
||||
this.timerView.setVisibility(View.VISIBLE);
|
||||
this.timerView.setPercentComplete(0);
|
||||
@@ -158,12 +158,12 @@ public class ConversationItemFooter extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private void presentInsecureIndicator(@NonNull MessageRecord messageRecord) {
|
||||
private void presentInsecureIndicator(@NonNull DcMsg messageRecord) {
|
||||
insecureIndicatorView.setVisibility(messageRecord.isSecure() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private void presentDeliveryStatus(@NonNull MessageRecord messageRecord) {
|
||||
if (!messageRecord.isFailed() && !messageRecord.isPendingInsecureSmsFallback()) {
|
||||
private void presentDeliveryStatus(@NonNull DcMsg messageRecord) {
|
||||
if (!messageRecord.isFailed()) {
|
||||
if (!messageRecord.isOutgoing()) deliveryStatusView.setNone();
|
||||
else if (messageRecord.isPending()) deliveryStatusView.setPending();
|
||||
else if (messageRecord.isRemoteRead()) deliveryStatusView.setRead();
|
||||
|
||||
@@ -99,6 +99,11 @@ public class ApplicationDcContext extends DcContext {
|
||||
* create objects compatible to the database model of Signal
|
||||
**********************************************************************************************/
|
||||
|
||||
@NonNull
|
||||
public Recipient getRecipient(int chatId) {
|
||||
return getRecipient(getChat(chatId));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Recipient getRecipient(DcChat chat) {
|
||||
RecipientProvider.RecipientDetails recipientDetails = new RecipientProvider.RecipientDetails(chat.getName(), null, false, null, null);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.thoughtcrime.securesms.connect;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.b44t.messenger.DcChatlist;
|
||||
|
||||
import org.thoughtcrime.securesms.util.AsyncLoader;
|
||||
|
||||
public class DcMsgListLoader extends AsyncLoader<int[]> {
|
||||
|
||||
private static final String TAG = DcMsgListLoader.class.getName();
|
||||
|
||||
private final int chatId;
|
||||
private final int listflags;
|
||||
private final int marker1before;
|
||||
|
||||
public DcMsgListLoader(Context context, int chatId, int listflags, int marker1before) {
|
||||
super(context);
|
||||
this.chatId = chatId;
|
||||
this.listflags = listflags;
|
||||
this.marker1before = marker1before;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull
|
||||
int[] loadInBackground() {
|
||||
try {
|
||||
return DcHelper.getContext(getContext()).getChatMsgs(chatId, listflags, marker1before);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
@@ -125,10 +125,6 @@ public interface MmsSmsColumns {
|
||||
return (type & BASE_TYPE_MASK) == BASE_PENDING_SECURE_SMS_FALLBACK;
|
||||
}
|
||||
|
||||
public static boolean isPendingInsecureSmsFallbackType(long type) {
|
||||
return (type & BASE_TYPE_MASK) == BASE_PENDING_INSECURE_SMS_FALLBACK;
|
||||
}
|
||||
|
||||
public static boolean isInboxType(long type) {
|
||||
return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE;
|
||||
}
|
||||
|
||||
@@ -171,6 +171,6 @@ public abstract class DisplayRecord {
|
||||
}
|
||||
|
||||
public boolean isPendingInsecureSmsFallback() {
|
||||
return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user