From d2f44f6584e779a1215cfff9e0b45fcd353eb4d7 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 21 Oct 2015 15:32:19 -0700 Subject: [PATCH] Join attachments instead of running an asynchronous query. No more SlideDeck futures, just SlideDecks. // FREEBIE --- .../securesms/ConversationFragment.java | 18 +- .../securesms/ConversationItem.java | 5 +- .../securesms/components/ThumbnailView.java | 78 +-------- .../database/AttachmentDatabase.java | 23 ++- .../securesms/database/MmsDatabase.java | 63 +------ .../securesms/database/MmsSmsDatabase.java | 163 +++++++++--------- .../database/model/MediaMmsMessageRecord.java | 59 ++----- .../thoughtcrime/securesms/mms/SlideDeck.java | 20 +-- .../notifications/MessageNotifier.java | 18 +- .../notifications/NotificationItem.java | 6 +- .../SingleRecipientNotificationBuilder.java | 37 ++-- 11 files changed, 155 insertions(+), 335 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java index 4a81544bcd..16cb98a5ab 100644 --- a/src/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationFragment.java @@ -41,7 +41,6 @@ import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment; @@ -305,21 +304,16 @@ public class ConversationFragment extends Fragment private void handleSaveAttachment(final MediaMmsMessageRecord message) { SaveAttachmentTask.showWarningDialog(getActivity(), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - - message.fetchMediaSlide(new FutureTaskListener() { - @Override - public void onSuccess(Slide slide) { + for (Slide slide : message.getSlideDeck().getSlides()) { + if (slide.hasImage() || slide.hasVideo() || slide.hasAudio()) { SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity(), masterSecret); saveTask.execute(new Attachment(slide.getUri(), slide.getContentType(), message.getDateReceived())); + return; } + } - @Override - public void onFailure(Throwable error) { - Log.w(TAG, "No slide with attachable media found, failing nicely."); - Log.w(TAG, error); - Toast.makeText(getActivity(), R.string.ConversationFragment_error_while_saving_attachment_to_sd_card, Toast.LENGTH_LONG).show(); - } - }); + Log.w(TAG, "No slide with attachable media found, failing nicely."); + Toast.makeText(getActivity(), R.string.ConversationFragment_error_while_saving_attachment_to_sd_card, Toast.LENGTH_LONG).show(); } }); } diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index 48c0e62c54..2b5b8c1332 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -240,7 +240,7 @@ public class ConversationItem extends LinearLayout private boolean hasMedia(MessageRecord messageRecord) { return messageRecord.isMms() && !messageRecord.isMmsNotification() && - ((MediaMmsMessageRecord)messageRecord).getPartCount() > 0; + ((MediaMmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null; } private void setBodyText(MessageRecord messageRecord) { @@ -262,8 +262,9 @@ public class ConversationItem extends LinearLayout setNotificationMmsAttributes((NotificationMmsMessageRecord) messageRecord); } else if (hasMedia(messageRecord)) { mediaThumbnail.setVisibility(View.VISIBLE); + //noinspection ConstantConditions mediaThumbnail.setImageResource(masterSecret, - ((MediaMmsMessageRecord)messageRecord).getSlideDeckFuture(), + ((MediaMmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide(), !messageRecord.isFailed() && (!messageRecord.isOutgoing() || messageRecord.isPending()), false); bodyText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java index 3f25d2bbe8..c1dda574a5 100644 --- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -29,9 +29,6 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.RoundedCorners; import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.util.FutureTaskListener; -import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.whispersystems.libaxolotl.util.guava.Optional; @@ -46,8 +43,6 @@ public class ThumbnailView extends FrameLayout { private OnClickListener parentClickListener; private Optional transferControls = Optional.absent(); - private ListenableFutureTask slideDeckFuture = null; - private SlideDeckListener slideDeckListener = null; private ThumbnailClickListener thumbnailClickListener = null; private ThumbnailClickListener downloadClickListener = null; private Slide slide = null; @@ -117,25 +112,6 @@ public class ThumbnailView extends FrameLayout { this.backgroundColorHint = color; } - public void setImageResource(@NonNull MasterSecret masterSecret, - @NonNull ListenableFutureTask slideDeckFuture, - boolean showControls, boolean showRemove) - { - if (this.slideDeckFuture != null && this.slideDeckListener != null) { - this.slideDeckFuture.removeListener(this.slideDeckListener); - } - - if (!slideDeckFuture.equals(this.slideDeckFuture)) { - if (transferControls.isPresent()) getTransferControls().clear(); - image.setImageDrawable(null); - this.slide = null; - } - - this.slideDeckListener = new SlideDeckListener(masterSecret, showControls, showRemove); - this.slideDeckFuture = slideDeckFuture; - this.slideDeckFuture.addListener(this.slideDeckListener); - } - public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull Slide slide, boolean showControls, boolean showRemove) { @@ -182,11 +158,9 @@ public class ThumbnailView extends FrameLayout { public void clear() { if (isContextValid()) Glide.clear(image); - if (slideDeckFuture != null) slideDeckFuture.removeListener(slideDeckListener); if (transferControls.isPresent()) getTransferControls().clear(); - slide = null; - slideDeckFuture = null; - slideDeckListener = null; + + slide = null; } public void showProgressSpinner() { @@ -219,54 +193,6 @@ public class ThumbnailView extends FrameLayout { .fitCenter(); } - private class SlideDeckListener implements FutureTaskListener { - private final MasterSecret masterSecret; - private final boolean showControls; - private final boolean showRemove; - - public SlideDeckListener(@NonNull MasterSecret masterSecret, boolean showControls, boolean showRemove) { - this.masterSecret = masterSecret; - this.showControls = showControls; - this.showRemove = showRemove; - } - - @Override - public void onSuccess(final SlideDeck slideDeck) { - if (slideDeck == null) return; - - final Slide slide = slideDeck.getThumbnailSlide(); - - if (slide != null) { - Util.runOnMain(new Runnable() { - @Override - public void run() { - setImageResource(masterSecret, slide, showControls, showRemove); - } - }); - } else { - Util.runOnMain(new Runnable() { - @Override - public void run() { - Log.w(TAG, "Resolved slide was null!"); - setVisibility(View.GONE); - } - }); - } - } - - @Override - public void onFailure(Throwable error) { - Log.w(TAG, error); - Util.runOnMain(new Runnable() { - @Override - public void run() { - Log.w(TAG, "onFailure!"); - setVisibility(View.GONE); - } - }); - } - } - public interface ThumbnailClickListener { void onClick(View v, Slide slide); } diff --git a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java index 17022793e3..dbe572b829 100644 --- a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java +++ b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java @@ -61,11 +61,12 @@ public class AttachmentDatabase extends Database { static final String TABLE_NAME = "part"; static final String ROW_ID = "_id"; + static final String ATTACHMENT_ID_ALIAS = "attachment_id"; static final String MMS_ID = "mid"; static final String CONTENT_TYPE = "ct"; - private static final String NAME = "name"; - private static final String CONTENT_DISPOSITION = "cd"; - private static final String CONTENT_LOCATION = "cl"; + static final String NAME = "name"; + static final String CONTENT_DISPOSITION = "cd"; + static final String CONTENT_LOCATION = "cl"; static final String DATA = "_data"; static final String TRANSFER_STATE = "pending_push"; static final String SIZE = "data_size"; @@ -80,6 +81,12 @@ public class AttachmentDatabase extends Database { private static final String PART_ID_WHERE = ROW_ID + " = ? AND " + UNIQUE_ID + " = ?"; + private static final String[] PROJECTION = new String[] {ROW_ID + " AS " + ATTACHMENT_ID_ALIAS, + MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION, + CONTENT_LOCATION, DATA, TRANSFER_STATE, + SIZE, THUMBNAIL, THUMBNAIL_ASPECT_RATIO, + UNIQUE_ID}; + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " + MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " + CONTENT_TYPE + " TEXT, " + NAME + " TEXT, " + "chset" + " INTEGER, " + @@ -148,7 +155,7 @@ public class AttachmentDatabase extends Database { Cursor cursor = null; try { - cursor = database.query(TABLE_NAME, null, PART_ID_WHERE, attachmentId.toStrings(), null, null, null); + cursor = database.query(TABLE_NAME, PROJECTION, PART_ID_WHERE, attachmentId.toStrings(), null, null, null); if (cursor != null && cursor.moveToFirst()) return getAttachment(cursor); else return null; @@ -165,7 +172,7 @@ public class AttachmentDatabase extends Database { Cursor cursor = null; try { - cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {mmsId+""}, + cursor = database.query(TABLE_NAME, PROJECTION, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null); while (cursor != null && cursor.moveToNext()) { @@ -185,7 +192,7 @@ public class AttachmentDatabase extends Database { Cursor cursor = null; try { - cursor = database.query(TABLE_NAME, null, TRANSFER_STATE + " = ?", new String[] {String.valueOf(TRANSFER_PROGRESS_STARTED)}, null, null, null); + cursor = database.query(TABLE_NAME, PROJECTION, TRANSFER_STATE + " = ?", new String[] {String.valueOf(TRANSFER_PROGRESS_STARTED)}, null, null, null); while (cursor != null && cursor.moveToNext()) { attachments.add(getAttachment(cursor)); } @@ -417,8 +424,8 @@ public class AttachmentDatabase extends Database { } } - private DatabaseAttachment getAttachment(Cursor cursor) { - return new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ROW_ID)), + DatabaseAttachment getAttachment(Cursor cursor) { + return new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ATTACHMENT_ID_ALIAS)), cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))), cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)), !cursor.isNull(cursor.getColumnIndexOrThrow(DATA)), diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index e1d0aa5226..89c125c35d 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -1012,8 +1012,7 @@ public class MmsDatabase extends MessagingDatabase { Recipients recipients = getRecipientsFor(address); List mismatches = getMismatchedIdentities(mismatchDocument); List networkFailures = getFailures(networkDocument); - - ListenableFutureTask slideDeck = getSlideDeck(dateReceived, id); + SlideDeck slideDeck = getSlideDeck(cursor); return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), addressDeviceId, dateSent, dateReceived, receiptCount, @@ -1078,63 +1077,9 @@ public class MmsDatabase extends MessagingDatabase { } } - private ListenableFutureTask getSlideDeck(final long timestamp, - final long id) - { - ListenableFutureTask future = getCachedSlideDeck(timestamp, id); - - if (future != null) { - return future; - } - - Callable task = new Callable() { - @Override - public SlideDeck call() throws Exception { - AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); - List attachments = new LinkedList(attachmentDatabase.getAttachmentsForMessage(id)); - SlideDeck slideDeck = new SlideDeck(context, attachments); - boolean progress = false; - - for (Attachment attachment : attachments) { - if (attachment.isInProgress()) progress = true; - } - - if (!progress) { - slideCache.put(timestamp + "::" + id, new SoftReference<>(slideDeck)); - } - - return slideDeck; - } - }; - - future = new ListenableFutureTask<>(task, timestamp + "::" + id); - slideResolver.execute(future); - - return future; - } - - private ListenableFutureTask getCachedSlideDeck(final long timestamp, final long id) { - SoftReference reference = slideCache.get(timestamp + "::" + id); - - if (reference != null) { - final SlideDeck slideDeck = reference.get(); - - if (slideDeck != null) { - Callable task = new Callable() { - @Override - public SlideDeck call() throws Exception { - return slideDeck; - } - }; - - ListenableFutureTask future = new ListenableFutureTask<>(task); - future.run(); - - return future; - } - } - - return null; + private SlideDeck getSlideDeck(@NonNull Cursor cursor) { + Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor); + return new SlideDeck(context, attachment); } public void close() { diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index c82c8a6345..3f2cdee56d 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -16,11 +16,13 @@ */ package org.thoughtcrime.securesms.database; +import android.annotation.TargetApi; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; +import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; @@ -34,33 +36,44 @@ import java.util.Set; public class MmsSmsDatabase extends Database { + private static final String TAG = MmsSmsDatabase.class.getSimpleName(); + public static final String TRANSPORT = "transport_type"; public static final String MMS_TRANSPORT = "mms"; public static final String SMS_TRANSPORT = "sms"; + private static final String[] PROJECTION = {MmsSmsColumns.ID, SmsDatabase.BODY, SmsDatabase.TYPE, + MmsSmsColumns.THREAD_ID, + SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, + MmsSmsColumns.NORMALIZED_DATE_SENT, + MmsSmsColumns.NORMALIZED_DATE_RECEIVED, + MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, + SmsDatabase.STATUS, MmsDatabase.PART_COUNT, + MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, + MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, + MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, + MmsSmsColumns.MISMATCHED_IDENTITIES, + MmsDatabase.NETWORK_FAILURE, TRANSPORT, + AttachmentDatabase.ATTACHMENT_ID_ALIAS, + AttachmentDatabase.UNIQUE_ID, + AttachmentDatabase.MMS_ID, + AttachmentDatabase.SIZE, + AttachmentDatabase.DATA, + AttachmentDatabase.CONTENT_TYPE, + AttachmentDatabase.CONTENT_LOCATION, + AttachmentDatabase.CONTENT_DISPOSITION, + AttachmentDatabase.NAME, + AttachmentDatabase.TRANSFER_STATE}; + public MmsSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) { super(context, databaseHelper); } public Cursor getConversation(long threadId, long limit) { - String[] projection = {MmsSmsColumns.ID, SmsDatabase.BODY, SmsDatabase.TYPE, - MmsSmsColumns.THREAD_ID, - SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, - MmsSmsColumns.NORMALIZED_DATE_SENT, - MmsSmsColumns.NORMALIZED_DATE_RECEIVED, - MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, - SmsDatabase.STATUS, MmsDatabase.PART_COUNT, - MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, - MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, - MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, - MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT}; + String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC"; + String selection = MmsSmsColumns.THREAD_ID + " = " + threadId; - String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC"; - - String selection = MmsSmsColumns.THREAD_ID + " = " + threadId; - - Cursor cursor = queryTables(projection, selection, selection, order, null, limit > 0 ? String.valueOf(limit) : null); + Cursor cursor = queryTables(PROJECTION, selection, order, limit > 0 ? String.valueOf(limit) : null); setNotifyConverationListeners(cursor, threadId); return cursor; @@ -71,67 +84,27 @@ public class MmsSmsDatabase extends Database { } public Cursor getIdentityConflictMessagesForThread(long threadId) { - String[] projection = {MmsSmsColumns.ID, SmsDatabase.BODY, SmsDatabase.TYPE, - MmsSmsColumns.THREAD_ID, - SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, - MmsSmsColumns.NORMALIZED_DATE_SENT, - MmsSmsColumns.NORMALIZED_DATE_RECEIVED, - MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, - SmsDatabase.STATUS, MmsDatabase.PART_COUNT, - MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, - MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, - MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, - MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT}; - String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC"; - String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsSmsColumns.MISMATCHED_IDENTITIES + " IS NOT NULL"; - Cursor cursor = queryTables(projection, selection, selection, order, null, null); + Cursor cursor = queryTables(PROJECTION, selection, order, null); setNotifyConverationListeners(cursor, threadId); return cursor; } public Cursor getConversationSnippet(long threadId) { - String[] projection = {MmsSmsColumns.ID, SmsDatabase.BODY, SmsDatabase.TYPE, - MmsSmsColumns.THREAD_ID, - SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, - MmsSmsColumns.NORMALIZED_DATE_SENT, - MmsSmsColumns.NORMALIZED_DATE_RECEIVED, - MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, - SmsDatabase.STATUS, MmsDatabase.PART_COUNT, - MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, - MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, - MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, - MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT}; + String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC"; + String selection = MmsSmsColumns.THREAD_ID + " = " + threadId; - String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC"; - String selection = MmsSmsColumns.THREAD_ID + " = " + threadId; - - return queryTables(projection, selection, selection, order, null, "1"); + return queryTables(PROJECTION, selection, order, "1"); } public Cursor getUnread() { - String[] projection = {MmsSmsColumns.ID, SmsDatabase.BODY, SmsDatabase.READ, SmsDatabase.TYPE, - SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsSmsColumns.THREAD_ID, - SmsDatabase.STATUS, - MmsSmsColumns.NORMALIZED_DATE_SENT, - MmsSmsColumns.NORMALIZED_DATE_RECEIVED, - MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, - MmsDatabase.PART_COUNT, - MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, - MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, - MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, - MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT}; - String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC"; String selection = MmsSmsColumns.READ + " = 0"; - return queryTables(projection, selection, selection, order, null, null); + return queryTables(PROJECTION, selection, order, null); } public int getConversationCount(long threadId) { @@ -146,27 +119,47 @@ public class MmsSmsDatabase extends Database { DatabaseFactory.getMmsDatabase(context).incrementDeliveryReceiptCount(address, timestamp); } - private Cursor queryTables(String[] projection, String smsSelection, String mmsSelection, String order, String groupBy, String limit) { + private Cursor queryTables(String[] projection, String selection, String order, String limit) { String[] mmsProjection = {MmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT, MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED, - MmsSmsColumns.ID, SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID, + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " AS " + MmsSmsColumns.ID, + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " AS " + AttachmentDatabase.ATTACHMENT_ID_ALIAS, + SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID, SmsDatabase.TYPE, SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT, MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT}; + MmsDatabase.NETWORK_FAILURE, TRANSPORT, + AttachmentDatabase.UNIQUE_ID, + AttachmentDatabase.MMS_ID, + AttachmentDatabase.SIZE, + AttachmentDatabase.DATA, + AttachmentDatabase.CONTENT_TYPE, + AttachmentDatabase.CONTENT_LOCATION, + AttachmentDatabase.CONTENT_DISPOSITION, + AttachmentDatabase.NAME, + AttachmentDatabase.TRANSFER_STATE}; String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT, SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED, - MmsSmsColumns.ID, SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID, + MmsSmsColumns.ID, "NULL AS " + AttachmentDatabase.ATTACHMENT_ID_ALIAS, + SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID, SmsDatabase.TYPE, SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT, MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT}; - + MmsDatabase.NETWORK_FAILURE, TRANSPORT, + AttachmentDatabase.UNIQUE_ID, + AttachmentDatabase.MMS_ID, + AttachmentDatabase.SIZE, + AttachmentDatabase.DATA, + AttachmentDatabase.CONTENT_TYPE, + AttachmentDatabase.CONTENT_LOCATION, + AttachmentDatabase.CONTENT_DISPOSITION, + AttachmentDatabase.NAME, + AttachmentDatabase.TRANSFER_STATE}; SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder(); SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder(); @@ -174,10 +167,10 @@ public class MmsSmsDatabase extends Database { mmsQueryBuilder.setDistinct(true); smsQueryBuilder.setDistinct(true); - mmsQueryBuilder.setTables(MmsDatabase.TABLE_NAME); + mmsQueryBuilder.setTables(MmsDatabase.TABLE_NAME + " LEFT OUTER JOIN " + AttachmentDatabase.TABLE_NAME + " ON (" + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " = " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ")"); smsQueryBuilder.setTables(SmsDatabase.TABLE_NAME); - Set mmsColumnsPresent = new HashSet(); + Set mmsColumnsPresent = new HashSet<>(); mmsColumnsPresent.add(MmsSmsColumns.ID); mmsColumnsPresent.add(MmsSmsColumns.READ); mmsColumnsPresent.add(MmsSmsColumns.THREAD_ID); @@ -198,7 +191,16 @@ public class MmsSmsDatabase extends Database { mmsColumnsPresent.add(MmsDatabase.STATUS); mmsColumnsPresent.add(MmsDatabase.NETWORK_FAILURE); - Set smsColumnsPresent = new HashSet(); + mmsColumnsPresent.add(AttachmentDatabase.ROW_ID); + mmsColumnsPresent.add(AttachmentDatabase.UNIQUE_ID); + mmsColumnsPresent.add(AttachmentDatabase.SIZE); + mmsColumnsPresent.add(AttachmentDatabase.CONTENT_TYPE); + mmsColumnsPresent.add(AttachmentDatabase.CONTENT_LOCATION); + mmsColumnsPresent.add(AttachmentDatabase.CONTENT_DISPOSITION); + mmsColumnsPresent.add(AttachmentDatabase.NAME); + mmsColumnsPresent.add(AttachmentDatabase.TRANSFER_STATE); + + Set smsColumnsPresent = new HashSet<>(); smsColumnsPresent.add(MmsSmsColumns.ID); smsColumnsPresent.add(MmsSmsColumns.BODY); smsColumnsPresent.add(MmsSmsColumns.ADDRESS); @@ -213,16 +215,16 @@ public class MmsSmsDatabase extends Database { smsColumnsPresent.add(SmsDatabase.DATE_RECEIVED); smsColumnsPresent.add(SmsDatabase.STATUS); - String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 2, MMS_TRANSPORT, mmsSelection, null, null, null); - String smsSubQuery = smsQueryBuilder.buildUnionSubQuery(TRANSPORT, smsProjection, smsColumnsPresent, 2, SMS_TRANSPORT, smsSelection, null, null, null); + String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 3, MMS_TRANSPORT, selection, null, null, null); + String smsSubQuery = smsQueryBuilder.buildUnionSubQuery(TRANSPORT, smsProjection, smsColumnsPresent, 3, SMS_TRANSPORT, selection, null, null, null); SQLiteQueryBuilder unionQueryBuilder = new SQLiteQueryBuilder(); - String unionQuery = unionQueryBuilder.buildUnionQuery(new String[] {smsSubQuery, mmsSubQuery}, order, null); + String unionQuery = unionQueryBuilder.buildUnionQuery(new String[] {smsSubQuery, mmsSubQuery}, order, limit); SQLiteQueryBuilder outerQueryBuilder = new SQLiteQueryBuilder(); outerQueryBuilder.setTables("(" + unionQuery + ")"); - String query = outerQueryBuilder.buildQuery(projection, null, null, groupBy, null, null, limit); + String query = outerQueryBuilder.buildQuery(projection, null, null, null, null, null, null); Log.w("MmsSmsDatabase", "Executing query: " + query); SQLiteDatabase db = databaseHelper.getReadableDatabase(); @@ -277,14 +279,15 @@ public class MmsSmsDatabase extends Database { return getCurrent(); } + @TargetApi(Build.VERSION_CODES.HONEYCOMB) public MessageRecord getCurrent() { String type = cursor.getString(cursor.getColumnIndexOrThrow(TRANSPORT)); - if (MmsSmsDatabase.MMS_TRANSPORT.equals(type)) { - return getMmsReader().getCurrent(); - } else { - return getSmsReader().getCurrent(); - } + Log.w("MmsSmsDatabase", "Type: " + type); + + if (MmsSmsDatabase.MMS_TRANSPORT.equals(type)) return getMmsReader().getCurrent(); + else if (MmsSmsDatabase.SMS_TRANSPORT.equals(type)) return getSmsReader().getCurrent(); + else throw new AssertionError("Bad type: " + type); } public void close() { diff --git a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java index 592308d242..c2473713c9 100644 --- a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java @@ -17,23 +17,18 @@ package org.thoughtcrime.securesms.database.model; import android.content.Context; +import android.support.annotation.NonNull; import android.text.SpannableString; -import android.util.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.mms.MediaNotFoundException; -import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.util.FutureTaskListener; -import org.thoughtcrime.securesms.util.ListenableFutureTask; import java.util.List; -import java.util.concurrent.ExecutionException; /** * Represents the message record model for MMS messages that contain @@ -48,13 +43,13 @@ public class MediaMmsMessageRecord extends MessageRecord { private final Context context; private final int partCount; - private final ListenableFutureTask slideDeckFutureTask; + private final @NonNull SlideDeck slideDeck; public MediaMmsMessageRecord(Context context, long id, Recipients recipients, Recipient individualRecipient, int recipientDeviceId, long dateSent, long dateReceived, int deliveredCount, long threadId, Body body, - ListenableFutureTask slideDeck, + @NonNull SlideDeck slideDeck, int partCount, long mailbox, List mismatches, List failures) @@ -63,51 +58,17 @@ public class MediaMmsMessageRecord extends MessageRecord { dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, deliveredCount, mailbox, mismatches, failures); - this.context = context.getApplicationContext(); - this.partCount = partCount; - this.slideDeckFutureTask = slideDeck; + this.context = context.getApplicationContext(); + this.partCount = partCount; + this.slideDeck = slideDeck; } - public ListenableFutureTask getSlideDeckFuture() { - return slideDeckFutureTask; - } - - private SlideDeck getSlideDeckSync() { - try { - return slideDeckFutureTask.get(); - } catch (InterruptedException e) { - Log.w(TAG, e); - return null; - } catch (ExecutionException e) { - Log.w(TAG, e); - return null; - } + public @NonNull SlideDeck getSlideDeck() { + return slideDeck; } public boolean containsMediaSlide() { - SlideDeck deck = getSlideDeckSync(); - return deck != null && deck.containsMediaSlide(); - } - - - public void fetchMediaSlide(final FutureTaskListener listener) { - slideDeckFutureTask.addListener(new FutureTaskListener() { - @Override - public void onSuccess(SlideDeck deck) { - for (Slide slide : deck.getSlides()) { - if (slide.hasImage() || slide.hasVideo() || slide.hasAudio()) { - listener.onSuccess(slide); - return; - } - } - listener.onFailure(new MediaNotFoundException("no media slide found")); - } - - @Override - public void onFailure(Throwable error) { - listener.onFailure(error); - } - }); + return slideDeck.containsMediaSlide(); } public int getPartCount() { diff --git a/src/org/thoughtcrime/securesms/mms/SlideDeck.java b/src/org/thoughtcrime/securesms/mms/SlideDeck.java index 2c0a03624e..546eef1967 100644 --- a/src/org/thoughtcrime/securesms/mms/SlideDeck.java +++ b/src/org/thoughtcrime/securesms/mms/SlideDeck.java @@ -17,29 +17,14 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; -import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; -import android.util.Pair; -import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.dom.smil.parser.SmilXmlSerializer; -import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.SmilUtil; -import org.thoughtcrime.securesms.util.Util; -import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; import java.util.LinkedList; import java.util.List; -import ws.com.google.android.mms.ContentType; -import ws.com.google.android.mms.pdu.CharacterSets; -import ws.com.google.android.mms.pdu.PduBody; -import ws.com.google.android.mms.pdu.PduPart; - public class SlideDeck { private final List slides = new LinkedList<>(); @@ -51,6 +36,11 @@ public class SlideDeck { } } + public SlideDeck(Context context, Attachment attachment) { + Slide slide = MediaUtil.getSlideForAttachment(context, attachment); + if (slide != null) slides.add(slide); + } + public SlideDeck() { } diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java index 0e1150e7b9..ffff998c4d 100644 --- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -347,13 +347,13 @@ public class MessageNotifier { else reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor, masterSecret); while ((record = reader.getNext()) != null) { - Recipient recipient = record.getIndividualRecipient(); - Recipients recipients = record.getRecipients(); - long threadId = record.getThreadId(); - CharSequence body = record.getDisplayBody(); - Recipients threadRecipients = null; - ListenableFutureTask slideDeck = null; - long timestamp; + Recipient recipient = record.getIndividualRecipient(); + Recipients recipients = record.getRecipients(); + long threadId = record.getThreadId(); + CharSequence body = record.getDisplayBody(); + Recipients threadRecipients = null; + SlideDeck slideDeck = null; + long timestamp; if (record.isPush()) timestamp = record.getDateSent(); else timestamp = record.getDateReceived(); @@ -366,12 +366,12 @@ public class MessageNotifier { body = SpanUtil.italic(context.getString(R.string.MessageNotifier_locked_message)); } else if (record.isMms() && TextUtils.isEmpty(body)) { body = SpanUtil.italic(context.getString(R.string.MessageNotifier_media_message)); - slideDeck = ((MediaMmsMessageRecord)record).getSlideDeckFuture(); + slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); } else if (record.isMms() && !record.isMmsNotification()) { String message = context.getString(R.string.MessageNotifier_media_message_with_text, body); int italicLength = message.length() - body.length(); body = SpanUtil.italic(message, italicLength); - slideDeck = ((MediaMmsMessageRecord)record).getSlideDeckFuture(); + slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); } if (threadRecipients == null || !threadRecipients.isMuted()) { diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationItem.java b/src/org/thoughtcrime/securesms/notifications/NotificationItem.java index 1b2fae2296..9b86777155 100644 --- a/src/org/thoughtcrime/securesms/notifications/NotificationItem.java +++ b/src/org/thoughtcrime/securesms/notifications/NotificationItem.java @@ -21,12 +21,12 @@ public class NotificationItem { private final long threadId; private final CharSequence text; private final long timestamp; - private final ListenableFutureTask slideDeck; + private final @Nullable SlideDeck slideDeck; public NotificationItem(Recipient individualRecipient, Recipients recipients, Recipients threadRecipients, long threadId, CharSequence text, long timestamp, - @Nullable ListenableFutureTask slideDeck) + @Nullable SlideDeck slideDeck) { this.individualRecipient = individualRecipient; this.recipients = recipients; @@ -57,7 +57,7 @@ public class NotificationItem { return threadId; } - public @Nullable ListenableFutureTask getSlideDeck() { + public @Nullable SlideDeck getSlideDeck() { return slideDeck; } diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index 05333b41e5..971665f5fd 100644 --- a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -14,7 +14,6 @@ import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.Action; import android.support.v4.app.RemoteInput; import android.text.SpannableStringBuilder; -import android.util.Log; import com.bumptech.glide.Glide; @@ -26,7 +25,6 @@ import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.ListenableFutureTask; import java.util.LinkedList; import java.util.List; @@ -38,8 +36,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil private final List messageBodies = new LinkedList<>(); - private ListenableFutureTask slideDeck; - private final MasterSecret masterSecret; + private SlideDeck slideDeck; + private final MasterSecret masterSecret; public SingleRecipientNotificationBuilder(@NonNull Context context, @Nullable MasterSecret masterSecret, @@ -81,7 +79,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil setNumber(messageCount); } - public void setPrimaryMessageBody(CharSequence message, @Nullable ListenableFutureTask slideDeck) { + public void setPrimaryMessageBody(CharSequence message, @Nullable SlideDeck slideDeck) { if (privacy.isDisplayMessage()) { setContentText(message); this.slideDeck = slideDeck; @@ -166,30 +164,25 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil } } - private boolean hasBigPictureSlide(@Nullable ListenableFutureTask slideDeck) { - try { - if (masterSecret == null || slideDeck == null || Build.VERSION.SDK_INT < 16) { - return false; - } - - Slide thumbnailSlide = slideDeck.get().getThumbnailSlide(); - - return thumbnailSlide != null && - thumbnailSlide.hasImage() && - !thumbnailSlide.isInProgress() && - thumbnailSlide.getThumbnailUri() != null; - - } catch (InterruptedException | ExecutionException e) { - Log.w(TAG, e); + private boolean hasBigPictureSlide(@Nullable SlideDeck slideDeck) { + if (masterSecret == null || slideDeck == null || Build.VERSION.SDK_INT < 16) { return false; } + + Slide thumbnailSlide = slideDeck.getThumbnailSlide(); + + return thumbnailSlide != null && + thumbnailSlide.hasImage() && + !thumbnailSlide.isInProgress() && + thumbnailSlide.getThumbnailUri() != null; } private Bitmap getBigPicture(@NonNull MasterSecret masterSecret, - @NonNull ListenableFutureTask slideDeck) + @NonNull SlideDeck slideDeck) { try { - Uri uri = slideDeck.get().getThumbnailSlide().getThumbnailUri(); + @SuppressWarnings("ConstantConditions") + Uri uri = slideDeck.getThumbnailSlide().getThumbnailUri(); return Glide.with(context) .load(new DecryptableStreamUriLoader.DecryptableUri(masterSecret, uri))