Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.messaging.ui;
     17 
     18 import android.app.Activity;
     19 import android.app.Fragment;
     20 import android.app.PendingIntent;
     21 import android.appwidget.AppWidgetManager;
     22 import android.content.ActivityNotFoundException;
     23 import android.content.ClipData;
     24 import android.content.ComponentName;
     25 import android.content.ContentValues;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.graphics.Point;
     29 import android.graphics.Rect;
     30 import android.media.RingtoneManager;
     31 import android.net.Uri;
     32 import android.os.Bundle;
     33 import android.provider.ContactsContract.Contacts;
     34 import android.provider.ContactsContract.Intents;
     35 import android.provider.MediaStore;
     36 import android.provider.Telephony;
     37 import android.support.annotation.Nullable;
     38 import android.support.v4.app.TaskStackBuilder;
     39 import android.support.v4.content.LocalBroadcastManager;
     40 import android.text.TextUtils;
     41 
     42 import com.android.ex.photo.Intents.PhotoViewIntentBuilder;
     43 import com.android.messaging.R;
     44 import com.android.messaging.datamodel.ConversationImagePartsView;
     45 import com.android.messaging.datamodel.MediaScratchFileProvider;
     46 import com.android.messaging.datamodel.MessagingContentProvider;
     47 import com.android.messaging.datamodel.data.MessageData;
     48 import com.android.messaging.datamodel.data.MessagePartData;
     49 import com.android.messaging.datamodel.data.ParticipantData;
     50 import com.android.messaging.receiver.NotificationReceiver;
     51 import com.android.messaging.sms.MmsSmsUtils;
     52 import com.android.messaging.ui.appsettings.ApnEditorActivity;
     53 import com.android.messaging.ui.appsettings.ApnSettingsActivity;
     54 import com.android.messaging.ui.appsettings.ApplicationSettingsActivity;
     55 import com.android.messaging.ui.appsettings.PerSubscriptionSettingsActivity;
     56 import com.android.messaging.ui.appsettings.SettingsActivity;
     57 import com.android.messaging.ui.attachmentchooser.AttachmentChooserActivity;
     58 import com.android.messaging.ui.conversation.ConversationActivity;
     59 import com.android.messaging.ui.conversation.LaunchConversationActivity;
     60 import com.android.messaging.ui.conversationlist.ArchivedConversationListActivity;
     61 import com.android.messaging.ui.conversationlist.ConversationListActivity;
     62 import com.android.messaging.ui.conversationlist.ForwardMessageActivity;
     63 import com.android.messaging.ui.conversationsettings.PeopleAndOptionsActivity;
     64 import com.android.messaging.ui.debug.DebugMmsConfigActivity;
     65 import com.android.messaging.ui.photoviewer.BuglePhotoViewActivity;
     66 import com.android.messaging.util.Assert;
     67 import com.android.messaging.util.ContentType;
     68 import com.android.messaging.util.ConversationIdSet;
     69 import com.android.messaging.util.LogUtil;
     70 import com.android.messaging.util.UiUtils;
     71 import com.android.messaging.util.UriUtil;
     72 
     73 /**
     74  * A central repository of Intents used to start activities.
     75  */
     76 public class UIIntentsImpl extends UIIntents {
     77     private static final String CELL_BROADCAST_LIST_ACTIVITY =
     78             "com.android.cellbroadcastreceiver.CellBroadcastListActivity";
     79     private static final String CALL_TARGET_CLICK_KEY = "touchPoint";
     80     private static final String CALL_TARGET_CLICK_EXTRA_KEY =
     81             "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     82     private static final String MEDIA_SCANNER_CLASS =
     83             "com.android.providers.media.MediaScannerService";
     84     private static final String MEDIA_SCANNER_PACKAGE = "com.android.providers.media";
     85     private static final String MEDIA_SCANNER_SCAN_ACTION = "android.media.IMediaScannerService";
     86 
     87     /**
     88      * Get an intent which takes you to a conversation
     89      */
     90     private Intent getConversationActivityIntent(final Context context,
     91             final String conversationId, final MessageData draft,
     92             final boolean withCustomTransition) {
     93         final Intent intent = new Intent(context, ConversationActivity.class);
     94 
     95         // Always try to reuse the same ConversationActivity in the current task so that we don't
     96         // have two conversation activities in the back stack.
     97         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
     98 
     99         // Otherwise we're starting a new conversation
    100         if (conversationId != null) {
    101             intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
    102         }
    103         if (draft != null) {
    104             intent.putExtra(UI_INTENT_EXTRA_DRAFT_DATA, draft);
    105 
    106             // If draft attachments came from an external content provider via a share intent, we
    107             // need to propagate the URI permissions through to ConversationActivity. This requires
    108             // putting the URIs into the ClipData (setData also works, but accepts only one URI).
    109             ClipData clipData = null;
    110             for (final MessagePartData partData : draft.getParts()) {
    111                 if (partData.isAttachment()) {
    112                     final Uri uri = partData.getContentUri();
    113                     if (clipData == null) {
    114                         clipData = ClipData.newRawUri("Attachments", uri);
    115                     } else {
    116                         clipData.addItem(new ClipData.Item(uri));
    117                     }
    118                 }
    119             }
    120             if (clipData != null) {
    121                 intent.setClipData(clipData);
    122                 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    123             }
    124         }
    125         if (withCustomTransition) {
    126             intent.putExtra(UI_INTENT_EXTRA_WITH_CUSTOM_TRANSITION, true);
    127         }
    128 
    129         if (!(context instanceof Activity)) {
    130             // If the caller supplies an application context, and not an activity context, we must
    131             // include this flag
    132             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    133         }
    134         return intent;
    135     }
    136 
    137     @Override
    138     public void launchPermissionCheckActivity(final Context context) {
    139         final Intent intent = new Intent(context, PermissionCheckActivity.class);
    140         context.startActivity(intent);
    141     }
    142 
    143     /**
    144      * Get an intent which takes you to the conversation list
    145      */
    146     private Intent getConversationListActivityIntent(final Context context) {
    147         return new Intent(context, ConversationListActivity.class);
    148     }
    149 
    150     @Override
    151     public void launchConversationListActivity(final Context context) {
    152         final Intent intent = getConversationListActivityIntent(context);
    153         context.startActivity(intent);
    154     }
    155 
    156     /**
    157      * Get an intent which shows the low storage warning activity.
    158      */
    159     private Intent getSmsStorageLowWarningActivityIntent(final Context context) {
    160         return new Intent(context, SmsStorageLowWarningActivity.class);
    161     }
    162 
    163     @Override
    164     public void launchConversationActivity(final Context context,
    165             final String conversationId, final MessageData draft, final Bundle activityOptions,
    166             final boolean withCustomTransition) {
    167         Assert.isTrue(!withCustomTransition || activityOptions != null);
    168         final Intent intent = getConversationActivityIntent(context, conversationId, draft,
    169                 withCustomTransition);
    170         context.startActivity(intent, activityOptions);
    171     }
    172 
    173     @Override
    174     public void launchConversationActivityNewTask(
    175             final Context context, final String conversationId) {
    176         final Intent intent = getConversationActivityIntent(context, conversationId, null,
    177                 false /* withCustomTransition */);
    178         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    179         context.startActivity(intent);
    180     }
    181 
    182     @Override
    183     public void launchConversationActivityWithParentStack(final Context context,
    184                 final String conversationId, final String smsBody) {
    185         final MessageData messageData = TextUtils.isEmpty(smsBody)
    186                 ? null
    187                 : MessageData.createDraftSmsMessage(conversationId, null, smsBody);
    188         TaskStackBuilder.create(context)
    189                 .addNextIntentWithParentStack(
    190                         getConversationActivityIntent(context, conversationId, messageData,
    191                                 false /* withCustomTransition */))
    192                 .startActivities();
    193     }
    194 
    195     @Override
    196     public void launchCreateNewConversationActivity(final Context context,
    197             final MessageData draft) {
    198         final Intent intent = getConversationActivityIntent(context, null, draft,
    199                 false /* withCustomTransition */);
    200         context.startActivity(intent);
    201     }
    202 
    203     @Override
    204     public void launchDebugMmsConfigActivity(final Context context) {
    205         context.startActivity(new Intent(context, DebugMmsConfigActivity.class));
    206     }
    207 
    208     @Override
    209     public void launchAddContactActivity(final Context context, final String destination) {
    210         final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
    211         final String destinationType = MmsSmsUtils.isEmailAddress(destination) ?
    212                 Intents.Insert.EMAIL : Intents.Insert.PHONE;
    213         intent.setType(Contacts.CONTENT_ITEM_TYPE);
    214         intent.putExtra(destinationType, destination);
    215         startExternalActivity(context, intent);
    216     }
    217 
    218     @Override
    219     public void launchSettingsActivity(final Context context) {
    220         final Intent intent = new Intent(context, SettingsActivity.class);
    221         context.startActivity(intent);
    222     }
    223 
    224     @Override
    225     public void launchArchivedConversationsActivity(final Context context) {
    226         final Intent intent = new Intent(context, ArchivedConversationListActivity.class);
    227         context.startActivity(intent);
    228     }
    229 
    230     @Override
    231     public void launchBlockedParticipantsActivity(final Context context) {
    232         final Intent intent = new Intent(context, BlockedParticipantsActivity.class);
    233         context.startActivity(intent);
    234     }
    235 
    236     @Override
    237     public void launchDocumentImagePicker(final Fragment fragment) {
    238         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    239         intent.putExtra(Intent.EXTRA_MIME_TYPES, MessagePartData.ACCEPTABLE_IMAGE_TYPES);
    240         intent.addCategory(Intent.CATEGORY_OPENABLE);
    241         intent.setType(ContentType.IMAGE_UNSPECIFIED);
    242 
    243         fragment.startActivityForResult(intent, REQUEST_PICK_IMAGE_FROM_DOCUMENT_PICKER);
    244     }
    245 
    246     @Override
    247     public void launchPeopleAndOptionsActivity(final Activity activity,
    248             final String conversationId) {
    249         final Intent intent = new Intent(activity, PeopleAndOptionsActivity.class);
    250         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
    251         activity.startActivityForResult(intent, 0);
    252     }
    253 
    254     @Override
    255     public void launchPhoneCallActivity(final Context context, final String phoneNumber,
    256                                         final Point clickPosition) {
    257         final Intent intent = new Intent(Intent.ACTION_CALL,
    258                 Uri.parse(UriUtil.SCHEME_TEL + phoneNumber));
    259         final Bundle extras = new Bundle();
    260         extras.putParcelable(CALL_TARGET_CLICK_KEY, clickPosition);
    261         intent.putExtra(CALL_TARGET_CLICK_EXTRA_KEY, extras);
    262         startExternalActivity(context, intent);
    263     }
    264 
    265     @Override
    266     public void launchClassZeroActivity(final Context context, final ContentValues messageValues) {
    267         final Intent classZeroIntent = new Intent(context, ClassZeroActivity.class)
    268                 .putExtra(UI_INTENT_EXTRA_MESSAGE_VALUES, messageValues)
    269                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
    270         context.startActivity(classZeroIntent);
    271     }
    272 
    273     @Override
    274     public void launchForwardMessageActivity(final Context context, final MessageData message) {
    275         final Intent forwardMessageIntent = new Intent(context, ForwardMessageActivity.class)
    276                 .putExtra(UI_INTENT_EXTRA_DRAFT_DATA, message);
    277         context.startActivity(forwardMessageIntent);
    278     }
    279 
    280     @Override
    281     public void launchVCardDetailActivity(final Context context, final Uri vcardUri) {
    282         final Intent vcardDetailIntent = new Intent(context, VCardDetailActivity.class)
    283                 .putExtra(UI_INTENT_EXTRA_VCARD_URI, vcardUri);
    284         context.startActivity(vcardDetailIntent);
    285     }
    286 
    287     @Override
    288     public void launchSaveVCardToContactsActivity(final Context context, final Uri vcardUri) {
    289         Assert.isTrue(MediaScratchFileProvider.isMediaScratchSpaceUri(vcardUri));
    290         final Intent intent = new Intent();
    291         intent.setAction(Intent.ACTION_VIEW);
    292         intent.setDataAndType(vcardUri, ContentType.TEXT_VCARD.toLowerCase());
    293         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    294         startExternalActivity(context, intent);
    295     }
    296 
    297     @Override
    298     public void launchAttachmentChooserActivity(final Activity activity,
    299             final String conversationId, final int requestCode) {
    300         final Intent intent = new Intent(activity, AttachmentChooserActivity.class);
    301         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
    302         activity.startActivityForResult(intent, requestCode);
    303     }
    304 
    305     @Override
    306     public void launchFullScreenVideoViewer(final Context context, final Uri videoUri) {
    307         final Intent intent = new Intent(Intent.ACTION_VIEW);
    308         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    309 
    310         // So we don't see "surrounding" images in Gallery
    311         intent.putExtra("SingleItemOnly", true);
    312         intent.setDataAndType(videoUri, ContentType.VIDEO_UNSPECIFIED);
    313         startExternalActivity(context, intent);
    314     }
    315 
    316     @Override
    317     public void launchFullScreenPhotoViewer(final Activity activity, final Uri initialPhoto,
    318             final Rect initialPhotoBounds, final Uri photosUri) {
    319         final PhotoViewIntentBuilder builder =
    320                 com.android.ex.photo.Intents.newPhotoViewIntentBuilder(
    321                         activity, BuglePhotoViewActivity.class);
    322         builder.setPhotosUri(photosUri.toString());
    323         builder.setInitialPhotoUri(initialPhoto.toString());
    324         builder.setProjection(ConversationImagePartsView.PhotoViewQuery.PROJECTION);
    325 
    326         // Set the location of the imageView so that the photoviewer can animate from that location
    327         // to full screen.
    328         builder.setScaleAnimation(initialPhotoBounds.left, initialPhotoBounds.top,
    329                 initialPhotoBounds.width(), initialPhotoBounds.height());
    330 
    331         builder.setDisplayThumbsFullScreen(false);
    332         builder.setMaxInitialScale(8);
    333         activity.startActivity(builder.build());
    334         activity.overridePendingTransition(0, 0);
    335     }
    336 
    337     @Override
    338     public void launchApplicationSettingsActivity(final Context context, final boolean topLevel) {
    339         final Intent intent = new Intent(context, ApplicationSettingsActivity.class);
    340         intent.putExtra(UI_INTENT_EXTRA_TOP_LEVEL_SETTINGS, topLevel);
    341         context.startActivity(intent);
    342     }
    343 
    344     @Override
    345     public void launchPerSubscriptionSettingsActivity(final Context context, final int subId,
    346             final String settingTitle) {
    347         final Intent intent = getPerSubscriptionSettingsIntent(context, subId, settingTitle);
    348         context.startActivity(intent);
    349     }
    350 
    351     @Override
    352     public Intent getViewUrlIntent(final String url) {
    353         final Uri uri = Uri.parse(url);
    354         return new Intent(Intent.ACTION_VIEW, uri);
    355     }
    356 
    357     @Override
    358     public void broadcastConversationSelfIdChange(final Context context,
    359             final String conversationId, final String conversationSelfId) {
    360         final Intent intent = new Intent(CONVERSATION_SELF_ID_CHANGE_BROADCAST_ACTION);
    361         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
    362         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_SELF_ID, conversationSelfId);
    363         LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
    364     }
    365 
    366     @Override
    367     public PendingIntent getPendingIntentForConversationListActivity(final Context context) {
    368         final Intent intent = getConversationListActivityIntent(context);
    369         return getPendingIntentWithParentStack(context, intent, 0);
    370     }
    371 
    372     @Override
    373     public PendingIntent getPendingIntentForConversationActivity(final Context context,
    374             final String conversationId, final MessageData draft) {
    375         final Intent intent = getConversationActivityIntent(context, conversationId, draft,
    376                 false /* withCustomTransition */);
    377         // Ensure that the platform doesn't reuse PendingIntents across conversations
    378         intent.setData(MessagingContentProvider.buildConversationMetadataUri(conversationId));
    379         return getPendingIntentWithParentStack(context, intent, 0);
    380     }
    381 
    382     @Override
    383     public Intent getIntentForConversationActivity(final Context context,
    384             final String conversationId, final MessageData draft) {
    385         final Intent intent = getConversationActivityIntent(context, conversationId, draft,
    386                 false /* withCustomTransition */);
    387         return intent;
    388     }
    389 
    390     @Override
    391     public PendingIntent getPendingIntentForSendingMessageToConversation(final Context context,
    392             final String conversationId, final String selfId, final boolean requiresMms,
    393             final int requestCode) {
    394         final Intent intent = new Intent(context, RemoteInputEntrypointActivity.class);
    395         intent.setAction(Intent.ACTION_SENDTO);
    396         // Ensure that the platform doesn't reuse PendingIntents across conversations
    397         intent.setData(MessagingContentProvider.buildConversationMetadataUri(conversationId));
    398         intent.putExtra(UIIntents.UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
    399         intent.putExtra(UIIntents.UI_INTENT_EXTRA_SELF_ID, selfId);
    400         intent.putExtra(UIIntents.UI_INTENT_EXTRA_REQUIRES_MMS, requiresMms);
    401         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    402         return getPendingIntentWithParentStack(context, intent, requestCode);
    403     }
    404 
    405     @Override
    406     public PendingIntent getPendingIntentForClearingNotifications(final Context context,
    407             final int updateTargets, final ConversationIdSet conversationIdSet,
    408             final int requestCode) {
    409         final Intent intent = new Intent(context, NotificationReceiver.class);
    410         intent.setAction(ACTION_RESET_NOTIFICATIONS);
    411         intent.putExtra(UI_INTENT_EXTRA_NOTIFICATIONS_UPDATE, updateTargets);
    412         if (conversationIdSet != null) {
    413             intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID_SET,
    414                     conversationIdSet.getDelimitedString());
    415         }
    416         return PendingIntent.getBroadcast(context,
    417                 requestCode, intent,
    418                 PendingIntent.FLAG_UPDATE_CURRENT);
    419     }
    420 
    421     /**
    422      * Gets a PendingIntent associated with an Intent to start an Activity. All notifications
    423      * that starts an Activity must use this method to get a PendingIntent, which achieves two
    424      * goals:
    425      * 1. The target activities will be created, with any existing ones destroyed. This ensures
    426      *    we don't end up with multiple instances of ConversationListActivity, for example.
    427      * 2. The target activity, when launched, will have its backstack correctly constructed so
    428      *    back navigation will work correctly.
    429      */
    430     private static PendingIntent getPendingIntentWithParentStack(final Context context,
    431             final Intent intent, final int requestCode) {
    432         final TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
    433         // Adds the back stack for the Intent (plus the Intent itself)
    434         stackBuilder.addNextIntentWithParentStack(intent);
    435         final PendingIntent resultPendingIntent =
    436             stackBuilder.getPendingIntent(requestCode, PendingIntent.FLAG_UPDATE_CURRENT);
    437         return resultPendingIntent;
    438     }
    439 
    440     @Override
    441     public Intent getRingtonePickerIntent(final String title, final Uri existingUri,
    442             final Uri defaultUri, final int toneType) {
    443         return new Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
    444                 .putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, toneType)
    445                 .putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, title)
    446                 .putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existingUri)
    447                 .putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultUri);
    448     }
    449 
    450     @Override
    451     public PendingIntent getPendingIntentForLowStorageNotifications(final Context context) {
    452         final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
    453         final Intent conversationListIntent = getConversationListActivityIntent(context);
    454         taskStackBuilder.addNextIntent(conversationListIntent);
    455         taskStackBuilder.addNextIntentWithParentStack(
    456                 getSmsStorageLowWarningActivityIntent(context));
    457 
    458         return taskStackBuilder.getPendingIntent(
    459                 0, PendingIntent.FLAG_UPDATE_CURRENT);
    460     }
    461 
    462     @Override
    463     public PendingIntent getPendingIntentForSecondaryUserNewMessageNotification(
    464             final Context context) {
    465         return getPendingIntentForConversationListActivity(context);
    466     }
    467 
    468     @Override
    469     public Intent getWirelessAlertsIntent() {
    470         final Intent intent = new Intent(Intent.ACTION_MAIN);
    471         intent.setComponent(new ComponentName(CMAS_COMPONENT, CELL_BROADCAST_LIST_ACTIVITY));
    472         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    473         return intent;
    474     }
    475 
    476     @Override
    477     public Intent getApnEditorIntent(final Context context, final String rowId, final int subId) {
    478         final Intent intent = new Intent(context, ApnEditorActivity.class);
    479         intent.putExtra(UI_INTENT_EXTRA_APN_ROW_ID, rowId);
    480         intent.putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
    481         return intent;
    482     }
    483 
    484     @Override
    485     public Intent getApnSettingsIntent(final Context context, final int subId) {
    486         final Intent intent = new Intent(context, ApnSettingsActivity.class)
    487                 .putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
    488         return intent;
    489     }
    490 
    491     @Override
    492     public Intent getAdvancedSettingsIntent(final Context context) {
    493         return getPerSubscriptionSettingsIntent(context, ParticipantData.DEFAULT_SELF_SUB_ID, null);
    494     }
    495 
    496     @Override
    497     public Intent getChangeDefaultSmsAppIntent(final Activity activity) {
    498         final Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
    499         intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, activity.getPackageName());
    500         return intent;
    501     }
    502 
    503     @Override
    504     public void launchBrowserForUrl(final Context context, final String url) {
    505         final Intent intent = getViewUrlIntent(url);
    506         startExternalActivity(context, intent);
    507     }
    508 
    509     /**
    510      * Provides a safe way to handle external activities which may not exist.
    511      */
    512     private void startExternalActivity(final Context context, final Intent intent) {
    513         try {
    514             context.startActivity(intent);
    515         } catch (final ActivityNotFoundException ex) {
    516             LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
    517             UiUtils.showToastAtBottom(R.string.activity_not_found_message);
    518         }
    519     }
    520 
    521     private Intent getPerSubscriptionSettingsIntent(final Context context, final int subId,
    522             @Nullable final String settingTitle) {
    523         return new Intent(context, PerSubscriptionSettingsActivity.class)
    524             .putExtra(UI_INTENT_EXTRA_SUB_ID, subId)
    525             .putExtra(UI_INTENT_EXTRA_PER_SUBSCRIPTION_SETTING_TITLE, settingTitle);
    526     }
    527 
    528     @Override
    529     public Intent getLaunchConversationActivityIntent(final Context context) {
    530         final Intent intent = new Intent(context, LaunchConversationActivity.class);
    531         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY);
    532         return intent;
    533     }
    534 
    535     @Override
    536     public void kickMediaScanner(final Context context, final String volume) {
    537         final Intent intent = new Intent(MEDIA_SCANNER_SCAN_ACTION)
    538             .putExtra(MediaStore.MEDIA_SCANNER_VOLUME, volume)
    539             .setClassName(MEDIA_SCANNER_PACKAGE, MEDIA_SCANNER_CLASS);
    540         context.startService(intent);
    541     }
    542 
    543     @Override
    544     public PendingIntent getWidgetPendingIntentForConversationActivity(final Context context,
    545             final String conversationId, final int requestCode) {
    546         final Intent intent = getConversationActivityIntent(context, null, null,
    547                 false /* withCustomTransition */);
    548         if (conversationId != null) {
    549             intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
    550 
    551             // Set the action to something unique to this conversation so if someone calls this
    552             // function again on a different conversation, they'll get a new PendingIntent instead
    553             // of the old one.
    554             intent.setAction(ACTION_WIDGET_CONVERSATION + conversationId);
    555         }
    556         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    557         return getPendingIntentWithParentStack(context, intent, requestCode);
    558     }
    559 
    560     @Override
    561     public PendingIntent getWidgetPendingIntentForConversationListActivity(
    562             final Context context) {
    563         final Intent intent = getConversationListActivityIntent(context);
    564         return getPendingIntentWithParentStack(context, intent, 0);
    565     }
    566 
    567     @Override
    568     public PendingIntent getWidgetPendingIntentForConfigurationActivity(final Context context,
    569             final int appWidgetId) {
    570         final Intent configureIntent = new Intent(context, WidgetPickConversationActivity.class);
    571         configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    572         configureIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
    573         configureIntent.setData(Uri.parse(configureIntent.toUri(Intent.URI_INTENT_SCHEME)));
    574         configureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
    575         return getPendingIntentWithParentStack(context, configureIntent, 0);
    576     }
    577 }
    578