diff --git a/.gitignore b/.gitignore
index 426ff4445..b04540b57 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,13 @@
-libs
-obj
-gen
-bin
-doc
-default.properties
-local.properties
-project.properties
-tests/*$py.class
-tests/build.xml
-res/.DS_Store
-bc-android.keystore
+libs
+obj
+gen
+bin
+doc
+default.properties
+local.properties
+project.properties
+tests/*$py.class
+tests/build.xml
+res/.DS_Store
+bc-android.keystore
+res/raw/lpconfig.xsd
\ No newline at end of file
diff --git a/res/drawable-xhdpi/route_bluetooth_on_default.png b/res/drawable-xhdpi/route_bluetooth_on_default.png
index 841c93b65..fe59fa42a 100644
Binary files a/res/drawable-xhdpi/route_bluetooth_on_default.png and b/res/drawable-xhdpi/route_bluetooth_on_default.png differ
diff --git a/res/drawable-xhdpi/route_phone_on_default.png b/res/drawable-xhdpi/route_phone_on_default.png
index 16cc9e2f3..8485d5c0d 100644
Binary files a/res/drawable-xhdpi/route_phone_on_default.png and b/res/drawable-xhdpi/route_phone_on_default.png differ
diff --git a/res/drawable-xhdpi/route_speaker_on_default.png b/res/drawable-xhdpi/route_speaker_on_default.png
index 8cfdc036a..5601ecd8e 100644
Binary files a/res/drawable-xhdpi/route_speaker_on_default.png and b/res/drawable-xhdpi/route_speaker_on_default.png differ
diff --git a/res/drawable-xhdpi/routes_default.png b/res/drawable-xhdpi/routes_default.png
index a0502e293..23fa7350f 100644
Binary files a/res/drawable-xhdpi/routes_default.png and b/res/drawable-xhdpi/routes_default.png differ
diff --git a/res/drawable-xhdpi/routes_selected.png b/res/drawable-xhdpi/routes_selected.png
index 9930fd81e..77546f8b2 100644
Binary files a/res/drawable-xhdpi/routes_selected.png and b/res/drawable-xhdpi/routes_selected.png differ
diff --git a/res/layout-FR/chat.xml b/res/layout-FR/chat.xml
index 8acf785ba..5eeb2081a 100644
--- a/res/layout-FR/chat.xml
+++ b/res/layout-FR/chat.xml
@@ -31,7 +31,7 @@
-
-
+
-
-
+
-
-
+
-
-
+
latestImageMessages;
+ private int messagesFilterLimit = 0;
+ private List messagesList;
private ProgressBar progressBar;
private int bytesSent;
@@ -148,7 +152,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
textLayout = (RelativeLayout) view.findViewById(R.id.messageLayout);
messagesLayout = (RelativeLayout) view.findViewById(R.id.messages);
- messagesScrollView = (ScrollView) view.findViewById(R.id.chatScrollView);
+ messagesScrollView = (LinphoneScrollView) view.findViewById(R.id.chatScrollView);
progressBar = (ProgressBar) view.findViewById(R.id.progressbar);
sendImage = (TextView) view.findViewById(R.id.sendPicture);
@@ -171,6 +175,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
});
+ if (savedInstanceState != null) {
+ messagesFilterLimit = savedInstanceState.getInt("messagesFilterLimit");
+ }
displayChat(displayName, pictureUri);
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
@@ -223,10 +230,15 @@ 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);
outState.putParcelable("imageToUpload", imageToUpload);
+ outState.putInt("messagesFilterLimit", messagesFilterLimit);
super.onSaveInstanceState(outState);
}
@@ -255,32 +267,73 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
public void showKeyboardVisibleMode() {
LinphoneActivity.instance().hideMenu(true);
contactPicture.setVisibility(View.GONE);
- scrollToEnd();
+ //scrollToEnd();
}
public void hideKeyboardVisibleMode() {
LinphoneActivity.instance().hideMenu(false);
contactPicture.setVisibility(View.VISIBLE);
- scrollToEnd();
+ //scrollToEnd();
}
private void invalidate() {
- messagesLayout.removeAllViews();
- List messagesList = LinphoneActivity.instance().getChatMessages(sipUri);
+ refreshMessages();
- previousMessageID = -1;
- ChatStorage chatStorage = LinphoneActivity.instance().getChatStorage();
- for (ChatMessage msg : messagesList) {
- if (msg.getMessage() != null) {
- 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);
- }
- chatStorage.markMessageAsRead(msg.getId());
- }
- LinphoneActivity.instance().updateMissedChatCount();
-
- scrollToEnd();
+ 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();
+ }
+ 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();
+
+ for (int i = messagesList.size() - limit; i < messagesList.size(); i++) {
+ ChatMessage msg = messagesList.get(i);
+ if (msg.getMessage() != null) {
+ 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);
+ }
+
+ if (!msg.isRed())
+ chatStorage.markMessageAsRead(msg.getId());
+ }
+ LinphoneActivity.instance().updateMissedChatCount();
+
+ if (limit < messagesList.size()) {
+ messagesScrollView.setScrollViewListener(new ScrollViewListener() {
+ @Override
+ public void OnScrollToTop(final int previousHeight) {
+ invalidate();mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ //Scroll to latest saw message
+ messagesScrollView.scrollTo(0, messagesLayout.getChildAt(MESSAGES_STEP-1).getBottom());
+ }
+ }, 300);
+ }
+ });
+ } else {
+ messagesScrollView.setScrollViewListener(null);
+ }
+ }
+ });
}
private void displayChat(String displayName, String pictureUri) {
@@ -288,8 +341,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
contactName.setText(LinphoneUtils.getUsernameFromAddress(sipUri));
} else if (displayName == null) {
contactName.setText(sipUri);
- }
- else {
+ } else {
contactName.setText(displayName);
}
@@ -299,44 +351,37 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
contactPicture.setImageResource(R.drawable.unknown_small);
}
- messagesScrollView.post(new Runnable() {
- @Override
- public void run() {
- scrollToEnd();
- }
- });
+ if (messagesFilterLimit == 0)
+ invalidate();
+ else {
+ invalidate(messagesFilterLimit);
+ }
- invalidate();
+ scrollToEnd();
}
- private void displayMessage(final int id, final String message, final String time, final boolean isIncoming, final LinphoneChatMessage.State status, final RelativeLayout layout) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- BubbleChat bubble = new BubbleChat(layout.getContext(), id, message, null, time, isIncoming, status, previousMessageID);
- if (!isIncoming) {
- lastSentMessageBubble = bubble;
- }
- previousMessageID = id;
- layout.addView(bubble.getView());
- registerForContextMenu(bubble.getView());
- }
- });
+ private void displayMessage(int id, String message, String time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout) {
+ BubbleChat bubble = new BubbleChat(layout.getContext(), id, message, null, time, isIncoming, status, previousMessageID);
+ if (!isIncoming) {
+ lastSentMessageBubble = bubble;
+ }
+
+ View v = bubble.getView();
+ previousMessageID = id;
+ layout.addView(v);
+ registerForContextMenu(v);
}
- private void displayImageMessage(final int id, final Bitmap image, final String time, final boolean isIncoming, final LinphoneChatMessage.State status, final RelativeLayout layout) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- BubbleChat bubble = new BubbleChat(layout.getContext(), id, null, image, time, isIncoming, status, previousMessageID);
- if (!isIncoming) {
- lastSentMessageBubble = bubble;
- }
- previousMessageID = id;
- layout.addView(bubble.getView());
- registerForContextMenu(bubble.getView());
- }
- });
+ private void displayImageMessage(int id, Bitmap image, String time, boolean isIncoming, LinphoneChatMessage.State status, RelativeLayout layout) {
+ BubbleChat bubble = new BubbleChat(layout.getContext(), id, null, image, time, isIncoming, status, previousMessageID);
+ if (!isIncoming) {
+ lastSentMessageBubble = bubble;
+ }
+
+ View v = bubble.getView();
+ previousMessageID = id;
+ layout.addView(v);
+ registerForContextMenu(v);
}
public void changeDisplayedChat(String newSipUri, String displayName, String pictureUri) {
@@ -351,6 +396,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
LinphoneActivity.instance().getChatStorage().deleteDraft(sipUri);
}
+ messagesFilterLimit = 0;
sipUri = newSipUri;
if (LinphoneActivity.isInstanciated()) {
String draft = LinphoneActivity.instance().getChatStorage().getDraft(sipUri);
@@ -444,7 +490,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
LinphoneActivity.instance().selectMenu(FragmentsAvailable.CHAT);
LinphoneActivity.instance().updateChatFragment(this);
}
- scrollToEnd();
if (LinphoneActivity.isInstanciated()) {
String draft = LinphoneActivity.instance().getChatStorage().getDraft(sipUri);
@@ -503,7 +548,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
private void scrollToEnd() {
- mHandler.postDelayed(new Runnable() {
+ messagesScrollView.postDelayed(new Runnable() {
@Override
public void run() {
messagesScrollView.fullScroll(View.FOCUS_DOWN);
@@ -519,14 +564,24 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
}
- public void onMessageReceived(int id, LinphoneAddress from, LinphoneChatMessage message) {
+ public void onMessageReceived(final int id, LinphoneAddress from, final LinphoneChatMessage message) {
if (from.asStringUriOnly().equals(sipUri)) {
if (message.getText() != null) {
- displayMessage(id, message.getText(), String.valueOf(System.currentTimeMillis()), true, null, messagesLayout);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ displayMessage(id, message.getText(), String.valueOf(System.currentTimeMillis()), true, null, messagesLayout);
+ }
+ });
} else if (message.getExternalBodyUrl() != null) {
byte[] rawImage = LinphoneActivity.instance().getChatStorage().getRawImageFromMessage(id);
- Bitmap bm = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
- displayImageMessage(id, bm, String.valueOf(System.currentTimeMillis()), true, null, messagesLayout);
+ final Bitmap bm = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ displayImageMessage(id, bm, String.valueOf(System.currentTimeMillis()), true, null, messagesLayout);
+ }
+ });
}
scrollToEnd();
}
diff --git a/src/org/linphone/ChatMessage.java b/src/org/linphone/ChatMessage.java
index 081784ef9..185e3f474 100644
--- a/src/org/linphone/ChatMessage.java
+++ b/src/org/linphone/ChatMessage.java
@@ -33,8 +33,9 @@ public class ChatMessage {
private int status;
private int id;
private Bitmap image;
+ private boolean isRed;
- public ChatMessage(int id, String message, byte[] rawImage, String timestamp, boolean incoming, int status) {
+ public ChatMessage(int id, String message, byte[] rawImage, String timestamp, boolean incoming, int status, boolean red) {
super();
this.id = id;
this.message = message;
@@ -42,6 +43,7 @@ public class ChatMessage {
this.incoming = incoming;
this.status = status;
this.image = rawImage != null ? BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length) : null;
+ this.isRed = red;
}
public int getId() {
@@ -83,4 +85,8 @@ public class ChatMessage {
public Bitmap getImage() {
return image;
}
+
+ public boolean isRed() {
+ return isRed;
+ }
}
diff --git a/src/org/linphone/ChatStorage.java b/src/org/linphone/ChatStorage.java
index 54c9f4a8c..714de16b1 100644
--- a/src/org/linphone/ChatStorage.java
+++ b/src/org/linphone/ChatStorage.java
@@ -193,8 +193,9 @@ public class ChatStorage {
timestamp = c.getString(c.getColumnIndex("time"));
int status = c.getInt(c.getColumnIndex("status"));
byte[] rawImage = c.getBlob(c.getColumnIndex("image"));
+ int read = c.getInt(c.getColumnIndex("read"));
- ChatMessage chatMessage = new ChatMessage(id, message, rawImage, timestamp, direction == INCOMING, status);
+ ChatMessage chatMessage = new ChatMessage(id, message, rawImage, timestamp, direction == INCOMING, status, read == READ);
chatMessages.add(chatMessage);
} catch (Exception e) {
e.printStackTrace();
diff --git a/src/org/linphone/ui/LinphoneScrollView.java b/src/org/linphone/ui/LinphoneScrollView.java
new file mode 100644
index 000000000..39d9a60ab
--- /dev/null
+++ b/src/org/linphone/ui/LinphoneScrollView.java
@@ -0,0 +1,56 @@
+package org.linphone.ui;
+/*
+LinphoneScrollView.java
+Copyright (C) 2013 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ScrollView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class LinphoneScrollView extends ScrollView {
+ private ScrollViewListener scrollViewListener = null;
+
+ public LinphoneScrollView(Context context) {
+ super(context);
+ }
+
+ public LinphoneScrollView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public LinphoneScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setScrollViewListener(ScrollViewListener scrollViewListener) {
+ this.scrollViewListener = scrollViewListener;
+ }
+
+ @Override
+ protected void onScrollChanged(int x, int y, int oldx, int oldy) {
+ super.onScrollChanged(x, y, oldx, oldy);
+ if (y >= getMeasuredHeight() && scrollViewListener != null) {
+ //scrollViewListener.OnScrollToBottom();
+ }
+ else if (y == 0 && scrollViewListener != null) {
+ scrollViewListener.OnScrollToTop(getMeasuredHeight());
+ }
+ }
+}
diff --git a/src/org/linphone/ui/ScrollViewListener.java b/src/org/linphone/ui/ScrollViewListener.java
new file mode 100644
index 000000000..ff964f604
--- /dev/null
+++ b/src/org/linphone/ui/ScrollViewListener.java
@@ -0,0 +1,26 @@
+package org.linphone.ui;
+/*
+ScrollViewListener.java
+Copyright (C) 2013 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @author Sylvain Berfini
+ */
+public interface ScrollViewListener {
+ void OnScrollToTop(int previousHeight);
+}