Improved chat messages performances

This commit is contained in:
Sylvain Berfini 2013-08-20 12:13:20 +02:00
parent 389d3177ec
commit 5ed2e63ca0
5 changed files with 111 additions and 78 deletions

View file

@ -116,8 +116,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
private BubbleChat lastSentMessageBubble; private BubbleChat lastSentMessageBubble;
private HashMap<Integer, String> latestImageMessages; private HashMap<Integer, String> latestImageMessages;
private int messagesFilterLimit = 0; private int messagesFilterLimit = 0;
private List<ChatMessage> messagesList;
private boolean useLinphoneMessageStorage; private boolean useLinphoneMessageStorage;
private boolean messagesArePresentButHidden = false;
private ProgressBar progressBar; private ProgressBar progressBar;
private int bytesSent; private int bytesSent;
@ -239,10 +239,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
return view; return view;
} }
private void refreshMessages() {
messagesList = LinphoneActivity.instance().getChatMessages(sipUri);
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
outState.putString("fileToUploadPath", fileToUploadPath); outState.putString("fileToUploadPath", fileToUploadPath);
@ -286,52 +282,46 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
} }
private void invalidate() { private void invalidate() {
refreshMessages(); messagesFilterLimit += MESSAGES_STEP;
if (messagesFilterLimit == 0) { if (!messagesArePresentButHidden) {
if (messagesList.size() > MESSAGES_STEP) LinphoneChatMessage[] history = chatRoom.getHistory();
messagesFilterLimit = MESSAGES_STEP; for (int i = 0; i < history.length; i++) {
else LinphoneChatMessage msg = history[i];
messagesFilterLimit = messagesList.size(); if (msg.getExternalBodyUrl() != null) {
} else { Bitmap bm = BitmapFactory.decodeFile(msg.getExternalBodyUrl());
if (messagesFilterLimit + MESSAGES_STEP <= messagesList.size()) displayImageMessage(msg.getStorageId(), bm, msg.getTime(), !msg.isOutgoing(), msg.getStatus(), messagesLayout, msg.getExternalBodyUrl(), i >= history.length - messagesFilterLimit);
messagesFilterLimit += MESSAGES_STEP; } else {
else displayMessage(msg.getStorageId(), msg.getText(), msg.getTime(), !msg.isOutgoing(), msg.getStatus(), messagesLayout, i >= history.length - messagesFilterLimit);
messagesFilterLimit = messagesList.size(); }
}
messagesArePresentButHidden = true;
} }
invalidate(messagesFilterLimit); invalidate(messagesFilterLimit);
} }
private void invalidate(final int limit) { private void invalidate(final int limit) {
messagesLayout.removeAllViews();
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
previousMessageID = -1; int start = messagesLayout.getChildCount() - messagesFilterLimit;
ChatStorage chatStorage = LinphoneActivity.instance().getChatStorage(); if (start < 0)
start = 0;
for (int i = messagesList.size() - limit; i < messagesList.size(); i++) { for (int i = start; i < messagesLayout.getChildCount(); i++) {
ChatMessage msg = messagesList.get(i); messagesLayout.getChildAt(i).setVisibility(View.VISIBLE);
if (msg.getMessage() != null && msg.getMessage().length() > 0) { }
displayMessage(msg.getId(), msg.getMessage(), msg.getTimestamp(), msg.isIncoming(), msg.getStatus(), messagesLayout);
} else {
displayImageMessage(msg.getId(), msg.getImage(), msg.getTimestamp(), msg.isIncoming(), msg.getStatus(), messagesLayout, msg.getUrl());
}
if (!msg.isRead()) { LinphoneActivity.instance().updateMissedChatCount();
// Only works if not using liblinphone storage
chatStorage.markMessageAsRead(msg.getId());
}
}
LinphoneActivity.instance().updateMissedChatCount();
if (limit < messagesList.size()) { if (start != 0) {
messagesScrollView.setScrollViewListener(new ScrollViewListener() { messagesScrollView.setScrollViewListener(new ScrollViewListener() {
@Override @Override
public void OnScrollToTop(final int previousHeight) { public void OnScrollToTop(final int previousHeight) {
invalidate();mHandler.postDelayed(new Runnable() { invalidate();
mHandler.postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
//Scroll to latest saw message //Scroll to latest saw message
@ -341,9 +331,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}, 300); }, 300);
} }
}); });
} else { } else {
messagesScrollView.setScrollViewListener(null); messagesScrollView.setScrollViewListener(null);
} }
} }
}); });
} }
@ -379,7 +369,11 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
return id; return id;
} }
private void displayMessage(int id, String message, String time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout) { private void displayMessage(int id, String message, long time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout) {
displayMessage(id, message, time, isIncoming, status, layout, true);
}
private void displayMessage(int id, String message, long time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout, boolean show) {
id = checkId(id); id = checkId(id);
BubbleChat bubble = new BubbleChat(layout.getContext(), id, message, null, time, isIncoming, status, previousMessageID); BubbleChat bubble = new BubbleChat(layout.getContext(), id, message, null, time, isIncoming, status, previousMessageID);
if (!isIncoming) { if (!isIncoming) {
@ -387,12 +381,19 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
} }
View v = bubble.getView(); View v = bubble.getView();
if (!show)
v.setVisibility(View.GONE);
previousMessageID = id; previousMessageID = id;
layout.addView(v); layout.addView(v);
registerForContextMenu(v); registerForContextMenu(v);
} }
private void displayImageMessage(int id, Bitmap image, long time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout, final String url) {
displayImageMessage(id, image, time, isIncoming, status, layout, url, true);
}
private void displayImageMessage(int id, Bitmap image, String time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout, final String url) { private void displayImageMessage(int id, Bitmap image, long time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout, final String url, boolean show) {
id = checkId(id); id = checkId(id);
BubbleChat bubble = new BubbleChat(layout.getContext(), id, null, image, time, isIncoming, status, previousMessageID); BubbleChat bubble = new BubbleChat(layout.getContext(), id, null, image, time, isIncoming, status, previousMessageID);
if (!isIncoming) { if (!isIncoming) {
@ -435,6 +436,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
} }
}); });
if (!show)
v.setVisibility(View.GONE);
previousMessageID = id; previousMessageID = id;
layout.addView(v); layout.addView(v);
registerForContextMenu(v); registerForContextMenu(v);
@ -489,7 +493,13 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
switch (item.getItemId()) { switch (item.getItemId()) {
case MENU_DELETE_MESSAGE: case MENU_DELETE_MESSAGE:
LinphoneActivity.instance().getChatStorage().deleteMessage(chatRoom, item.getGroupId()); LinphoneActivity.instance().getChatStorage().deleteMessage(chatRoom, item.getGroupId());
invalidate(); for (int i = 0; i < messagesLayout.getChildCount(); i++) {
View v = messagesLayout.getChildAt(i);
if (v.getId() == item.getGroupId()) {
v.setVisibility(View.GONE);
break;
}
}
break; break;
case MENU_SAVE_PICTURE: case MENU_SAVE_PICTURE:
saveImage(item.getGroupId()); saveImage(item.getGroupId());
@ -576,7 +586,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
newId = LinphoneActivity.instance().onMessageSent(sipUri, messageToSend); newId = LinphoneActivity.instance().onMessageSent(sipUri, messageToSend);
} }
displayMessage(newId, messageToSend, String.valueOf(System.currentTimeMillis()), false, State.InProgress, messagesLayout); displayMessage(newId, messageToSend, System.currentTimeMillis(), false, State.InProgress, messagesLayout);
scrollToEnd(); scrollToEnd();
} else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) {
LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG); LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG);
@ -602,7 +612,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
if (useLinphoneMessageStorage) if (useLinphoneMessageStorage)
saveImage(bitmap, newId, chatMessage); saveImage(bitmap, newId, chatMessage);
displayImageMessage(newId, bitmap, String.valueOf(System.currentTimeMillis()), false, State.InProgress, messagesLayout, url); displayImageMessage(newId, bitmap, System.currentTimeMillis(), false, State.InProgress, messagesLayout, url);
scrollToEnd(); scrollToEnd();
} else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) {
LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG); LinphoneActivity.instance().displayCustomToast(getString(R.string.error_network_unreachable), Toast.LENGTH_LONG);
@ -632,7 +642,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
displayMessage(id, message.getText(), String.valueOf(message.getTime()), true, null, messagesLayout); displayMessage(id, message.getText(), message.getTime(), true, null, messagesLayout);
} }
}); });
} else if (message.getExternalBodyUrl() != null) { } else if (message.getExternalBodyUrl() != null) {
@ -647,7 +657,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
displayImageMessage(id, fbm, String.valueOf(message.getTime()), true, null, messagesLayout, fbm == null ? "" : message.getExternalBodyUrl()); displayImageMessage(id, fbm, message.getTime(), true, null, messagesLayout, fbm == null ? "" : message.getExternalBodyUrl());
} }
}); });
} }

View file

@ -67,6 +67,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
private ImageView clearFastChat; private ImageView clearFastChat;
private EditText fastNewChat; private EditText fastNewChat;
private boolean isEditMode = false; private boolean isEditMode = false;
private boolean useLinphoneStorage;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -104,7 +105,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
} else { } else {
noChatHistory.setVisibility(View.GONE); noChatHistory.setVisibility(View.GONE);
chatList.setVisibility(View.VISIBLE); chatList.setVisibility(View.VISIBLE);
chatList.setAdapter(new ChatListAdapter()); chatList.setAdapter(new ChatListAdapter(useLinphoneStorage));
} }
} }
@ -125,7 +126,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
super.onResume(); super.onResume();
//Check if the is the first time we show the chat view since we use liblinphone chat storage //Check if the is the first time we show the chat view since we use liblinphone chat storage
boolean useLinphoneStorage = getResources().getBoolean(R.bool.use_linphone_chat_storage); useLinphoneStorage = getResources().getBoolean(R.bool.use_linphone_chat_storage);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(LinphoneActivity.instance()); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(LinphoneActivity.instance());
boolean updateNeeded = prefs.getBoolean(getString(R.string.pref_first_time_linphone_chat_storage), true); boolean updateNeeded = prefs.getBoolean(getString(R.string.pref_first_time_linphone_chat_storage), true);
if (useLinphoneStorage && updateNeeded) { if (useLinphoneStorage && updateNeeded) {
@ -256,7 +257,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
String correspondent = conversations.get(j); String correspondent = conversations.get(j);
LinphoneChatRoom room = LinphoneManager.getLc().getOrCreateChatRoom(correspondent); LinphoneChatRoom room = LinphoneManager.getLc().getOrCreateChatRoom(correspondent);
for (ChatMessage message : db.getMessages(correspondent)) { for (ChatMessage message : db.getMessages(correspondent)) {
LinphoneChatMessage msg = room.createLinphoneChatMessage(message.getMessage(), message.getUrl(), message.getStatus(), Long.parseLong(message.getTimestamp()), message.isRead(), message.isIncoming()); LinphoneChatMessage msg = room.createLinphoneChatMessage(message.getMessage(), message.getUrl(), message.getStatus(), Long.parseLong(message.getTimestamp()), true, message.isIncoming());
if (message.getImage() != null) { if (message.getImage() != null) {
String path = saveImageAsFile(message.getId(), message.getImage()); String path = saveImageAsFile(message.getId(), message.getImage());
if (path != null) if (path != null)
@ -301,7 +302,10 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
} }
class ChatListAdapter extends BaseAdapter { class ChatListAdapter extends BaseAdapter {
ChatListAdapter() { private boolean useNativeAPI;
ChatListAdapter(boolean useNativeAPI) {
this.useNativeAPI = useNativeAPI;
} }
public int getCount() { public int getCount() {
@ -345,25 +349,38 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
} }
LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver()); LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver());
List<ChatMessage> messages = LinphoneActivity.instance().getChatMessages(contact); String message = "";
if (messages != null && messages.size() > 0) { if (useNativeAPI) {
int iterator = messages.size() - 1; LinphoneChatRoom chatRoom = LinphoneManager.getLc().getOrCreateChatRoom(contact);
ChatMessage lastMessage = null; LinphoneChatMessage[] history = chatRoom.getHistory(20);
if (history != null && history.length > 0) {
while (iterator >= 0) { for (int i = history.length - 1; i >= 0; i--) {
lastMessage = messages.get(iterator); LinphoneChatMessage msg = history[i];
if (lastMessage.getMessage() == null) { if (msg.getText() != null && msg.getText().length() > 0) {
iterator--; message = msg.getText();
} else { break;
iterator = -1; }
} }
} }
} else {
List<ChatMessage> messages = LinphoneActivity.instance().getChatMessages(contact);
if (messages != null && messages.size() > 0) {
int iterator = messages.size() - 1;
ChatMessage lastMessage = null;
String message = ""; while (iterator >= 0) {
message = (lastMessage == null || lastMessage.getMessage() == null) ? "" : lastMessage.getMessage(); lastMessage = messages.get(iterator);
TextView lastMessageView = (TextView) view.findViewById(R.id.lastMessage); if (lastMessage.getMessage() == null) {
lastMessageView.setText(message); iterator--;
} else {
iterator = -1;
}
}
message = (lastMessage == null || lastMessage.getMessage() == null) ? "" : lastMessage.getMessage();
}
} }
TextView lastMessageView = (TextView) view.findViewById(R.id.lastMessage);
lastMessageView.setText(message);
TextView sipUri = (TextView) view.findViewById(R.id.sipUri); TextView sipUri = (TextView) view.findViewById(R.id.sipUri);
sipUri.setSelected(true); // For animation sipUri.setSelected(true); // For animation

View file

@ -320,9 +320,11 @@ public class ChatStorage {
if (useNativeAPI) { if (useNativeAPI) {
LinphoneChatMessage[] history = chatroom.getHistory(); LinphoneChatMessage[] history = chatroom.getHistory();
if (history.length > id-1) { for (LinphoneChatMessage msg : history) {
LinphoneChatMessage msg = history[id-1]; if (msg.getStorageId() == id) {
message = msg.getText(); message = msg.getText();
break;
}
} }
} else { } else {
Cursor c = db.query(TABLE_NAME, null, "id LIKE " + id, null, null, null, null); Cursor c = db.query(TABLE_NAME, null, "id LIKE " + id, null, null, null, null);
@ -377,8 +379,12 @@ public class ChatStorage {
public void deleteMessage(LinphoneChatRoom chatroom, int id) { public void deleteMessage(LinphoneChatRoom chatroom, int id) {
if (useNativeAPI) { if (useNativeAPI) {
LinphoneChatMessage[] history = chatroom.getHistory(); LinphoneChatMessage[] history = chatroom.getHistory();
LinphoneChatMessage message = history[id-1]; for (LinphoneChatMessage message : history) {
chatroom.deleteMessage(message); if (message.getStorageId() == id) {
chatroom.deleteMessage(message);
break;
}
}
} else { } else {
db.delete(TABLE_NAME, "id LIKE " + id, null); db.delete(TABLE_NAME, "id LIKE " + id, null);
} }

View file

@ -87,7 +87,7 @@ public class BubbleChat {
private ImageView statusView; private ImageView statusView;
private Button download; private Button download;
public BubbleChat(Context context, int id, String message, Bitmap image, String time, boolean isIncoming, LinphoneChatMessage.State status, int previousID) { public BubbleChat(Context context, int id, String message, Bitmap image, long time, boolean isIncoming, LinphoneChatMessage.State status, int previousID) {
view = new RelativeLayout(context); view = new RelativeLayout(context);
LayoutParams layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); LayoutParams layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
@ -201,10 +201,10 @@ public class BubbleChat {
return view; return view;
} }
private String timestampToHumanDate(Context context, String timestamp) { private String timestampToHumanDate(Context context, long timestamp) {
try { try {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(Long.parseLong(timestamp)); cal.setTimeInMillis(timestamp);
SimpleDateFormat dateFormat; SimpleDateFormat dateFormat;
if (isToday(cal)) { if (isToday(cal)) {
@ -215,7 +215,7 @@ public class BubbleChat {
return dateFormat.format(cal.getTime()); return dateFormat.format(cal.getTime());
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
return timestamp; return String.valueOf(timestamp);
} }
} }

@ -1 +1 @@
Subproject commit e033f0ed6991a28bce4309b4480a50a38d52f79c Subproject commit 34f405894cca981328688eb435d414552b2b345c