From ba2aff6706279a2be6bc4c64e3a9298af5c0d979 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 15 Jan 2017 23:24:53 +0530 Subject: [PATCH 1/9] Revert commit: Hide unread counts until cache issue fixed. --- .../ExpandableStreamDrawerAdapter.java | 13 +++++++----- .../android/activities/ZulipActivity.java | 20 +++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java b/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java index 663e285fb..49d7af396 100644 --- a/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java +++ b/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java @@ -31,19 +31,22 @@ public ExpandableStreamDrawerAdapter(final Context context, Cursor cursor, int g @Override public Cursor getChildrenCursor(Cursor groupCursor) { + int pointer = zulipApp.getPointer(); List results = new ArrayList<>(); try { - results = ZulipApp.get().getDao(Message.class).queryRaw("SELECT DISTINCT subject FROM messages " + - "JOIN streams ON streams.id=messages.stream " + - "WHERE streams.id=" + groupCursor.getInt(0) + " and streams." + Stream.SUBSCRIBED_FIELD + " = " + "1 group by subject").getResults(); + results = zulipApp.getDao(Message.class).queryRaw("SELECT DISTINCT subject, " + + "count(case when messages.id > " + pointer + " and (messages." + + Message.MESSAGE_READ_FIELD + " = 0) then 1 end) as unreadcount FROM messages " + + "JOIN streams ON streams.name=messages.recipients " + + "WHERE streams.name LIKE ? group by subject", groupCursor.getString(1)).getResults(); } catch (SQLException e) { ZLog.logException(e); } - MatrixCursor matrixCursor = new MatrixCursor(new String[]{"subject", "_id"}); + MatrixCursor matrixCursor = new MatrixCursor(new String[]{"subject", "_id", UNREAD_TABLE_NAME}); for (String[] result : results) { try { - matrixCursor.addRow(new String[]{result[0], String.valueOf(groupCursor.getInt(0))}); + matrixCursor.addRow(new String[]{result[0], String.valueOf(groupCursor.getInt(0)), result[1]}); } catch (Exception e) { ZLog.logException(e); } diff --git a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java index 1ea800a07..39e45884d 100644 --- a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java +++ b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java @@ -1193,11 +1193,11 @@ public void onAnimationRepeat(Animator animator) { */ private void setupListViewAdapter() { streamsDrawerAdapter = null; - String[] groupFrom = {Stream.NAME_FIELD, Stream.COLOR_FIELD}; - int[] groupTo = {R.id.name, R.id.stream_dot}; + String[] groupFrom = {Stream.NAME_FIELD, Stream.COLOR_FIELD, ExpandableStreamDrawerAdapter.UNREAD_TABLE_NAME}; + int[] groupTo = {R.id.name, R.id.stream_dot, R.id.unread_group};; // Comparison of data elements and View - String[] childFrom = {Message.SUBJECT_FIELD}; - int[] childTo = {R.id.name_child}; + String[] childFrom = {Message.SUBJECT_FIELD, ExpandableStreamDrawerAdapter.UNREAD_TABLE_NAME}; + int[] childTo = {R.id.name_child, R.id.unread_child}; final ExpandableListView streamsDrawer = (ExpandableListView) findViewById(R.id.streams_drawer); streamsDrawer.setGroupIndicator(null); try { @@ -1275,12 +1275,12 @@ public void onClick(View v) { case R.id.unread_group: TextView unreadGroupTextView = (TextView) view; final String unreadGroupCount = cursor.getString(columnIndex); -// if (unreadGroupCount.equals("0")) { -// unreadGroupTextView.setVisibility(View.GONE); -// } else { -// unreadGroupTextView.setText(unreadGroupCount); -// unreadGroupTextView.setVisibility(View.VISIBLE); -// } + if (unreadGroupCount.equals("0")) { + unreadGroupTextView.setVisibility(View.GONE); + } else { + unreadGroupTextView.setText(unreadGroupCount); + unreadGroupTextView.setVisibility(View.VISIBLE); + } return true; case R.id.unread_child: TextView unreadChildTextView = (TextView) view; From f4f5dfb653f1ef6b108fa9c7b6b68170f44a94ac Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 28 Feb 2017 04:50:15 +0530 Subject: [PATCH 2/9] Fix: sql query for unread counts and use primitive boolean as datatype. Fixes #170 --- .../activities/ExpandableStreamDrawerAdapter.java | 1 - .../android/activities/RecyclerMessageAdapter.java | 7 +------ .../zulip/android/activities/ZulipActivity.java | 14 ++++---------- .../java/com/zulip/android/models/Message.java | 6 +++--- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java b/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java index 49d7af396..070447fb0 100644 --- a/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java +++ b/app/src/main/java/com/zulip/android/activities/ExpandableStreamDrawerAdapter.java @@ -7,7 +7,6 @@ import com.zulip.android.ZulipApp; import com.zulip.android.models.Message; -import com.zulip.android.models.Stream; import com.zulip.android.util.ZLog; import java.sql.SQLException; diff --git a/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java b/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java index 304e98612..7c5f639e5 100644 --- a/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java +++ b/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java @@ -449,12 +449,7 @@ private void markThisMessageAsRead(Message message) { if (!startedFromFilter && zulipApp.getPointer() < mID) { zulipApp.syncPointer(mID); } - - boolean isMessageRead = false; - if (message.getMessageRead() != null) { - isMessageRead = message.getMessageRead(); - } - if (!isMessageRead) { + if (!message.getMessageRead()) { try { updateBuilder.where().eq(Message.ID_FIELD, message.getID()); updateBuilder.updateColumnValue(Message.MESSAGE_READ_FIELD, true); diff --git a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java index 39e45884d..b738403ed 100644 --- a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java +++ b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java @@ -671,8 +671,10 @@ private Callable getSteamCursorGenerator() { Callable steamCursorGenerator = new Callable() { @Override public Cursor call() throws Exception { - String query = "SELECT s.id as _id, s.name, s.color" - + " FROM streams as s LEFT JOIN messages as m ON s.id=m.stream "; + int pointer = app.getPointer(); + String query = "SELECT s.id as _id, s.name, s.color, count(case when m.id > " + pointer + " and (m." + Message.MESSAGE_READ_FIELD + + " = 0)"+ " then 1 end) as " + ExpandableStreamDrawerAdapter.UNREAD_TABLE_NAME + + " FROM streams as s LEFT JOIN messages as m ON s." + Stream.NAME_FIELD + " = m." + Message.RECIPIENTS_FIELD; String selectArg = null; if (!etSearchStream.getText().toString().equals("") && !etSearchStream.getText().toString().isEmpty()) { //append where clause @@ -1257,14 +1259,6 @@ public boolean setViewValue(View view, Cursor cursor, int columnIndex) { } else { name.setTextColor(ContextCompat.getColor(ZulipActivity.this, R.color.colorTextPrimary)); } - view.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - resetStreamSearch(); - doNarrowToLastRead(streamName); - onNarrowFillSendBoxStream(streamName, "", false); - } - }); return true; case R.id.stream_dot: // Set the color of the (currently white) dot diff --git a/app/src/main/java/com/zulip/android/models/Message.java b/app/src/main/java/com/zulip/android/models/Message.java index 559829209..e7e9c8eb8 100644 --- a/app/src/main/java/com/zulip/android/models/Message.java +++ b/app/src/main/java/com/zulip/android/models/Message.java @@ -116,7 +116,7 @@ public class Message { @DatabaseField(foreign = true, columnName = STREAM_FIELD, foreignAutoRefresh = true) private Stream stream; @DatabaseField(columnName = MESSAGE_READ_FIELD) - private Boolean messageRead; + private boolean messageRead; @DatabaseField(columnDefinition = MESSAGE_EDITED) private Boolean hasBeenEdited; //endregion @@ -371,11 +371,11 @@ public static HTMLSchema getSchema() { return schema; } - public Boolean getMessageRead() { + public boolean getMessageRead() { return messageRead; } - public void setMessageRead(Boolean messageRead) { + public void setMessageRead(boolean messageRead) { this.messageRead = messageRead; } From 3a6362c6df4c6d884d75a5b06884049556fd570a Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 3 Mar 2017 06:46:56 +0530 Subject: [PATCH 3/9] Db: Bump up database version to 9. --- .../main/java/com/zulip/android/database/DatabaseHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/zulip/android/database/DatabaseHelper.java b/app/src/main/java/com/zulip/android/database/DatabaseHelper.java index 53c55e4e1..ded7d9594 100644 --- a/app/src/main/java/com/zulip/android/database/DatabaseHelper.java +++ b/app/src/main/java/com/zulip/android/database/DatabaseHelper.java @@ -30,7 +30,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final String DATABASE_NAME = "zulip-%s.db"; // any time you make changes to your database objects, you may have to // increase the database version - private static final int DATABASE_VERSION = 8; + private static final int DATABASE_VERSION = 9; public DatabaseHelper(ZulipApp app, String email) { super(app, String.format(DATABASE_NAME, MD5Util.md5Hex(email)), null, From 251c8d22d70b63a23f0c6e8d90e62fbb38a63008 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 28 Feb 2017 06:08:04 +0530 Subject: [PATCH 4/9] Fix Stream.getLastMessageRead(). --- app/src/main/java/com/zulip/android/models/Stream.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/zulip/android/models/Stream.java b/app/src/main/java/com/zulip/android/models/Stream.java index ad9337b23..2b594208f 100644 --- a/app/src/main/java/com/zulip/android/models/Stream.java +++ b/app/src/main/java/com/zulip/android/models/Stream.java @@ -175,6 +175,7 @@ public static Message getLastMessageRead(ZulipApp app, String streamName) { // query for message in given stream and orderby timestamp decreasingly return messageDao.queryBuilder().orderBy(Message.TIMESTAMP_FIELD, false) .where().eq(Message.RECIPIENTS_FIELD, new SelectArg(Message.RECIPIENTS_FIELD, streamName)) + .and().eq(Message.MESSAGE_READ_FIELD, true) .queryForFirst(); } catch (SQLException e) { ZLog.logException(e); From 6c9d300aa89bc44f99bae4b8fc7d2e013bfefcc9 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 28 Feb 2017 08:10:52 +0530 Subject: [PATCH 5/9] Add message update flags event. --- .../android/networking/AsyncGetEvents.java | 53 ++++++++++++++++--- .../response/events/EventsBranch.java | 3 +- .../events/UpdateMessageFlagsWrapper.java | 28 ++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/zulip/android/networking/response/events/UpdateMessageFlagsWrapper.java diff --git a/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java b/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java index 8a18f77a2..3d2609530 100644 --- a/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java +++ b/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java @@ -10,6 +10,7 @@ import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.RuntimeExceptionDao; import com.j256.ormlite.misc.TransactionManager; +import com.j256.ormlite.stmt.UpdateBuilder; import com.zulip.android.R; import com.zulip.android.ZulipApp; import com.zulip.android.activities.LoginActivity; @@ -27,6 +28,7 @@ import com.zulip.android.networking.response.events.MutedTopicsWrapper; import com.zulip.android.networking.response.events.StreamWrapper; import com.zulip.android.networking.response.events.SubscriptionWrapper; +import com.zulip.android.networking.response.events.UpdateMessageFlagsWrapper; import com.zulip.android.networking.response.events.UpdateMessageWrapper; import com.zulip.android.util.MutedTopics; import com.zulip.android.util.TypeSwapper; @@ -366,12 +368,12 @@ public Message convert(MessageWrapper messageWrapper) { processUpdateMessages(updateMessageEvents); } - // get message time limit events - List messageTimeLimit = events.getEventsOfBranchType(EventsBranch.BranchType.EDIT_MESSAGE_TIME_LIMIT); - if (!messageTimeLimit.isEmpty()) { - Log.i("AsyncGetEvents", "Received " + messageTimeLimit.size() - + " realm event"); - processMessageEditParam(messageTimeLimit); + // update message flags + List updateMessageFlags = events.getEventsOfBranchType(EventsBranch.BranchType.UPDATE_MESSAGE_FLAGS); + if (!updateMessageFlags.isEmpty()) { + Log.i("AsyncGetEvents", "Received " + updateMessageFlags.size() + + " update message flags"); + processUpdateMessageFlags(updateMessageFlags); } // get stream events @@ -382,6 +384,13 @@ public Message convert(MessageWrapper messageWrapper) { processStreams(streamEvents); } + // get message time limit events + List messageTimeLimit = events.getEventsOfBranchType(EventsBranch.BranchType.EDIT_MESSAGE_TIME_LIMIT); + if (!messageTimeLimit.isEmpty()) { + Log.i("AsyncGetEvents", "Received " + messageTimeLimit.size() + + " realm event"); + processMessageEditParam(messageTimeLimit); + } } /** @@ -559,4 +568,36 @@ public void processStreams(List events) { } } } + + private void processUpdateMessageFlags(List eventList) { + for (EventsBranch event : eventList) { + UpdateMessageFlagsWrapper updateMsgFlag = (UpdateMessageFlagsWrapper) event; + switch (updateMsgFlag.getFlag()) { + case "read": + markMessagesAsRead(updateMsgFlag.getMessageIds()); + break; + default: + Log.d("AsyncEvents", "unhandled update_message_flags event"); + } + } + } + + private void markMessagesAsRead(List messageIds) { + Dao messageDao = app.getDao(Message.class); + if (messageDao == null) { + return; + } + for (Integer id : messageIds) { + if (id != null) { + try { + UpdateBuilder updateBuilder = messageDao.updateBuilder(); + updateBuilder.where().eq(Message.ID_FIELD, id); + updateBuilder.updateColumnValue(Message.MESSAGE_READ_FIELD, true); + updateBuilder.update(); + } catch (SQLException e) { + ZLog.logException(e); + } + } + } + } } diff --git a/app/src/main/java/com/zulip/android/networking/response/events/EventsBranch.java b/app/src/main/java/com/zulip/android/networking/response/events/EventsBranch.java index 3d4ac4eb9..bdd9fdc22 100644 --- a/app/src/main/java/com/zulip/android/networking/response/events/EventsBranch.java +++ b/app/src/main/java/com/zulip/android/networking/response/events/EventsBranch.java @@ -28,7 +28,8 @@ public enum BranchType { MUTED_TOPICS(MutedTopicsWrapper.class, "muted_topics"), EDIT_MESSAGE_TIME_LIMIT(EditMessageWrapper.class, "realm"), UPDATE_MESSAGE(UpdateMessageWrapper.class, "update_message"), - STREAM(StreamWrapper.class, "stream"); + STREAM(StreamWrapper.class, "stream"), + UPDATE_MESSAGE_FLAGS(UpdateMessageFlagsWrapper.class, "update_message_flags"); private final Class klazz; diff --git a/app/src/main/java/com/zulip/android/networking/response/events/UpdateMessageFlagsWrapper.java b/app/src/main/java/com/zulip/android/networking/response/events/UpdateMessageFlagsWrapper.java new file mode 100644 index 000000000..10fae6937 --- /dev/null +++ b/app/src/main/java/com/zulip/android/networking/response/events/UpdateMessageFlagsWrapper.java @@ -0,0 +1,28 @@ +package com.zulip.android.networking.response.events; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +/** + * This class is used to deserialize the update_message_flags type events {@link EventsBranch.BranchType#UPDATE_MESSAGE_FLAGS}. + * example: {"all":false,"timestamp":1488246444.8514339924,"messages":[6,7,9,10,14],"flag":"read","operation":"add","type":"update_message_flags","id":12} + * {"all":false,"timestamp":1488247612.6961550713,"messages":[55],"flag":"starred","operation":"add","type":"update_message_flags","id":3} + * {"all":false,"timestamp":1488247614.0365629196,"messages":[55],"flag":"starred","operation":"remove","type":"update_message_flags","id":4} + */ + +public class UpdateMessageFlagsWrapper extends EventsBranch { + @SerializedName("messages") + private List messageIds; + + @SerializedName("flag") + private String flag; + + public String getFlag() { + return this.flag; + } + + public List getMessageIds() { + return this.messageIds; + } +} From 781378ab9a5e1f0d3519bb50272fad27b1201211 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 28 Feb 2017 10:21:35 +0530 Subject: [PATCH 6/9] Sync message updates for read flag from server. --- .../android/activities/RecyclerMessageAdapter.java | 9 +-------- .../main/java/com/zulip/android/models/Message.java | 9 +++++++++ .../com/zulip/android/networking/AsyncGetEvents.java | 10 +++++++++- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java b/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java index 7c5f639e5..94a3b5f9d 100644 --- a/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java +++ b/app/src/main/java/com/zulip/android/activities/RecyclerMessageAdapter.java @@ -42,7 +42,6 @@ import com.zulip.android.viewholders.MessageHeaderParent; import com.zulip.android.viewholders.MessageHolder; -import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -450,13 +449,7 @@ private void markThisMessageAsRead(Message message) { zulipApp.syncPointer(mID); } if (!message.getMessageRead()) { - try { - updateBuilder.where().eq(Message.ID_FIELD, message.getID()); - updateBuilder.updateColumnValue(Message.MESSAGE_READ_FIELD, true); - updateBuilder.update(); - } catch (SQLException e) { - ZLog.logException(e); - } + // leaving message update to "update_message_flag" event zulipApp.markMessageAsRead(message); } } catch (NullPointerException e) { diff --git a/app/src/main/java/com/zulip/android/models/Message.java b/app/src/main/java/com/zulip/android/models/Message.java index e7e9c8eb8..bbebb326c 100644 --- a/app/src/main/java/com/zulip/android/models/Message.java +++ b/app/src/main/java/com/zulip/android/models/Message.java @@ -90,6 +90,8 @@ public class Message { private String senderShortName; @SerializedName("subject_links") private List subjectLinks; + @SerializedName("flags") + private List flags; @DatabaseField(foreign = true, columnName = SENDER_FIELD, foreignAutoRefresh = true) private Person sender; @SerializedName("type") @@ -243,6 +245,9 @@ public Void call() throws Exception { stream = Stream.getByName(app, m.getRecipients()); } m.setStream(stream); + if (m.getFlags() != null) { + m.setMessageRead(m.getFlags().contains(Message.MESSAGE_READ_FIELD)); + } messageDao.createOrUpdate(m); } return null; @@ -757,4 +762,8 @@ public String getDisplayRecipient() { return displayRecipient; } } + + public List getFlags() { + return this.flags; + } } diff --git a/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java b/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java index 3d2609530..dbd8cb46e 100644 --- a/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java +++ b/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java @@ -591,7 +591,7 @@ private void markMessagesAsRead(List messageIds) { if (id != null) { try { UpdateBuilder updateBuilder = messageDao.updateBuilder(); - updateBuilder.where().eq(Message.ID_FIELD, id); + updateBuilder.where().eq(Message.ID_FIELD, id).and().eq(Message.MESSAGE_READ_FIELD, false); updateBuilder.updateColumnValue(Message.MESSAGE_READ_FIELD, true); updateBuilder.update(); } catch (SQLException e) { @@ -599,5 +599,13 @@ private void markMessagesAsRead(List messageIds) { } } } + + // update streams and people drawers to reflect updated unread counts + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mActivity.checkAndSetupStreamsDrawer(); + } + }); } } From e8696817f678f94366254f62e51f3fdf7b0f3c7b Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 1 Mar 2017 23:12:45 +0530 Subject: [PATCH 7/9] Fetch 1000 messages on startup. --- .../zulip/android/activities/MessageListFragment.java | 10 ++++++---- .../com/zulip/android/activities/ZulipActivity.java | 8 ++++---- .../com/zulip/android/networking/AsyncGetEvents.java | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/zulip/android/activities/MessageListFragment.java b/app/src/main/java/com/zulip/android/activities/MessageListFragment.java index 4957d62b3..9086d8b0a 100644 --- a/app/src/main/java/com/zulip/android/activities/MessageListFragment.java +++ b/app/src/main/java/com/zulip/android/activities/MessageListFragment.java @@ -261,7 +261,7 @@ private void initializeNarrow() { adapter.setFooterShowing(true); } - public void onReadyToDisplay(boolean registered) { + public void onReadyToDisplay(boolean registered, boolean startup) { if (initialized && !registered) { // Already have state, and already processed any events that came in // when resuming the existing queue. @@ -272,7 +272,9 @@ public void onReadyToDisplay(boolean registered) { } initializeNarrow(); - fetch(); + + // 1000 messages in a loop on startup + fetch(startup); initialized = true; } @@ -304,7 +306,7 @@ private void showEmptyView() { emptyTextView.setVisibility(View.VISIBLE); } - private void fetch() { + private void fetch(boolean onStartup) { final AsyncGetOldMessages oldMessagesReq = new AsyncGetOldMessages(this); oldMessagesReq.setCallback(new ZulipAsyncPushTask.AsyncTaskCompleteListener() { @Override @@ -323,7 +325,7 @@ public void onTaskFailure(String result) { } }); oldMessagesReq.execute(app.getPointer(), LoadPosition.INITIAL, 100, - 100, filter); + (onStartup ? 1000 : 100), filter); } /** diff --git a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java index b738403ed..a1222bb23 100644 --- a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java +++ b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java @@ -1847,7 +1847,7 @@ public void doNarrow(NarrowFilter filter) { narrowedList = MessageListFragment.newInstance(filter); // Push to the back stack if we are not already narrowed pushListFragment(narrowedList, NARROW); - narrowedList.onReadyToDisplay(true); + narrowedList.onReadyToDisplay(true, false); showView(appBarLayout); } @@ -2244,10 +2244,10 @@ private void startRequests() { } - public void onReadyToDisplay(boolean registered) { - homeList.onReadyToDisplay(registered); + public void onReadyToDisplay(boolean registered, boolean startup) { + homeList.onReadyToDisplay(registered, startup); if (narrowedList != null) { - narrowedList.onReadyToDisplay(registered); + narrowedList.onReadyToDisplay(registered, startup); } } diff --git a/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java b/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java index dbd8cb46e..7833d2eef 100644 --- a/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java +++ b/app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java @@ -222,7 +222,8 @@ public Void call() throws Exception { @Override public void run() { mActivity.getPeopleAdapter().refresh(); - mActivity.onReadyToDisplay(true); + // fetch 1000 messages on startup + mActivity.onReadyToDisplay(true, true); mActivity.checkAndSetupStreamsDrawer(); if (mActivity.commonProgressDialog != null && mActivity.commonProgressDialog.isShowing()) { mActivity.commonProgressDialog.dismiss(); @@ -292,7 +293,7 @@ public void run() { mActivity.runOnUiThread(new Runnable() { @Override public void run() { - mActivity.onReadyToDisplay(false); + mActivity.onReadyToDisplay(false, false); } }); } @@ -465,7 +466,7 @@ private void processSubsciptions(List subscriptionWrapperList) thr mActivity.runOnUiThread(new Runnable() { @Override public void run() { - mActivity.onReadyToDisplay(true); + mActivity.onReadyToDisplay(true, false); mActivity.checkAndSetupStreamsDrawer(); } }); @@ -487,7 +488,7 @@ private void processMutedTopics(List genericMutedTopics) { mActivity.runOnUiThread(new Runnable() { @Override public void run() { - mActivity.onReadyToDisplay(true); + mActivity.onReadyToDisplay(true, false); mActivity.checkAndSetupStreamsDrawer(); } }); From 121469cd3bf32a58de916ded02e91a6d0ff84dc8 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 2 Mar 2017 21:19:12 +0530 Subject: [PATCH 8/9] Add bankruptcy menu option. Fixes #326 --- .../activities/MessageListFragment.java | 88 ++++++++++++++++--- .../android/activities/ZulipActivity.java | 27 ++++++ app/src/main/res/menu/options.xml | 5 ++ app/src/main/res/values/strings.xml | 4 + 4 files changed, 111 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/zulip/android/activities/MessageListFragment.java b/app/src/main/java/com/zulip/android/activities/MessageListFragment.java index 9086d8b0a..cda7a1468 100644 --- a/app/src/main/java/com/zulip/android/activities/MessageListFragment.java +++ b/app/src/main/java/com/zulip/android/activities/MessageListFragment.java @@ -26,7 +26,9 @@ import android.widget.TextView; import android.widget.Toast; +import com.j256.ormlite.dao.Dao; import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.stmt.UpdateBuilder; import com.j256.ormlite.stmt.Where; import com.zulip.android.R; import com.zulip.android.ZulipApp; @@ -65,6 +67,7 @@ * initiated and called by {@link ZulipActivity} */ public class MessageListFragment extends Fragment implements MessageListener { + public static final String LOG_TAG = MessageListFragment.class.getSimpleName(); private static final String PARAM_FILTER = "filter"; public NarrowFilter filter; public ZulipApp app; @@ -270,11 +273,13 @@ public void onReadyToDisplay(boolean registered, boolean startup) { Log.i("onReadyToDisplay", "just a resume"); return; } - initializeNarrow(); - - // 1000 messages in a loop on startup - fetch(startup); + if (startup) { + // fetch 1000 messages after pointer on startup + fetch(app.getPointer(), 1000); + } else { + fetch(app.getPointer(), 100); + } initialized = true; } @@ -295,18 +300,23 @@ public void onReadyToDisplay(boolean registered, int messageId) { } initializeNarrow(); - fetch(messageId); + fetch(messageId, 100); initialized = true; } - private void showEmptyView() { Log.d("ErrorRecieving", "No Messages found for current list" + ((filter != null) ? ":" + filter.getTitle() : "")); recyclerView.setVisibility(View.GONE); emptyTextView.setVisibility(View.VISIBLE); } - private void fetch(boolean onStartup) { + /** + * Fetches all messages past current pointer and calls {@link #skipAllMessages()} + * to marks messages as read, update pointer and scroll to latest message. + * + * @param messagesFetched number of messages fetched in preceding call + */ + public void fetchAllMessages(final int messagesFetched, final CommonProgressDialog dialog) { final AsyncGetOldMessages oldMessagesReq = new AsyncGetOldMessages(this); oldMessagesReq.setCallback(new ZulipAsyncPushTask.AsyncTaskCompleteListener() { @Override @@ -314,7 +324,15 @@ public void onTaskComplete(String result, JSONObject jsonObject) { loadingMessages = false; adapter.setHeaderShowing(false); if (result.equals("0")) { - showEmptyView(); + skipAllMessages(); + dialog.dismiss(); + Toast.makeText(getContext(), R.string.bankruptcy_confirmation_msg, Toast.LENGTH_SHORT).show(); + } else { + try { + fetchAllMessages(messagesFetched + Integer.parseInt(result), dialog); + } catch (NumberFormatException e) { + Log.e(LOG_TAG, "fetchAllMessages()", e); + } } } @@ -324,8 +342,52 @@ public void onTaskFailure(String result) { adapter.setHeaderShowing(false); } }); - oldMessagesReq.execute(app.getPointer(), LoadPosition.INITIAL, 100, - (onStartup ? 1000 : 100), filter); + oldMessagesReq.execute(app.getPointer() + messagesFetched, LoadPosition.INITIAL, 0, + 200, filter); + } + + /** + * Marks all messages as read, updates pointer and scrolls to last message. + */ + private void skipAllMessages() { + // mark all messages as read + try { + markAllMessagesAsRead(); + } catch (SQLException e) { + Toast.makeText(getContext(), R.string.try_again_error_msg, Toast.LENGTH_SHORT).show(); + ZLog.logException(e); + } + + // sync pointer to latest message + int lastMessageId = app.getMaxMessageId(); + app.syncPointer(lastMessageId); + + // scroll to latest message + try { + RecyclerMessageAdapter adapter = getAdapter(); + int lastMsgIndex = adapter.getItemIndex(lastMessageId); + if (lastMsgIndex != -1) { + recyclerView.scrollToPosition(lastMsgIndex); + } else { + // when the current narrow doesn't contain the latest message + // do nothing + } + } catch (NullPointerException e) { + Toast.makeText(getContext(), R.string.try_again_error_msg, Toast.LENGTH_SHORT).show(); + ZLog.logException(e); + } + } + + /** + * Marks all messages as read after current pointer. + * @throws SQLException + */ + private void markAllMessagesAsRead() throws SQLException { + Dao messageDao = app.getDao(Message.class); + UpdateBuilder builder = messageDao.updateBuilder(); + builder.where().ge(Message.ID_FIELD, app.getPointer()); + builder.updateColumnValue(Message.MESSAGE_READ_FIELD, true); + builder.update(); } /** @@ -334,8 +396,8 @@ public void onTaskFailure(String result) { * * @param messageID anchor message id */ - private void fetch(int messageID) { - // set the achor for fetching messages as the message clicked + private void fetch(int messageID, int messages) { + // set the anchor for fetching messages as the message clicked this.anchorId = messageID; final AsyncGetOldMessages oldMessagesReq = new AsyncGetOldMessages(this); @@ -356,7 +418,7 @@ public void onTaskFailure(String result) { } }); oldMessagesReq.execute(messageID, LoadPosition.INITIAL, 100, - 100, filter); + messages, filter); } private void selectPointer() { diff --git a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java index a1222bb23..3dca1fb2d 100644 --- a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java +++ b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java @@ -2101,6 +2101,9 @@ public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) { datePickerDialog.getDatePicker().setMaxDate(new Date().getTime()); datePickerDialog.show(); break; + case R.id.bankruptcy: + declareBankruptcy(); + break; case R.id.logout: logout(); break; @@ -2113,6 +2116,30 @@ public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) { return true; } + private void declareBankruptcy() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + final CommonProgressDialog progressDialog = new CommonProgressDialog(this); + progressDialog.showWithMessage(getString(R.string.bankruptcy_progress_dialog)); + builder.setMessage(R.string.bankruptcy_alert_message) + .setTitle(R.string.bankruptcy); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // fetch all messages past current pointer + if (narrowedList != null) { + onBackPressed(); + } + homeList.fetchAllMessages(0, progressDialog); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // do nothing + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } + /** * Switches the current Day/Night mode to Night/Day mode * diff --git a/app/src/main/res/menu/options.xml b/app/src/main/res/menu/options.xml index 1fb54e804..e7825be9a 100644 --- a/app/src/main/res/menu/options.xml +++ b/app/src/main/res/menu/options.xml @@ -32,6 +32,11 @@ android:id="@+id/daynight" android:title="@string/switch_theme" /> + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 607007be5..9b8fda222 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -156,4 +156,8 @@ Copying message to clipboard Copying to clipboard failed Something went wrong, try again + Declare bankruptcy + Are you sure you want to skip all messages? + Skipping messages + Marked all messages as read! From d28f016c39d0af515ae57bc003c02450a235a403 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 3 Mar 2017 04:29:35 +0530 Subject: [PATCH 9/9] Better UI for unread counts. --- .../android/activities/ZulipActivity.java | 37 +++++++++++++ .../res/drawable/unread_counts_background.xml | 8 +++ .../main/res/layout-v21/stream_tile_child.xml | 50 ++++++++++++++++++ .../main/res/layout-v21/stream_tile_new.xml | 52 +++++++++++++++++++ app/src/main/res/layout/stream_tile_child.xml | 22 +++++--- app/src/main/res/layout/stream_tile_new.xml | 18 +++++-- app/src/main/res/values/dimens.xml | 7 +++ 7 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 app/src/main/res/drawable/unread_counts_background.xml create mode 100644 app/src/main/res/layout-v21/stream_tile_child.xml create mode 100644 app/src/main/res/layout-v21/stream_tile_new.xml create mode 100644 app/src/main/res/values/dimens.xml diff --git a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java index 3dca1fb2d..454c4c7f5 100644 --- a/app/src/main/java/com/zulip/android/activities/ZulipActivity.java +++ b/app/src/main/java/com/zulip/android/activities/ZulipActivity.java @@ -20,8 +20,10 @@ import android.database.MatrixCursor; import android.database.MergeCursor; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -1268,7 +1270,12 @@ public boolean setViewValue(View view, Cursor cursor, int columnIndex) { return true; case R.id.unread_group: TextView unreadGroupTextView = (TextView) view; + // set appropriate size for background drawable + GradientDrawable backgroundGroup = (GradientDrawable) unreadGroupTextView.getBackground(); + backgroundGroup.mutate(); final String unreadGroupCount = cursor.getString(columnIndex); + int groupSize = findBubbleSize(unreadGroupCount); + backgroundGroup.setSize(groupSize, groupSize); if (unreadGroupCount.equals("0")) { unreadGroupTextView.setVisibility(View.GONE); } else { @@ -1278,7 +1285,15 @@ public boolean setViewValue(View view, Cursor cursor, int columnIndex) { return true; case R.id.unread_child: TextView unreadChildTextView = (TextView) view; + // change background drawable color of child unread count to faint gray + GradientDrawable backgroundChild = (GradientDrawable) unreadChildTextView.getBackground(); + backgroundChild.mutate(); + backgroundChild.setColor(Color.LTGRAY); + + // set appropriate size for background of drawable final String unreadChildNumber = cursor.getString(columnIndex); + int childSize = findBubbleSize(unreadChildNumber); + backgroundChild.setSize(childSize, childSize); if (unreadChildNumber.equals("0")) { unreadChildTextView.setVisibility(View.GONE); } else { @@ -1300,6 +1315,28 @@ public boolean setViewValue(View view, Cursor cursor, int columnIndex) { streamsDrawer.setAdapter(streamsDrawerAdapter); } + private int findBubbleSize(String strCounts) { + int counts = 0; + try { + counts = Integer.parseInt(strCounts); + } catch (NumberFormatException e) { + ZLog.logException(e); + return counts; + } + + int size; + if (counts / 10 == 0) { + size = (int) getResources().getDimension(R.dimen.small_bubble); + } else if (counts / 100 == 0) { + size = (int) getResources().getDimension(R.dimen.medium_bubble); + } else if (counts / 1000 == 0) { + size = (int) getResources().getDimension(R.dimen.large_bubble); + } else { + size = (int) getResources().getDimension(R.dimen.extra_large_bubble); + } + return size; + } + /** * Helper function used to call {@link ZulipActivity#onNarrow(NarrowFilter, int)} or * {@link ZulipActivity#onNarrow(NarrowFilter)} based on last message read in {@param streamName} stream. diff --git a/app/src/main/res/drawable/unread_counts_background.xml b/app/src/main/res/drawable/unread_counts_background.xml new file mode 100644 index 000000000..aed147982 --- /dev/null +++ b/app/src/main/res/drawable/unread_counts_background.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-v21/stream_tile_child.xml b/app/src/main/res/layout-v21/stream_tile_child.xml new file mode 100644 index 000000000..4469ccff8 --- /dev/null +++ b/app/src/main/res/layout-v21/stream_tile_child.xml @@ -0,0 +1,50 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-v21/stream_tile_new.xml b/app/src/main/res/layout-v21/stream_tile_new.xml new file mode 100644 index 000000000..16835fd78 --- /dev/null +++ b/app/src/main/res/layout-v21/stream_tile_new.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/stream_tile_child.xml b/app/src/main/res/layout/stream_tile_child.xml index 1352ad4c7..f9f78b07e 100644 --- a/app/src/main/res/layout/stream_tile_child.xml +++ b/app/src/main/res/layout/stream_tile_child.xml @@ -16,6 +16,7 @@ android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_gravity="center_vertical" + android:layout_toLeftOf="@+id/child_container" android:layout_marginLeft="54dp" android:paddingBottom="16dp" android:paddingLeft="16dp" @@ -25,15 +26,24 @@ android:textColor="@color/colorTextSecondary" android:textSize="16sp" /> - - + android:layout_marginRight="30dp"> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/stream_tile_new.xml b/app/src/main/res/layout/stream_tile_new.xml index 7c1d5ffb1..e299e7dd8 100644 --- a/app/src/main/res/layout/stream_tile_new.xml +++ b/app/src/main/res/layout/stream_tile_new.xml @@ -13,8 +13,8 @@ android:layout_marginLeft="16dp" android:background="@drawable/dot" /> - + android:layout_marginRight="30dp"> + + + + 18dp + 22dp + 26dp + 30dp + \ No newline at end of file