diff --git a/res/drawable-xxhdpi/last_seen_background.9.png b/res/drawable-xxhdpi/last_seen_background.9.png new file mode 100644 index 0000000000..e84de6dd3d Binary files /dev/null and b/res/drawable-xxhdpi/last_seen_background.9.png differ diff --git a/res/drawable/last_seen_divider_text_background.xml b/res/drawable/last_seen_divider_text_background.xml new file mode 100644 index 0000000000..8814e2a0a5 --- /dev/null +++ b/res/drawable/last_seen_divider_text_background.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/res/layout/conversation_item_last_seen.xml b/res/layout/conversation_item_last_seen.xml new file mode 100644 index 0000000000..b125457b2b --- /dev/null +++ b/res/layout/conversation_item_last_seen.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 22664f52b1..ca039d5cc8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -146,6 +146,12 @@ Error sending voice message There is no app available to handle this link on your device. + + + %d unread message + %d unread messages + + Message details Transport: %1$s\nSent/Received: %2$s @@ -1310,7 +1316,6 @@ Transport icon - diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 76be321e8f..6c252aabfa 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -184,6 +184,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public static final String TEXT_EXTRA = "draft_text"; public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type"; public static final String TIMING_EXTRA = "timing"; + public static final String LAST_SEEN_EXTRA = "last_seen"; private static final int PICK_IMAGE = 1; private static final int PICK_VIDEO = 2; @@ -321,6 +322,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity quickAttachmentDrawer.onPause(); inputPanel.onPause(); + fragment.setLastSeen(System.currentTimeMillis()); + markLastSeen(); AudioSlidePlayer.stopAll(); } @@ -541,9 +544,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipients(), System.currentTimeMillis(), expirationTime * 1000); MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false); - invalidateOptionsMenu(); return null; } + + @Override + protected void onPostExecute(Void result) { + invalidateOptionsMenu(); + if (fragment != null) fragment.setLastSeen(0); + } }.execute(); } }); @@ -1421,6 +1429,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity }.execute(threadId); } + private void markLastSeen() { + new AsyncTask() { + @Override + protected Void doInBackground(Long... params) { + DatabaseFactory.getThreadDatabase(ConversationActivity.this).setLastSeen(params[0]); + return null; + } + }.execute(threadId); + } + protected void sendComplete(long threadId) { boolean refreshFragment = (threadId != this.threadId); this.threadId = threadId; @@ -1429,6 +1447,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity return; } + fragment.setLastSeen(0); + if (refreshFragment) { fragment.reload(recipients, threadId); MessageNotifier.setVisibleThread(threadId); diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java index d68ecc17a0..a048c35dad 100644 --- a/src/org/thoughtcrime/securesms/ConversationAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java @@ -18,6 +18,8 @@ package org.thoughtcrime.securesms; import android.content.Context; import android.database.Cursor; +import android.graphics.Canvas; +import android.graphics.Rect; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -104,15 +106,15 @@ public class ConversationAdapter } - protected static class HeaderViewHolder extends RecyclerView.ViewHolder { - protected TextView textView; + static class HeaderViewHolder extends RecyclerView.ViewHolder { + TextView textView; - public HeaderViewHolder(View itemView) { + HeaderViewHolder(View itemView) { super(itemView); textView = ViewUtil.findById(itemView, R.id.text); } - public HeaderViewHolder(TextView textView) { + HeaderViewHolder(TextView textView) { super(textView); this.textView = textView; } @@ -263,6 +265,24 @@ public class ConversationAdapter getCursor().close(); } + public int findLastSeenPosition(long lastSeen) { + if (lastSeen <= 0) return -1; + if (!isActiveCursor()) return -1; + + int count = getItemCount(); + + for (int i=0;i return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR)); } + public long getTimestamp(int position) { + if (!isActiveCursor()) return 0; + if (isHeaderPosition(position)) return 0; + if (isFooterPosition(position)) return 0; + if (position >= getItemCount()) return 0; + if (position < 0) return 0; + + Cursor cursor = getCursorAtPositionOrThrow(position); + MessageRecord messageRecord = getMessageRecord(cursor); + + return messageRecord.getTimestamp(); + } + @Override public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_header, parent, false)); } + public HeaderViewHolder onCreateLastSeenViewHolder(ViewGroup parent) { + return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_last_seen, parent, false)); + } + @Override public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) { Cursor cursor = getCursorAtPositionOrThrow(position); viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, getMessageRecord(cursor).getDateReceived())); } + + public void onBindLastSeenViewHolder(HeaderViewHolder viewHolder, int position) { + viewHolder.setText(getContext().getResources().getQuantityString(R.plurals.ConversationAdapter_n_unread_messages, (position + 1), (position + 1))); + } + + public static class LastSeenHeader extends StickyHeaderDecoration { + + private final ConversationAdapter adapter; + private final long lastSeenTimestamp; + + public LastSeenHeader(ConversationAdapter adapter, long lastSeenTimestamp) { + super(adapter, false, false); + this.adapter = adapter; + this.lastSeenTimestamp = lastSeenTimestamp; + } + + @Override + protected boolean hasHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) { + if (!adapter.isActiveCursor()) { + return false; + } + + if (lastSeenTimestamp <= 0) { + return false; + } + + long currentRecordTimestamp = adapter.getTimestamp(position); + long previousRecordTimestamp = adapter.getTimestamp(position + 1); + + return currentRecordTimestamp > lastSeenTimestamp && previousRecordTimestamp < lastSeenTimestamp; + } + + @Override + protected HeaderViewHolder getHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) { + HeaderViewHolder viewHolder = adapter.onCreateLastSeenViewHolder(parent); + adapter.onBindLastSeenViewHolder(viewHolder, position); + + int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); + + int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), viewHolder.itemView.getLayoutParams().width); + int childHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), viewHolder.itemView.getLayoutParams().height); + + viewHolder.itemView.measure(childWidth, childHeight); + viewHolder.itemView.layout(0, 0, viewHolder.itemView.getMeasuredWidth(), viewHolder.itemView.getMeasuredHeight()); + + return viewHolder; + } + } + } diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java index e840a4bc68..a27a739e23 100644 --- a/src/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationFragment.java @@ -87,16 +87,19 @@ public class ConversationFragment extends Fragment private ConversationFragmentListener listener; - private MasterSecret masterSecret; - private Recipients recipients; - private long threadId; - private ActionMode actionMode; - private Locale locale; - private RecyclerView list; - private View loadMoreView; - private View composeDivider; - private View scrollToBottomButton; - private TextView scrollDateHeader; + private MasterSecret masterSecret; + private Recipients recipients; + private long threadId; + private long lastSeen; + private boolean firstLoad; + private ActionMode actionMode; + private Locale locale; + private RecyclerView list; + private RecyclerView.ItemDecoration lastSeenDecoration; + private View loadMoreView; + private View composeDivider; + private View scrollToBottomButton; + private TextView scrollDateHeader; @Override public void onCreate(Bundle icicle) { @@ -180,6 +183,8 @@ public class ConversationFragment extends Fragment private void initializeResources() { this.recipients = RecipientFactory.getRecipientsForIds(getActivity(), getActivity().getIntent().getLongArrayExtra("recipients"), true); this.threadId = this.getActivity().getIntent().getLongExtra("thread_id", -1); + this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1); + this.firstLoad = true; OnScrollListener scrollListener = new ConversationScrollListener(getActivity()); list.addOnScrollListener(scrollListener); @@ -191,6 +196,7 @@ public class ConversationFragment extends Fragment list.setAdapter(adapter); list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false)); + setLastSeen(lastSeen); getLoaderManager().restartLoader(0, Bundle.EMPTY, this); } } @@ -259,6 +265,16 @@ public class ConversationFragment extends Fragment list.smoothScrollToPosition(0); } + public void setLastSeen(long lastSeen) { + this.lastSeen = lastSeen; + if (lastSeenDecoration != null) { + list.removeItemDecoration(lastSeenDecoration); + } + + lastSeenDecoration = new ConversationAdapter.LastSeenHeader(getListAdapter(), lastSeen); + list.addItemDecoration(lastSeenDecoration); + } + private void handleCopyMessage(final Set messageRecords) { List messageList = new LinkedList<>(messageRecords); Collections.sort(messageList, new Comparator() { @@ -389,18 +405,31 @@ public class ConversationFragment extends Fragment @Override public Loader onCreateLoader(int id, Bundle args) { - return new ConversationLoader(getActivity(), threadId, args.getLong("limit", PARTIAL_CONVERSATION_LIMIT)); + return new ConversationLoader(getActivity(), threadId, args.getLong("limit", PARTIAL_CONVERSATION_LIMIT), lastSeen); } + @Override - public void onLoadFinished(Loader loader, Cursor cursor) { + public void onLoadFinished(Loader cursorLoader, Cursor cursor) { + ConversationLoader loader = (ConversationLoader)cursorLoader; + if (list.getAdapter() != null) { - if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && ((ConversationLoader)loader).hasLimit()) { + if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) { getListAdapter().setFooterView(loadMoreView); } else { getListAdapter().setFooterView(null); } + + if (lastSeen == -1) { + setLastSeen(loader.getLastSeen()); + } + getListAdapter().changeCursor(cursor); + + if (firstLoad) { + scrollToLastSeenPosition(lastSeen); + firstLoad = false; + } } } @@ -411,6 +440,16 @@ public class ConversationFragment extends Fragment } } + private void scrollToLastSeenPosition(long lastSeen) { + int lastSeenPosition = getListAdapter().findLastSeenPosition(lastSeen); + + if (lastSeenPosition > 0) { + ((LinearLayoutManager)list.getLayoutManager()).scrollToPositionWithOffset(lastSeenPosition, list.getHeight()); + } else { + setLastSeen(0); + } + } + public interface ConversationFragmentListener { void setThreadId(long threadId); } diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 2533d61853..128bbaae77 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -161,12 +161,13 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit } @Override - public void onCreateConversation(long threadId, Recipients recipients, int distributionType) { + public void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeen) { Intent intent = new Intent(this, ConversationActivity.class); intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType); intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis()); + intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, lastSeen); startActivity(intent); overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out); diff --git a/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java b/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java index ff3361657f..37bd2d70cf 100644 --- a/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java @@ -54,12 +54,13 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar } @Override - public void onCreateConversation(long threadId, Recipients recipients, int distributionType) { + public void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeenTime) { Intent intent = new Intent(this, ConversationActivity.class); intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.IS_ARCHIVED_EXTRA, true); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType); + intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, lastSeenTime); startActivity(intent); overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out); diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java index d39bb88a2f..44938282a2 100644 --- a/src/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java @@ -307,8 +307,8 @@ public class ConversationListFragment extends Fragment getListAdapter().getBatchSelections().size())); } - private void handleCreateConversation(long threadId, Recipients recipients, int distributionType) { - ((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipients, distributionType); + private void handleCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeen) { + ((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipients, distributionType, lastSeen); } @Override @@ -330,7 +330,7 @@ public class ConversationListFragment extends Fragment public void onItemClick(ConversationListItem item) { if (actionMode == null) { handleCreateConversation(item.getThreadId(), item.getRecipients(), - item.getDistributionType()); + item.getDistributionType(), item.getLastSeen()); } else { ConversationListAdapter adapter = (ConversationListAdapter)list.getAdapter(); adapter.toggleThreadInBatchSet(item.getThreadId()); @@ -361,7 +361,7 @@ public class ConversationListFragment extends Fragment } public interface ConversationSelectedListener { - void onCreateConversation(long threadId, Recipients recipients, int distributionType); + void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeen); void onSwitchToArchive(); } diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java index ab7316e810..ed231c7c77 100644 --- a/src/org/thoughtcrime/securesms/ConversationListItem.java +++ b/src/org/thoughtcrime/securesms/ConversationListItem.java @@ -73,6 +73,7 @@ public class ConversationListItem extends RelativeLayout private TextView archivedView; private DeliveryStatusView deliveryStatusIndicator; private AlertView alertView; + private long lastSeen; private boolean read; private AvatarImageView contactPhotoImage; @@ -119,6 +120,7 @@ public class ConversationListItem extends RelativeLayout this.threadId = thread.getThreadId(); this.read = thread.isRead(); this.distributionType = thread.getDistributionType(); + this.lastSeen = thread.getLastSeen(); this.recipients.addListener(this); this.fromView.setText(recipients, read); @@ -171,6 +173,10 @@ public class ConversationListItem extends RelativeLayout return distributionType; } + public long getLastSeen() { + return lastSeen; + } + private void setThumbnailSnippet(MasterSecret masterSecret, ThreadRecord thread) { if (thread.getSnippetUri() != null) { this.thumbnailView.setVisibility(View.VISIBLE); diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index 42c9a7f26d..050123f876 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -73,7 +73,8 @@ public class DatabaseFactory { private static final int MIGRATED_CONVERSATION_LIST_STATUS_VERSION = 26; private static final int INTRODUCED_SUBSCRIPTION_ID_VERSION = 27; private static final int INTRODUCED_EXPIRE_MESSAGES_VERSION = 28; - private static final int DATABASE_VERSION = 28; + private static final int INTRODUCED_LAST_SEEN = 29; + private static final int DATABASE_VERSION = 29; private static final String DATABASE_NAME = "messages.db"; private static final Object lock = new Object(); @@ -830,6 +831,10 @@ public class DatabaseFactory { db.execSQL("ALTER TABLE thread ADD COLUMN expires_in INTEGER DEFAULT 0"); } + if (oldVersion < INTRODUCED_LAST_SEEN) { + db.execSQL("ALTER TABLE thread ADD COLUMN last_seen INTEGER DEFAULT 0"); + } + db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java index c3ba4e88bd..0f16866b0f 100644 --- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -69,6 +69,7 @@ public class ThreadDatabase extends Database { public static final String STATUS = "status"; public static final String RECEIPT_COUNT = "delivery_receipt_count"; public static final String EXPIRES_IN = "expires_in"; + public static final String LAST_SEEN = "last_seen"; public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + @@ -77,7 +78,8 @@ public class ThreadDatabase extends Database { TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " + - RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0);"; + RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + + LAST_SEEN + " INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");", @@ -391,6 +393,31 @@ public class ThreadDatabase extends Database { notifyConversationListListeners(); } + public void setLastSeen(long threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(1); + contentValues.put(LAST_SEEN, System.currentTimeMillis()); + + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)}); + notifyConversationListListeners(); + } + + public long getLastSeen(long threadId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{LAST_SEEN}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); + + try { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(0); + } + + return -1; + } finally { + if (cursor != null) cursor.close(); + } + + } + public void deleteConversation(long threadId) { DatabaseFactory.getSmsDatabase(context).deleteThread(threadId); DatabaseFactory.getMmsDatabase(context).deleteThread(threadId); @@ -582,11 +609,12 @@ public class ThreadDatabase extends Database { int status = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.STATUS)); int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.RECEIPT_COUNT)); long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.EXPIRES_IN)); + long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN)); Uri snippetUri = getSnippetUri(cursor); return new ThreadRecord(context, body, snippetUri, recipients, date, count, read == 1, threadId, receiptCount, status, type, distributionType, archived, - expiresIn); + expiresIn, lastSeen); } private DisplayRecord.Body getPlaintextBody(Cursor cursor) { diff --git a/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java b/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java index 292ff3e6f1..21cace94e3 100644 --- a/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java +++ b/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java @@ -9,19 +9,29 @@ import org.thoughtcrime.securesms.util.AbstractCursorLoader; public class ConversationLoader extends AbstractCursorLoader { private final long threadId; private long limit; + private long lastSeen; - public ConversationLoader(Context context, long threadId, long limit) { + public ConversationLoader(Context context, long threadId, long limit, long lastSeen) { super(context); this.threadId = threadId; - this.limit = limit; + this.limit = limit; + this.lastSeen = lastSeen; } public boolean hasLimit() { return limit > 0; } + public long getLastSeen() { + return lastSeen; + } + @Override public Cursor getCursor() { + if (lastSeen == -1) { + this.lastSeen = DatabaseFactory.getThreadDatabase(context).getLastSeen(threadId); + } + return DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId, limit); } } diff --git a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java index 21dfb0004a..ab766227c4 100644 --- a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -48,11 +48,12 @@ public class ThreadRecord extends DisplayRecord { private final int distributionType; private final boolean archived; private final long expiresIn; + private final long lastSeen; public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri, @NonNull Recipients recipients, long date, long count, boolean read, long threadId, int receiptCount, int status, long snippetType, - int distributionType, boolean archived, long expiresIn) + int distributionType, boolean archived, long expiresIn, long lastSeen) { super(context, body, recipients, date, date, threadId, status, receiptCount, snippetType); this.context = context.getApplicationContext(); @@ -62,6 +63,7 @@ public class ThreadRecord extends DisplayRecord { this.distributionType = distributionType; this.archived = archived; this.expiresIn = expiresIn; + this.lastSeen = lastSeen; } public @Nullable Uri getSnippetUri() { @@ -147,4 +149,8 @@ public class ThreadRecord extends DisplayRecord { public long getExpiresIn() { return expiresIn; } + + public long getLastSeen() { + return lastSeen; + } } diff --git a/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java b/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java index 6c1005bf29..d1691e6b54 100644 --- a/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java @@ -62,6 +62,7 @@ public class AndroidAutoHeardReceiver extends MasterSecretBroadcastReceiver { for (long threadId : threadIds) { Log.i(TAG, "Marking meassage as read: " + threadId); List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId); + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); messageIdsCollection.addAll(messageIds); } diff --git a/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java index 5169b857ed..81d3c46b00 100644 --- a/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java @@ -91,6 +91,8 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver { } List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(replyThreadId); + DatabaseFactory.getThreadDatabase(context).setLastSeen(replyThreadId); + MessageNotifier.updateNotification(context, masterSecret); MarkReadReceiver.process(context, messageIds); diff --git a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java index 67700ebc40..1314cc4b22 100644 --- a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java @@ -47,6 +47,8 @@ public class MarkReadReceiver extends MasterSecretBroadcastReceiver { Log.w(TAG, "Marking as read: " + threadId); List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId); messageIdsCollection.addAll(messageIds); + + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); } process(context, messageIdsCollection); diff --git a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java index 8efba0c74d..74402483dc 100644 --- a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java @@ -81,6 +81,8 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver { } List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId); + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + MessageNotifier.updateNotification(context, masterSecret); MarkReadReceiver.process(context, messageIds); diff --git a/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java b/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java index 2f3e729ddc..573cbacabb 100644 --- a/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java +++ b/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java @@ -57,7 +57,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration { outRect.set(0, headerHeight, 0, 0); } - private boolean hasHeader(RecyclerView parent, StickyHeaderAdapter adapter, int adapterPos) { + protected boolean hasHeader(RecyclerView parent, StickyHeaderAdapter adapter, int adapterPos) { boolean isReverse = isReverseLayout(parent); int itemCount = ((RecyclerView.Adapter)adapter).getItemCount(); @@ -74,7 +74,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration { return headerId != NO_HEADER_ID && previousHeaderId != NO_HEADER_ID && headerId != previousHeaderId; } - private ViewHolder getHeader(RecyclerView parent, StickyHeaderAdapter adapter, int position) { + protected ViewHolder getHeader(RecyclerView parent, StickyHeaderAdapter adapter, int position) { final long key = adapter.getHeaderId(position); if (headerCache.containsKey(key)) {