introduced sroll to bottom button. based on #2777 by @harshitbansal05
This commit is contained in:
		
							parent
							
								
									d5c4a987a1
								
							
						
					
					
						commit
						419e7f5ea6
					
				|  | @ -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); | ||||
| 	} | ||||
|  |  | |||
|  | @ -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<Message> messageList = new ArrayList<>(); | ||||
| 	private String lastMessageUuid = 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<>(); | ||||
| 	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)) { | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:height="24dp" | ||||
|     android:width="24dp" | ||||
|     android:viewportWidth="24" | ||||
|     android:viewportHeight="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> | ||||
|  | @ -0,0 +1,36 @@ | |||
| <!-- | ||||
|   ~ 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:height="24dp" | ||||
|     android:width="24dp" | ||||
|     android:viewportWidth="24" | ||||
|     android:viewportHeight="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> | ||||
|  | @ -1,5 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <layout xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
| <layout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
| 
 | ||||
|     <RelativeLayout | ||||
|         xmlns:tools="http://schemas.android.com/tools" | ||||
|  | @ -23,6 +24,29 @@ | |||
|             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_margin="12dp" | ||||
|             android:layout_alignParentEnd="true" | ||||
|             android:layout_alignBottom="@+id/messages_view" | ||||
|             android:alpha="0.85" | ||||
|             app:backgroundTint="?attr/color_background_primary" | ||||
|             android:src="?attr/icon_scroll_down" | ||||
|             app:fabSize="mini" | ||||
|             android:visibility="gone"/> | ||||
| 
 | ||||
|         <eu.siacs.conversations.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/textsend" | ||||
|             android:layout_width="fill_parent" | ||||
|  |  | |||
|  | @ -58,6 +58,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"/> | ||||
|  |  | |||
|  | @ -70,6 +70,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_qr_code_scan_white_24dp</item> | ||||
|         <item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_black</item> | ||||
| 
 | ||||
|         <item type="reference" name="icon_notifications">@drawable/ic_notifications_black_24dp</item> | ||||
|         <item type="reference" name="icon_notifications_off">@drawable/ic_notifications_off_black_24dp</item> | ||||
|  | @ -147,6 +148,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_qr_code_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> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Daniel Gultsch
						Daniel Gultsch