Added pager for chat messages to prevent long loading time

This commit is contained in:
Sylvain Berfini 2018-10-03 16:32:10 +02:00
parent 8822e63c0e
commit 41ed28e7f6
3 changed files with 155 additions and 8 deletions

View file

@ -69,6 +69,7 @@ import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
@ -93,6 +94,7 @@ public class ChatEventsAdapter extends SelectableAdapter<ChatBubbleViewHolder> {
mContext = mFragment.getActivity();
mItemResource = itemResource;
mHistory = new ArrayList<>(Arrays.asList(history));
Collections.reverse(mHistory);
mParticipants = participants;
mClickListener = clickListener;
mListener = new ChatMessageListenerStub() {
@ -414,9 +416,16 @@ public class ChatEventsAdapter extends SelectableAdapter<ChatBubbleViewHolder> {
}
public void addToHistory(EventLog log) {
mHistory.add(log);
notifyDataSetChanged();
notifyItemChanged(0);
mHistory.add(0, log);
notifyItemInserted(0);
}
public void addAllToHistory(ArrayList<EventLog> logs) {
int currentSize = mHistory.size() - 1;
Collections.reverse(logs);
mHistory.addAll(logs);
notifyItemRangeInserted(currentSize + 1, logs.size());
}
public void setContacts(ArrayList<LinphoneContact> participants) {
@ -425,6 +434,7 @@ public class ChatEventsAdapter extends SelectableAdapter<ChatBubbleViewHolder> {
public void refresh(EventLog[] history) {
mHistory = new ArrayList<>(Arrays.asList(history));
Collections.reverse(mHistory);
notifyDataSetChanged();
}

View file

@ -0,0 +1,104 @@
package org.linphone.chat;
/*
ChatScrollListener.java
Copyright (C) 2010-2018 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public abstract class ChatScrollListener extends RecyclerView.OnScrollListener {
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;
private LinearLayoutManager mLayoutManager;
public ChatScrollListener(LinearLayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
public int getLastVisibleItem(int[] lastVisibleItemPositions) {
int maxSize = 0;
for (int i = 0; i < lastVisibleItemPositions.length; i++) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i];
}
else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i];
}
}
return maxSize;
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
int lastVisibleItemPosition = 0;
int totalItemCount = mLayoutManager.getItemCount();
lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) {
this.loading = true;
}
}
// If its still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false;
previousTotalItemCount = totalItemCount;
}
// If it isnt currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
// threshold should reflect how many total columns there are too
if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
currentPage++;
onLoadMore(currentPage, totalItemCount, view);
loading = true;
}
}
// Call this method whenever performing new searches
public void resetState() {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = 0;
this.loading = true;
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view);
}

View file

@ -32,6 +32,8 @@ import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.support.v7.widget.LinearLayoutManager;
@ -80,6 +82,8 @@ import org.linphone.ui.SelectableHelper;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static android.content.Context.INPUT_METHOD_SERVICE;
@ -87,7 +91,9 @@ import static org.linphone.fragments.FragmentsAvailable.CHAT;
public class GroupChatFragment extends Fragment implements ChatRoomListener, ContactsUpdatedListener, ChatBubbleViewHolder.ClickListener, SelectableHelper.DeleteListener {
private static final int ADD_PHOTO = 1337;
private static final int MESSAGES_PER_PAGE = 20;
private Handler mHandler = new Handler(Looper.getMainLooper());
private ImageView mBackButton, mCallButton, mBackToCallButton, mGroupInfosButton;
private ImageView mAttachImageButton, mSendMessageButton;
private TextView mRoomLabel, mParticipantsLabel, mRemoteComposing;
@ -106,6 +112,7 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
private ArrayList<LinphoneContact> mParticipants;
private LinearLayoutManager layoutManager;
private int mContextMenuMessagePosition;
private ChatScrollListener mChatScrollListener;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -218,9 +225,17 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
mChatEventsList = view.findViewById(R.id.chat_message_list);
mSelectionHelper = new SelectableHelper(view, this);
layoutManager = new LinearLayoutManager(mContext);
layoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, true);
mChatEventsList.setLayoutManager(layoutManager);
mChatScrollListener = new ChatScrollListener(layoutManager) {
@Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
loadMoreData(totalItemsCount);
}
};
mChatEventsList.addOnScrollListener(mChatScrollListener);
if (getArguments() != null) {
String fileSharedUri = getArguments().getString("fileSharedUri");
if (fileSharedUri != null) {
@ -350,9 +365,9 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
eventLog.deleteFromDatabase();
}
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
mEventsAdapter.refresh(mChatRoom.getHistoryMessageEvents(0));
mEventsAdapter.refresh(mChatRoom.getHistoryMessageEvents(MESSAGES_PER_PAGE));
} else {
mEventsAdapter.refresh(mChatRoom.getHistoryEvents(0));
mEventsAdapter.refresh(mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE));
}
}
@ -424,6 +439,24 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
return super.onContextItemSelected(item);
}
private void loadMoreData(final int totalItemsCount) {
mHandler.post(new Runnable() {
@Override
public void run() {
int maxSize = mChatRoom.getHistoryEventsSize();
if (totalItemsCount < maxSize) {
int upperBound = totalItemsCount + MESSAGES_PER_PAGE;
if (upperBound > maxSize) {
upperBound = maxSize;
}
EventLog[] newLogs = mChatRoom.getHistoryRangeEvents(totalItemsCount, upperBound);
ArrayList<EventLog> logsList = new ArrayList<>(Arrays.asList(newLogs));
mEventsAdapter.addAllToHistory(logsList);
}
}
});
}
/**
* Keyboard management
*/
@ -563,9 +596,9 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
if (mChatRoom == null) return;
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
mEventsAdapter = new ChatEventsAdapter(this, mSelectionHelper, R.layout.chat_bubble, mChatRoom.getHistoryMessageEvents(0), mParticipants, this);
mEventsAdapter = new ChatEventsAdapter(this, mSelectionHelper, R.layout.chat_bubble, mChatRoom.getHistoryMessageEvents(MESSAGES_PER_PAGE), mParticipants, this);
} else {
mEventsAdapter = new ChatEventsAdapter(this, mSelectionHelper, R.layout.chat_bubble, mChatRoom.getHistoryEvents(0), mParticipants, this);
mEventsAdapter = new ChatEventsAdapter(this, mSelectionHelper, R.layout.chat_bubble, mChatRoom.getHistoryEvents(MESSAGES_PER_PAGE), mParticipants, this);
}
mSelectionHelper.setAdapter(mEventsAdapter);
mChatEventsList.setAdapter(mEventsAdapter);
@ -573,7 +606,7 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
}
public void scrollToBottom() {
mChatEventsList.getLayoutManager().scrollToPosition(mEventsAdapter.getCount() - 1);
mChatEventsList.getLayoutManager().scrollToPosition(0);
}
public String getRemoteSipUri() {