diff options
author | Christian Schneppe <christian@pix-art.de> | 2018-04-22 14:19:03 +0200 |
---|---|---|
committer | Christian Schneppe <christian@pix-art.de> | 2018-04-22 14:19:03 +0200 |
commit | 60494b249b13b09319d6ff944935b6dbbf71ba6b (patch) | |
tree | 8d7a918c32497ecb4af61ebeb3a7b76ce8cc28d7 /src | |
parent | 13f58b1708b37757a7c4e934badcdd09b9aa12d5 (diff) |
introduced sroll to bottom button
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/de/pixart/messenger/entities/Conversation.java | 19 | ||||
-rw-r--r-- | src/main/java/de/pixart/messenger/ui/ConversationFragment.java | 82 | ||||
-rw-r--r-- | src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java | 77 | ||||
-rw-r--r-- | src/main/res/drawable/ic_scroll_to_end_black.xml | 9 | ||||
-rw-r--r-- | src/main/res/drawable/ic_scroll_to_end_white.xml | 38 | ||||
-rw-r--r-- | src/main/res/layout/fragment_conversation.xml | 31 | ||||
-rw-r--r-- | src/main/res/values/attrs.xml | 5 | ||||
-rw-r--r-- | src/main/res/values/themes.xml | 4 |
8 files changed, 252 insertions, 13 deletions
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<Message> messageList = new ArrayList<>(); final private List<Uri> mPendingImageUris = new ArrayList<>(); + private String lastMessageUuid = null; public Uri mPendingEditorContent = null; private final PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>(); private final PendingItem<String> pendingConversationsUuid = new PendingItem<>(); private final PendingItem<Bundle> pendingExtras = new PendingItem<>(); private final PendingItem<Uri> pendingTakePhotoUri = new PendingItem<>(); private final PendingItem<ScrollState> pendingScrollState = new PendingItem<>(); + private final PendingItem<String> pendingLastMessageUuid = new PendingItem<>(); private final PendingItem<Message> 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 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#8a000000" + android:pathData="M16.59,5.59L18,7L12,13L6,7L7.41,5.59L12,10.17L16.59,5.59M16.59,11.59L18,13L12,19L6,13L7.41,11.59L12,16.17L16.59,11.59Z" /> +</vector>
\ 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 @@ +<!-- + ~ Copyright (c) 2018, Daniel Gultsch All rights reserved. + ~ + ~ Redistribution and use in source and binary forms, with or without modification, + ~ are permitted provided that the following conditions are met: + ~ + ~ 1. Redistributions of source code must retain the above copyright notice, this + ~ list of conditions and the following disclaimer. + ~ + ~ 2. Redistributions in binary form must reproduce the above copyright notice, + ~ this list of conditions and the following disclaimer in the documentation and/or + ~ other materials provided with the distribution. + ~ + ~ 3. Neither the name of the copyright holder nor the names of its contributors + ~ may be used to endorse or promote products derived from this software without + ~ specific prior written permission. + ~ + ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ~ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + ~ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ~ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + ~ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + ~ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ~ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + ~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#b2ffffff" + android:pathData="M16.59,5.59L18,7L12,13L6,7L7.41,5.59L12,10.17L16.59,5.59M16.59,11.59L18,13L12,19L6,13L7.41,11.59L12,16.17L16.59,11.59Z" /> +</vector>
\ 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 @@ <RelativeLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:clickable="false" - android:background="?attr/color_background_secondary"> + android:background="?attr/color_background_secondary" + android:clickable="false"> <ListView android:id="@+id/messages_view" @@ -23,6 +23,29 @@ android:transcriptMode="normal" tools:listitem="@layout/message_sent"></ListView> + <android.support.design.widget.FloatingActionButton + android:id="@+id/scroll_to_bottom_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/messages_view" + android:layout_alignParentEnd="true" + android:layout_margin="12dp" + android:alpha="0.85" + android:src="?attr/icon_scroll_down" + android:visibility="gone" + app:backgroundTint="?attr/color_background_primary" + app:fabSize="mini" /> + + <de.pixart.messenger.ui.widget.UnreadCountCustomView + android:id="@+id/unread_count_custom_view" + android:layout_width="?attr/IconSize" + android:layout_height="?attr/IconSize" + android:layout_alignTop="@+id/scroll_to_bottom_button" + android:layout_alignEnd="@+id/scroll_to_bottom_button" + android:elevation="8dp" + android:visibility="gone" + app:backgroundColor="?attr/unread_count" /> + <RelativeLayout android:id="@+id/input" android:layout_width="match_parent" @@ -100,7 +123,6 @@ android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/textSendButton" android:background="?attr/color_background_secondary" - android:textColor="?attr/text_Color_Main" android:ems="10" android:imeOptions="flagNoExtractUi|actionSend" android:inputType="textShortMessage|textMultiLine|textCapSentences" @@ -111,7 +133,8 @@ android:paddingLeft="8dp" android:paddingRight="8dp" android:paddingTop="12dp" - android:singleLine="false"> + android:singleLine="false" + android:textColor="?attr/text_Color_Main"> </de.pixart.messenger.ui.widget.EditMessage> 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 @@ <attr name="icon_import_export" format="reference" /> <attr name="icon_scan_qr_code" format="reference" /> <attr name="icon_enable_undecided_device" format="reference" /> + <attr name="icon_scroll_down" format="reference"/> <attr name="icon_notifications" format="reference" /> <attr name="icon_notifications_off" format="reference" /> @@ -92,4 +93,8 @@ <attr name="ic_settings_security" format="reference"/> <attr name="ic_settings_expert" format="reference"/> <attr name="ic_settings_about" format="reference"/> + + <declare-styleable name="UnreadCountCustomView"> + <attr name="backgroundColor" format="reference|color" /> + </declare-styleable> </resources>
\ 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 @@ <item type="reference" name="icon_import_export">@drawable/ic_import_export_white_24dp</item> <item type="reference" name="icon_share">@drawable/ic_share_white_24dp</item> <item type="reference" name="icon_scan_qr_code">@drawable/ic_barcode_scan_white_24dp</item> + <item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_black</item> <!-- settings--> <item type="reference" name="ic_settings_ui">@drawable/ic_image_black_24dp</item> @@ -173,7 +174,8 @@ <item type="reference" name="icon_settings">@drawable/ic_settings_white_24dp</item> <item type="reference" name="icon_import_export">@drawable/ic_import_export_white_24dp</item> <item type="reference" name="icon_share">@drawable/ic_share_white_24dp</item> - <item name="icon_scan_qr_code">@drawable/ic_barcode_scan_white_24dp</item> + <item type="reference" name="icon_scan_qr_code">@drawable/ic_barcode_scan_white_24dp</item> + <item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_white</item> <item type="reference" name="icon_notifications">@drawable/ic_notifications_white_24dp</item> <item type="reference" name="icon_notifications_off">@drawable/ic_notifications_off_white_24dp</item> |