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.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
@ -93,6 +94,7 @@ public class ChatEventsAdapter extends SelectableAdapter<ChatBubbleViewHolder> {
mContext = mFragment.getActivity(); mContext = mFragment.getActivity();
mItemResource = itemResource; mItemResource = itemResource;
mHistory = new ArrayList<>(Arrays.asList(history)); mHistory = new ArrayList<>(Arrays.asList(history));
Collections.reverse(mHistory);
mParticipants = participants; mParticipants = participants;
mClickListener = clickListener; mClickListener = clickListener;
mListener = new ChatMessageListenerStub() { mListener = new ChatMessageListenerStub() {
@ -414,9 +416,16 @@ public class ChatEventsAdapter extends SelectableAdapter<ChatBubbleViewHolder> {
} }
public void addToHistory(EventLog log) { public void addToHistory(EventLog log) {
mHistory.add(log); notifyItemChanged(0);
notifyDataSetChanged(); 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) { public void setContacts(ArrayList<LinphoneContact> participants) {
@ -425,6 +434,7 @@ public class ChatEventsAdapter extends SelectableAdapter<ChatBubbleViewHolder> {
public void refresh(EventLog[] history) { public void refresh(EventLog[] history) {
mHistory = new ArrayList<>(Arrays.asList(history)); mHistory = new ArrayList<>(Arrays.asList(history));
Collections.reverse(mHistory);
notifyDataSetChanged(); 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.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable; import android.os.Parcelable;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
@ -80,6 +82,8 @@ import org.linphone.ui.SelectableHelper;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import static android.content.Context.INPUT_METHOD_SERVICE; 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 { public class GroupChatFragment extends Fragment implements ChatRoomListener, ContactsUpdatedListener, ChatBubbleViewHolder.ClickListener, SelectableHelper.DeleteListener {
private static final int ADD_PHOTO = 1337; 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 mBackButton, mCallButton, mBackToCallButton, mGroupInfosButton;
private ImageView mAttachImageButton, mSendMessageButton; private ImageView mAttachImageButton, mSendMessageButton;
private TextView mRoomLabel, mParticipantsLabel, mRemoteComposing; private TextView mRoomLabel, mParticipantsLabel, mRemoteComposing;
@ -106,6 +112,7 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
private ArrayList<LinphoneContact> mParticipants; private ArrayList<LinphoneContact> mParticipants;
private LinearLayoutManager layoutManager; private LinearLayoutManager layoutManager;
private int mContextMenuMessagePosition; private int mContextMenuMessagePosition;
private ChatScrollListener mChatScrollListener;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 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); mChatEventsList = view.findViewById(R.id.chat_message_list);
mSelectionHelper = new SelectableHelper(view, this); mSelectionHelper = new SelectableHelper(view, this);
layoutManager = new LinearLayoutManager(mContext); layoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, true);
mChatEventsList.setLayoutManager(layoutManager); 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) { if (getArguments() != null) {
String fileSharedUri = getArguments().getString("fileSharedUri"); String fileSharedUri = getArguments().getString("fileSharedUri");
if (fileSharedUri != null) { if (fileSharedUri != null) {
@ -350,9 +365,9 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
eventLog.deleteFromDatabase(); eventLog.deleteFromDatabase();
} }
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
mEventsAdapter.refresh(mChatRoom.getHistoryMessageEvents(0)); mEventsAdapter.refresh(mChatRoom.getHistoryMessageEvents(MESSAGES_PER_PAGE));
} else { } 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); 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 * Keyboard management
*/ */
@ -563,9 +596,9 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
if (mChatRoom == null) return; if (mChatRoom == null) return;
if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { 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 { } 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); mSelectionHelper.setAdapter(mEventsAdapter);
mChatEventsList.setAdapter(mEventsAdapter); mChatEventsList.setAdapter(mEventsAdapter);
@ -573,7 +606,7 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
} }
public void scrollToBottom() { public void scrollToBottom() {
mChatEventsList.getLayoutManager().scrollToPosition(mEventsAdapter.getCount() - 1); mChatEventsList.getLayoutManager().scrollToPosition(0);
} }
public String getRemoteSipUri() { public String getRemoteSipUri() {