From 419e7f5ea669c0f7452b8e71c0377d1f763a06e6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 30 Mar 2018 10:26:01 +0200 Subject: [PATCH] introduced sroll to bottom button. based on #2777 by @harshitbansal05 --- .../conversations/entities/Conversation.java | 19 +++++ .../ui/ConversationFragment.java | 76 +++++++++++++++++-- .../res/drawable/ic_scroll_to_end_black.xml | 7 ++ .../res/drawable/ic_scroll_to_end_white.xml | 36 +++++++++ src/main/res/layout/fragment_conversation.xml | 26 ++++++- src/main/res/values/attrs.xml | 1 + src/main/res/values/themes.xml | 2 + 7 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 src/main/res/drawable/ic_scroll_to_end_black.xml create mode 100644 src/main/res/drawable/ic_scroll_to_end_white.xml diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index aa5d0b9fd..d33e411cd 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -925,6 +925,25 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl && sentMessagesCount() == 0; } + public int getReceivedMessagesCountSinceUuid(String uuid) { + if (uuid == null) { + return 0; + } + int count = 0; + synchronized (this.messages) { + for (int i = messages.size() - 1; i >= 0; i--) { + final Message message = messages.get(i); + if (uuid.equals(message.getUuid())) { + return count; + } + if (message.getStatus() <= Message.STATUS_RECEIVED) { + ++count; + } + } + } + return 0; + } + public interface OnMessageFound { void onMessageFound(final Message message); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index e5fb96445..2aeacba22 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -128,14 +128,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke public static final String STATE_CONVERSATION_UUID = ConversationFragment.class.getName() + ".uuid"; public static final String STATE_SCROLL_POSITION = ConversationFragment.class.getName() + ".scroll_position"; public static final String STATE_PHOTO_URI = ConversationFragment.class.getName() + ".take_photo_uri"; - + private static final String STATE_LAST_MESSAGE_UUID = "state_last_message_uuid"; final protected List messageList = new ArrayList<>(); + private String lastMessageUuid = null; private final PendingItem postponedActivityResult = new PendingItem<>(); private final PendingItem pendingConversationsUuid = new PendingItem<>(); private final PendingItem pendingExtras = new PendingItem<>(); private final PendingItem pendingTakePhotoUri = new PendingItem<>(); private final PendingItem pendingScrollState = new PendingItem<>(); + private final PendingItem pendingLastMessageUuid = new PendingItem<>(); private final PendingItem pendingMessage = new PendingItem<>(); public Uri mPendingEditorContent = null; protected MessageAdapter messageListAdapter; @@ -183,16 +185,37 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke }); } }; + + private void toggleScrollDownButton() { + toggleScrollDownButton(binding.messagesView, binding.messagesView.getCount()); + } + + private void toggleScrollDownButton(AbsListView listView, int count) { + if (listView.getLastVisiblePosition() < count - 5) { + binding.scrollToBottomButton.setEnabled(true); + binding.scrollToBottomButton.setVisibility(View.VISIBLE); + if (lastMessageUuid == null) { + lastMessageUuid = conversation.getLatestMessage().getUuid(); + } + if (conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid) > 0) { + binding.unreadCountCustomView.setVisibility(View.VISIBLE); + } + } else if (scrolledToBottom(listView)){ + lastMessageUuid = null; + hideUnreadMessagesCount(); + } + } + private OnScrollListener mOnScrollListener = new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { - // TODO Auto-generated method stub } @Override public void onScroll(final AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + toggleScrollDownButton(view, totalItemCount); synchronized (ConversationFragment.this.messageList) { if (firstVisibleItem < 5 && conversation != null && conversation.messagesLoaded.compareAndSet(true, false) && messageList.size() > 0) { long timestamp; @@ -361,6 +384,14 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return false; } }; + private OnClickListener mScrollButtonListener = new OnClickListener() { + + @Override + public void onClick(View v) { + stopScrolling(); + setSelection(binding.messagesView.getCount() - 1); + } + }; private OnClickListener mSendButtonListener = new OnClickListener() { @Override @@ -523,10 +554,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } } - private void setScrollPosition(ScrollState scrollPosition) { + private void setScrollPosition(ScrollState scrollPosition, String lastMessageUuid) { if (scrollPosition != null) { + + this.lastMessageUuid = lastMessageUuid; + if (lastMessageUuid != null) { + binding.unreadCountCustomView.setUnreadCount(conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid)); + } //TODO maybe this needs a 'post' this.binding.messagesView.setSelectionFromTop(scrollPosition.position, scrollPosition.offset); + toggleScrollDownButton(); } } @@ -868,6 +905,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke binding.textSendButton.setOnClickListener(this.mSendButtonListener); + binding.scrollToBottomButton.setOnClickListener(this.mScrollButtonListener); binding.messagesView.setOnScrollListener(mOnScrollListener); binding.messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); messageListAdapter = new MessageAdapter((XmppActivity) getActivity(), this.messageList); @@ -1687,6 +1725,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke super.onSaveInstanceState(outState); if (conversation != null) { outState.putString(STATE_CONVERSATION_UUID, conversation.getUuid()); + outState.putString(STATE_LAST_MESSAGE_UUID, lastMessageUuid); final Uri uri = pendingTakePhotoUri.peek(); if (uri != null) { outState.putString(STATE_PHOTO_URI, uri.toString()); @@ -1705,6 +1744,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return; } String uuid = savedInstanceState.getString(STATE_CONVERSATION_UUID); + pendingLastMessageUuid.push(savedInstanceState.getString(STATE_LAST_MESSAGE_UUID, null)); if (uuid != null) { this.pendingConversationsUuid.push(uuid); String takePhotoUri = savedInstanceState.getString(STATE_PHOTO_URI); @@ -1781,6 +1821,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke this.reInitRequiredOnStart = true; pendingExtras.push(extras); } + updateUnreadMessagesCount(); } private void reInit(Conversation conversation) { @@ -1822,7 +1863,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke messageListAdapter.updatePreferences(); refresh(false); this.conversation.messagesLoaded.set(true); - Log.d(Config.LOGTAG, "scrolledToBottomAndNoPending=" + Boolean.toString(scrolledToBottomAndNoPending)); if (hasExtras || scrolledToBottomAndNoPending) { @@ -1847,6 +1887,20 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return true; } + private void updateUnreadMessagesCount() { + lastMessageUuid = null; + hideUnreadMessagesCount(); + } + + private void hideUnreadMessagesCount() { + if (this.binding == null) { + return; + } + this.binding.scrollToBottomButton.setEnabled(false); + this.binding.scrollToBottomButton.setVisibility(View.GONE); + this.binding.unreadCountCustomView.setVisibility(View.GONE); + } + private void setSelection(int pos) { this.binding.messagesView.setSelection(pos); this.binding.messagesView.post(() -> this.binding.messagesView.setSelection(pos)); @@ -1856,8 +1910,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke if (this.binding == null) { return false; } - final ListView listView = this.binding.messagesView; - if (listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1) { + return scrolledToBottom(this.binding.messagesView); + } + + private static boolean scrolledToBottom(AbsListView listView) { + if (listView.getLastVisiblePosition() == listView.getCount() - 1) { final View lastChild = listView.getChildAt(listView.getChildCount() - 1); return lastChild != null && lastChild.getBottom() <= listView.getHeight(); } else { @@ -2009,6 +2066,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke conversation.populateWithMessages(this.messageList); updateSnackBar(conversation); updateStatusMessages(); + if (conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid) != 0) { + binding.unreadCountCustomView.setVisibility(View.VISIBLE); + binding.unreadCountCustomView.setUnreadCount(conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid)); + } this.messageListAdapter.notifyDataSetChanged(); updateChatMsgHint(); if (notifyConversationRead && activity != null) { @@ -2474,8 +2535,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } reInit(conversation); ScrollState scrollState = pendingScrollState.pop(); + String lastMessageUuid = pendingLastMessageUuid.pop(); if (scrollState != null) { - setScrollPosition(scrollState); + setScrollPosition(scrollState, lastMessageUuid); } } else { if (!activity.xmppConnectionService.isConversationStillOpen(conversation)) { diff --git a/src/main/res/drawable/ic_scroll_to_end_black.xml b/src/main/res/drawable/ic_scroll_to_end_black.xml new file mode 100644 index 000000000..cab667644 --- /dev/null +++ b/src/main/res/drawable/ic_scroll_to_end_black.xml @@ -0,0 +1,7 @@ + + + diff --git a/src/main/res/drawable/ic_scroll_to_end_white.xml b/src/main/res/drawable/ic_scroll_to_end_white.xml new file mode 100644 index 000000000..d15c55a20 --- /dev/null +++ b/src/main/res/drawable/ic_scroll_to_end_white.xml @@ -0,0 +1,36 @@ + + + + + diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index c917cc533..52ba6eba1 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -1,5 +1,6 @@ - + + + + + + diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 79404e4ce..417c5b7a7 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -70,6 +70,7 @@ @drawable/ic_import_export_white_24dp @drawable/ic_share_white_24dp @drawable/ic_qr_code_scan_white_24dp + @drawable/ic_scroll_to_end_black @drawable/ic_notifications_black_24dp @drawable/ic_notifications_off_black_24dp @@ -147,6 +148,7 @@ @drawable/ic_import_export_white_24dp @drawable/ic_share_white_24dp @drawable/ic_qr_code_scan_white_24dp + @drawable/ic_scroll_to_end_white @drawable/ic_notifications_white_24dp @drawable/ic_notifications_off_white_24dp