From 5ed2e63ca02de1bf30efe2574c15487d6878732a Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 20 Aug 2013 12:13:20 +0200 Subject: [PATCH] Improved chat messages performances --- src/org/linphone/ChatFragment.java | 106 ++++++++++++++----------- src/org/linphone/ChatListFragment.java | 57 ++++++++----- src/org/linphone/ChatStorage.java | 16 ++-- src/org/linphone/ui/BubbleChat.java | 8 +- submodules/linphone | 2 +- 5 files changed, 111 insertions(+), 78 deletions(-) diff --git a/src/org/linphone/ChatFragment.java b/src/org/linphone/ChatFragment.java index 35cd31533..13d85795c 100644 --- a/src/org/linphone/ChatFragment.java +++ b/src/org/linphone/ChatFragment.java @@ -116,8 +116,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC private BubbleChat lastSentMessageBubble; private HashMap latestImageMessages; private int messagesFilterLimit = 0; - private List messagesList; private boolean useLinphoneMessageStorage; + private boolean messagesArePresentButHidden = false; private ProgressBar progressBar; private int bytesSent; @@ -239,10 +239,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC return view; } - private void refreshMessages() { - messagesList = LinphoneActivity.instance().getChatMessages(sipUri); - } - @Override public void onSaveInstanceState(Bundle outState) { outState.putString("fileToUploadPath", fileToUploadPath); @@ -286,52 +282,46 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } private void invalidate() { - refreshMessages(); + messagesFilterLimit += MESSAGES_STEP; - if (messagesFilterLimit == 0) { - if (messagesList.size() > MESSAGES_STEP) - messagesFilterLimit = MESSAGES_STEP; - else - messagesFilterLimit = messagesList.size(); - } else { - if (messagesFilterLimit + MESSAGES_STEP <= messagesList.size()) - messagesFilterLimit += MESSAGES_STEP; - else - messagesFilterLimit = messagesList.size(); + if (!messagesArePresentButHidden) { + LinphoneChatMessage[] history = chatRoom.getHistory(); + for (int i = 0; i < history.length; i++) { + LinphoneChatMessage msg = history[i]; + if (msg.getExternalBodyUrl() != null) { + Bitmap bm = BitmapFactory.decodeFile(msg.getExternalBodyUrl()); + displayImageMessage(msg.getStorageId(), bm, msg.getTime(), !msg.isOutgoing(), msg.getStatus(), messagesLayout, msg.getExternalBodyUrl(), i >= history.length - messagesFilterLimit); + } else { + displayMessage(msg.getStorageId(), msg.getText(), msg.getTime(), !msg.isOutgoing(), msg.getStatus(), messagesLayout, i >= history.length - messagesFilterLimit); + } + } + messagesArePresentButHidden = true; } + invalidate(messagesFilterLimit); } private void invalidate(final int limit) { - messagesLayout.removeAllViews(); - mHandler.post(new Runnable() { @Override public void run() { - previousMessageID = -1; - ChatStorage chatStorage = LinphoneActivity.instance().getChatStorage(); + int start = messagesLayout.getChildCount() - messagesFilterLimit; + if (start < 0) + start = 0; - for (int i = messagesList.size() - limit; i < messagesList.size(); i++) { - ChatMessage msg = messagesList.get(i); - 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()) { - // Only works if not using liblinphone storage - chatStorage.markMessageAsRead(msg.getId()); - } - } - LinphoneActivity.instance().updateMissedChatCount(); + for (int i = start; i < messagesLayout.getChildCount(); i++) { + messagesLayout.getChildAt(i).setVisibility(View.VISIBLE); + } + + LinphoneActivity.instance().updateMissedChatCount(); - if (limit < messagesList.size()) { - messagesScrollView.setScrollViewListener(new ScrollViewListener() { - @Override + if (start != 0) { + messagesScrollView.setScrollViewListener(new ScrollViewListener() { + @Override public void OnScrollToTop(final int previousHeight) { - invalidate();mHandler.postDelayed(new Runnable() { + invalidate(); + mHandler.postDelayed(new Runnable() { @Override public void run() { //Scroll to latest saw message @@ -341,9 +331,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC }, 300); } }); - } else { - messagesScrollView.setScrollViewListener(null); - } + } else { + messagesScrollView.setScrollViewListener(null); + } } }); } @@ -379,7 +369,11 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC 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); BubbleChat bubble = new BubbleChat(layout.getContext(), id, message, null, time, isIncoming, status, previousMessageID); if (!isIncoming) { @@ -387,12 +381,19 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } View v = bubble.getView(); + + if (!show) + v.setVisibility(View.GONE); + previousMessageID = id; layout.addView(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); BubbleChat bubble = new BubbleChat(layout.getContext(), id, null, image, time, isIncoming, status, previousMessageID); if (!isIncoming) { @@ -435,6 +436,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } }); + if (!show) + v.setVisibility(View.GONE); + previousMessageID = id; layout.addView(v); registerForContextMenu(v); @@ -489,7 +493,13 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC switch (item.getItemId()) { case MENU_DELETE_MESSAGE: 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; case MENU_SAVE_PICTURE: saveImage(item.getGroupId()); @@ -576,7 +586,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC 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(); } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { 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) 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(); } else if (!isNetworkReachable && LinphoneActivity.isInstanciated()) { 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() { @Override 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) { @@ -647,7 +657,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC mHandler.post(new Runnable() { @Override 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()); } }); } diff --git a/src/org/linphone/ChatListFragment.java b/src/org/linphone/ChatListFragment.java index 7f6450de8..92d81717e 100644 --- a/src/org/linphone/ChatListFragment.java +++ b/src/org/linphone/ChatListFragment.java @@ -67,6 +67,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte private ImageView clearFastChat; private EditText fastNewChat; private boolean isEditMode = false; + private boolean useLinphoneStorage; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -104,7 +105,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte } else { noChatHistory.setVisibility(View.GONE); 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(); //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()); boolean updateNeeded = prefs.getBoolean(getString(R.string.pref_first_time_linphone_chat_storage), true); if (useLinphoneStorage && updateNeeded) { @@ -256,7 +257,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte String correspondent = conversations.get(j); LinphoneChatRoom room = LinphoneManager.getLc().getOrCreateChatRoom(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) { String path = saveImageAsFile(message.getId(), message.getImage()); if (path != null) @@ -301,7 +302,10 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte } class ChatListAdapter extends BaseAdapter { - ChatListAdapter() { + private boolean useNativeAPI; + + ChatListAdapter(boolean useNativeAPI) { + this.useNativeAPI = useNativeAPI; } public int getCount() { @@ -345,25 +349,38 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte } LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver()); - List messages = LinphoneActivity.instance().getChatMessages(contact); - if (messages != null && messages.size() > 0) { - int iterator = messages.size() - 1; - ChatMessage lastMessage = null; - - while (iterator >= 0) { - lastMessage = messages.get(iterator); - if (lastMessage.getMessage() == null) { - iterator--; - } else { - iterator = -1; + String message = ""; + if (useNativeAPI) { + LinphoneChatRoom chatRoom = LinphoneManager.getLc().getOrCreateChatRoom(contact); + LinphoneChatMessage[] history = chatRoom.getHistory(20); + if (history != null && history.length > 0) { + for (int i = history.length - 1; i >= 0; i--) { + LinphoneChatMessage msg = history[i]; + if (msg.getText() != null && msg.getText().length() > 0) { + message = msg.getText(); + break; + } } } - - String message = ""; - message = (lastMessage == null || lastMessage.getMessage() == null) ? "" : lastMessage.getMessage(); - TextView lastMessageView = (TextView) view.findViewById(R.id.lastMessage); - lastMessageView.setText(message); + } else { + List messages = LinphoneActivity.instance().getChatMessages(contact); + if (messages != null && messages.size() > 0) { + int iterator = messages.size() - 1; + ChatMessage lastMessage = null; + + while (iterator >= 0) { + lastMessage = messages.get(iterator); + if (lastMessage.getMessage() == null) { + 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); sipUri.setSelected(true); // For animation diff --git a/src/org/linphone/ChatStorage.java b/src/org/linphone/ChatStorage.java index 71ecbd6e2..2bc8c0e00 100644 --- a/src/org/linphone/ChatStorage.java +++ b/src/org/linphone/ChatStorage.java @@ -320,9 +320,11 @@ public class ChatStorage { if (useNativeAPI) { LinphoneChatMessage[] history = chatroom.getHistory(); - if (history.length > id-1) { - LinphoneChatMessage msg = history[id-1]; - message = msg.getText(); + for (LinphoneChatMessage msg : history) { + if (msg.getStorageId() == id) { + message = msg.getText(); + break; + } } } else { 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) { if (useNativeAPI) { LinphoneChatMessage[] history = chatroom.getHistory(); - LinphoneChatMessage message = history[id-1]; - chatroom.deleteMessage(message); + for (LinphoneChatMessage message : history) { + if (message.getStorageId() == id) { + chatroom.deleteMessage(message); + break; + } + } } else { db.delete(TABLE_NAME, "id LIKE " + id, null); } diff --git a/src/org/linphone/ui/BubbleChat.java b/src/org/linphone/ui/BubbleChat.java index 7df7792f7..9689f67df 100644 --- a/src/org/linphone/ui/BubbleChat.java +++ b/src/org/linphone/ui/BubbleChat.java @@ -87,7 +87,7 @@ public class BubbleChat { private ImageView statusView; 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); LayoutParams layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); @@ -201,10 +201,10 @@ public class BubbleChat { return view; } - private String timestampToHumanDate(Context context, String timestamp) { + private String timestampToHumanDate(Context context, long timestamp) { try { Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(Long.parseLong(timestamp)); + cal.setTimeInMillis(timestamp); SimpleDateFormat dateFormat; if (isToday(cal)) { @@ -215,7 +215,7 @@ public class BubbleChat { return dateFormat.format(cal.getTime()); } catch (NumberFormatException nfe) { - return timestamp; + return String.valueOf(timestamp); } } diff --git a/submodules/linphone b/submodules/linphone index e033f0ed6..34f405894 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit e033f0ed6991a28bce4309b4480a50a38d52f79c +Subproject commit 34f405894cca981328688eb435d414552b2b345c