package org.gsm.rcsApp.activities; import java.io.UnsupportedEncodingException; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.entity.StringEntity; import org.gsm.rcsApp.ServiceURL; import org.gsm.rcsApp.RCS.ChatMessage; import org.gsm.rcsApp.RCS.Contact; import org.gsm.rcsApp.RCS.ContactState; import org.gsm.rcsApp.RCS.ContactStateManager; import org.gsm.rcsApp.adapters.MessageRowAdapter; import org.gsm.rcsApp.misc.RCSJsonHttpResponseHandler; import org.gsm.rcsApp.misc.Utils; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.Editable; import android.util.Log; import android.view.View; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import com.loopj.android.http.AsyncHttpClient; import org.gsm.RCSDemo.R; public class ChatSessionActivity extends Activity implements Runnable { private static String destinationUri=null; private static String displayName=null; static boolean running=false; static Thread background=null; private static ContactState contactState=null; static MessageRowAdapter mradapter=null; static ListView messageListView=null; static TextView isComposingIndicator=null; private static Handler messageHandler = null; private static Handler composingIndicatorHandler = null; static boolean sentComposing=false; static ChatSessionActivity _instance=null; private static boolean viewIsVisible=false; private static final String RECEIVED_MESSAGE="receivedMessage"; private static final int COMPOSINGUPDATEFREQUENCYSECONDS=60; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chatsession); _instance=this; Intent intent = getIntent(); Contact retrievedContact=intent.getParcelableExtra(MainActivity.SELECTED_CONTACT); displayName=retrievedContact.getDisplayName(); if (displayName==null) displayName=retrievedContact.getContactId(); this.setTitle(displayName); destinationUri=retrievedContact.getContactId(); messageListView=(ListView) findViewById(R.id.messageList); isComposingIndicator=(TextView) findViewById(R.id.isComposingIndicator); mradapter=new MessageRowAdapter(this, contactState.getMessageBuffer()); messageListView.setAdapter(mradapter); messageHandler = new Handler() { public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.getData()!=null) { Bundle data=msg.getData(); if (data.containsKey(RECEIVED_MESSAGE)) { ChatMessage receivedMessage=(ChatMessage) data.get(RECEIVED_MESSAGE); if (receivedMessage!=null) { contactState.getMessageBuffer().add(receivedMessage); } } } messageListView.smoothScrollToPosition(contactState.getMessageBuffer()!=null?(contactState.getMessageBuffer().size() - 1):0); mradapter.notifyDataSetChanged(); messageListView.refreshDrawableState(); } }; composingIndicatorHandler = new Handler() { public void handleMessage(Message msg) { if (msg.what==1) { isComposingIndicator.setVisibility(View.VISIBLE); isComposingIndicator.setText(displayName+" is writing"); } else { isComposingIndicator.setVisibility(View.INVISIBLE); } } }; if (!running) { running=true; background=new Thread(this); background.start(); } } public void onStart() { super.onStart(); if (!running) { running=true; background=new Thread(this); background.start(); } viewIsVisible=true; isComposingIndicator.setVisibility(View.INVISIBLE); } public void onStop() { super.onStop(); running=false; if (background!=null) { background.interrupt(); background=null; } viewIsVisible=false; if (sentComposing) { clearComposingIndicator(); } MainActivity.chatSessionClosed(destinationUri); } /* * invoked when the user presses the button to send a message */ public void sendMessageClicked(View view) { EditText messageInputBox=(EditText) findViewById(R.id.message_input_box); final Editable text=messageInputBox.getText(); Thread t = new Thread(){ public void run(){ String trimmed=text.toString().trim(); if (trimmed.length()>0) { ChatMessage sent=new ChatMessage(); sent.setMessageText(trimmed); sent.setMessageDirection(ChatMessage.MESSAGE_SENT); sent.setMessageTime(Utils.getNowAsDisplayString()); sent.setViewed(true); sent.setStatus(ChatMessage.MESSAGE_STATUS_PENDING); sent.setContactUri(destinationUri); contactState.storeMessage(sent); sendAdhocMessage(trimmed, sent); messageHandler.sendEmptyMessage(0); } } }; t.start(); messageInputBox.setText(""); } /* * invoked when the list of messages needs to be restored from saved state */ public static boolean refreshMessageList(String contactUri, String recipient, ChatMessage chatMessage) { boolean viewed=false; if (recipient!=null && recipient.equals(SplashActivity.userId)) { Message newChatMessage=new Message(); Bundle msgBundle=new Bundle(); msgBundle.putParcelable(RECEIVED_MESSAGE, chatMessage); newChatMessage.setData(msgBundle); messageHandler.sendMessage(newChatMessage); viewed=viewIsVisible; } return viewed; } /* * sends a message in ad-hoc mode */ private void sendAdhocMessage(String message, ChatMessage chatMessage) { try { final String url=ServiceURL.sendAdhocIMMessageURL(SplashActivity.userId, destinationUri); final String messageInternalId=chatMessage.getMessageInternalId(); ContactStateManager.registerOutgoingMessage(chatMessage); JSONObject chatMessageJSON=new JSONObject(); chatMessageJSON.put("reportRequest", "Sent"); // possible status values are "Sent,Delivered,Displayed,Failed" chatMessageJSON.put("text", message); String jsonData="{\"chatMessage\":"+chatMessageJSON.toString()+"}"; AsyncHttpClient client = new AsyncHttpClient(); AuthScope authscope=new AuthScope(ServiceURL.serverName, ServiceURL.serverPort, AuthScope.ANY_REALM); client.setBasicAuth(SplashActivity.userId, SplashActivity.appCredentialPassword, authscope); try { StringEntity requestData=new StringEntity(jsonData); client.post(_instance.getApplication().getApplicationContext(), url, requestData, "application/json", new RCSJsonHttpResponseHandler() { @Override public void onSuccess(JSONObject response, int errorCode) { Log.d("ChatSessionActivity", "sendAdhocMessage::success = "+response.toString()+" errorCode="+errorCode); JSONObject resourceReference=Utils.getJSONObject(response, "resourceReference"); String resourceURL=Utils.getJSONStringElement(resourceReference, "resourceURL"); String messageId=Utils.getMessageIdFromResourceURL(resourceURL, destinationUri); Log.d("ChatSessionActivity", "messageId="+messageId+" resourceURL="+resourceURL); if (errorCode==201) { ContactStateManager.setMessageIdForSentMessage(messageInternalId, messageId, resourceURL); ContactStateManager.updateStatusFor(messageId, ChatMessage.MESSAGE_STATUS_SENT); messageHandler.sendEmptyMessage(0); } } }); } catch (UnsupportedEncodingException e) { } } catch (JSONException e1) { } } /* * background thread to deal with 'isComposing' indicator */ public void run() { long lastSent=0; while (running) { try { Thread.sleep (1000); if (ContactStateManager.haveSentMessageTo(destinationUri) && running) { EditText messageInputBox=(EditText) findViewById(R.id.message_input_box); Editable text=messageInputBox.getText(); String trimmed=text.toString().trim(); if (trimmed.length()>0) { long now=System.currentTimeMillis(); if ((now-lastSent)>=(COMPOSINGUPDATEFREQUENCYSECONDS*1000)) { sendComposingIndicator(SplashActivity.userId, destinationUri,"active",COMPOSINGUPDATEFREQUENCYSECONDS,new java.util.Date(), "text/plain"); lastSent=now; } sentComposing=true; } else if (sentComposing) { clearComposingIndicator(); lastSent=0; } } } catch (InterruptedException ie) {} } } /* * send the isComposing indicator (generic) */ private void sendComposingIndicator(String userId, String contactId, String state, int refresh, java.util.Date lastActive, String contentType) { final String pingUrl=ServiceURL.getSendIsComposingAutomaticContactURL(SplashActivity.userId, contactId); JSONObject isComposing=new JSONObject(); try { AsyncHttpClient client = new AsyncHttpClient(); AuthScope authscope=new AuthScope(ServiceURL.serverName, ServiceURL.serverPort, AuthScope.ANY_REALM); client.setBasicAuth(SplashActivity.userId, SplashActivity.appCredentialPassword, authscope); isComposing.put("state", state); isComposing.put("refresh", refresh); isComposing.put("contentType", contentType); //isComposing.put("lastActive", lastActive); String jsonData="{\"isComposing\":"+isComposing.toString()+"}"; try { StringEntity requestData=new StringEntity(jsonData); client.post(_instance.getApplication().getApplicationContext(), pingUrl, requestData, "application/json", new RCSJsonHttpResponseHandler() { @Override public void onSuccess(JSONObject response, int statusCode) { Log.d("ChatSessionActivity", "sendComposingIndicator::response = "+(response!=null?response.toString():null)+" statusCode="+statusCode); } }); } catch (UnsupportedEncodingException e) { } } catch (JSONException e) { } } /* * clear the isComposing indicator */ private void clearComposingIndicator() { sendComposingIndicator(SplashActivity.userId, destinationUri,"idle",15,new java.util.Date(), "text/plain"); sentComposing=false; } /* * called on startup of the activity to set the state of the contact */ public static void setContactState(ContactState contactState) { ChatSessionActivity.contactState=contactState; } /* * called from the MainActivity when a message (send) status has been updated */ public static void updateStatus(String messageId, String status) { ContactStateManager.updateStatusFor(messageId, status); messageHandler.sendEmptyMessage(0); } /* * called from the MainActivity when the isComposing indicator has been received */ public static void updateComposingIndicator(String state) { int code=(state!=null && state.equalsIgnoreCase("active"))?1:0; composingIndicatorHandler.sendEmptyMessage(code); } }