From 534dec282f8f9326f5ccae378ec04940129d310c Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Mon, 13 Nov 2017 18:01:05 -0800 Subject: [PATCH] Brighten light theme 1) Brighten background color 2) Add unread indicator in conversation list 3) Eliminate some conversation list overdraw --- ... => conversation_list_item_background.xml} | 1 - ...onversation_list_item_background_dark.xml} | 1 - ...nversation_list_item_unread_background.xml | 11 --- ...ation_list_item_unread_background_dark.xml | 11 --- ... => conversation_list_item_background.xml} | 1 - ...onversation_list_item_background_dark.xml} | 1 - ...nversation_list_item_unread_background.xml | 7 -- ...ation_list_item_unread_background_dark.xml | 7 -- res/layout/conversation_activity.xml | 12 ++-- res/layout/conversation_fragment.xml | 5 +- res/layout/conversation_input_panel.xml | 1 - res/layout/conversation_list_item_view.xml | 9 +++ res/values/attrs.xml | 5 +- res/values/colors.xml | 8 --- res/values/themes.xml | 16 ++--- .../securesms/ConversationActivity.java | 6 ++ .../securesms/ConversationItem.java | 4 +- .../securesms/ConversationListActivity.java | 17 ++--- .../securesms/ConversationListFragment.java | 14 ++-- .../securesms/ConversationListItem.java | 68 ++++++++++--------- .../thoughtcrime/securesms/ShareListItem.java | 2 +- .../securesms/database/DatabaseFactory.java | 30 +++++++- .../securesms/database/MmsDatabase.java | 4 +- .../securesms/database/SmsDatabase.java | 7 +- .../securesms/database/ThreadDatabase.java | 46 ++++++++----- .../database/model/ThreadRecord.java | 13 ++-- 26 files changed, 151 insertions(+), 156 deletions(-) rename res/drawable-v21/{conversation_list_item_read_background_dark.xml => conversation_list_item_background.xml} (82%) rename res/drawable-v21/{conversation_list_item_read_background.xml => conversation_list_item_background_dark.xml} (81%) delete mode 100644 res/drawable-v21/conversation_list_item_unread_background.xml delete mode 100644 res/drawable-v21/conversation_list_item_unread_background_dark.xml rename res/drawable/{conversation_list_item_read_background_dark.xml => conversation_list_item_background.xml} (83%) rename res/drawable/{conversation_list_item_read_background.xml => conversation_list_item_background_dark.xml} (82%) delete mode 100644 res/drawable/conversation_list_item_unread_background.xml delete mode 100644 res/drawable/conversation_list_item_unread_background_dark.xml diff --git a/res/drawable-v21/conversation_list_item_read_background_dark.xml b/res/drawable-v21/conversation_list_item_background.xml similarity index 82% rename from res/drawable-v21/conversation_list_item_read_background_dark.xml rename to res/drawable-v21/conversation_list_item_background.xml index 19f004e729..642879178e 100644 --- a/res/drawable-v21/conversation_list_item_read_background_dark.xml +++ b/res/drawable-v21/conversation_list_item_background.xml @@ -5,7 +5,6 @@ - diff --git a/res/drawable-v21/conversation_list_item_read_background.xml b/res/drawable-v21/conversation_list_item_background_dark.xml similarity index 81% rename from res/drawable-v21/conversation_list_item_read_background.xml rename to res/drawable-v21/conversation_list_item_background_dark.xml index 169f87c14b..642879178e 100644 --- a/res/drawable-v21/conversation_list_item_read_background.xml +++ b/res/drawable-v21/conversation_list_item_background_dark.xml @@ -5,7 +5,6 @@ - diff --git a/res/drawable-v21/conversation_list_item_unread_background.xml b/res/drawable-v21/conversation_list_item_unread_background.xml deleted file mode 100644 index 06c7a5f786..0000000000 --- a/res/drawable-v21/conversation_list_item_unread_background.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/res/drawable-v21/conversation_list_item_unread_background_dark.xml b/res/drawable-v21/conversation_list_item_unread_background_dark.xml deleted file mode 100644 index 0b33205dee..0000000000 --- a/res/drawable-v21/conversation_list_item_unread_background_dark.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/res/drawable/conversation_list_item_read_background_dark.xml b/res/drawable/conversation_list_item_background.xml similarity index 83% rename from res/drawable/conversation_list_item_read_background_dark.xml rename to res/drawable/conversation_list_item_background.xml index 237d729853..92908acc6a 100644 --- a/res/drawable/conversation_list_item_read_background_dark.xml +++ b/res/drawable/conversation_list_item_background.xml @@ -3,5 +3,4 @@ - diff --git a/res/drawable/conversation_list_item_read_background.xml b/res/drawable/conversation_list_item_background_dark.xml similarity index 82% rename from res/drawable/conversation_list_item_read_background.xml rename to res/drawable/conversation_list_item_background_dark.xml index 8ad9fae358..92908acc6a 100644 --- a/res/drawable/conversation_list_item_read_background.xml +++ b/res/drawable/conversation_list_item_background_dark.xml @@ -3,5 +3,4 @@ - diff --git a/res/drawable/conversation_list_item_unread_background.xml b/res/drawable/conversation_list_item_unread_background.xml deleted file mode 100644 index 2f11d8cc99..0000000000 --- a/res/drawable/conversation_list_item_unread_background.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/res/drawable/conversation_list_item_unread_background_dark.xml b/res/drawable/conversation_list_item_unread_background_dark.xml deleted file mode 100644 index bf0b92d290..0000000000 --- a/res/drawable/conversation_list_item_unread_background_dark.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml index be65a5ce12..dc9dc014e6 100644 --- a/res/layout/conversation_activity.xml +++ b/res/layout/conversation_activity.xml @@ -1,12 +1,12 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/layout_container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + android:layout_height="match_parent"> diff --git a/res/layout/conversation_list_item_view.xml b/res/layout/conversation_list_item_view.xml index 6e74c90326..b475f7af6d 100644 --- a/res/layout/conversation_list_item_view.xml +++ b/res/layout/conversation_list_item_view.xml @@ -3,6 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" + android:background="?attr/conversation_list_item_background" android:layout_width="match_parent" android:focusable="true" android:nextFocusRight="@+id/fab" @@ -144,5 +145,13 @@ android:layout_alignWithParentIfMissing="true" app:iconColor="?attr/conversation_list_item_subject_color"/> + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 689384eb8f..2d8cb341b2 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -2,10 +2,7 @@ - - - - + diff --git a/res/values/colors.xml b/res/values/colors.xml index 6ce9a15bdd..601fb1cfc1 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -27,14 +27,6 @@ #32000000 - @color/gray5 - #ffffffff - #ff000000 - #ff333333 - - - - #ff33b5e5 #ff111111 diff --git a/res/values/themes.xml b/res/values/themes.xml index dfc50988c7..45ad3fe0dc 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -110,15 +110,12 @@ @color/textsecure_primary_dark @color/signal_primary @color/signal_primary - @color/gray5 + @color/white @style/AppCompatAlertDialogStyleLight @style/AppCompatDialogStyleLight @color/white - @drawable/list_selected_holo_light - @drawable/conversation_list_item_unread_background - @drawable/conversation_list_item_read_background - #66333333 + @drawable/conversation_list_item_background #FF333333 #FF444444 #ff999999 @@ -129,7 +126,7 @@ #99000000 - #eeeeee + @color/gray5 #22000000 #ff111111 @drawable/ic_send_sms_insecure @@ -240,10 +237,7 @@ @color/black @style/AppCompatAlertDialogStyleDark @style/AppCompatDialogStyleDark - @drawable/list_selected_holo_dark - @drawable/conversation_list_item_unread_background_dark - @drawable/conversation_list_item_read_background_dark - #66dddddd + @drawable/conversation_list_item_background_dark #ffdddddd #ffdddddd #ffdddddd @@ -277,7 +271,7 @@ @color/textsecure_primary_dark @drawable/divet_lower_right_light - #22ffffff + @color/black #22ffffff #ffeeeeee @drawable/ic_send_sms_insecure_dark diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index b711fc5e7a..b1b2be7c2a 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -265,6 +265,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY); setContentView(R.layout.conversation_activity); + TypedArray typedArray = obtainStyledAttributes(new int[] {R.attr.conversation_background}); + int color = typedArray.getColor(0, Color.WHITE); + typedArray.recycle(); + + getWindow().getDecorView().setBackgroundColor(color); + fragment = initFragment(R.id.fragment_content, new ConversationFragment(), masterSecret, dynamicLanguage.getCurrentLocale()); diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index 14e439acd8..38561718ba 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -240,9 +240,7 @@ public class ConversationItem extends LinearLayout } private void initializeAttributes() { - final int[] attributes = new int[] {R.attr.conversation_item_bubble_background, - R.attr.conversation_list_item_background_selected, - R.attr.conversation_item_background}; + final int[] attributes = new int[] {R.attr.conversation_item_bubble_background}; final TypedArray attrs = context.obtainStyledAttributes(attributes); defaultBubbleColor = attrs.getColor(0, Color.WHITE); diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index b2dff8044e..e7cbaf6e80 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2014 Open Whisper Systems +/* + * Copyright (C) 2014-2017 Open Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,6 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.View; import android.widget.ImageView; import android.widget.Toast; @@ -117,9 +116,8 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit } private void initializeSearchListener() { - searchAction.setOnClickListener(v -> { - searchToolbar.display(searchAction.getX() + (searchAction.getWidth() / 2), searchAction.getY() + (searchAction.getHeight() / 2)); - }); + searchAction.setOnClickListener(v -> searchToolbar.display(searchAction.getX() + (searchAction.getWidth() / 2), + searchAction.getY() + (searchAction.getHeight() / 2))); searchToolbar.setListener(new SearchToolbar.SearchListener() { @Override @@ -235,12 +233,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit super.onChange(selfChange); Log.w(TAG, "Detected android contact data changed, refreshing cache"); Recipient.clearCache(ConversationListActivity.this); - ConversationListActivity.this.runOnUiThread(new Runnable() { - @Override - public void run() { - fragment.getListAdapter().notifyDataSetChanged(); - } - }); + ConversationListActivity.this.runOnUiThread(() -> fragment.getListAdapter().notifyDataSetChanged()); } }; diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java index fccbecb68d..e4675600e9 100644 --- a/src/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java @@ -130,7 +130,7 @@ public class ConversationListFragment extends Fragment TypedArray typedArray = getContext().obtainStyledAttributes(new int[]{R.attr.conversation_list_item_divider}); Drawable itemDrawable = typedArray.getDrawable(0); DividerItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), LinearLayoutManager.VERTICAL); - itemDecoration.setDrawable(itemDrawable); + if (itemDrawable != null) itemDecoration.setDrawable(itemDrawable); list.addItemDecoration(itemDecoration); typedArray.recycle(); @@ -472,8 +472,8 @@ public class ConversationListFragment extends Fragment @SuppressLint("StaticFieldLeak") @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { - final long threadId = ((ConversationListItem)viewHolder.itemView).getThreadId(); - final boolean read = ((ConversationListItem)viewHolder.itemView).getRead(); + final long threadId = ((ConversationListItem)viewHolder.itemView).getThreadId(); + final int unreadCount = ((ConversationListItem)viewHolder.itemView).getUnreadCount(); if (archive) { new SnackbarAsyncTask(getView(), @@ -503,7 +503,7 @@ public class ConversationListFragment extends Fragment protected void executeAction(@Nullable Long parameter) { DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); - if (!read) { + if (unreadCount > 0) { List messageIds = DatabaseFactory.getThreadDatabase(getActivity()).setRead(threadId, false); MessageNotifier.updateNotification(getActivity(), masterSecret); MarkReadReceiver.process(getActivity(), messageIds); @@ -514,8 +514,8 @@ public class ConversationListFragment extends Fragment protected void reverseAction(@Nullable Long parameter) { DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); - if (!read) { - DatabaseFactory.getThreadDatabase(getActivity()).setUnread(threadId); + if (unreadCount > 0) { + DatabaseFactory.getThreadDatabase(getActivity()).incrementUnread(threadId, unreadCount); MessageNotifier.updateNotification(getActivity(), masterSecret); } } @@ -560,7 +560,7 @@ public class ConversationListFragment extends Fragment } } } - + } diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java index 8cf9ea0c6d..cd1e28e901 100644 --- a/src/org/thoughtcrime/securesms/ConversationListItem.java +++ b/src/org/thoughtcrime/securesms/ConversationListItem.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2014-2017 Open Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,17 +19,20 @@ package org.thoughtcrime.securesms; import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; +import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.RippleDrawable; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; -import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.View; +import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import com.amulyakhare.textdrawable.TextDrawable; + import org.thoughtcrime.securesms.components.AlertView; import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.DeliveryStatusView; @@ -41,7 +44,6 @@ import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.ResUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; @@ -50,17 +52,11 @@ import java.util.Set; import static org.thoughtcrime.securesms.util.SpanUtil.color; -/** - * A view that displays the element in a list of multiple conversation threads. - * Used by SecureSMS's ListActivity via a ConversationListAdapter. - * - * @author Moxie Marlinspike - */ - public class ConversationListItem extends RelativeLayout implements RecipientModifiedListener, BindableConversationListItem, Unbindable { + @SuppressWarnings("unused") private final static String TAG = ConversationListItem.class.getSimpleName(); private final static Typeface BOLD_TYPEFACE = Typeface.create("sans-serif", Typeface.BOLD); @@ -76,15 +72,13 @@ public class ConversationListItem extends RelativeLayout private TextView archivedView; private DeliveryStatusView deliveryStatusIndicator; private AlertView alertView; + private ImageView unreadIndicator; private long lastSeen; - private boolean read; + private int unreadCount; private AvatarImageView contactPhotoImage; private ThumbnailView thumbnailView; - private final @DrawableRes int readBackground; - private final @DrawableRes int unreadBackround; - private int distributionType; public ConversationListItem(Context context) { @@ -93,8 +87,6 @@ public class ConversationListItem extends RelativeLayout public ConversationListItem(Context context, AttributeSet attrs) { super(context, attrs); - readBackground = ResUtil.getDrawableRes(context, R.attr.conversation_list_item_background_read); - unreadBackround = ResUtil.getDrawableRes(context, R.attr.conversation_list_item_background_unread); } @Override @@ -107,7 +99,8 @@ public class ConversationListItem extends RelativeLayout this.alertView = findViewById(R.id.indicators_parent); this.contactPhotoImage = findViewById(R.id.contact_photo_image); this.thumbnailView = findViewById(R.id.thumbnail); - this.archivedView = ViewUtil.findById(this, R.id.archived); + this.archivedView = findViewById(R.id.archived); + this.unreadIndicator = findViewById(R.id.unread_indicator); thumbnailView.setClickable(false); ViewUtil.setTextViewGravityStart(this.fromView, getContext()); @@ -123,20 +116,20 @@ public class ConversationListItem extends RelativeLayout this.recipient = thread.getRecipient(); this.threadId = thread.getThreadId(); this.glideRequests = glideRequests; - this.read = thread.isRead(); + this.unreadCount = thread.getUnreadCount(); this.distributionType = thread.getDistributionType(); this.lastSeen = thread.getLastSeen(); this.recipient.addListener(this); - this.fromView.setText(recipient, read); + this.fromView.setText(recipient, unreadCount == 0); this.subjectView.setText(thread.getDisplayBody()); - this.subjectView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE); +// this.subjectView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE); if (thread.getDate() > 0) { CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate()); - dateView.setText(read ? date : color(getResources().getColor(R.color.textsecure_primary), date)); - dateView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE); + dateView.setText(unreadCount == 0 ? date : color(getResources().getColor(R.color.textsecure_primary_dark), date)); + dateView.setTypeface(unreadCount == 0 ? LIGHT_TYPEFACE : BOLD_TYPEFACE); } if (thread.isArchived()) { @@ -148,8 +141,8 @@ public class ConversationListItem extends RelativeLayout setStatusIcons(thread); setThumbnailSnippet(masterSecret, thread); setBatchState(batchMode); - setBackground(thread); setRippleColor(recipient); + setUnreadIndicator(thread); this.contactPhotoImage.setAvatar(glideRequests, recipient, true); } @@ -170,8 +163,8 @@ public class ConversationListItem extends RelativeLayout return threadId; } - public boolean getRead() { - return read; + public int getUnreadCount() { + return unreadCount; } public int getDistributionType() { @@ -226,12 +219,6 @@ public class ConversationListItem extends RelativeLayout } } - private void setBackground(ThreadRecord thread) { - if (thread.isRead()) setBackgroundResource(readBackground); - else setBackgroundResource(unreadBackround); - } - - @TargetApi(VERSION_CODES.LOLLIPOP) private void setRippleColor(Recipient recipient) { if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ((RippleDrawable)(getBackground()).mutate()) @@ -239,10 +226,27 @@ public class ConversationListItem extends RelativeLayout } } + private void setUnreadIndicator(ThreadRecord thread) { + if (thread.isOutgoing() || thread.getUnreadCount() == 0) { + unreadIndicator.setVisibility(View.GONE); + return; + } + + unreadIndicator.setImageDrawable(TextDrawable.builder() + .beginConfig() + .width(ViewUtil.dpToPx(getContext(), 24)) + .height(ViewUtil.dpToPx(getContext(), 24)) + .textColor(Color.WHITE) + .bold() + .endConfig() + .buildRound(String.valueOf(thread.getUnreadCount()), getResources().getColor(R.color.textsecure_primary_dark))); + unreadIndicator.setVisibility(View.VISIBLE); + } + @Override public void onModified(final Recipient recipient) { Util.runOnMain(() -> { - fromView.setText(recipient, read); + fromView.setText(recipient, unreadCount == 0); contactPhotoImage.setAvatar(glideRequests, recipient, true); setRippleColor(recipient); }); diff --git a/src/org/thoughtcrime/securesms/ShareListItem.java b/src/org/thoughtcrime/securesms/ShareListItem.java index 97b577ffae..b5e9f46dee 100644 --- a/src/org/thoughtcrime/securesms/ShareListItem.java +++ b/src/org/thoughtcrime/securesms/ShareListItem.java @@ -85,7 +85,7 @@ public class ShareListItem extends RelativeLayout } private void setBackground() { - int[] attributes = new int[]{R.attr.conversation_list_item_background_read}; + int[] attributes = new int[]{R.attr.conversation_list_item_background}; TypedArray drawables = context.obtainStyledAttributes(attributes); setBackgroundDrawable(drawables.getDrawable(0)); diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index bb95a72733..01512adac6 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -109,7 +109,8 @@ public class DatabaseFactory { private static final int UNSEEN_NUMBER_OFFER = 43; private static final int READ_RECEIPTS = 44; private static final int GROUP_RECEIPT_TRACKING = 45; - private static final int DATABASE_VERSION = 45; + private static final int UNREAD_COUNT_VERSION = 46; + private static final int DATABASE_VERSION = 46; private static final String DATABASE_NAME = "messages.db"; private static final Object lock = new Object(); @@ -1346,6 +1347,33 @@ public class DatabaseFactory { db.execSQL("CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON group_receipts (mms_id)"); } + if (oldVersion < UNREAD_COUNT_VERSION) { + db.execSQL("ALTER TABLE thread ADD COLUMN unread_count INTEGER DEFAULT 0"); + + try (Cursor cursor = db.query("thread", new String[] {"_id"}, "read = 0", null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + long threadId = cursor.getLong(0); + int unreadCount = 0; + + try (Cursor smsCursor = db.rawQuery("SELECT COUNT(*) FROM sms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { + if (smsCursor != null && smsCursor.moveToFirst()) { + unreadCount += smsCursor.getInt(0); + } + } + + try (Cursor mmsCursor = db.rawQuery("SELECT COUNT(*) FROM mms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { + if (mmsCursor != null && mmsCursor.moveToFirst()) { + unreadCount += mmsCursor.getInt(0); + } + } + + db.execSQL("UPDATE thread SET unread_count = ? WHERE _id = ?", + new String[] {String.valueOf(unreadCount), + String.valueOf(threadId)}); + } + } + } + db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index 4a5ef4d68c..0ae9b62e33 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -677,7 +677,7 @@ public class MmsDatabase extends MessagingDatabase { long messageId = insertMediaMessage(masterSecret, retrieved.getBody(), retrieved.getAttachments(), contentValues, null); if (!Types.isExpirationTimerUpdate(mailbox)) { - DatabaseFactory.getThreadDatabase(context).setUnread(threadId); + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); DatabaseFactory.getThreadDatabase(context).update(threadId, true); } @@ -775,7 +775,7 @@ public class MmsDatabase extends MessagingDatabase { DatabaseFactory.getThreadDatabase(context).update(threadId, true); if (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context)) { - DatabaseFactory.getThreadDatabase(context).setUnread(threadId); + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); } jobManager.add(new TrimThreadJob(context, threadId)); diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index f8ba35c1bb..154ee2e6ee 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -1,5 +1,6 @@ -/** +/* * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 - 2017 Open Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -486,7 +487,7 @@ public class SmsDatabase extends MessagingDatabase { jobManager.add(new TrimThreadJob(context, threadId)); if (unread) { - DatabaseFactory.getThreadDatabase(context).setUnread(threadId); + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); } return new Pair<>(messageId, threadId); @@ -561,7 +562,7 @@ public class SmsDatabase extends MessagingDatabase { long messageId = db.insert(TABLE_NAME, null, values); if (unread) { - DatabaseFactory.getThreadDatabase(context).setUnread(threadId); + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); } if (!message.isIdentityUpdate() && !message.isIdentityVerified() && !message.isIdentityDefault()) { diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java index 9d72d5d3c5..abf15b3969 100644 --- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -1,5 +1,6 @@ -/** +/* * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013-2017 Open Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -65,6 +66,7 @@ public class ThreadDatabase extends Database { public static final String SNIPPET = "snippet"; private static final String SNIPPET_CHARSET = "snippet_cs"; public static final String READ = "read"; + public static final String UNREAD_COUNT = "unread_count"; public static final String TYPE = "type"; private static final String ERROR = "error"; public static final String SNIPPET_TYPE = "snippet_type"; @@ -86,15 +88,15 @@ public class ThreadDatabase extends Database { ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " + DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " + - READ_RECEIPT_COUNT + " INTEGER DEFAULT 0);"; + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);"; - public static final String[] CREATE_INDEXS = { + static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", "CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");", }; private static final String[] THREAD_PROJECTION = { - ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, TYPE, ERROR, SNIPPET_TYPE, + ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, TYPE, ERROR, SNIPPET_TYPE, SNIPPET_URI, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT }; @@ -248,6 +250,7 @@ public class ThreadDatabase extends Database { SQLiteDatabase db = databaseHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(1); contentValues.put(READ, 1); + contentValues.put(UNREAD_COUNT, 0); db.update(TABLE_NAME, contentValues, null, null); @@ -265,6 +268,7 @@ public class ThreadDatabase extends Database { public List setRead(long threadId, boolean lastSeen) { ContentValues contentValues = new ContentValues(1); contentValues.put(READ, 1); + contentValues.put(UNREAD_COUNT, 0); if (lastSeen) { contentValues.put(LAST_SEEN, System.currentTimeMillis()); @@ -284,13 +288,22 @@ public class ThreadDatabase extends Database { }}; } - public void setUnread(long threadId) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(READ, 0); +// public void setUnread(long threadId, int unreadCount) { +// ContentValues contentValues = new ContentValues(1); +// contentValues.put(READ, 0); +// contentValues.put(UNREAD_COUNT, unreadCount); +// +// SQLiteDatabase db = databaseHelper.getWritableDatabase(); +// db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); +// notifyConversationListListeners(); +// } + public void incrementUnread(long threadId, int amount) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); - notifyConversationListListeners(); + db.execSQL("UPDATE " + TABLE_NAME + " SET " + READ + " = 0, " + + UNREAD_COUNT + " = " + UNREAD_COUNT + " + ? WHERE " + ID + " = ?", + new String[] {String.valueOf(amount), + String.valueOf(threadId)}); } public void setDistributionType(long threadId, int distributionType) { @@ -530,11 +543,12 @@ public class ThreadDatabase extends Database { notifyConversationListeners(threadId); } - public void updateReadState(long threadId) { + void updateReadState(long threadId) { int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId); ContentValues contentValues = new ContentValues(); contentValues.put(READ, unreadCount == 0); + contentValues.put(UNREAD_COUNT, unreadCount); databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,ID_WHERE, new String[] {String.valueOf(threadId)}); @@ -595,8 +609,8 @@ public class ThreadDatabase extends Database { " ORDER BY " + TABLE_NAME + "." + DATE + " DESC"; } - public static interface ProgressListener { - public void onProgress(int complete, int total); + public interface ProgressListener { + void onProgress(int complete, int total); } public Reader readerFor(Cursor cursor, MasterCipher masterCipher) { @@ -648,7 +662,7 @@ public class ThreadDatabase extends Database { DisplayRecord.Body body = getPlaintextBody(cursor); long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT)); - long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ)); + int unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.UNREAD_COUNT)); long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE)); boolean archived = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.ARCHIVED)) != 0; int status = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.STATUS)); @@ -662,9 +676,9 @@ public class ThreadDatabase extends Database { readReceiptCount = 0; } - return new ThreadRecord(context, body, snippetUri, recipient, date, count, read == 1, - threadId, deliveryReceiptCount, status, type, distributionType, archived, - expiresIn, lastSeen, readReceiptCount); + return new ThreadRecord(context, body, snippetUri, recipient, date, count, + unreadCount, threadId, deliveryReceiptCount, status, type, + distributionType, archived, expiresIn, lastSeen, readReceiptCount); } private DisplayRecord.Body getPlaintextBody(Cursor cursor) { diff --git a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java index 8ea53a899a..33f83faa6f 100644 --- a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -1,5 +1,6 @@ -/** +/* * Copyright (C) 2012 Moxie Marlinspike + * Copyright (C) 2013-2017 Open Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,14 +43,14 @@ public class ThreadRecord extends DisplayRecord { private @NonNull final Context context; private @Nullable final Uri snippetUri; private final long count; - private final boolean read; + private final int unreadCount; 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 Recipient recipient, long date, long count, boolean read, + @NonNull Recipient recipient, long date, long count, int unreadCount, long threadId, int deliveryReceiptCount, int status, long snippetType, int distributionType, boolean archived, long expiresIn, long lastSeen, int readReceiptCount) @@ -58,7 +59,7 @@ public class ThreadRecord extends DisplayRecord { this.context = context.getApplicationContext(); this.snippetUri = snippetUri; this.count = count; - this.read = read; + this.unreadCount = unreadCount; this.distributionType = distributionType; this.archived = archived; this.expiresIn = expiresIn; @@ -134,8 +135,8 @@ public class ThreadRecord extends DisplayRecord { return count; } - public boolean isRead() { - return read; + public int getUnreadCount() { + return unreadCount; } public long getDate() {