/** * File transfer intent service * * @author Philippe LEMORDANT */ public class FileTransferIntentService extends IntentService { private static final String LOGTAG = LogUtils.getTag(FileTransferIntentService.class.getName()); private static final String[] PROJ_UNDELIVERED_FT = new String[] {FileTransferLog.FT_ID}; private static final String SEL_UNDELIVERED_FTS = FileTransferLog.CHAT_ID + "=? AND " + FileTransferLog.EXPIRED_DELIVERY + "='1'"; /** Constructor */ public FileTransferIntentService() { super("FileTransferIntentService"); } @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; } String transferId = intent.getStringExtra(FileTransferIntent.EXTRA_TRANSFER_ID); if (transferId == null) { if (LogUtils.isActive) { Log.e(LOGTAG, "Cannot read transfer ID"); } return; } if (LogUtils.isActive) { Log.d(LOGTAG, "onHandleIntent file transfer with ID ".concat(transferId)); } switch (action) { case FileTransferIntent.ACTION_FILE_TRANSFER_DELIVERY_EXPIRED: handleUndeliveredFileTransfer(intent, transferId); break; case FileTransferIntent.ACTION_NEW_INVITATION: handleFileTransferInvitation(intent, transferId); break; case FileTransferIntent.ACTION_RESUME: handleFileTransferResume(intent, transferId); break; default: Log.e(LOGTAG, "Unknown action ".concat(action)); } } private void handleFileTransferResume(Intent intent, String transferId) { FileTransferDAO ftDao = FileTransferDAO.getFileTransferDAO(this, transferId); if (ftDao != null) { if (LogUtils.isActive) { Log.d(LOGTAG, "onHandleIntent file transfer resume with ID ".concat(transferId)); } if (Direction.INCOMING == ftDao.getDirection()) { startActivity(ReceiveFileTransfer.forgeResumeIntent(this, ftDao, intent)); } else { startActivity(InitiateFileTransfer.forgeResumeIntent(this, ftDao, intent)); } } } private void handleFileTransferInvitation(Intent intent, String transferId) { FileTransferDAO ftDao = FileTransferDAO.getFileTransferDAO(this, transferId); if (ftDao != null) { if (FileTransfer.State.REJECTED == ftDao.getState()) { Log.e(LOGTAG, "File transfer already rejected. Id=".concat(transferId)); return; } if (LogUtils.isActive) { Log.d( LOGTAG, "File Transfer invitation filename=" + ftDao.getFilename() + " size=" + ftDao.getSize()); } forwardFileTransferInvitationToUi(intent, ftDao); } } /** * Forward file transfer invitation to UI * * @param invitation Intent invitation * @param ftDao the file transfer data object */ private void forwardFileTransferInvitationToUi(Intent invitation, FileTransferDAO ftDao) { ContactId contact = ftDao.getContact(); if (ftDao.getContact() == null) { if (LogUtils.isActive) { Log.e(LOGTAG, "forwardFileTransferInvitationToUi failed: cannot parse contact"); } return; } Intent intent = ReceiveFileTransfer.forgeInvitationIntent(this, ftDao, invitation); /* * 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 pi = PendingIntent.getActivity(this, uniqueId, intent, PendingIntent.FLAG_ONE_SHOT); String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact); String title = getString(R.string.title_recv_file_transfer); String message = getString(R.string.label_from_args, displayName); /* Send notification */ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Notification notif = buildNotification(pi, title, message); notificationManager.notify(uniqueId, notif); TalkList.notifyNewConversationEvent(this, FileTransferIntent.ACTION_NEW_INVITATION); } private void handleUndeliveredFileTransfer(Intent intent, String transferId) { ContactId contact = intent.getParcelableExtra(FileTransferIntent.EXTRA_CONTACT); if (contact == null) { if (LogUtils.isActive) { Log.e(LOGTAG, "Cannot read contact for ftId=".concat(transferId)); } return; } if (LogUtils.isActive) { Log.d(LOGTAG, "Undelivered file transfer ID=" + transferId + " for contact " + contact); } forwardUndeliveredFileTransferToUi(intent, contact); } private void forwardUndeliveredFileTransferToUi(Intent undeliveredIntent, ContactId contact) { Intent intent = OneToOneTalkView.forgeIntentOnStackEvent(this, contact, undeliveredIntent); ChatPendingIntentManager pendingIntentmanager = ChatPendingIntentManager.getChatPendingIntentManager(this); Integer uniqueId = pendingIntentmanager.tryContinueChatConversation(intent, contact.toString()); if (uniqueId != null) { PendingIntent contentIntent = PendingIntent.getActivity(this, uniqueId, intent, PendingIntent.FLAG_UPDATE_CURRENT); String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact); String title = getString(R.string.title_undelivered_filetransfer); String msg = getString(R.string.label_undelivered_filetransfer, displayName); Notification notif = buildNotification(contentIntent, title, msg); pendingIntentmanager.postNotification(uniqueId, notif); } } /** * Generate a notification * * @param pendingIntent pending intent * @param title title * @param message message * @return the notification */ private Notification buildNotification( PendingIntent pendingIntent, String title, String message) { NotificationCompat.Builder notif = new NotificationCompat.Builder(this); notif.setContentIntent(pendingIntent); notif.setSmallIcon(R.drawable.ri_notif_file_transfer_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(message); return notif.build(); } /** * Get set of undelivered file transfers * * @param ctx The context * @param contact The contact * @return set of undelivered file transfers */ public static Set<String> getUndelivered(Context ctx, ContactId contact) { Set<String> ids = new HashSet<>(); Cursor cursor = null; try { cursor = ctx.getContentResolver() .query( FileTransferLog.CONTENT_URI, PROJ_UNDELIVERED_FT, SEL_UNDELIVERED_FTS, new String[] {contact.toString()}, null); if (cursor == null) { throw new SQLException("Cannot query undelivered file transfers for contact=" + contact); } if (!cursor.moveToFirst()) { return ids; } int idColumnIdx = cursor.getColumnIndexOrThrow(FileTransferLog.FT_ID); do { ids.add(cursor.getString(idColumnIdx)); } while (cursor.moveToNext()); return ids; } finally { if (cursor != null) { cursor.close(); } } } }
/** * Refresh capabilities of a given contact * * @author Jean-Marc AUFFRET */ public class RequestCapabilities extends RcsActivity { private final Handler mHandler = new Handler(); private MyCapabilitiesListener mCapabilitiesListener = new MyCapabilitiesListener(); private static final String EXTENSION_SEPARATOR = "\n"; private static final String LOGTAG = LogUtils.getTag(RequestCapabilities.class.getSimpleName()); /** Spinner for contact selection */ private Spinner mSpinner; private OnClickListener mBtnRefreshListener; private OnItemSelectedListener mListenerContact; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initialize(); /* Set layout */ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setContentView(R.layout.capabilities_request); /* Set the contact selector */ mSpinner = (Spinner) findViewById(R.id.contact); mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this)); mSpinner.setOnItemSelectedListener(mListenerContact); /* Set button callback */ Button refreshBtn = (Button) findViewById(R.id.refresh_btn); refreshBtn.setOnClickListener(mBtnRefreshListener); /* Update refresh button */ if (mSpinner.getAdapter().getCount() == 0) { // Disable button if no contact available refreshBtn.setEnabled(false); } else { refreshBtn.setEnabled(true); } /* Register to API connection manager */ if (!isServiceConnected(RcsServiceName.CAPABILITY)) { showMessageThenExit(R.string.label_service_not_available); return; } startMonitorServices(RcsServiceName.CAPABILITY); try { getCapabilityApi().addCapabilitiesListener(mCapabilitiesListener); } catch (RcsServiceException e) { showExceptionThenExit(e); } } @Override public void onDestroy() { super.onDestroy(); if (isServiceConnected(RcsServiceName.CAPABILITY)) { // Remove image sharing listener try { getCapabilityApi().removeCapabilitiesListener(mCapabilitiesListener); } catch (RcsServiceException e) { Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e)); } } } /** Capabilities event listener */ private class MyCapabilitiesListener extends CapabilitiesListener { /** * Callback called when new capabilities are received for a given contact * * @param contact Contact * @param capabilities Capabilities */ public void onCapabilitiesReceived(final ContactId contact, final Capabilities capabilities) { if (LogUtils.isActive) { Log.d(LOGTAG, "onCapabilitiesReceived " + contact); } final ContactId selectedContact = getSelectedContact(); if (!contact.equals(selectedContact)) { // Discard capabilities if not for selected contact return; } mHandler.post( new Runnable() { public void run() { // Check if this intent concerns the current selected // contact if (contact.equals(selectedContact)) { // Update UI displayCapabilities(capabilities); } } }); } } /** * Returns the selected contact * * @return Contact */ private ContactId getSelectedContact() { // get selected phone number ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter(); return ContactUtil.formatContact(adapter.getSelectedNumber(mSpinner.getSelectedView())); } private void updateCapabilities(ContactId contact) { // Display info Utils.displayLongToast( RequestCapabilities.this, getString(R.string.label_request_in_background, contact)); try { Set<ContactId> contactSet = new HashSet<>(); contactSet.add(contact); getCapabilityApi().requestContactCapabilities(contactSet); } catch (RcsServiceException e) { showExceptionThenExit(e); } } private void displayCapabilities(Capabilities capabilities) { CheckBox imageCSh = (CheckBox) findViewById(R.id.image_sharing); CheckBox videoCSh = (CheckBox) findViewById(R.id.video_sharing); CheckBox ft = (CheckBox) findViewById(R.id.file_transfer); CheckBox im = (CheckBox) findViewById(R.id.im); CheckBox geoloc = (CheckBox) findViewById(R.id.geoloc_push); TextView extensions = (TextView) findViewById(R.id.extensions); TextView timestamp = (TextView) findViewById(R.id.last_refresh); CheckBox automata = (CheckBox) findViewById(R.id.automata); if (capabilities != null) { // Set capabilities imageCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IMAGE_SHARING)); videoCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_VIDEO_SHARING)); ft.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_FILE_TRANSFER)); im.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IM)); geoloc.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_GEOLOC_PUSH)); } // Set extensions extensions.setVisibility(View.VISIBLE); extensions.setText(getExtensions(capabilities)); automata.setChecked((capabilities != null) && capabilities.isAutomata()); timestamp.setText( (capabilities != null) ? DateUtils.getRelativeTimeSpanString( capabilities.getTimestamp(), System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE) : ""); } /* package private */ static String getExtensions(Capabilities capabilities) { if (capabilities == null || capabilities.getSupportedExtensions().isEmpty()) { return ""; } StringBuilder extensions = new StringBuilder(); for (String capability : capabilities.getSupportedExtensions()) { extensions.append(EXTENSION_SEPARATOR).append(capability); } return extensions.substring(EXTENSION_SEPARATOR.length()); } private void initialize() { mBtnRefreshListener = new OnClickListener() { public void onClick(View v) { // Check if the service is available try { if (!getCapabilityApi().isServiceRegistered()) { showMessage(R.string.error_not_registered); return; } } catch (RcsServiceException e) { showExceptionThenExit(e); return; } ContactId contact = getSelectedContact(); if (contact != null) { updateCapabilities(contact); } } }; mListenerContact = new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { CapabilityService capabilityApi = getCapabilityApi(); try { // Get selected contact ContactId contactId = getSelectedContact(); // Get current capabilities Capabilities currentCapabilities = capabilityApi.getContactCapabilities(contactId); // Display default capabilities displayCapabilities(currentCapabilities); if (currentCapabilities == null) { Utils.displayLongToast( RequestCapabilities.this, getString(R.string.label_no_capabilities, contactId.toString())); } } catch (RcsServiceException e) { showExceptionThenExit(e); } } @Override public void onNothingSelected(AdapterView<?> arg0) {} }; } }