diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 275ff23c9..5846ccbcb 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -227,12 +227,6 @@ android:theme="@style/TextSecure.LightTheme.Popup" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" /> - - diff --git a/src/com/b44t/messenger/DcMsg.java b/src/com/b44t/messenger/DcMsg.java index 295b588a9..9abb0063e 100644 --- a/src/com/b44t/messenger/DcMsg.java +++ b/src/com/b44t/messenger/DcMsg.java @@ -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(); diff --git a/src/org/thoughtcrime/securesms/BindableConversationItem.java b/src/org/thoughtcrime/securesms/BindableConversationItem.java index 4f853989e..864d60cc3 100644 --- a/src/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/src/org/thoughtcrime/securesms/BindableConversationItem.java @@ -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 previousMessageRecord, - @NonNull Optional nextMessageRecord, + void bind(@NonNull DcMsg messageRecord, + @NonNull Optional previousMessageRecord, + @NonNull Optional nextMessageRecord, @NonNull GlideRequests glideRequests, @NonNull Locale locale, - @NonNull Set batchSelected, + @NonNull Set 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 choices); diff --git a/src/org/thoughtcrime/securesms/ConfirmIdentityDialog.java b/src/org/thoughtcrime/securesms/ConfirmIdentityDialog.java deleted file mode 100644 index 855310525..000000000 --- a/src/org/thoughtcrime/securesms/ConfirmIdentityDialog.java +++ /dev/null @@ -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() - { - @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); - } - } - -} diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java index c1816e554..7b2d5b2b2 100644 --- a/src/org/thoughtcrime/securesms/ConversationAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java @@ -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 - extends FastCursorRecyclerViewAdapter // TODO: base this on RecyclerView.Adapter (see ConversationListAdapter) + extends RecyclerView.Adapter implements StickyHeaderDecoration.StickyHeaderAdapter { private static final int MAX_CACHE_SIZE = 40; private static final String TAG = ConversationAdapter.class.getSimpleName(); - private final Map> messageRecordCache = - Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE)); + private final Map> messageRecordCache = + Collections.synchronizedMap(new LRUCache>(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 private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7; private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8; - private final Set batchSelected = Collections.synchronizedSet(new HashSet()); + private final Set batchSelected = Collections.synchronizedSet(new HashSet()); private final @Nullable ItemClickListener clickListener; private final @NonNull GlideRequests glideRequests; private final @NonNull Locale locale; private final @NonNull Recipient recipient; - private final @NonNull MmsSmsDatabase db; private final @NonNull LayoutInflater inflater; + private final @NonNull 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 ViewHolder(final @NonNull V itemView) { @@ -117,8 +118,20 @@ public class ConversationAdapter public 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 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 @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 } @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 previous = position <= 0? Optional.absent() : Optional.of(getMsg(position-1)); + Optional 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 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 } @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 } } - @Override - protected boolean isRecordForId(@NonNull MessageRecord record, long id) { - return record.getId() == id; - } - - @Override - public long getItemId(@NonNull Cursor cursor) { - List attachments = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(cursor); - List messageAttachments = Stream.of(attachments).filterNot(DatabaseAttachment::isQuote).toList(); - - if (messageAttachments.size() > 0 && messageAttachments.get(0).getFastPreflightId() != null) { - return Long.valueOf(messageAttachments.get(0).getFastPreflightId()); - } - - final String unique = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsColumns.UNIQUE_ROW_ID)); - final byte[] bytes = digest.digest(unique.getBytes()); - return Conversions.byteArrayToLong(bytes); - } - - @Override - protected long getItemId(@NonNull MessageRecord record) { - if (record.isOutgoing() && record.isMms()) { - 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 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 batchSelected.clear(); } - public Set getSelectedItems() { + public Set 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 @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 @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 } } + 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 + } } diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java index 7e04d4747..575a8e503 100644 --- a/src/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationFragment.java @@ -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 + implements LoaderManager.LoaderCallbacks { 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 onCreateLoader(int id, Bundle args) { + public Loader 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 cursorLoader, Cursor cursor) { + public void onLoadFinished(Loader 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 arg0) { + public void onLoaderReset(Loader 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; diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index a68af4025..41c6670da 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -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 batchSelected = new HashSet<>(); + private @NonNull Set batchSelected = new HashSet<>(); private @NonNull Recipient conversationRecipient; private @NonNull Stub mediaThumbnailStub; private @NonNull Stub 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 previousMessageRecord, - @NonNull Optional nextMessageRecord, + public void bind(@NonNull DcMsg messageRecord, + @NonNull Optional previousDcMsg, + @NonNull Optional nextDcMsg, @NonNull GlideRequests glideRequests, @NonNull Locale locale, - @NonNull Set batchSelected, - @NonNull Recipient conversationRecipient, - boolean pulseHighlight) + @NonNull Set 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 previousRecord, - @NonNull Optional nextRecord, + private void setMediaAttributes(@NonNull DcMsg messageRecord, + @NonNull Optional previousRecord, + @NonNull Optional 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 previous, - @NonNull Optional next, + private void setThumbnailOutlineCorners(@NonNull DcMsg current, + @NonNull Optional previous, + @NonNull Optional 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 previous, @NonNull Optional next, boolean isGroupThread) { + private void setSharedContactCorners(@NonNull DcMsg current, @NonNull Optional previous, @NonNull Optional 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 previous, @NonNull Optional 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 previous, @NonNull Optional 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 next, @NonNull Locale locale, boolean isGroupThread) { + private void setFooter(@NonNull DcMsg current, @NonNull Optional 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 previous, @NonNull Optional next, boolean isGroupThread) { + private void setAuthor(@NonNull DcMsg current, @NonNull Optional previous, @NonNull Optional 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 previous, @NonNull Optional next, boolean isGroupThread) { + private void setMessageShape(@NonNull DcMsg current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { int background; if (isSingularMessage(current, previous, next, isGroupThread)) { background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_alone @@ -747,31 +753,31 @@ public class ConversationItem extends LinearLayout bodyBubble.setBackgroundResource(background); } - private boolean isStartOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional previous, boolean isGroupThread) { + private boolean isStartOfMessageCluster(@NonNull DcMsg current, @NonNull Optional previous, boolean isGroupThread) { if (isGroupThread) { return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) || - !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()); + 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 next, boolean isGroupThread) { + private boolean isEndOfMessageCluster(@NonNull DcMsg current, @NonNull Optional next, boolean isGroupThread) { if (isGroupThread) { return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) || - !current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress()); + 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 previous, @NonNull Optional next, boolean isGroupThread) { + private boolean isSingularMessage(@NonNull DcMsg current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { return isStartOfMessageCluster(current, previous, isGroupThread) && isEndOfMessageCluster(current, next, isGroupThread); } - private void setMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + private void setMessageSpacing(@NonNull Context context, @NonNull DcMsg current, @NonNull Optional previous, @NonNull Optional 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 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(); - } } diff --git a/src/org/thoughtcrime/securesms/ConversationListAdapter.java b/src/org/thoughtcrime/securesms/ConversationListAdapter.java index e54a18f16..846430b97 100644 --- a/src/org/thoughtcrime/securesms/ConversationListAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationListAdapter.java @@ -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(); } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java index 1fe541ee8..9f2479337 100644 --- a/src/org/thoughtcrime/securesms/ConversationListItem.java +++ b/src/org/thoughtcrime/securesms/ConversationListItem.java @@ -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(); diff --git a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java index 9d6a3c217..a64d7e302 100644 --- a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java +++ b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java @@ -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 batchSelected; + private Set 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 previousMessageRecord, - @NonNull Optional nextMessageRecord, + public void bind(@NonNull DcMsg messageRecord, + @NonNull Optional previousMessageRecord, + @NonNull Optional nextMessageRecord, @NonNull GlideRequests glideRequests, @NonNull Locale locale, - @NonNull Set batchSelected, + @NonNull Set 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()); diff --git a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java deleted file mode 100644 index ca4815ec5..000000000 --- a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java +++ /dev/null @@ -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 . - */ -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, 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 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 onCreateLoader(int id, Bundle args) { - return new MessageDetailsLoader(this, getIntent().getStringExtra(TYPE_EXTRA), - getIntent().getLongExtra(MESSAGE_ID_EXTRA, -1)); - } - - @Override - public void onLoadFinished(Loader loader, Cursor cursor) { - MessageRecord messageRecord = getMessageRecord(this, cursor, getIntent().getStringExtra(TYPE_EXTRA)); - - if (messageRecord == null) { - finish(); - } else { - new MessageRecipientAsyncTask(this, messageRecord).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - @Override - public void onLoaderReset(Loader loader) { - recipientsList.setAdapter(null); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - - switch (item.getItemId()) { - case android.R.id.home: finish(); return true; - } - - return false; - } - - @SuppressLint("StaticFieldLeak") - private class MessageRecipientAsyncTask extends AsyncTask> { - - private final WeakReference weakContext; - private final MessageRecord messageRecord; - - MessageRecipientAsyncTask(@NonNull Context context, @NonNull MessageRecord messageRecord) { - this.weakContext = new WeakReference<>(context); - this.messageRecord = messageRecord; - } - - protected Context getContext() { - return weakContext.get(); - } - - @Override - public List doInBackground(Void... voids) { - Context context = getContext(); - - if (context == null) { - Log.w(TAG, "associated context is destroyed, finishing early"); - return null; - } - - List recipients = new LinkedList<>(); - - if (!messageRecord.getRecipient().isGroupRecipient()) { - recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), -1)); - } else { - List receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId()); - - if (receiptInfoList.isEmpty()) { - List group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false); - - for (Recipient recipient : group) { - recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, -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 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(); - } - - } -} diff --git a/src/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java b/src/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java deleted file mode 100644 index bb1ddfec1..000000000 --- a/src/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java +++ /dev/null @@ -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 members; - private final boolean isPushGroup; - - MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, - @NonNull MessageRecord record, @NonNull List 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; - } - - } - -} diff --git a/src/org/thoughtcrime/securesms/MessageRecipientListItem.java b/src/org/thoughtcrime/securesms/MessageRecipientListItem.java deleted file mode 100644 index 6ca94abbe..000000000 --- a/src/org/thoughtcrime/securesms/MessageRecipientListItem.java +++ /dev/null @@ -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 . - */ -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 { - 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; - } - } - -} diff --git a/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java index f3457875f..b46814382 100644 --- a/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java +++ b/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java @@ -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(); diff --git a/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java b/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java index c084ebf86..11f8fe59b 100644 --- a/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java +++ b/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java @@ -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); diff --git a/src/org/thoughtcrime/securesms/connect/DcMsgListLoader.java b/src/org/thoughtcrime/securesms/connect/DcMsgListLoader.java new file mode 100644 index 000000000..9c24f4400 --- /dev/null +++ b/src/org/thoughtcrime/securesms/connect/DcMsgListLoader.java @@ -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 { + + 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]; + } +} diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java index 39b1eafaf..551cbc2dd 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -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; } diff --git a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java index dbe83adfb..daa9a5e5f 100644 --- a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java @@ -171,6 +171,6 @@ public abstract class DisplayRecord { } public boolean isPendingInsecureSmsFallback() { - return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type); + return false; } } diff --git a/test/unitTest/java/org/thoughtcrime/securesms/ConversationAdapterTest.java b/test/unitTest/java/org/thoughtcrime/securesms/ConversationAdapterTest.java deleted file mode 100644 index 62ad3497a..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/ConversationAdapterTest.java +++ /dev/null @@ -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); - } -} \ No newline at end of file