/** * Process the MultiMedia Session invitation<br> * Purpose is to retrieve the contactId from the service to build the notification. * * @author YPLO6403 */ public class MultiMediaSessionIntentService extends IntentService { private String mSessionId; private boolean mMultimediaMessagingSession = false; private ConnectionManager mCnxManager; private static final String LOGTAG = LogUtils.getTag(MultiMediaSessionIntentService.class.getSimpleName()); /** Constructor */ public MultiMediaSessionIntentService() { super("MultiMediaSessionIntentService"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); /* * We want this service to stop running if forced stop so return not sticky. */ return START_NOT_STICKY; } @Override protected void onHandleIntent(Intent intent) { String action; if ((action = intent.getAction()) == null) { return; } if (MultimediaMessagingSessionIntent.ACTION_NEW_INVITATION.equals(action)) { mMultimediaMessagingSession = true; } else { if (!MultimediaStreamingSessionIntent.ACTION_NEW_INVITATION.equals(action)) { if (LogUtils.isActive) { Log.d(LOGTAG, "Unknown action=".concat(action)); } return; } } /* Since there is no provider associated to multimedia sessions, we must connect to the API */ mCnxManager = ConnectionManager.getInstance(); if (!mCnxManager.isServiceConnected(RcsServiceName.MULTIMEDIA)) { return; } // Get invitation info mSessionId = intent.getStringExtra(MultimediaMessagingSessionIntent.EXTRA_SESSION_ID); if (LogUtils.isActive) { Log.d(LOGTAG, "onHandleIntent sessionId=".concat(mSessionId)); } initiateSession(intent); } private void initiateSession(Intent intent) { try { if (mMultimediaMessagingSession) { MultimediaMessagingSession mms = mCnxManager.getMultimediaSessionApi().getMessagingSession(mSessionId); if (mms != null) { addSessionInvitationNotification(intent, mms.getRemoteContact()); } else { if (LogUtils.isActive) { Log.w(LOGTAG, "Cannot get messaging session for ID ".concat(mSessionId)); } } } else { MultimediaStreamingSession mss = mCnxManager.getMultimediaSessionApi().getStreamingSession(mSessionId); if (mss != null) { addSessionInvitationNotification(intent, mss.getRemoteContact()); } else { if (LogUtils.isActive) { Log.w(LOGTAG, "Cannot get streaming session for ID ".concat(mSessionId)); } } } } catch (RcsServiceException e) { if (LogUtils.isActive) { Log.e(LOGTAG, "Cannot get MM API", e); } } } private void addSessionInvitationNotification(Intent intent, ContactId contact) { /* Create pending intent */ Intent invitation = new Intent(intent); String title; if (mMultimediaMessagingSession) { invitation.setClass(this, MessagingSessionView.class); title = getString(R.string.title_recv_messaging_session); } else { invitation.setClass(this, StreamingSessionView.class); title = getString(R.string.title_recv_streaming_session); } invitation.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); /* * If the PendingIntent has the same operation, action, data, categories, components, and * flags it will be replaced. Invitation should be notified individually so we use a random * generator to provide a unique request code and reuse it for the notification. */ int uniqueId = Utils.getUniqueIdForPendingIntent(); PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, invitation, PendingIntent.FLAG_ONE_SHOT); String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact); /* Create notification */ NotificationCompat.Builder notif = new NotificationCompat.Builder(this); notif.setContentIntent(contentIntent); notif.setSmallIcon(R.drawable.ri_notif_mm_session_icon); notif.setWhen(System.currentTimeMillis()); notif.setAutoCancel(true); notif.setOnlyAlertOnce(true); notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)); notif.setDefaults(Notification.DEFAULT_VIBRATE); notif.setContentTitle(title); notif.setContentText(getString(R.string.label_from_args, displayName)); /* Send notification */ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(uniqueId, notif.build()); } }
/** * CHAT API * * @author Jean-Marc AUFFRET */ public class TestChatApi extends ListActivity { private static final String[] PROJECTION = new String[] {CapabilitiesLog.CONTACT}; private static final String LOGTAG = LogUtils.getTag(TestChatApi.class.getSimpleName()); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Set layout setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // @formatter:off String[] items = { getString(R.string.menu_initiate_chat), getString(R.string.menu_chat_log), getString(R.string.menu_initiate_group_chat), getString(R.string.menu_group_chat_log), getString(R.string.menu_chat_service_config), getString(R.string.menu_showus_map), }; // @formatter:on setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items)); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { switch (position) { case 0: startActivity(new Intent(this, InitiateSingleChat.class)); break; case 1: startActivity(new Intent(this, SingleChatList.class)); break; case 2: /* Check if Group chat initialization is allowed */ ChatService chatService = ConnectionManager.getInstance().getChatApi(); try { if (chatService.isAllowedToInitiateGroupChat()) { startActivity(new Intent(this, InitiateGroupChat.class)); } else { Utils.showMessage(this, getString(R.string.label_NotAllowedToInitiateGroupChat)); } } catch (RcsServiceException e) { if (LogUtils.isActive) { Log.d(LOGTAG, "Cannot check if Group chat initialization is allowed", e); } Utils.showMessage(this, getString(R.string.label_api_failed)); } break; case 3: startActivity(new Intent(this, GroupChatList.class)); break; case 4: startActivity(new Intent(this, ChatServiceConfigActivity.class)); break; case 5: Set<ContactId> contacts = new HashSet<ContactId>(); Cursor cursor = null; try { cursor = getContentResolver().query(CapabilitiesLog.CONTENT_URI, PROJECTION, null, null, null); while (cursor.moveToNext()) { String contact = cursor.getString(cursor.getColumnIndex(CapabilitiesLog.CONTACT)); contacts.add(ContactUtil.formatContact(contact)); } DisplayGeoloc.showContactsOnMap(this, contacts); } catch (Exception e) { // Skip intentionally } finally { if (cursor != null) { cursor.close(); } } break; } } }
/** * Initiate file transfer * * @author Jean-Marc AUFFRET * @author Philippe LEMORDANT */ public class InitiateFileTransfer extends RcsActivity { private static final int SELECT_IMAGE = 0; private static final int SELECT_TEXT_FILE = 1; private static final String BUNDLE_FTDAO_ID = "ftdao"; /** UI handler */ private final Handler mHandler = new Handler(); private String mFilename; private Uri mFile; private long mFilesize = -1; private FileTransfer mFileTransfer; private Dialog mProgressDialog; private static final String LOGTAG = LogUtils.getTag(InitiateFileTransfer.class.getSimpleName()); private String mFileTransferId; /** Spinner for contact selection */ private Spinner mSpinner; private Button mResumeBtn; private Button mPauseBtn; private Button mInviteBtn; private Button mSelectBtn; private OneToOneFileTransferListener mFileTransferListener; private OnClickListener mBtnInviteListener; private OnClickListener mBtnSelectListener; private OnClickListener mBtnPauseListener; private OnClickListener mBtnResumeListener; private FileTransferService mFileTransferService; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); intitialize(); ContactId remoteContact; Intent intent = getIntent(); boolean resuming = FileTransferIntent.ACTION_RESUME.equals(intent.getAction()); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setContentView(R.layout.filetransfer_initiate); /* Set contact selector */ mSpinner = (Spinner) findViewById(R.id.contact); /* Set buttons callback */ mInviteBtn = (Button) findViewById(R.id.invite_btn); mInviteBtn.setOnClickListener(mBtnInviteListener); mInviteBtn.setEnabled(false); mSelectBtn = (Button) findViewById(R.id.select_btn); mSelectBtn.setOnClickListener(mBtnSelectListener); mSelectBtn.setEnabled(false); mPauseBtn = (Button) findViewById(R.id.pause_btn); mPauseBtn.setOnClickListener(mBtnPauseListener); mPauseBtn.setEnabled(false); mResumeBtn = (Button) findViewById(R.id.resume_btn); mResumeBtn.setOnClickListener(mBtnResumeListener); mResumeBtn.setEnabled(false); TableRow expiration = (TableRow) findViewById(R.id.expiration); expiration.setVisibility(View.GONE); if (!isServiceConnected(RcsServiceName.FILE_TRANSFER)) { showMessageThenExit(R.string.label_service_not_available); return; } startMonitorServices(RcsServiceName.FILE_TRANSFER); mFileTransferService = getFileTransferApi(); try { mFileTransferService.addEventListener(mFileTransferListener); if (resuming) { /* Get resuming info */ FileTransferDAO ftdao = (FileTransferDAO) (intent.getExtras().getSerializable(BUNDLE_FTDAO_ID)); if (ftdao == null) { if (LogUtils.isActive) { Log.e(LOGTAG, "onCreate cannot read File Transfer resuming info"); } finish(); return; } remoteContact = ftdao.getContact(); mFileTransferId = ftdao.getTransferId(); mFilename = ftdao.getFilename(); mFilesize = ftdao.getSize(); ArrayAdapter<String> adapter = new ArrayAdapter<>( this, android.R.layout.simple_spinner_item, new String[] {remoteContact.toString()}); mSpinner.setAdapter(adapter); TextView uriEdit = (TextView) findViewById(R.id.uri); TextView sizeEdit = (TextView) findViewById(R.id.size); sizeEdit.setText((mFilesize / 1024) + " KB"); uriEdit.setText(mFilename); /* Check if session still exists */ if (mFileTransferService.getFileTransfer(mFileTransferId) == null) { /* Session not found or expired */ showMessageThenExit(R.string.label_transfer_session_has_expired); return; } if (LogUtils.isActive) { Log.d( LOGTAG, "onCreate (file=" + mFilename + ") (size=" + mFilesize + ") (contact=" + remoteContact + ")"); } } else { mSpinner.setAdapter(ContactListAdapter.createRcsContactListAdapter(this)); /* Enable button if contact available */ if (mSpinner.getAdapter().getCount() != 0) { mSelectBtn.setEnabled(true); } if (LogUtils.isActive) { Log.d(LOGTAG, "onCreate"); } } } catch (RcsServiceException e) { showExceptionThenExit(e); } } @Override public void onDestroy() { if (LogUtils.isActive) { Log.d(LOGTAG, "onDestroy"); } super.onDestroy(); if (mFileTransferService != null && isServiceConnected(RcsServiceName.FILE_TRANSFER)) { // Remove file transfer listener try { mFileTransferService.removeEventListener(mFileTransferListener); } catch (RcsServiceException e) { Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e)); } } } private void initiateTransfer(ContactId remote) { /* Get thumbnail option */ CheckBox ftThumb = (CheckBox) findViewById(R.id.ft_thumb); boolean tryToSendFileicon = ftThumb.isChecked(); String mimeType = getContentResolver().getType(mFile); if (tryToSendFileicon && mimeType != null && !mimeType.startsWith("image")) { tryToSendFileicon = false; } try { /* Only take persistable permission for content Uris */ takePersistableContentUriPermission(this, mFile); /* Initiate transfer */ mFileTransfer = mFileTransferService.transferFile(remote, mFile, tryToSendFileicon); mFileTransferId = mFileTransfer.getTransferId(); mProgressDialog = showProgressDialog(getString(R.string.label_command_in_progress)); mProgressDialog.setOnCancelListener( new OnCancelListener() { public void onCancel(DialogInterface dialog) { Toast.makeText( InitiateFileTransfer.this, getString(R.string.label_transfer_cancelled), Toast.LENGTH_SHORT) .show(); quitSession(); } }); /* Disable UI */ mSpinner.setEnabled(false); /* Hide buttons */ mInviteBtn.setVisibility(View.INVISIBLE); mSelectBtn.setVisibility(View.INVISIBLE); ftThumb.setVisibility(View.INVISIBLE); } catch (RcsServiceException e) { showExceptionThenExit(e); } } /** Display a alert dialog to select the kind of file to transfer */ private void selectDocument() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.label_select_file); builder.setCancelable(true); builder.setItems( R.array.select_filetotransfer, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (which == SELECT_IMAGE) { FileUtils.openFile(InitiateFileTransfer.this, "image/*", SELECT_IMAGE); } else { FileUtils.openFile(InitiateFileTransfer.this, "text/plain", SELECT_TEXT_FILE); } } }); registerDialog(builder.show()); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != RESULT_OK || (data == null) || (data.getData() == null)) { return; } mFile = data.getData(); TextView uriEdit = (TextView) findViewById(R.id.uri); TextView sizeEdit = (TextView) findViewById(R.id.size); switch (requestCode) { case SELECT_IMAGE: case SELECT_TEXT_FILE: if (LogUtils.isActive) { Log.d(LOGTAG, "Selected file uri:" + mFile); } /* * Display file info and the selected filename attribute. */ mFilename = FileUtils.getFileName(this, mFile); mFilesize = FileUtils.getFileSize(this, mFile) / 1024; sizeEdit.setText(mFilesize + " KB"); uriEdit.setText(mFilename); if (LogUtils.isActive) { Log.i(LOGTAG, "Select file " + mFilename + " of size " + mFilesize + " file=" + mFile); } mInviteBtn.setEnabled(true); break; } } private void hideProgressDialog() { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); mProgressDialog = null; } } private void updateProgressBar(long currentSize, long totalSize) { TextView statusView = (TextView) findViewById(R.id.progress_status); ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar); statusView.setText(Utils.getProgressLabel(currentSize, totalSize)); double position = ((double) currentSize / (double) totalSize) * 100.0; progressBar.setProgress((int) position); } private void quitSession() { if (LogUtils.isActive) { Log.d(LOGTAG, "quitSession"); } try { if (mFileTransfer != null && RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) { mFileTransfer.abortTransfer(); } } catch (RcsServiceException e) { showException(e); } finally { mFileTransfer = null; finish(); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { try { if (KeyEvent.KEYCODE_BACK == keyCode) { if (mFileTransfer == null || !RcsSessionUtil.isAllowedToAbortFileTransferSession(mFileTransfer)) { finish(); return true; } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.label_confirm_close); builder.setPositiveButton( R.string.label_ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { quitSession(); } }); builder.setNegativeButton( R.string.label_cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { /* Exit activity */ finish(); } }); builder.setCancelable(true); registerDialog(builder.show()); return true; } } catch (RcsServiceException e) { showException(e); } return super.onKeyDown(keyCode, event); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = new MenuInflater(getApplicationContext()); inflater.inflate(R.menu.menu_ft, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_close_session: quitSession(); break; } return true; } private void displayFileExpiration(long fileExpiration) { if (FileTransferLog.UNKNOWN_EXPIRATION == fileExpiration) { return; } TableRow expirationTableRow = (TableRow) findViewById(R.id.expiration); expirationTableRow.setVisibility(View.VISIBLE); TextView expirationView = (TextView) findViewById(R.id.value_expiration); CharSequence expirationDate = DateUtils.getRelativeTimeSpanString( fileExpiration, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE); expirationView.setText(expirationDate); } private void intitialize() { mFileTransferListener = new OneToOneFileTransferListener() { @Override public void onProgressUpdate( ContactId contact, String transferId, final long currentSize, final long totalSize) { /* Discard event if not for current transferId */ if (InitiateFileTransfer.this.mFileTransferId == null || !InitiateFileTransfer.this.mFileTransferId.equals(transferId)) { return; } mHandler.post( new Runnable() { public void run() { updateProgressBar(currentSize, totalSize); } }); } @Override public void onStateChanged( ContactId contact, String transferId, final FileTransfer.State state, final FileTransfer.ReasonCode reasonCode) { /* Discard event if not for current transferId */ if (InitiateFileTransfer.this.mFileTransferId == null || !InitiateFileTransfer.this.mFileTransferId.equals(transferId)) { return; } final String _reasonCode = RiApplication.sFileTransferReasonCodes[reasonCode.toInt()]; final String _state = RiApplication.sFileTransferStates[state.toInt()]; if (LogUtils.isActive) { Log.d( LOGTAG, "onStateChanged contact=" + contact + " transferId=" + transferId + " state=" + _state + " reason=" + _reasonCode); } mHandler.post( new Runnable() { public void run() { if (mFileTransfer != null) { try { mResumeBtn.setEnabled(mFileTransfer.isAllowedToResumeTransfer()); } catch (RcsServiceException e) { mResumeBtn.setEnabled(false); showException(e); } try { mPauseBtn.setEnabled(mFileTransfer.isAllowedToPauseTransfer()); } catch (RcsServiceException e) { mPauseBtn.setEnabled(false); showException(e); } } TextView statusView = (TextView) findViewById(R.id.progress_status); switch (state) { case STARTED: /* Session is well established : hide progress dialog */ hideProgressDialog(); /* Display session status */ statusView.setText(_state); break; case ABORTED: showMessageThenExit( getString(R.string.label_transfer_aborted, _reasonCode)); break; case REJECTED: showMessageThenExit( getString(R.string.label_transfer_rejected, _reasonCode)); break; case FAILED: showMessageThenExit(getString(R.string.label_transfer_failed, _reasonCode)); break; case TRANSFERRED: hideProgressDialog(); // TODO check if required /* Display transfer progress */ statusView.setText(_state); try { displayFileExpiration(mFileTransfer.getFileExpiration()); Uri fileIconUri = mFileTransfer.getFileIcon(); long fileIconExpiration = mFileTransfer.getFileIconExpiration(); if (LogUtils.isActive) { Log.d( LOGTAG, "File transferred icon uri= " + fileIconUri + " expiration=" + fileIconExpiration); } } catch (RcsServiceException e) { showException(e); } break; default: statusView.setText(_state); if (LogUtils.isActive) { Log.d( LOGTAG, "onStateChanged " .concat( getString( R.string.label_ft_state_changed, _state, _reasonCode))); } } } }); } @Override public void onDeleted(ContactId contact, Set<String> transferIds) { if (LogUtils.isActive) { Log.w(LOGTAG, "onDeleted contact=" + contact + " transferIds=" + transferIds); } } }; mBtnInviteListener = new OnClickListener() { public void onClick(View v) { ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter(); String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView()); final ContactId remote = ContactUtil.formatContact(phoneNumber); long warnSize; try { warnSize = mFileTransferService.getConfiguration().getWarnSize(); } catch (RcsServiceException e) { showExceptionThenExit(e); return; } if ((warnSize > 0) && (mFilesize >= warnSize)) { // Display a warning message AlertDialog.Builder builder = new AlertDialog.Builder(InitiateFileTransfer.this); builder.setMessage(getString(R.string.label_sharing_warn_size, mFilesize)); builder.setCancelable(false); builder.setPositiveButton( R.string.label_yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int position) { initiateTransfer(remote); } }); builder.setNegativeButton(R.string.label_no, null); registerDialog(builder.show()); } else { initiateTransfer(remote); } } }; mBtnSelectListener = new OnClickListener() { public void onClick(View v) { selectDocument(); } }; mBtnPauseListener = new OnClickListener() { public void onClick(View v) { try { if (mFileTransfer.isAllowedToPauseTransfer()) { mFileTransfer.pauseTransfer(); } else { mPauseBtn.setEnabled(false); showMessage(R.string.label_pause_ft_not_allowed); } } catch (RcsServiceException e) { showExceptionThenExit(e); } } }; mBtnResumeListener = new OnClickListener() { public void onClick(View v) { try { if (mFileTransfer.isAllowedToResumeTransfer()) { mFileTransfer.resumeTransfer(); } else { mResumeBtn.setEnabled(false); showMessage(R.string.label_resume_ft_not_allowed); } } catch (RcsServiceException e) { showExceptionThenExit(e); } } }; } /** * Forge intent to resume Originating File Transfer * * @param ctx The context * @param ftDao The FileTransfer DAO * @param resume intent * @return intent */ public static Intent forgeResumeIntent(Context ctx, FileTransferDAO ftDao, Intent resume) { resume.setClass(ctx, InitiateFileTransfer.class); resume.addFlags(Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_ACTIVITY_NEW_TASK); /* Save FileTransferDAO into intent */ Bundle bundle = new Bundle(); bundle.putParcelable(BUNDLE_FTDAO_ID, ftDao); resume.putExtras(bundle); return resume; } }
/** Group chat view */ public class GroupChatView extends ChatView { /** Intent parameters */ private static final String EXTRA_PARTICIPANTS = "participants"; private static final String EXTRA_SUBJECT = "subject"; private static final String EXTRA_MODE = "mode"; private enum GroupChatMode { INCOMING, OUTGOING, OPEN } /** List of items for contextual menu */ private static final int GROUPCHAT_MENU_ITEM_DELETE = 0; private static final int GROUPCHAT_MENU_ITEM_VIEW_GC_INFO = 1; private static final String WHERE_CLAUSE = HistoryLog.CHAT_ID + "=?"; private String mSubject; private String mChatId; private GroupChat mGroupChat; private Dialog mProgressDialog; private Set<ContactId> mParticipants = new HashSet<>(); private static final String LOGTAG = LogUtils.getTag(GroupChatView.class.getSimpleName()); private static final String OPEN_GROUPCHAT = "OPEN_GROUPCHAT"; private static final String INTITIATE_GROUPCHAT = "INTITIATE_GROUPCHAT"; private GroupChatListener mChatListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (isExiting()) { return; } try { addChatEventListener(mChatService); ChatServiceConfiguration configuration = mChatService.getConfiguration(); /* Set max label length */ int maxMsgLength = configuration.getGroupChatMessageMaxLength(); if (maxMsgLength > 0) { // Set the message composer max length InputFilter[] filterArray = new InputFilter[1]; filterArray[0] = new InputFilter.LengthFilter(maxMsgLength); mComposeText.setFilters(filterArray); } mComposingManager = new IsComposingManager(configuration.getIsComposingTimeout(), getNotifyComposing()); } catch (RcsServiceException e) { showExceptionThenExit(e); } if (LogUtils.isActive) { Log.d(LOGTAG, "onCreate"); } } @Override public void onDestroy() { if (LogUtils.isActive) { Log.d(LOGTAG, "onDestroy"); } if (mGroupChat != null) { try { mGroupChat.setComposingStatus(false); } catch (RcsServiceException e) { Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e)); } } super.onDestroy(); } @Override protected void onResume() { super.onResume(); sChatIdOnForeground = mChatId; } @Override protected void onPause() { super.onPause(); sChatIdOnForeground = null; } @Override public boolean processIntent(Intent intent) { String action = intent.getAction(); if (LogUtils.isActive) { Log.d(LOGTAG, "processIntent: " + action); } String oldChatId = mChatId; try { switch ((GroupChatMode) intent.getSerializableExtra(EXTRA_MODE)) { case OUTGOING: /* Initiate a Group Chat: Get subject */ mSubject = intent.getStringExtra(GroupChatView.EXTRA_SUBJECT); updateGroupChatViewTitle(mSubject); /* Get the list of participants */ ContactUtil contactUtil = ContactUtil.getInstance(this); List<String> contacts = intent.getStringArrayListExtra(GroupChatView.EXTRA_PARTICIPANTS); if (contacts == null || contacts.isEmpty()) { showMessageThenExit(R.string.label_invalid_contacts); return false; } for (String contact : contacts) { mParticipants.add(contactUtil.formatContact(contact)); } if (mParticipants.isEmpty()) { showMessageThenExit(R.string.label_invalid_contacts); return false; } return initiateGroupChat(oldChatId == null); case OPEN: /* Open an existing session from the history log */ mChatId = intent.getStringExtra(GroupChatIntent.EXTRA_CHAT_ID); mGroupChat = mChatService.getGroupChat(mChatId); if (mGroupChat == null) { if (LogUtils.isActive) { Log.e(LOGTAG, "Groupchat not found for Id=".concat(mChatId)); } showMessageThenExit(R.string.label_session_not_found); return false; } setCursorLoader(oldChatId == null); sChatIdOnForeground = mChatId; mSubject = mGroupChat.getSubject(); updateGroupChatViewTitle(mSubject); /* Set list of participants */ mParticipants = mGroupChat.getParticipants().keySet(); if (LogUtils.isActive) { Log.i(LOGTAG, "processIntent chatId=" + mChatId + " subject='" + mSubject + "'"); } return true; case INCOMING: String rxChatId = intent.getStringExtra(GroupChatIntent.EXTRA_CHAT_ID); if (GroupChatIntent.ACTION_NEW_GROUP_CHAT_MESSAGE.equals(action)) { String rxMsgId = intent.getStringExtra(GroupChatIntent.EXTRA_MESSAGE_ID); mChatService.markMessageAsRead(rxMsgId); } mChatId = rxChatId; mGroupChat = mChatService.getGroupChat(mChatId); if (mGroupChat == null) { showMessageThenExit(R.string.label_session_not_found); return false; } setCursorLoader(oldChatId == null); sChatIdOnForeground = mChatId; ContactId contact = mGroupChat.getRemoteContact(); mSubject = mGroupChat.getSubject(); updateGroupChatViewTitle(mSubject); mParticipants = mGroupChat.getParticipants().keySet(); /* Display accept/reject dialog */ if (GroupChat.State.INVITED == mGroupChat.getState()) { displayAcceptRejectDialog(contact); } if (LogUtils.isActive) { Log.d(LOGTAG, "New group chat for chatId ".concat(mChatId)); } return true; } } catch (RcsServiceException e) { showExceptionThenExit(e); } return false; } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); /* Get the list item position. */ AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; Cursor cursor = (Cursor) mAdapter.getItem(info.position); menu.add(0, GROUPCHAT_MENU_ITEM_DELETE, 0, R.string.menu_delete_message); Direction direction = Direction.valueOf(cursor.getInt(cursor.getColumnIndexOrThrow(Message.DIRECTION))); if (Direction.OUTGOING == direction) { menu.add(0, GROUPCHAT_MENU_ITEM_VIEW_GC_INFO, 1, R.string.menu_view_groupdelivery); // TODO depending on mime-type and provider ID, allow user to view file image } } @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); Cursor cursor = (Cursor) (mAdapter.getItem(info.position)); String messageId = cursor.getString(cursor.getColumnIndexOrThrow(HistoryLog.ID)); if (LogUtils.isActive) { Log.d(LOGTAG, "onContextItemSelected Id=".concat(messageId)); } int providerId = cursor.getInt(cursor.getColumnIndexOrThrow(HistoryLog.PROVIDER_ID)); switch (item.getItemId()) { case GROUPCHAT_MENU_ITEM_VIEW_GC_INFO: GroupDeliveryInfoList.startActivity(this, messageId); return true; case GROUPCHAT_MENU_ITEM_DELETE: try { if (ChatLog.Message.HISTORYLOG_MEMBER_ID == providerId) { mChatService.deleteMessage(messageId); } else { mFileTransferService.deleteFileTransfer(messageId); } } catch (RcsServiceException e) { showException(e); } return true; default: return super.onContextItemSelected(item); } } /** * Update the view title * * @param subject the group chat subject or null */ private void updateGroupChatViewTitle(String subject) { if (!TextUtils.isEmpty(subject)) { setTitle(getString(R.string.title_group_chat) + " '" + mSubject + "'"); } } @Override public Loader<Cursor> onCreateLoader(int id, Bundle arg) { /* Create a new CursorLoader with the following query parameters. */ return new CursorLoader( this, mUriHistoryProvider, PROJ_CHAT_MSG, WHERE_CLAUSE, new String[] {mChatId}, ORDER_CHAT_MSG); } /** * Display notification to accept or reject invitation * * @param remote remote contact */ private void displayAcceptRejectDialog(ContactId remote) { /* Manual accept */ AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.title_group_chat); String from = RcsContactUtil.getInstance(this).getDisplayName(remote); String topic = (TextUtils.isEmpty(mSubject)) ? getString(R.string.label_no_subject) : mSubject; String msg = getString(R.string.label_gc_from_subject, from, topic); builder.setMessage(msg); builder.setCancelable(false); builder.setIcon(R.drawable.ri_notif_chat_icon); builder.setPositiveButton( R.string.label_accept, new android.content.DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { try { /* Accept the invitation */ mGroupChat.openChat(); } catch (RcsServiceException e) { showExceptionThenExit(e); } } }); builder.setNegativeButton( R.string.label_decline, new android.content.DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { /* * Let session die by timeout. Exit activity */ finish(); } }); registerDialog(builder.show()); } /** * get a set of contact from a set of participant info * * @param setOfParticipant a set of participant info * @return a set of contact */ private Set<String> getSetOfParticipants(Map<ContactId, ParticipantStatus> setOfParticipant) { Set<String> result = new HashSet<>(); if (setOfParticipant.size() != 0) { for (ContactId contact : setOfParticipant.keySet()) { // TODO consider status ? result.add(contact.toString()); } } return result; } /** * Initiate the group chat and open a progress dialog waiting for the session to start * * @return True if successful */ private boolean initiateGroupChat(boolean firstLoad) { /* Initiate the group chat session in background */ try { mGroupChat = mChatService.initiateGroupChat(new HashSet<>(mParticipants), mSubject); mChatId = mGroupChat.getChatId(); setCursorLoader(firstLoad); sChatIdOnForeground = mChatId; } catch (RcsServiceException e) { showExceptionThenExit(e); return false; } /* Display a progress dialog waiting for the session to start */ mProgressDialog = showProgressDialog(getString(R.string.label_command_in_progress)); mProgressDialog.setOnCancelListener( new OnCancelListener() { public void onCancel(DialogInterface dialog) { Utils.displayToast( GroupChatView.this, getString(R.string.label_chat_initiation_canceled)); quitSession(); } }); return true; } private void quitSession() { try { /* check if the session is not already stopped */ if (mGroupChat != null) { mGroupChat.leave(); } } catch (RcsServiceException e) { showException(e); } finally { mGroupChat = null; finish(); } } /** Add participants to be invited in the session */ private void addParticipants() { /* Build list of available contacts not already in the conference */ Set<ContactId> availableParticipants = new HashSet<>(); try { Set<RcsContact> contacts = getContactApi().getRcsContacts(); for (RcsContact rcsContact : contacts) { ContactId contact = rcsContact.getContactId(); if (mGroupChat.isAllowedToInviteParticipant(contact)) { availableParticipants.add(contact); } } } catch (RcsServiceException e) { showException(e); return; } /* Check if some participants are available */ if (availableParticipants.size() == 0) { showMessage(R.string.label_no_participant_found); return; } /* Display contacts */ final List<String> selectedParticipants = new ArrayList<>(); final CharSequence[] items = new CharSequence[availableParticipants.size()]; int i = 0; for (ContactId contact : availableParticipants) { items[i++] = contact.toString(); } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.label_select_contacts); builder.setCancelable(true); builder.setMultiChoiceItems( items, null, new DialogInterface.OnMultiChoiceClickListener() { public void onClick(DialogInterface dialog, int which, boolean isChecked) { String c = (String) items[which]; if (isChecked) { selectedParticipants.add(c); } else { selectedParticipants.remove(c); } } }); builder.setNegativeButton(R.string.label_cancel, null); builder.setPositiveButton( R.string.label_ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int position) { /* Add new participants in the session in background */ try { int max = mGroupChat.getMaxParticipants() - 1; int connected = mGroupChat.getParticipants().size(); int limit = max - connected; if (selectedParticipants.size() > limit) { showMessage(R.string.label_max_participants); return; } Set<ContactId> contacts = new HashSet<>(); ContactUtil contactUtils = ContactUtil.getInstance(GroupChatView.this); for (String participant : selectedParticipants) { contacts.add(contactUtils.formatContact(participant)); } /* Add participants */ mGroupChat.inviteParticipants(contacts); } catch (RcsServiceException e) { showException(e); } } }); registerDialog(builder.show()); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = new MenuInflater(getApplicationContext()); inflater.inflate(R.menu.menu_group_chat, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem menuItemParticipants = menu.findItem(R.id.menu_participants); MenuItem menuItemSendFile = menu.findItem(R.id.menu_send_file); MenuItem menuItemLeave = menu.findItem(R.id.menu_close_session); try { if (mGroupChat != null) { menuItemParticipants.setEnabled(mGroupChat.isAllowedToInviteParticipants()); menuItemLeave.setEnabled(mGroupChat.isAllowedToLeave()); FileTransferService fileTransferService = getFileTransferApi(); menuItemSendFile.setEnabled( fileTransferService.isAllowedToTransferFileToGroupChat(mChatId)); } else { menuItemParticipants.setEnabled(false); menuItemSendFile.setEnabled(false); menuItemLeave.setEnabled(false); } } catch (RcsServiceException e) { showException(e); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { try { switch (item.getItemId()) { case R.id.menu_insert_smiley: AlertDialog alert = Smileys.showSmileyDialog( this, mComposeText, getResources(), getString(R.string.menu_insert_smiley)); registerDialog(alert); break; case R.id.menu_participants: alert = Utils.showList( this, getString(R.string.menu_participants), getSetOfParticipants(mGroupChat.getParticipants())); registerDialog(alert); break; case R.id.menu_add_participant: addParticipants(); break; case R.id.menu_quicktext: addQuickText(); break; case R.id.menu_send_file: SendGroupFile.startActivity(this, mChatId); break; case R.id.menu_send_geoloc: getGeoLoc(); break; case R.id.menu_showus_map: DisplayGeoloc.showContactsOnMap(this, mGroupChat.getParticipants().keySet()); break; case R.id.menu_close_session: AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.title_chat_exit); builder.setPositiveButton( R.string.label_ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (mGroupChat != null) { try { mGroupChat.leave(); } catch (RcsServiceException e) { showExceptionThenExit(e); } } GroupChatView.this.finish(); } }); builder.setNegativeButton(R.string.label_cancel, null); builder.setCancelable(true); registerDialog(builder.show()); break; } } catch (RcsServiceException e) { showException(e); } return true; } private void hideProgressDialog() { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); mProgressDialog = null; } } /** * Initiate a new Group Chat * * @param ctx context * @param subject subject * @param participants list of participants */ public static void initiateGroupChat( Context ctx, String subject, ArrayList<String> participants) { Intent intent = new Intent(ctx, GroupChatView.class); intent.setAction(INTITIATE_GROUPCHAT); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.putStringArrayListExtra(GroupChatView.EXTRA_PARTICIPANTS, participants); intent.putExtra(GroupChatView.EXTRA_MODE, GroupChatMode.OUTGOING); intent.putExtra(GroupChatView.EXTRA_SUBJECT, subject); ctx.startActivity(intent); } /** * Open a Group Chat * * @param ctx The context. * @param chatId The chat ID. */ public static void openGroupChat(Context ctx, String chatId) { Intent intent = new Intent(ctx, GroupChatView.class); intent.setAction(OPEN_GROUPCHAT); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.putExtra(GroupChatView.EXTRA_MODE, GroupChatMode.OPEN); intent.putExtra(GroupChatIntent.EXTRA_CHAT_ID, chatId); ctx.startActivity(intent); } /** * Forge intent to notify Group Chat message * * @param ctx The context. * @param newgroupChatMessage The original intent. * @param chatId the chat ID * @return intent */ public static Intent forgeIntentNewMessage( Context ctx, Intent newgroupChatMessage, String chatId) { newgroupChatMessage.setClass(ctx, GroupChatView.class); newgroupChatMessage.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); newgroupChatMessage.putExtra(GroupChatView.EXTRA_MODE, GroupChatMode.INCOMING); newgroupChatMessage.putExtra(GroupChatIntent.EXTRA_CHAT_ID, chatId); return newgroupChatMessage; } /** * Forge intent to notify new Group Chat * * @param ctx The context. * @param invitation The original intent. * @return intent */ public static Intent forgeIntentInvitation(Context ctx, Intent invitation) { invitation.setClass(ctx, GroupChatView.class); invitation.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); invitation.putExtra(GroupChatView.EXTRA_MODE, GroupChatMode.INCOMING); return invitation; } @Override public ChatMessage sendMessage(String message) throws RcsServiceException { if (LogUtils.isActive) { Log.d(LOGTAG, "sendTextMessage: ".concat(message)); } return mGroupChat.sendMessage(message); } @Override public ChatMessage sendMessage(Geoloc geoloc) throws RcsServiceException { if (LogUtils.isActive) { Log.d(LOGTAG, "sendGeolocMessage: ".concat(geoloc.toString())); } return mGroupChat.sendMessage(geoloc); } @Override public void addChatEventListener(ChatService chatService) throws RcsServiceException { mChatService.addEventListener(mChatListener); } @Override public void removeChatEventListener(ChatService chatService) throws RcsServiceException { mChatService.removeEventListener(mChatListener); } @Override public boolean isSingleChat() { return false; } @Override public INotifyComposing getNotifyComposing() { return new INotifyComposing() { public void setTypingStatus(boolean isTyping) { try { if (mGroupChat != null) { mGroupChat.setComposingStatus(isTyping); if (LogUtils.isActive) { Log.d(LOGTAG, "sendIsComposingEvent ".concat(String.valueOf(isTyping))); } } } catch (RcsGenericException e) { showException(e); } } }; } @Override public void initialize() { mChatListener = new GroupChatListener() { @Override public void onMessageStatusChanged( String chatId, String mimeType, String msgId, Content.Status status, Content.ReasonCode reasonCode) { if (LogUtils.isActive) { Log.i( LOGTAG, "onMessageStatusChanged chatId=" + chatId + " mime-type=" + mimeType + " msgId=" + msgId + " status=" + status + " reason=" + reasonCode); } } // Callback called when an Is-composing event has been received public void onComposingEvent(String chatId, ContactId contact, boolean status) { // Discard event if not for current chatId if (!mChatId.equals(chatId)) { return; } displayComposingEvent(contact, status); } @Override public void onParticipantStatusChanged( String chatId, ContactId contact, ParticipantStatus status) { if (LogUtils.isActive) { Log.d( LOGTAG, "onParticipantStatusChanged chatId=" + chatId + " contact=" + contact + " status=" + status); } } @Override public void onMessageGroupDeliveryInfoChanged( String chatId, ContactId contact, String mimeType, String msgId, GroupDeliveryInfo.Status status, GroupDeliveryInfo.ReasonCode reasonCode) { if (LogUtils.isActive) { Log.d( LOGTAG, "onMessageGroupDeliveryInfoChanged chatId=" + chatId + " contact=" + contact + " msgId=" + msgId + " status=" + status + " reason=" + reasonCode); } } @Override public void onStateChanged( String chatId, final GroupChat.State state, GroupChat.ReasonCode reasonCode) { if (LogUtils.isActive) { Log.d( LOGTAG, "onStateChanged chatId=" + chatId + " state=" + state + " reason=" + reasonCode); } /* Discard event if not for current chatId */ if (mChatId == null || !mChatId.equals(chatId)) { return; } final String _reasonCode = RiApplication.sGroupChatReasonCodes[reasonCode.toInt()]; mHandler.post( new Runnable() { public void run() { switch (state) { case STARTED: /* Session is well established : hide progress dialog. */ hideProgressDialog(); break; case ABORTED: showMessageThenExit(getString(R.string.label_chat_aborted, _reasonCode)); break; case REJECTED: showMessageThenExit(getString(R.string.label_chat_rejected, _reasonCode)); break; case FAILED: showMessageThenExit(getString(R.string.label_chat_failed, _reasonCode)); break; default: } } }); } @Override public void onDeleted(Set<String> chatIds) { if (LogUtils.isActive) { Log.i(LOGTAG, "onDeleted chatIds=".concat(Arrays.toString(chatIds.toArray()))); } } @Override public void onMessagesDeleted(String chatId, Set<String> msgIds) { if (LogUtils.isActive) { Log.i( LOGTAG, "onMessagesDeleted chatId=" + chatId + " msgIds=" + Arrays.toString(msgIds.toArray())); } } }; } }