diff --git a/res/layout/chat_bubble_alt_outgoing.xml b/res/layout/chat_bubble_alt_outgoing.xml index 8b2d3ae6a..8a0f994b4 100644 --- a/res/layout/chat_bubble_alt_outgoing.xml +++ b/res/layout/chat_bubble_alt_outgoing.xml @@ -5,6 +5,13 @@ android:layout_height="wrap_content" android:orientation="horizontal" > + + - + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 1b3454476..001fe9aea 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -301,6 +301,7 @@ + Account Setup Assistant diff --git a/src/org/linphone/ChatFragment.java b/src/org/linphone/ChatFragment.java index e76ff7e46..666d77aff 100644 --- a/src/org/linphone/ChatFragment.java +++ b/src/org/linphone/ChatFragment.java @@ -21,6 +21,8 @@ import java.util.List; import org.linphone.LinphoneSimpleListener.LinphoneOnMessageReceivedListener; import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneChatMessage; +import org.linphone.core.LinphoneChatMessage.State; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; import org.linphone.ui.AvatarWithShadow; @@ -46,7 +48,7 @@ import android.widget.TextView; /** * @author Sylvain Berfini */ -public class ChatFragment extends Fragment implements OnClickListener, LinphoneOnMessageReceivedListener { +public class ChatFragment extends Fragment implements OnClickListener, LinphoneOnMessageReceivedListener, LinphoneChatMessage.StateListener { private LinphoneChatRoom chatRoom; private View view; private String sipUri; @@ -57,6 +59,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneO private ScrollView messagesScrollView; private int previousMessageID; private Handler mHandler = new Handler(); + private BubbleChat lastSentMessageBubble; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -93,7 +96,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneO previousMessageID = -1; ChatStorage chatStorage = LinphoneActivity.instance().getChatStorage(); for (ChatMessage msg : messagesList) { - displayMessage(msg.getId(), msg.getMessage(), msg.getTimestamp(), msg.isIncoming(), messagesLayout); + displayMessage(msg.getId(), msg.getMessage(), msg.getTimestamp(), msg.isIncoming(), msg.getStatus(), messagesLayout); chatStorage.markMessageAsRead(msg.getId()); } LinphoneActivity.instance().updateMissedChatCount(); @@ -125,11 +128,14 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneO invalidate(); } - private void displayMessage(final int id, final String message, final String time, final boolean isIncoming, final RelativeLayout layout) { + 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, time, isIncoming, previousMessageID); + BubbleChat bubble = new BubbleChat(layout.getContext(), id, message, time, isIncoming, status, previousMessageID); + if (!isIncoming) { + lastSentMessageBubble = bubble; + } previousMessageID = id; layout.addView(bubble.getView()); registerForContextMenu(bubble.getView()); @@ -170,14 +176,15 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneO if (chatRoom != null && message != null && message.getText().length() > 0) { String messageToSend = message.getText().toString(); message.setText(""); - - chatRoom.sendMessage(messageToSend); + + LinphoneChatMessage chatMessage = chatRoom.createLinphoneChatMessage(messageToSend); + chatRoom.sendMessage(chatMessage, this); if (LinphoneActivity.isInstanciated()) { LinphoneActivity.instance().onMessageSent(sipUri, messageToSend); } - displayMessage(previousMessageID + 2, messageToSend, String.valueOf(System.currentTimeMillis()), false, messagesLayout); + displayMessage(previousMessageID + 2, messageToSend, String.valueOf(System.currentTimeMillis()), false, State.InProgress, messagesLayout); scrollToEnd(); } } @@ -195,10 +202,25 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneO public void onMessageReceived(LinphoneAddress from, String message) { if (from.asStringUriOnly().equals(sipUri)) { int id = previousMessageID + 2; - displayMessage(id, message, String.valueOf(System.currentTimeMillis()), true, messagesLayout); + displayMessage(id, message, String.valueOf(System.currentTimeMillis()), true, null, messagesLayout); scrollToEnd(); } } + + @Override + public void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, State state) { + final String finalMessage = msg.getMessage(); + final State finalState = state; + if (LinphoneActivity.isInstanciated() && state != State.InProgress) { + mHandler.post(new Runnable() { + @Override + public void run() { + LinphoneActivity.instance().onMessageStateChanged(sipUri, finalMessage, finalState.toInt()); + lastSentMessageBubble.updateStatusView(finalState); + } + }); + } + } public String getSipUri() { return sipUri; diff --git a/src/org/linphone/ChatMessage.java b/src/org/linphone/ChatMessage.java index 1b6bc42b3..cbd8b30fa 100644 --- a/src/org/linphone/ChatMessage.java +++ b/src/org/linphone/ChatMessage.java @@ -1,4 +1,6 @@ package org.linphone; + +import org.linphone.core.LinphoneChatMessage; /* ChatMessage.java Copyright (C) 2012 Belledonne Communications, Grenoble, France @@ -25,14 +27,16 @@ public class ChatMessage { private String message; private String timestamp; private boolean incoming; + private int status; private int id; - public ChatMessage(int id, String message, String timestamp, boolean incoming) { + public ChatMessage(int id, String message, String timestamp, boolean incoming, int status) { super(); this.id = id; this.message = message; this.timestamp = timestamp; this.incoming = incoming; + this.status = status; } public int getId() { @@ -62,4 +66,12 @@ public class ChatMessage { public void setIncoming(boolean incoming) { this.incoming = incoming; } + + public void setStatus(int status) { + this.status = status; + } + + public LinphoneChatMessage.State getStatus() { + return LinphoneChatMessage.State.fromInt(status); + } } diff --git a/src/org/linphone/ChatStorage.java b/src/org/linphone/ChatStorage.java index 8df0b5c23..e50cfd5d0 100644 --- a/src/org/linphone/ChatStorage.java +++ b/src/org/linphone/ChatStorage.java @@ -20,6 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import java.util.ArrayList; import java.util.List; +import org.linphone.core.LinphoneChatMessage; + import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -48,6 +50,12 @@ public class ChatStorage { db.close(); } + public void updateMessageStatus(String to, String message, int status) { + ContentValues values = new ContentValues(); + values.put("status", status); + db.update(TABLE_NAME, values, "direction LIKE " + OUTGOING + " AND remoteContact LIKE \"" + to + "\" AND message LIKE \"" + message + "\"", null); + } + public int saveMessage(String from, String to, String message) { ContentValues values = new ContentValues(); if (from.equals("")) { @@ -55,11 +63,13 @@ public class ChatStorage { values.put("remoteContact", to); values.put("direction", OUTGOING); values.put("read", READ); + values.put("status", LinphoneChatMessage.State.InProgress.toInt()); } else if (to.equals("")) { values.put("localContact", to); values.put("remoteContact", from); values.put("direction", INCOMING); values.put("read", NOT_READ); + values.put("status", LinphoneChatMessage.State.Idle.toInt()); } values.put("message", message); values.put("time", System.currentTimeMillis()); @@ -77,8 +87,9 @@ public class ChatStorage { int direction = c.getInt(c.getColumnIndex("direction")); message = c.getString(c.getColumnIndex("message")); timestamp = c.getString(c.getColumnIndex("time")); + int status = c.getInt(c.getColumnIndex("status")); - ChatMessage chatMessage = new ChatMessage(id, message, timestamp, direction == INCOMING); + ChatMessage chatMessage = new ChatMessage(id, message, timestamp, direction == INCOMING, status); chatMessages.add(chatMessage); } @@ -117,7 +128,7 @@ public class ChatStorage { class ChatHelper extends SQLiteOpenHelper { - private static final int DATABASE_VERSION = 2; + private static final int DATABASE_VERSION = 3; private static final String DATABASE_NAME = "linphone-android"; ChatHelper(Context context) { @@ -126,7 +137,7 @@ public class ChatStorage { @Override public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, localContact TEXT NOT NULL, remoteContact TEXT NOT NULL, direction INTEGER, message TEXT NOT NULL, time NUMERIC, read INTEGER);"); + db.execSQL("CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, localContact TEXT NOT NULL, remoteContact TEXT NOT NULL, direction INTEGER, message TEXT NOT NULL, time NUMERIC, read INTEGER, status INTEGER);"); } @Override diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index eb952a462..06c8a907d 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -614,6 +614,10 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene public void onMessageSent(String to, String message) { getChatStorage().saveMessage("", to, message); } + + public void onMessageStateChanged(String to, String message, int newState) { + getChatStorage().updateMessageStatus(to, message, newState); + } @Override public void onRegistrationStateChanged(RegistrationState state) { diff --git a/src/org/linphone/StatusFragment.java b/src/org/linphone/StatusFragment.java index d01d45c9b..925ca4557 100644 --- a/src/org/linphone/StatusFragment.java +++ b/src/org/linphone/StatusFragment.java @@ -372,7 +372,6 @@ public class StatusFragment extends Fragment { zrtpToast = new Toast(getActivity()); zrtpToast.setGravity(Gravity.TOP | Gravity.RIGHT, 0, LinphoneUtils.pixelsToDpi(getResources(), 40)); zrtpToast.setDuration(Toast.LENGTH_LONG); - zrtpToast.setView(layout); ImageView ok = (ImageView) layout.findViewById(R.id.toastOK); ok.setOnClickListener(new OnClickListener() { @@ -419,8 +418,9 @@ public class StatusFragment extends Fragment { }; - zrtpToast.show(); + zrtpToast.setView(layout); hideZrtpToast = false; + zrtpToast.show(); zrtpHack.start(); } diff --git a/src/org/linphone/core/LinphoneChatMessageImpl.java b/src/org/linphone/core/LinphoneChatMessageImpl.java new file mode 100644 index 000000000..a6c54e10d --- /dev/null +++ b/src/org/linphone/core/LinphoneChatMessageImpl.java @@ -0,0 +1,38 @@ +package org.linphone.core; + +public class LinphoneChatMessageImpl implements LinphoneChatMessage { + protected final long nativePtr; + private native void setUserData(long ptr); + private native String getMessage(long ptr); + private native LinphoneAddress getPeerAddress(long ptr); + + protected LinphoneChatMessageImpl(long aNativePtr) { + nativePtr = aNativePtr; + setUserData(); + } + + public long getNativePtr() { + return nativePtr; + } + + @Override + public Object getUserData() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setUserData() { + setUserData(nativePtr); + } + + @Override + public String getMessage() { + return getMessage(nativePtr); + } + + @Override + public LinphoneAddress getPeerAddress() { + return getPeerAddress(nativePtr); + } +} diff --git a/src/org/linphone/core/LinphoneChatRoomImpl.java b/src/org/linphone/core/LinphoneChatRoomImpl.java index 3513f3283..806e8b68a 100644 --- a/src/org/linphone/core/LinphoneChatRoomImpl.java +++ b/src/org/linphone/core/LinphoneChatRoomImpl.java @@ -22,8 +22,10 @@ import org.linphone.core.LinphoneChatMessage.StateListener; class LinphoneChatRoomImpl implements LinphoneChatRoom { protected final long nativePtr; + private native long createLinphoneChatMessage(long ptr, String message); private native long getPeerAddress(long ptr); private native void sendMessage(long ptr, String message); + private native void sendMessage2(long ptr, long message, StateListener listener); protected LinphoneChatRoomImpl(long aNativePtr) { nativePtr = aNativePtr; @@ -36,9 +38,10 @@ class LinphoneChatRoomImpl implements LinphoneChatRoom { public void sendMessage(String message) { sendMessage(nativePtr,message); } + @Override - public void sendMessage(LinphoneChatMessage msg, StateListener listener) { - // TODO To be implemened + public void sendMessage(LinphoneChatMessage message, StateListener listener) { + sendMessage2(nativePtr, message.getNativePtr(), listener); } @Override @@ -46,4 +49,8 @@ class LinphoneChatRoomImpl implements LinphoneChatRoom { // ignore, deprecated. } + @Override + public LinphoneChatMessage createLinphoneChatMessage(String message) { + return new LinphoneChatMessageImpl(createLinphoneChatMessage(nativePtr, message)); + } } diff --git a/src/org/linphone/ui/BubbleChat.java b/src/org/linphone/ui/BubbleChat.java index 69cf44e1e..0e69cb28e 100644 --- a/src/org/linphone/ui/BubbleChat.java +++ b/src/org/linphone/ui/BubbleChat.java @@ -24,6 +24,7 @@ import java.util.Map.Entry; import org.linphone.LinphoneUtils; import org.linphone.R; +import org.linphone.core.LinphoneChatMessage; import android.content.Context; import android.graphics.Color; @@ -32,6 +33,7 @@ import android.text.SpannableStringBuilder; import android.text.style.ImageSpan; import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; @@ -74,8 +76,9 @@ public class BubbleChat { } private RelativeLayout view; + private ImageView statusView; - public BubbleChat(Context context, int id, String message, String time, boolean isIncoming, int previousID) { + public BubbleChat(Context context, int id, String message, String time, boolean isIncoming, LinphoneChatMessage.State status, int previousID) { view = new RelativeLayout(context); LayoutParams layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); @@ -98,45 +101,43 @@ public class BubbleChat { view.setLayoutParams(layoutParams); if (context.getResources().getBoolean(R.bool.display_messages_time)) { + LinearLayout layout; if (context.getResources().getBoolean(R.bool.display_time_aside)) { - LinearLayout layout; if (isIncoming) { layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.chat_bubble_alt_incoming, null); } else { layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.chat_bubble_alt_outgoing, null); } - - TextView msgView = (TextView) layout.findViewById(R.id.message); - if (context.getResources().getBoolean(R.bool.emoticons_in_messages)) { - msgView.setText(getSmiledText(context, message)); - } else { - msgView.setText(message); - } - - TextView timeView = (TextView) layout.findViewById(R.id.time); - timeView.setText(timestampToHumanDate(context, time)); - - view.addView(layout); } else { - LinearLayout layout; if (isIncoming) { layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.chat_bubble_incoming, null); } else { layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.chat_bubble_outgoing, null); } - - TextView msgView = (TextView) layout.findViewById(R.id.message); - if (context.getResources().getBoolean(R.bool.emoticons_in_messages)) { - msgView.setText(getSmiledText(context, message)); - } else { - msgView.setText(message); - } - - TextView timeView = (TextView) layout.findViewById(R.id.time); - timeView.setText(timestampToHumanDate(context, time)); - - view.addView(layout); } + + TextView msgView = (TextView) layout.findViewById(R.id.message); + if (context.getResources().getBoolean(R.bool.emoticons_in_messages)) { + msgView.setText(getSmiledText(context, message)); + } else { + msgView.setText(message); + } + + TextView timeView = (TextView) layout.findViewById(R.id.time); + timeView.setText(timestampToHumanDate(context, time)); + + statusView = (ImageView) layout.findViewById(R.id.status); + if (statusView != null) { + if (status == LinphoneChatMessage.State.Delivered) { + statusView.setImageResource(R.drawable.led_connected); //FIXME + } else if (status == LinphoneChatMessage.State.NotDelivered) { + statusView.setImageResource(R.drawable.led_error); //FIXME + } else { + statusView.setImageResource(R.drawable.led_inprogress); //FIXME + } + } + + view.addView(layout); } else { TextView messageView = new TextView(context); messageView.setId(id); @@ -153,6 +154,21 @@ public class BubbleChat { } } + public void updateStatusView(LinphoneChatMessage.State status) { + if (statusView == null) { + return; + } + + if (status == LinphoneChatMessage.State.Delivered) { + statusView.setImageResource(R.drawable.led_connected); //FIXME + } else if (status == LinphoneChatMessage.State.NotDelivered) { + statusView.setImageResource(R.drawable.led_error); //FIXME + } else { + statusView.setImageResource(R.drawable.led_inprogress); //FIXME + } + view.invalidate(); + } + public View getView() { return view; } diff --git a/submodules/linphone b/submodules/linphone index 3e7882dab..d74083cfa 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit 3e7882dabf2b486f60d5e0516c50ce195ebd0ac5 +Subproject commit d74083cfaa2d757ab16588490c1844b090687f18