Display thumbnail previews for images in conversation list.

Closes #4262
// FREEBIE
pull/1/head
Moxie Marlinspike 9 years ago
parent 15c6f18750
commit 5111fe2e95

@ -22,66 +22,81 @@
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:layout_marginLeft="10dp" /> android:layout_marginLeft="10dp" />
<LinearLayout <RelativeLayout android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginLeft="4dip"
android:layout_marginTop="4dip" android:layout_marginRight="8dip"
android:layout_marginLeft="4dip" android:layout_centerVertical="true"
android:layout_marginRight="8dip" android:layout_toRightOf="@id/contact_photo_image"
android:layout_marginBottom="4dip" android:weightSum="1"
android:layout_centerVertical="true" android:orientation="horizontal">
android:layout_toRightOf="@id/contact_photo_image"
android:orientation="vertical">
<LinearLayout <org.thoughtcrime.securesms.components.FromTextView
android:layout_width="match_parent" android:id="@+id/from"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:orientation="horizontal" android:layout_height="wrap_content"
android:weightSum="1" android:layout_alignParentTop="true"
android:gravity="bottom"> android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/thumbnail"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?attr/conversation_list_item_contact_color"
android:singleLine="true"
tools:text="Jules Bonnot"
android:ellipsize="end"
android:drawablePadding="5dp"/>
<org.thoughtcrime.securesms.components.FromTextView <ImageView android:id="@+id/error"
android:id="@+id/from" android:layout_height="20dp"
android:layout_weight="1" android:layout_width="20dp"
android:layout_width="0dp" android:layout_alignParentLeft="true"
android:layout_height="wrap_content" android:layout_below="@id/from"
android:textAppearance="?android:attr/textAppearanceMedium" android:paddingBottom="3dp"
android:textColor="?attr/conversation_list_item_contact_color" android:visibility="gone"
android:singleLine="true" tools:visibility="visible"
tools:text="Jules Bonnot" android:src="@drawable/ic_action_warning_red"
android:ellipsize="end" android:contentDescription="@string/conversation_list_item_view__error_alert" />
android:drawablePadding="5dp"/>
<ImageView android:id="@+id/error" <org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:layout_marginLeft="3dip" android:id="@+id/subject"
android:layout_height="match_parent" android:layout_width="match_parent"
android:layout_width="20sp" android:layout_height="wrap_content"
android:visibility="gone" android:layout_below="@id/from"
android:src="@drawable/ic_action_warning_red" android:layout_toRightOf="@id/error"
android:contentDescription="@string/conversation_list_item_view__error_alert" /> android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/conversation_list_item_subject_color"
android:fontFamily="sans-serif-light"
android:singleLine="true"
tools:text="Wheels arrive at 3pm flat. This is a somewhat longer message."
android:ellipsize="end" />
<TextView android:id="@+id/date" <org.thoughtcrime.securesms.components.ThumbnailView
android:layout_marginLeft="3dip" android:id="@+id/thumbnail"
android:layout_height="wrap_content" android:layout_width="40dp"
android:layout_width="wrap_content" android:layout_height="40dp"
android:textAppearance="?android:attr/textAppearanceSmall" android:layout_gravity="center_horizontal"
android:textColor="?attr/conversation_list_item_date_color" android:layout_marginRight="5dip"
android:fontFamily="sans-serif-light" android:layout_marginLeft="5dip"
tools:text="30 mins" android:layout_toLeftOf="@+id/date"
android:singleLine="true" android:layout_alignParentTop="true"
android:layout_gravity="top" /> android:layout_marginTop="5dip"
</LinearLayout> android:contentDescription="@string/conversation_activity__attachment_thumbnail"
app:backgroundColorHint="?conversation_background"
android:visibility="gone"
tools:src="@drawable/ic_video_light"
tools:visibility="visible" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView <TextView android:id="@id/date"
android:id="@+id/subject" android:layout_marginLeft="3dip"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_above="@id/subject"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/conversation_list_item_subject_color" android:textColor="?attr/conversation_list_item_date_color"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
android:singleLine="true" tools:text="30 mins"
tools:text="Wheels arrive at 3pm flat." android:singleLine="true"/>
android:ellipsize="end" />
</LinearLayout> </RelativeLayout>
</org.thoughtcrime.securesms.ConversationListItem> </org.thoughtcrime.securesms.ConversationListItem>

@ -449,6 +449,7 @@
<string name="ThreadRecord_called">You called</string> <string name="ThreadRecord_called">You called</string>
<string name="ThreadRecord_called_you">Called you</string> <string name="ThreadRecord_called_you">Called you</string>
<string name="ThreadRecord_missed_call">Missed call</string> <string name="ThreadRecord_missed_call">Missed call</string>
<string name="ThreadRecord_media_message">Media message</string>
<!-- VerifyIdentityActivity --> <!-- VerifyIdentityActivity -->
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">You do not have an identity key.</string> <string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">You do not have an identity key.</string>

@ -1080,7 +1080,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipients(), thisDistributionType); if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipients(), thisDistributionType);
draftDatabase.insertDrafts(new MasterCipher(thisMasterSecret), threadId, drafts); draftDatabase.insertDrafts(new MasterCipher(thisMasterSecret), threadId, drafts);
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), System.currentTimeMillis(), Types.BASE_DRAFT_TYPE); threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
drafts.getUriSnippet(ConversationActivity.this),
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE);
} else if (threadId > 0) { } else if (threadId > 0) {
threadDatabase.update(threadId); threadDatabase.update(threadId);
} }

@ -47,6 +47,7 @@ import java.util.Set;
public class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationListAdapter.ViewHolder> { public class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationListAdapter.ViewHolder> {
private final ThreadDatabase threadDatabase; private final ThreadDatabase threadDatabase;
private final MasterSecret masterSecret;
private final MasterCipher masterCipher; private final MasterCipher masterCipher;
private final Locale locale; private final Locale locale;
private final Context context; private final Context context;
@ -86,6 +87,7 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
@Nullable Cursor cursor, @Nullable Cursor cursor,
@Nullable ItemClickListener clickListener) { @Nullable ItemClickListener clickListener) {
super(context, cursor); super(context, cursor);
this.masterSecret = masterSecret;
this.masterCipher = new MasterCipher(masterSecret); this.masterCipher = new MasterCipher(masterSecret);
this.context = context; this.context = context;
this.threadDatabase = DatabaseFactory.getThreadDatabase(context); this.threadDatabase = DatabaseFactory.getThreadDatabase(context);
@ -109,7 +111,7 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
ThreadDatabase.Reader reader = threadDatabase.readerFor(cursor, masterCipher); ThreadDatabase.Reader reader = threadDatabase.readerFor(cursor, masterCipher);
ThreadRecord record = reader.getCurrent(); ThreadRecord record = reader.getCurrent();
viewHolder.getItem().set(record, locale, batchSet, batchMode); viewHolder.getItem().set(masterSecret, record, locale, batchSet, batchMode);
} }
public void toggleThreadInBatchSet(long threadId) { public void toggleThreadInBatchSet(long threadId) {

@ -25,12 +25,16 @@ import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.FromTextView; import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
@ -64,6 +68,7 @@ public class ConversationListItem extends RelativeLayout
private TextView dateView; private TextView dateView;
private boolean read; private boolean read;
private AvatarImageView contactPhotoImage; private AvatarImageView contactPhotoImage;
private ThumbnailView thumbnailView;
private final @DrawableRes int readBackground; private final @DrawableRes int readBackground;
private final @DrawableRes int unreadBackround; private final @DrawableRes int unreadBackround;
@ -88,9 +93,12 @@ public class ConversationListItem extends RelativeLayout
this.fromView = (FromTextView) findViewById(R.id.from); this.fromView = (FromTextView) findViewById(R.id.from);
this.dateView = (TextView) findViewById(R.id.date); this.dateView = (TextView) findViewById(R.id.date);
this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image); this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image);
this.thumbnailView = (ThumbnailView) findViewById(R.id.thumbnail);
} }
public void set(ThreadRecord thread, Locale locale, Set<Long> selectedThreads, boolean batchMode) { public void set(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread,
@NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode)
{
this.selectedThreads = selectedThreads; this.selectedThreads = selectedThreads;
this.recipients = thread.getRecipients(); this.recipients = thread.getRecipients();
this.threadId = thread.getThreadId(); this.threadId = thread.getThreadId();
@ -109,6 +117,7 @@ public class ConversationListItem extends RelativeLayout
dateView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE); dateView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
} }
setThumbnailSnippet(masterSecret, thread);
setBatchState(batchMode); setBatchState(batchMode);
setBackground(thread); setBackground(thread);
setRippleColor(recipients); setRippleColor(recipients);
@ -136,6 +145,23 @@ public class ConversationListItem extends RelativeLayout
return distributionType; return distributionType;
} }
private void setThumbnailSnippet(MasterSecret masterSecret, ThreadRecord thread) {
if (thread.getSnippetUri() != null) {
this.thumbnailView.setVisibility(View.VISIBLE);
this.thumbnailView.setImageResource(masterSecret, thread.getSnippetUri());
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail);
this.subjectView.setLayoutParams(subjectParams);
} else {
this.thumbnailView.setVisibility(View.GONE);
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, 0);
this.subjectView.setLayoutParams(subjectParams);
}
}
private void setBackground(ThreadRecord thread) { private void setBackground(ThreadRecord thread) {
if (thread.isRead()) setBackgroundResource(readBackground); if (thread.isRead()) setBackgroundResource(readBackground);
else setBackgroundResource(unreadBackround); else setBackgroundResource(unreadBackround);

@ -5,6 +5,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri;
import android.os.Build.VERSION; import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -124,6 +125,15 @@ public class ThumbnailView extends FrameLayout {
else Glide.clear(image); else Glide.clear(image);
} }
public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull Uri uri) {
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE);
Glide.with(getContext()).load(new DecryptableUri(masterSecret, uri))
.crossFade()
.transform(new RoundedCorners(getContext(), true, radius, backgroundColorHint))
.into(image);
}
public void setThumbnailClickListener(SlideClickListener listener) { public void setThumbnailClickListener(SlideClickListener listener) {
this.thumbnailClickListener = listener; this.thumbnailClickListener = listener;
} }

@ -266,6 +266,7 @@ public class AttachmentDatabase extends Database {
partData.first.delete(); partData.first.delete();
} else { } else {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(mmsId)); notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(mmsId));
notifyConversationListListeners();
} }
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret, attachmentId)); thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret, attachmentId));

@ -46,28 +46,29 @@ import ws.com.google.android.mms.ContentType;
public class DatabaseFactory { public class DatabaseFactory {
private static final int INTRODUCED_IDENTITIES_VERSION = 2; private static final int INTRODUCED_IDENTITIES_VERSION = 2;
private static final int INTRODUCED_INDEXES_VERSION = 3; private static final int INTRODUCED_INDEXES_VERSION = 3;
private static final int INTRODUCED_DATE_SENT_VERSION = 4; private static final int INTRODUCED_DATE_SENT_VERSION = 4;
private static final int INTRODUCED_DRAFTS_VERSION = 5; private static final int INTRODUCED_DRAFTS_VERSION = 5;
private static final int INTRODUCED_NEW_TYPES_VERSION = 6; private static final int INTRODUCED_NEW_TYPES_VERSION = 6;
private static final int INTRODUCED_MMS_BODY_VERSION = 7; private static final int INTRODUCED_MMS_BODY_VERSION = 7;
private static final int INTRODUCED_MMS_FROM_VERSION = 8; private static final int INTRODUCED_MMS_FROM_VERSION = 8;
private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9; private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9;
private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10; private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10;
private static final int INTRODUCED_GROUP_DATABASE_VERSION = 11; private static final int INTRODUCED_GROUP_DATABASE_VERSION = 11;
private static final int INTRODUCED_PUSH_FIX_VERSION = 12; private static final int INTRODUCED_PUSH_FIX_VERSION = 12;
private static final int INTRODUCED_DELIVERY_RECEIPTS = 13; private static final int INTRODUCED_DELIVERY_RECEIPTS = 13;
private static final int INTRODUCED_PART_DATA_SIZE_VERSION = 14; private static final int INTRODUCED_PART_DATA_SIZE_VERSION = 14;
private static final int INTRODUCED_THUMBNAILS_VERSION = 15; private static final int INTRODUCED_THUMBNAILS_VERSION = 15;
private static final int INTRODUCED_IDENTITY_COLUMN_VERSION = 16; private static final int INTRODUCED_IDENTITY_COLUMN_VERSION = 16;
private static final int INTRODUCED_UNIQUE_PART_IDS_VERSION = 17; private static final int INTRODUCED_UNIQUE_PART_IDS_VERSION = 17;
private static final int INTRODUCED_RECIPIENT_PREFS_DB = 18; private static final int INTRODUCED_RECIPIENT_PREFS_DB = 18;
private static final int INTRODUCED_ENVELOPE_CONTENT_VERSION = 19; private static final int INTRODUCED_ENVELOPE_CONTENT_VERSION = 19;
private static final int INTRODUCED_COLOR_PREFERENCE_VERSION = 20; private static final int INTRODUCED_COLOR_PREFERENCE_VERSION = 20;
private static final int INTRODUCED_DB_OPTIMIZATIONS_VERSION = 21; private static final int INTRODUCED_DB_OPTIMIZATIONS_VERSION = 21;
private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22; private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22;
private static final int DATABASE_VERSION = 22; private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23;
private static final int DATABASE_VERSION = 23;
private static final String DATABASE_NAME = "messages.db"; private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object(); private static final Object lock = new Object();
@ -773,6 +774,10 @@ public class DatabaseFactory {
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN seen_invite_reminder INTEGER DEFAULT 0"); db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN seen_invite_reminder INTEGER DEFAULT 0");
} }
if (oldVersion < INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION) {
db.execSQL("ALTER TABLE thread ADD COLUMN snippet_uri TEXT DEFAULT NULL");
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
} }

@ -5,6 +5,8 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
@ -136,7 +138,7 @@ public class DraftDatabase extends Database {
public static class Drafts extends LinkedList<Draft> { public static class Drafts extends LinkedList<Draft> {
private Draft getDraftOfType(String type) { private Draft getDraftOfType(String type) {
for (Draft draft : this) { for (Draft draft : this) {
if (Draft.TEXT.equals(draft.getType())) { if (type.equals(draft.getType())) {
return draft; return draft;
} }
} }
@ -153,5 +155,15 @@ public class DraftDatabase extends Database {
return ""; return "";
} }
} }
public @Nullable Uri getUriSnippet(Context context) {
Draft imageDraft = getDraftOfType(Draft.IMAGE);
if (imageDraft != null && imageDraft.getValue() != null) {
return Uri.parse(imageDraft.getValue());
}
return null;
}
} }
} }

@ -791,14 +791,14 @@ public class MmsDatabase extends MessagingDatabase {
addressDatabase.insertAddressesForId(messageId, addresses); addressDatabase.insertAddressesForId(messageId, addresses);
partsDatabase.insertAttachmentsForMessage(masterSecret, messageId, attachments); partsDatabase.insertAttachmentsForMessage(masterSecret, messageId, attachments);
notifyConversationListeners(contentValues.getAsLong(THREAD_ID));
DatabaseFactory.getThreadDatabase(context).update(contentValues.getAsLong(THREAD_ID));
db.setTransactionSuccessful(); db.setTransactionSuccessful();
return messageId; return messageId;
} finally { } finally {
db.endTransaction(); db.endTransaction();
}
notifyConversationListeners(contentValues.getAsLong(THREAD_ID));
DatabaseFactory.getThreadDatabase(context).update(contentValues.getAsLong(THREAD_ID));
}
} }
public boolean delete(long messageId) { public boolean delete(long messageId) {

@ -22,6 +22,7 @@ import android.database.Cursor;
import android.database.MergeCursor; import android.database.MergeCursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -29,8 +30,11 @@ import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.database.model.DisplayRecord; import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
@ -45,6 +49,8 @@ import java.util.Set;
public class ThreadDatabase extends Database { public class ThreadDatabase extends Database {
private static final String TAG = ThreadDatabase.class.getSimpleName();
static final String TABLE_NAME = "thread"; static final String TABLE_NAME = "thread";
public static final String ID = "_id"; public static final String ID = "_id";
public static final String DATE = "date"; public static final String DATE = "date";
@ -55,14 +61,14 @@ public class ThreadDatabase extends Database {
public static final String READ = "read"; public static final String READ = "read";
private static final String TYPE = "type"; private static final String TYPE = "type";
private static final String ERROR = "error"; private static final String ERROR = "error";
private static final String HAS_ATTACHMENT = "has_attachment";
public static final String SNIPPET_TYPE = "snippet_type"; public static final String SNIPPET_TYPE = "snippet_type";
private static final String SNIPPET_URI = "snippet_uri";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " +
RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " +
READ + " INTEGER DEFAULT 1, " + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0);"; SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");", "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");",
@ -73,7 +79,7 @@ public class ThreadDatabase extends Database {
} }
private long[] getRecipientIds(Recipients recipients) { private long[] getRecipientIds(Recipients recipients) {
Set<Long> recipientSet = new HashSet<Long>(); Set<Long> recipientSet = new HashSet<>();
List<Recipient> recipientList = recipients.getRecipientsList(); List<Recipient> recipientList = recipients.getRecipientsList();
for (Recipient recipient : recipientList) { for (Recipient recipient : recipientList) {
@ -118,12 +124,13 @@ public class ThreadDatabase extends Database {
return db.insert(TABLE_NAME, null, contentValues); return db.insert(TABLE_NAME, null, contentValues);
} }
private void updateThread(long threadId, long count, String body, long date, long type) private void updateThread(long threadId, long count, String body, @Nullable Uri attachment, long date, long type)
{ {
ContentValues contentValues = new ContentValues(4); ContentValues contentValues = new ContentValues(4);
contentValues.put(DATE, date - date % 1000); contentValues.put(DATE, date - date % 1000);
contentValues.put(MESSAGE_COUNT, count); contentValues.put(MESSAGE_COUNT, count);
contentValues.put(SNIPPET, body); contentValues.put(SNIPPET, body);
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
contentValues.put(SNIPPET_TYPE, type); contentValues.put(SNIPPET_TYPE, type);
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
@ -131,12 +138,13 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners(); notifyConversationListListeners();
} }
public void updateSnippet(long threadId, String snippet, long date, long type) { public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type) {
ContentValues contentValues = new ContentValues(3); ContentValues contentValues = new ContentValues(3);
contentValues.put(DATE, date - date % 1000); contentValues.put(DATE, date - date % 1000);
contentValues.put(SNIPPET, snippet); contentValues.put(SNIPPET, snippet);
contentValues.put(SNIPPET_TYPE, type); contentValues.put(SNIPPET_TYPE, type);
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
notifyConversationListListeners(); notifyConversationListListeners();
@ -144,7 +152,7 @@ public class ThreadDatabase extends Database {
private void deleteThread(long threadId) { private void deleteThread(long threadId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.delete(TABLE_NAME, ID_WHERE, new String[] {threadId+""}); db.delete(TABLE_NAME, ID_WHERE, new String[] {threadId + ""});
notifyConversationListListeners(); notifyConversationListListeners();
} }
@ -247,7 +255,7 @@ public class ThreadDatabase extends Database {
contentValues.put(READ, 0); contentValues.put(READ, 0);
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""}); db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""});
notifyConversationListListeners(); notifyConversationListListeners();
} }
@ -256,7 +264,7 @@ public class ThreadDatabase extends Database {
contentValues.put(TYPE, distributionType); contentValues.put(TYPE, distributionType);
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""}); db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""});
notifyConversationListListeners(); notifyConversationListListeners();
} }
@ -413,7 +421,7 @@ public class ThreadDatabase extends Database {
if (record.isPush()) timestamp = record.getDateSent(); if (record.isPush()) timestamp = record.getDateSent();
else timestamp = record.getDateReceived(); else timestamp = record.getDateReceived();
updateThread(threadId, count, record.getBody().getBody(), timestamp, record.getType()); updateThread(threadId, count, record.getBody().getBody(), getAttachmentUriFor(record), timestamp, record.getType());
notifyConversationListListeners(); notifyConversationListListeners();
return false; return false;
} else { } else {
@ -427,6 +435,15 @@ public class ThreadDatabase extends Database {
} }
} }
private @Nullable Uri getAttachmentUriFor(MessageRecord record) {
if (!record.isMms() || record.isMmsNotification()) return null;
SlideDeck slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck();
Slide thumbnail = slideDeck.getThumbnailSlide();
return thumbnail != null ? thumbnail.getThumbnailUri() : null;
}
public static interface ProgressListener { public static interface ProgressListener {
public void onProgress(int complete, int total); public void onProgress(int complete, int total);
} }
@ -459,9 +476,9 @@ public class ThreadDatabase extends Database {
} }
public ThreadRecord getCurrent() { public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
String recipientId = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_IDS)); String recipientId = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_IDS));
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientId, true); Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientId, true);
DisplayRecord.Body body = getPlaintextBody(cursor); DisplayRecord.Body body = getPlaintextBody(cursor);
long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
@ -469,8 +486,9 @@ public class ThreadDatabase extends Database {
long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ)); long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE)); long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE)); int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
Uri snippetUri = getSnippetUri(cursor);
return new ThreadRecord(context, body, recipients, date, count, return new ThreadRecord(context, body, snippetUri, recipients, date, count,
read == 1, threadId, type, distributionType); read == 1, threadId, type, distributionType);
} }
@ -492,6 +510,19 @@ public class ThreadDatabase extends Database {
} }
} }
private @Nullable Uri getSnippetUri(Cursor cursor) {
if (cursor.isNull(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI))) {
return null;
}
try {
return Uri.parse(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI)));
} catch (IllegalArgumentException e) {
Log.w(TAG, e);
return null;
}
}
public void close() { public void close() {
cursor.close(); cursor.close();
} }

@ -17,6 +17,9 @@
package org.thoughtcrime.securesms.database.model; package org.thoughtcrime.securesms.database.model;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
@ -36,22 +39,28 @@ import org.thoughtcrime.securesms.util.GroupUtil;
*/ */
public class ThreadRecord extends DisplayRecord { public class ThreadRecord extends DisplayRecord {
private final Context context; private @NonNull final Context context;
private final long count; private @Nullable final Uri snippetUri;
private final boolean read; private final long count;
private final int distributionType; private final boolean read;
private final int distributionType;
public ThreadRecord(Context context, Body body, Recipients recipients, long date, public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri,
long count, boolean read, long threadId, long snippetType, @NonNull Recipients recipients, long date, long count, boolean read,
int distributionType) long threadId, long snippetType, int distributionType)
{ {
super(context, body, recipients, date, date, threadId, snippetType); super(context, body, recipients, date, date, threadId, snippetType);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.snippetUri = snippetUri;
this.count = count; this.count = count;
this.read = read; this.read = read;
this.distributionType = distributionType; this.distributionType = distributionType;
} }
public @Nullable Uri getSnippetUri() {
return snippetUri;
}
@Override @Override
public SpannableString getDisplayBody() { public SpannableString getDisplayBody() {
if (SmsDatabase.Types.isDecryptInProgressType(type)) { if (SmsDatabase.Types.isDecryptInProgressType(type)) {
@ -83,7 +92,7 @@ public class ThreadRecord extends DisplayRecord {
return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call)); return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call));
} else { } else {
if (TextUtils.isEmpty(getBody().getBody())) { if (TextUtils.isEmpty(getBody().getBody())) {
return new SpannableString(context.getString(R.string.MessageNotifier_no_subject)); return new SpannableString(emphasisAdded(context.getString(R.string.ThreadRecord_media_message)));
} else { } else {
return new SpannableString(getBody().getBody()); return new SpannableString(getBody().getBody());
} }

Loading…
Cancel
Save