From 60494b249b13b09319d6ff944935b6dbbf71ba6b Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sun, 22 Apr 2018 14:19:03 +0200 Subject: introduced sroll to bottom button --- .../de/pixart/messenger/entities/Conversation.java | 19 +++++ .../pixart/messenger/ui/ConversationFragment.java | 82 +++++++++++++++++++--- .../messenger/ui/widget/UnreadCountCustomView.java | 77 ++++++++++++++++++++ src/main/res/drawable/ic_scroll_to_end_black.xml | 9 +++ src/main/res/drawable/ic_scroll_to_end_white.xml | 38 ++++++++++ src/main/res/layout/fragment_conversation.xml | 31 ++++++-- src/main/res/values/attrs.xml | 5 ++ src/main/res/values/themes.xml | 4 +- 8 files changed, 252 insertions(+), 13 deletions(-) create mode 100644 src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java 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/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java index fa5e5ef60..8b3f06711 100644 --- a/src/main/java/de/pixart/messenger/entities/Conversation.java +++ b/src/main/java/de/pixart/messenger/entities/Conversation.java @@ -1110,6 +1110,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/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index 8a1db5736..b94b80d2a 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -141,14 +141,18 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke 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<>(); final private List mPendingImageUris = new ArrayList<>(); + private String lastMessageUuid = null; public Uri mPendingEditorContent = 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<>(); protected MessageAdapter messageListAdapter; private Conversation conversation; @@ -206,6 +210,27 @@ 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 @@ -215,8 +240,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } @Override - public void onScroll(final AbsListView view, int firstVisibleItem, - int visibleItemCount, final int totalItemCount) { + public void onScroll(final AbsListView view, int firstVisibleItem, int visibleItemCount, final int totalItemCount) { + toggleScrollDownButton(view, totalItemCount); synchronized (ConversationFragment.this.messageList) { if (firstVisibleItem < 25 && conversation != null && conversation.messagesLoaded.compareAndSet(true, false) && messageList.size() > 0) { long timestamp; @@ -414,6 +439,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } }; + private OnClickListener mScrollButtonListener = new OnClickListener() { + + @Override + public void onClick(View v) { + stopScrolling(); + setSelection(binding.messagesView.getCount() - 1); + } + }; + private OnClickListener mSendButtonListener = new OnClickListener() { @Override @@ -647,9 +681,15 @@ 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(); } } @@ -1129,7 +1169,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke @Override public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - this.binding = DataBindingUtil.inflate(inflater,R.layout.fragment_conversation,container,false); + this.binding = DataBindingUtil.inflate(inflater, R.layout.fragment_conversation, container, false); binding.getRoot().setOnClickListener(null); //TODO why the fuck did we do this? binding.textinput.addTextChangedListener(new StylingHelper.MessageEditorStyler(binding.textinput)); @@ -1138,6 +1178,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke binding.textSendButton.setOnClickListener(this.mSendButtonListener); binding.textSendButton.setOnLongClickListener(this.mSendButtonLongListener); + binding.scrollToBottomButton.setOnClickListener(this.mScrollButtonListener); binding.messagesView.setOnScrollListener(mOnScrollListener); binding.messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); @@ -1965,6 +2006,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()); @@ -1983,6 +2025,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); @@ -2059,6 +2102,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke this.reInitRequiredOnStart = true; pendingExtras.push(extras); } + updateUnreadMessagesCount(); } private void reInit(Conversation conversation) { @@ -2164,6 +2208,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)); @@ -2173,8 +2231,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 { @@ -2362,6 +2423,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke conversation.populateWithMessages(ConversationFragment.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) { @@ -2886,7 +2951,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke if (lastHistoryMessage != null) { int pos = getIndexOf(lastHistoryMessage.getUuid(), messageList); - setScrollPosition(getScrollPosition(pos, getView())); + setScrollPosition(getScrollPosition(pos, getView()), null); this.binding.messagesView.setSelection(pos); } return lastHistoryMessage; @@ -2916,8 +2981,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/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java b/src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java new file mode 100644 index 000000000..6b4a6de0f --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java @@ -0,0 +1,77 @@ +package de.pixart.messenger.ui.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.support.v4.content.ContextCompat; +import android.util.AttributeSet; +import android.view.View; + +import de.pixart.messenger.R; + +public class UnreadCountCustomView extends View { + + private int unreadCount; + private Paint paint, textPaint; + private int backgroundColor = 0xff326130; + + public UnreadCountCustomView(Context context) { + super(context); + init(); + } + + public UnreadCountCustomView(Context context, AttributeSet attrs) { + super(context, attrs); + initXMLAttrs(context, attrs); + init(); + } + + public UnreadCountCustomView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initXMLAttrs(context, attrs); + init(); + } + + private void initXMLAttrs(Context context, AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnreadCountCustomView); + //setBackgroundColor(a.getColor(a.getIndex(0), ContextCompat.getColor(context, R.color.accent))); + setBackgroundColor(ContextCompat.getColor(context, R.color.accent)); + a.recycle(); + } + + void init() { + paint = new Paint(); + paint.setColor(backgroundColor); + paint.setAntiAlias(true); + textPaint = new Paint(); + textPaint.setColor(Color.WHITE); + textPaint.setTextAlign(Paint.Align.CENTER); + textPaint.setAntiAlias(true); + textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + float midx = canvas.getWidth() / 2.0f; + float midy = canvas.getHeight() / 2.0f; + float radius = Math.min(canvas.getWidth(), canvas.getHeight()) / 2.0f; + float textOffset = canvas.getWidth() / 6.0f; + textPaint.setTextSize(0.95f * radius); + canvas.drawCircle(midx, midy, radius * 0.94f, paint); + canvas.drawText(unreadCount > 999 ? "\u221E" : String.valueOf(unreadCount), midx, midy + textOffset, textPaint); + + } + + public void setUnreadCount(int unreadCount) { + this.unreadCount = unreadCount; + invalidate(); + } + + public void setBackgroundColor(int backgroundColor) { + this.backgroundColor = backgroundColor; + } +} 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..6771786c1 --- /dev/null +++ b/src/main/res/drawable/ic_scroll_to_end_black.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file 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..82ed9911f --- /dev/null +++ b/src/main/res/drawable/ic_scroll_to_end_white.xml @@ -0,0 +1,38 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index 54c608f13..dcf825dc5 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -5,8 +5,8 @@ + android:background="?attr/color_background_secondary" + android:clickable="false"> + + + + + android:singleLine="false" + android:textColor="?attr/text_Color_Main"> diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index f9fb003f6..02461ec1a 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -52,6 +52,7 @@ + @@ -92,4 +93,8 @@ + + + + \ No newline at end of file diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index e757b7817..85c18e84e 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -61,6 +61,7 @@ @drawable/ic_import_export_white_24dp @drawable/ic_share_white_24dp @drawable/ic_barcode_scan_white_24dp + @drawable/ic_scroll_to_end_black @drawable/ic_image_black_24dp @@ -173,7 +174,8 @@ @drawable/ic_settings_white_24dp @drawable/ic_import_export_white_24dp @drawable/ic_share_white_24dp - @drawable/ic_barcode_scan_white_24dp + @drawable/ic_barcode_scan_white_24dp + @drawable/ic_scroll_to_end_white @drawable/ic_notifications_white_24dp @drawable/ic_notifications_off_white_24dp -- cgit v1.2.3