Java tutorial
/******************************************************************************* * Software Name : RCS IMS Stack * <p/> * Copyright (C) 2010-2016 Orange. * <p/> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.gsma.rcs.ri.messaging; import com.gsma.rcs.api.connection.ConnectionManager; import com.gsma.rcs.api.connection.utils.ExceptionUtil; import com.gsma.rcs.api.connection.utils.RcsFragmentActivity; import com.gsma.rcs.ri.R; import com.gsma.rcs.ri.RI; import com.gsma.rcs.ri.messaging.adapter.TalkCursorAdapter; import com.gsma.rcs.ri.messaging.chat.ChatCursorObserver; import com.gsma.rcs.ri.messaging.chat.ChatMessageLogView; import com.gsma.rcs.ri.messaging.chat.ChatPendingIntentManager; import com.gsma.rcs.ri.messaging.chat.IsComposingManager; import com.gsma.rcs.ri.messaging.chat.single.SendSingleFile; import com.gsma.rcs.ri.messaging.chat.single.SingleChatIntentService; import com.gsma.rcs.ri.messaging.filetransfer.FileTransferIntentService; import com.gsma.rcs.ri.messaging.filetransfer.FileTransferLogView; import com.gsma.rcs.ri.messaging.geoloc.EditGeoloc; import com.gsma.rcs.ri.utils.ContactUtil; import com.gsma.rcs.ri.utils.LogUtils; import com.gsma.rcs.ri.utils.RcsContactUtil; import com.gsma.rcs.ri.utils.Utils; import com.gsma.services.rcs.Geoloc; import com.gsma.services.rcs.RcsGenericException; import com.gsma.services.rcs.RcsPermissionDeniedException; import com.gsma.services.rcs.RcsPersistentStorageException; import com.gsma.services.rcs.RcsService; import com.gsma.services.rcs.RcsService.Direction; import com.gsma.services.rcs.RcsServiceException; import com.gsma.services.rcs.RcsServiceNotAvailableException; import com.gsma.services.rcs.RcsServiceNotRegisteredException; import com.gsma.services.rcs.capability.CapabilityService; import com.gsma.services.rcs.chat.ChatLog; import com.gsma.services.rcs.chat.ChatService; import com.gsma.services.rcs.chat.ChatServiceConfiguration; import com.gsma.services.rcs.chat.OneToOneChat; import com.gsma.services.rcs.chat.OneToOneChatIntent; import com.gsma.services.rcs.chat.OneToOneChatListener; import com.gsma.services.rcs.contact.ContactId; import com.gsma.services.rcs.filetransfer.FileTransfer; import com.gsma.services.rcs.filetransfer.FileTransferIntent; import com.gsma.services.rcs.filetransfer.FileTransferLog; import com.gsma.services.rcs.filetransfer.FileTransferService; import com.gsma.services.rcs.filetransfer.OneToOneFileTransferListener; import com.gsma.services.rcs.history.HistoryLog; import com.gsma.services.rcs.history.HistoryUriBuilder; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.text.Editable; import android.text.InputFilter; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * One to one talk view : aggregates the RCS IM messages. * * @author Philippe LEMORDANT */ public class OneToOneTalkView extends RcsFragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> { /** * The loader's unique ID. Loader IDs are specific to the Activity in which they reside. */ private static final int LOADER_ID = 1; // @formatter:off private static final String[] PROJECTION = new String[] { HistoryLog.BASECOLUMN_ID, HistoryLog.ID, HistoryLog.PROVIDER_ID, HistoryLog.MIME_TYPE, HistoryLog.CONTENT, HistoryLog.TIMESTAMP, HistoryLog.STATUS, HistoryLog.DIRECTION, HistoryLog.CONTACT, HistoryLog.EXPIRED_DELIVERY, HistoryLog.FILENAME, HistoryLog.FILESIZE, HistoryLog.TRANSFERRED, HistoryLog.REASON_CODE, HistoryLog.READ_STATUS }; // @formatter:on private final static String EXTRA_CONTACT = "contact"; private static final String LOGTAG = LogUtils.getTag(OneToOneTalkView.class.getSimpleName()); /** * Chat_id is set to contact id for one to one chat and file transfer messages. */ private static final String WHERE_CLAUSE = HistoryLog.CHAT_ID + "=?"; private final static String ORDER_ASC = HistoryLog.TIMESTAMP + " ASC"; private static final String OPEN_TALK = "open_talk"; private final static int SELECT_GEOLOCATION = 0; /** * The adapter that binds data to the ListView */ private TalkCursorAdapter mAdapter; private Uri mUriHistoryProvider; private ContactId mContact; private ChatCursorObserver mObserver; private EditText mComposeText; private ChatService mChatService; private OneToOneChat mChat; private FileTransferService mFileTransferService; private OneToOneChatListener mChatListener; private OneToOneFileTransferListener mFileTransferListener; private Handler mHandler; private AlertDialog mClearUndeliveredAlertDialog; private DialogInterface.OnCancelListener mUndeliveredCancelListener; /** * Utility class to manage the is-composing status */ private IsComposingManager mComposingManager; private CapabilityService mCapabilityService; private Context mCtx; // @formatter:off private static final Set<String> sAllowedIntentActions = new HashSet<>( Arrays.asList(OneToOneChatIntent.ACTION_MESSAGE_DELIVERY_EXPIRED, OneToOneChatIntent.ACTION_NEW_ONE_TO_ONE_CHAT_MESSAGE, FileTransferIntent.ACTION_FILE_TRANSFER_DELIVERY_EXPIRED, OPEN_TALK)); // @formatter:on private DialogInterface.OnClickListener mClearUndeliveredChat; private DialogInterface.OnClickListener mClearUndeliveredFt; private boolean mChatListenerSet; private boolean mFileTransferListenerSet; /** * Forge intent to start XmsView activity * * @param context The context * @param contact The contact ID * @return intent */ public static Intent forgeIntentToOpenConversation(Context context, ContactId contact) { Intent intent = new Intent(context, OneToOneTalkView.class); intent.setAction(OPEN_TALK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(EXTRA_CONTACT, (Parcelable) contact); return intent; } /** * Forge intent to start OneToOneTalkView activity upon reception of a stack event * * @param ctx The context * @param contact The contact ID * @param intent intent * @return intent */ public static Intent forgeIntentOnStackEvent(Context ctx, ContactId contact, Intent intent) { intent.setClass(ctx, OneToOneTalkView.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(EXTRA_CONTACT, (Parcelable) contact); return intent; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.chat_view); if (!isServiceConnected(ConnectionManager.RcsServiceName.CONTACT, ConnectionManager.RcsServiceName.CHAT, ConnectionManager.RcsServiceName.FILE_TRANSFER, ConnectionManager.RcsServiceName.CAPABILITY)) { showMessageThenExit(R.string.label_service_not_available); return; } startMonitorServices(ConnectionManager.RcsServiceName.CONTACT, ConnectionManager.RcsServiceName.CHAT, ConnectionManager.RcsServiceName.FILE_TRANSFER, ConnectionManager.RcsServiceName.CAPABILITY); try { initialize(); processIntent(getIntent()); } catch (RcsServiceException e) { showExceptionThenExit(e); } } private void sendText() { final String text = mComposeText.getText().toString(); if (TextUtils.isEmpty(text)) { return; } try { if (mChat != null) { mChat.sendMessage(text); } mComposeText.setText(null); } catch (RcsServiceException e) { showExceptionThenExit(e); } } private void initialize() throws RcsGenericException, RcsServiceNotAvailableException { mCtx = this; /* Set send button listener */ Button sendBtn = (Button) findViewById(R.id.send_button); sendBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendText(); } }); mHandler = new Handler(); mClearUndeliveredChat = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Set<String> msgIds = SingleChatIntentService.getUndelivered(mCtx, mContact); if (!msgIds.isEmpty()) { try { if (LogUtils.isActive) { Log.d(LOGTAG, "Clear delivery expiration for IDs=" + msgIds); } mChatService.clearMessageDeliveryExpiration(msgIds); } catch (RcsServiceException e) { showException(e); } finally { mClearUndeliveredAlertDialog = null; } } } }; mUndeliveredCancelListener = new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { mClearUndeliveredAlertDialog = null; } }; mClearUndeliveredFt = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { try { Set<String> transferIds = FileTransferIntentService.getUndelivered(mCtx, mContact); if (!transferIds.isEmpty()) { if (LogUtils.isActive) { Log.d(LOGTAG, "Clear delivery expiration for IDs=" + transferIds); } mFileTransferService.clearFileTransferDeliveryExpiration(transferIds); mClearUndeliveredAlertDialog = null; } } catch (RcsServiceException e) { showException(e); } finally { mClearUndeliveredAlertDialog = null; } } }; mChatListener = new OneToOneChatListener() { /* Callback called when an Is-composing event has been received */ @Override public void onComposingEvent(ContactId contact, boolean status) { /* Discard event if not for current contact */ if (!contact.equals(mContact)) { return; } if (LogUtils.isActive) { Log.d(LOGTAG, "onComposingEvent contact=" + contact + " status=" + status); } displayComposingEvent(contact, status); } @Override public void onMessageStatusChanged(ContactId contact, String mimeType, String msgId, ChatLog.Message.Content.Status status, ChatLog.Message.Content.ReasonCode reasonCode) { if (LogUtils.isActive) { Log.d(LOGTAG, "onMessageStatusChanged contact=" + contact + " mime-type=" + mimeType + " msgId=" + msgId + " status=" + status); } } @Override public void onMessagesDeleted(ContactId contact, Set<String> msgIds) { if (LogUtils.isActive) { Log.d(LOGTAG, "onMessagesDeleted contact=" + contact + " for IDs=" + msgIds); } } }; mFileTransferListener = new OneToOneFileTransferListener() { @Override public void onStateChanged(ContactId contact, String transferId, FileTransfer.State state, FileTransfer.ReasonCode reasonCode) { if (!contact.equals(mContact)) { return; } if (LogUtils.isActive) { Log.d(LOGTAG, "onStateChanged contact=" + contact + " transferId=" + transferId + " state=" + state + " reason=" + reasonCode); } if (FileTransfer.State.TRANSFERRED == state) { try { FileTransfer fileTransfer = mFileTransferService.getFileTransfer(transferId); if (fileTransfer == null) { return; } if (Utils.isAudioType(fileTransfer.getMimeType()) && FileTransfer.Disposition.RENDER == fileTransfer.getDisposition()) { Utils.playAudio(OneToOneTalkView.this, fileTransfer.getFile()); mFileTransferService.markFileTransferAsRead(transferId); } } catch (RcsPersistentStorageException | RcsServiceNotAvailableException | RcsGenericException e) { showException(e); } } } @Override public void onProgressUpdate(ContactId contact, String transferId, long currentSize, long totalSize) { } @Override public void onDeleted(ContactId contact, Set<String> transferIds) { } }; mChatService = getChatApi(); mCapabilityService = getCapabilityApi(); mFileTransferService = getFileTransferApi(); HistoryUriBuilder uriBuilder = new HistoryUriBuilder(HistoryLog.CONTENT_URI); uriBuilder.appendProvider(ChatLog.Message.HISTORYLOG_MEMBER_ID); uriBuilder.appendProvider(FileTransferLog.HISTORYLOG_MEMBER_ID); mUriHistoryProvider = uriBuilder.build(); mComposeText = (EditText) findViewById(R.id.userText); ChatServiceConfiguration configuration = mChatService.getConfiguration(); // Set max label length int maxMsgLength = configuration.getOneToOneChatMessageMaxLength(); if (maxMsgLength > 0) { /* Set the message composer max length */ InputFilter[] filterArray = new InputFilter[1]; filterArray[0] = new InputFilter.LengthFilter(maxMsgLength); mComposeText.setFilters(filterArray); } IsComposingManager.INotifyComposing iNotifyComposing = new IsComposingManager.INotifyComposing() { public void setTypingStatus(boolean isTyping) { try { if (mChat == null) { return; } mChat.setComposingStatus(isTyping); if (LogUtils.isActive) { Boolean _isTyping = isTyping; Log.d(LOGTAG, "sendIsComposingEvent ".concat(_isTyping.toString())); } } catch (RcsGenericException e) { showException(e); } } }; /* Instantiate the composing manager */ mComposingManager = new IsComposingManager(configuration.getIsComposingTimeout(), iNotifyComposing); mComposeText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // Check if the text is not null. // we do not wish to consider putting the edit text back to null // (like when sending message), is having activity if (!TextUtils.isEmpty(s)) { // Warn the composing manager that we have some activity if (mComposingManager != null) { mComposingManager.hasActivity(); } } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); /* Initialize the adapter. */ mAdapter = new TalkCursorAdapter(this, true, mChatService, mFileTransferService); /* Associate the list adapter with the ListView. */ ListView listView = (ListView) findViewById(android.R.id.list); listView.setDivider(null); listView.setAdapter(mAdapter); registerForContextMenu(listView); } private boolean processIntent(Intent intent) { String action = intent.getAction(); if (LogUtils.isActive) { Log.d(LOGTAG, "processIntent " + action); } ContactId newContact = intent.getParcelableExtra(EXTRA_CONTACT); if (newContact == null) { if (LogUtils.isActive) { Log.w(LOGTAG, "Cannot process intent: contact is null"); } return false; } if (action == null) { if (LogUtils.isActive) { Log.w(LOGTAG, "Cannot process intent: action is null"); } return false; } if (!sAllowedIntentActions.contains(action)) { if (LogUtils.isActive) { Log.w(LOGTAG, "Cannot process intent: unauthorized action " + action); } return false; } try { if (!newContact.equals(mContact)) { /* Either it is the first conversation loading or switch to another conversation */ loadConversation(newContact); } /* Set activity title with display name */ String displayName = RcsContactUtil.getInstance(this).getDisplayName(mContact); setTitle(getString(R.string.title_chat, displayName)); switch (action) { case OneToOneChatIntent.ACTION_NEW_ONE_TO_ONE_CHAT_MESSAGE: /* * Open chat to accept session if the parameter IM SESSION START is 0. Client * application is not aware of the one to one chat session state nor of the IM * session start mode so we call the method systematically. */ mChat.openChat(); break; case OneToOneChatIntent.ACTION_MESSAGE_DELIVERY_EXPIRED: processUndeliveredMessages(displayName); break; case FileTransferIntent.ACTION_FILE_TRANSFER_DELIVERY_EXPIRED: processUndeliveredFileTransfers(displayName); break; } return true; } catch (RcsServiceException e) { showExceptionThenExit(e); return false; } } private void clearNotification() { ChatPendingIntentManager pendingIntentManager = ChatPendingIntentManager.getChatPendingIntentManager(this); pendingIntentManager.clearNotification(mContact.toString()); } private void loadConversation(ContactId newContact) throws RcsServiceNotAvailableException, RcsGenericException, RcsPersistentStorageException { boolean firstLoad = (mContact == null); /* Save contact ID */ mContact = newContact; clearNotification(); /* * Open chat so that if the parameter IM SESSION START is 0 then the session is accepted * now. */ mChat = mChatService.getOneToOneChat(mContact); setCursorLoader(firstLoad); RI.sChatIdOnForeground = mContact.toString(); /* Request for capabilities ony if they are not available or expired */ requestCapabilities(mContact); } private void setCursorLoader(boolean firstLoad) { if (firstLoad) { /* * Initialize the Loader with id '1' and callbacks 'mCallbacks'. */ getSupportLoaderManager().initLoader(LOADER_ID, null, this); } else { /* We switched from one contact to another: reload history since */ getSupportLoaderManager().restartLoader(LOADER_ID, null, this); } } @Override protected void onPause() { if (LogUtils.isActive) { Log.d(LOGTAG, "--> onPause"); } super.onPause(); RI.sChatIdOnForeground = null; try { if (mChatListener != null && mChatService != null && mChatListenerSet) { mChatService.removeEventListener(mChatListener); mChatListenerSet = false; } if (mFileTransferListener != null && mFileTransferService != null && mFileTransferListenerSet) { mFileTransferService.removeEventListener(mFileTransferListener); mFileTransferListenerSet = false; } } catch (RcsServiceNotAvailableException ignore) { } catch (RcsGenericException e) { Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e)); } } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); /* Replace the value of intent */ setIntent(intent); processIntent(intent); } @Override protected void onResume() { if (LogUtils.isActive) { Log.d(LOGTAG, "--> onResume"); } super.onResume(); if (mContact != null) { RI.sChatIdOnForeground = mContact.toString(); clearNotification(); } try { if (mChatListener != null && mChatService != null && !mChatListenerSet) { mChatService.addEventListener(mChatListener); mChatListenerSet = true; } if (mFileTransferListener != null && mFileTransferService != null && !mFileTransferListenerSet) { mFileTransferService.addEventListener(mFileTransferListener); mFileTransferListenerSet = true; } } catch (RcsServiceNotAvailableException ignore) { } catch (RcsGenericException e) { Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e)); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = new MenuInflater(getApplicationContext()); inflater.inflate(R.menu.menu_1to1_talk, menu); return true; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != RESULT_OK) { return; } switch (requestCode) { case SELECT_GEOLOCATION: Geoloc geoloc = data.getParcelableExtra(EditGeoloc.EXTRA_GEOLOC); try { if (mChat != null) { mChat.sendMessage(geoloc); } } catch (RcsServiceException e) { showExceptionThenExit(e); } break; } } @Override public boolean onOptionsItemSelected(MenuItem item) { try { switch (item.getItemId()) { case R.id.menu_send_geoloc: /* Start a new activity to select a geolocation */ startActivityForResult(new Intent(this, EditGeoloc.class), SELECT_GEOLOCATION); break; case R.id.menu_send_rcs_file: SendSingleFile.startActivity(this, mContact); break; case R.id.menu_delete_talk: mFileTransferService.deleteOneToOneFileTransfers(mContact); mChatService.deleteOneToOneChat(mContact); break; } } catch (RcsServiceException e) { showExceptionThenExit(e); } return true; } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_1to1_talk_item, menu); menu.findItem(R.id.menu_resend_message).setVisible(false); menu.findItem(R.id.menu_display_content).setVisible(false); menu.findItem(R.id.menu_listen_content).setVisible(false); /* Get the list item position */ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; Cursor cursor = (Cursor) mAdapter.getItem(info.position); /* Adapt the contextual menu according to the selected item */ int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID)); String id = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID)); Direction direction = Direction.valueOf(cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.DIRECTION))); try { switch (providerId) { case ChatLog.Message.HISTORYLOG_MEMBER_ID: if (Direction.OUTGOING == direction) { ChatLog.Message.Content.Status status = ChatLog.Message.Content.Status .valueOf(cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.STATUS))); if (ChatLog.Message.Content.Status.FAILED == status) { String number = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.CONTACT)); if (number != null) { ContactId contact = ContactUtil.formatContact(number); OneToOneChat chat = mChatService.getOneToOneChat(contact); if (chat != null && chat.isAllowedToSendMessage()) { menu.findItem(R.id.menu_resend_message).setVisible(true); } } } } break; case FileTransferLog.HISTORYLOG_MEMBER_ID: String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.MIME_TYPE)); FileTransfer.State state = FileTransfer.State .valueOf(cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.STATUS))); if (FileTransfer.State.FAILED == state) { FileTransfer transfer = mFileTransferService.getFileTransfer(id); if (transfer != null && transfer.isAllowedToResendTransfer()) { menu.findItem(R.id.menu_resend_message).setVisible(true); } } else if (Utils.isImageType(mimeType)) { if (Direction.OUTGOING == direction) { menu.findItem(R.id.menu_display_content).setVisible(true); } else if (Direction.INCOMING == direction) { Long transferred = cursor.getLong(cursor.getColumnIndexOrThrow(HistoryLog.TRANSFERRED)); Long size = cursor.getLong(cursor.getColumnIndexOrThrow(HistoryLog.FILESIZE)); if (size.equals(transferred)) { menu.findItem(R.id.menu_display_content).setVisible(true); } } } else if (Utils.isAudioType(mimeType)) { if (Direction.OUTGOING == direction) { menu.findItem(R.id.menu_listen_content).setVisible(true); } else if (Direction.INCOMING == direction) { Long transferred = cursor.getLong(cursor.getColumnIndexOrThrow(HistoryLog.TRANSFERRED)); Long size = cursor.getLong(cursor.getColumnIndexOrThrow(HistoryLog.FILESIZE)); if (size.equals(transferred)) { menu.findItem(R.id.menu_listen_content).setVisible(true); } } } break; default: throw new IllegalArgumentException("Invalid provider ID=" + providerId); } } catch (RcsServiceNotAvailableException e) { menu.findItem(R.id.menu_resend_message).setVisible(false); } catch (RcsGenericException | RcsPersistentStorageException e) { menu.findItem(R.id.menu_resend_message).setVisible(false); showException(e); } } @Override public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); Cursor cursor = (Cursor) (mAdapter.getItem(info.position)); int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID)); String id = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID)); if (LogUtils.isActive) { Log.d(LOGTAG, "onContextItemSelected Id=".concat(id)); } try { switch (item.getItemId()) { case R.id.menu_delete_message: switch (providerId) { case ChatLog.Message.HISTORYLOG_MEMBER_ID: mChatService.deleteMessage(id); return true; case FileTransferLog.HISTORYLOG_MEMBER_ID: mFileTransferService.deleteFileTransfer(id); return true; } break; case R.id.menu_resend_message: switch (providerId) { case ChatLog.Message.HISTORYLOG_MEMBER_ID: OneToOneChat chat = mChatService.getOneToOneChat(mContact); if (chat != null) { chat.resendMessage(id); } return true; case FileTransferLog.HISTORYLOG_MEMBER_ID: FileTransfer fileTransfer = mFileTransferService.getFileTransfer(id); if (fileTransfer != null) { fileTransfer.resendTransfer(); } return true; } break; case R.id.menu_display_content: switch (providerId) { case FileTransferLog.HISTORYLOG_MEMBER_ID: String file = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.CONTENT)); Utils.showPicture(this, Uri.parse(file)); markFileTransferAsRead(cursor, id); return true; } break; case R.id.menu_view_detail: switch (providerId) { case ChatLog.Message.HISTORYLOG_MEMBER_ID: ChatMessageLogView.startActivity(this, id); return true; case FileTransferLog.HISTORYLOG_MEMBER_ID: FileTransferLogView.startActivity(this, id); return true; } break; case R.id.menu_listen_content: if (FileTransferLog.HISTORYLOG_MEMBER_ID == providerId) { String file = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.CONTENT)); Utils.playAudio(this, Uri.parse(file)); markFileTransferAsRead(cursor, id); return true; } break; } return super.onContextItemSelected(item); } catch (RcsGenericException | RcsPermissionDeniedException | RcsPersistentStorageException e) { showException(e); return true; } catch (RcsServiceNotAvailableException e) { Utils.displayLongToast(this, getString(R.string.label_service_not_available)); return true; } } private void markFileTransferAsRead(Cursor cursor, String ftId) { try { Direction dir = Direction.valueOf(cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.DIRECTION))); if (Direction.INCOMING == dir) { RcsService.ReadStatus status = RcsService.ReadStatus .valueOf(cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.READ_STATUS))); if (RcsService.ReadStatus.UNREAD == status) { mFileTransferService.markFileTransferAsRead(ftId); if (LogUtils.isActive) { Log.d(LOGTAG, "Mark file transfer " + ftId + " as read"); } } } } catch (RcsServiceNotAvailableException e) { if (LogUtils.isActive) { Log.d(LOGTAG, "Cannot mark message as read: service not available"); } } catch (RcsGenericException | RcsPersistentStorageException e) { Log.e(LOGTAG, ExceptionUtil.getFullStackTrace(e)); } } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { /* Create a new CursorLoader with the following query parameters. */ return new CursorLoader(this, mUriHistoryProvider, PROJECTION, WHERE_CLAUSE, new String[] { mContact.toString() }, ORDER_ASC); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if (LOADER_ID == loader.getId()) { /* * The asynchronous load is complete and the data is now available for use. Only now can * we associate the queried Cursor with the CursorAdapter. */ mAdapter.swapCursor(data); /** * Registering content observer for XMS message content URI. When this content URI will * change, this will notify the loader to reload its data. */ if (mObserver != null && !mObserver.getLoader().equals(loader)) { ContentResolver resolver = getContentResolver(); resolver.unregisterContentObserver(mObserver); mObserver = null; } if (mObserver == null) { if (LogUtils.isActive) { Log.d(LOGTAG, "onLoadFinished: register content observer"); } mObserver = new ChatCursorObserver(new Handler(), loader); ContentResolver resolver = getContentResolver(); resolver.registerContentObserver(ChatLog.Message.CONTENT_URI, true, mObserver); resolver.registerContentObserver(FileTransferLog.CONTENT_URI, true, mObserver); } } } @Override public void onLoaderReset(Loader<Cursor> loader) { /* * For whatever reason, the Loader's data is now unavailable. Remove any references to the * old data by replacing it with a null Cursor. */ mAdapter.swapCursor(null); } private void displayComposingEvent(final ContactId contact, final boolean status) { final String from = RcsContactUtil.getInstance(this).getDisplayName(contact); // Execute on UI handler since callback is executed from service mHandler.post(new Runnable() { public void run() { TextView view = (TextView) findViewById(R.id.isComposingText); if (status) { // Display is-composing notification view.setText(getString(R.string.label_contact_is_composing, from)); view.setVisibility(View.VISIBLE); } else { // Hide is-composing notification view.setVisibility(View.GONE); } } }); } private void processUndeliveredFileTransfers(String displayName) throws RcsGenericException, RcsServiceNotAvailableException, RcsPersistentStorageException, RcsPermissionDeniedException { if (LogUtils.isActive) { Log.d(LOGTAG, "processUndeliveredFileTransfers: ask"); } /* Do not propose to clear undelivered if a dialog is already opened */ if (mClearUndeliveredAlertDialog == null) { mClearUndeliveredAlertDialog = popUpDeliveryExpiration(this, getString(R.string.title_undelivered_filetransfer), getString(R.string.label_undelivered_filetransfer, displayName), mClearUndeliveredFt, null, mUndeliveredCancelListener); registerDialog(mClearUndeliveredAlertDialog); } } private void processUndeliveredMessages(String displayName) throws RcsGenericException, RcsPersistentStorageException, RcsServiceNotAvailableException { if (LogUtils.isActive) { Log.d(LOGTAG, "processUndeliveredMessages: ask"); } /* Do not propose to clear undelivered if a dialog is already opened */ if (mClearUndeliveredAlertDialog == null) { mClearUndeliveredAlertDialog = popUpDeliveryExpiration(this, getString(R.string.title_undelivered_message), getString(R.string.label_undelivered_message, displayName), mClearUndeliveredChat, null, mUndeliveredCancelListener); registerDialog(mClearUndeliveredAlertDialog); } } private AlertDialog popUpDeliveryExpiration(Context ctx, String title, String msg, DialogInterface.OnClickListener onPositiveClickListener, DialogInterface.OnClickListener onNegativeClickListener, DialogInterface.OnCancelListener onCancelListener) { AlertDialog.Builder builder = new AlertDialog.Builder(ctx); builder.setMessage(msg); builder.setTitle(title); if (onNegativeClickListener != null) { builder.setNegativeButton(R.string.label_cancel, onNegativeClickListener); } builder.setPositiveButton(R.string.label_ok, onPositiveClickListener); builder.setOnCancelListener(onCancelListener); return builder.show(); } private void requestCapabilities(ContactId contact) throws RcsServiceNotAvailableException, RcsGenericException { try { mCapabilityService.requestContactCapabilities(new HashSet<>(Collections.singletonList(contact))); } catch (RcsServiceNotRegisteredException e) { Log.w(LOGTAG, "Cannot request capabilities: RCS not registered!"); } } }