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 androidx.annotation.Nullable;
38 import androidx.core.app.TaskStackBuilder;
39 import androidx.localbroadcastmanager.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      */
getConversationActivityIntent(final Context context, final String conversationId, final MessageData draft, final boolean withCustomTransition)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
launchPermissionCheckActivity(final Context context)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      */
getConversationListActivityIntent(final Context context)146     private Intent getConversationListActivityIntent(final Context context) {
147         return new Intent(context, ConversationListActivity.class);
148     }
149 
150     @Override
launchConversationListActivity(final Context context)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      */
getSmsStorageLowWarningActivityIntent(final Context context)159     private Intent getSmsStorageLowWarningActivityIntent(final Context context) {
160         return new Intent(context, SmsStorageLowWarningActivity.class);
161     }
162 
163     @Override
launchConversationActivity(final Context context, final String conversationId, final MessageData draft, final Bundle activityOptions, final boolean withCustomTransition)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
launchConversationActivityNewTask( final Context context, final String conversationId)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
launchConversationActivityWithParentStack(final Context context, final String conversationId, final String smsBody)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
launchCreateNewConversationActivity(final Context context, final MessageData draft)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
launchDebugMmsConfigActivity(final Context context)204     public void launchDebugMmsConfigActivity(final Context context) {
205         context.startActivity(new Intent(context, DebugMmsConfigActivity.class));
206     }
207 
208     @Override
launchAddContactActivity(final Context context, final String destination)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
launchSettingsActivity(final Context context)219     public void launchSettingsActivity(final Context context) {
220         final Intent intent = new Intent(context, SettingsActivity.class);
221         context.startActivity(intent);
222     }
223 
224     @Override
launchArchivedConversationsActivity(final Context context)225     public void launchArchivedConversationsActivity(final Context context) {
226         final Intent intent = new Intent(context, ArchivedConversationListActivity.class);
227         context.startActivity(intent);
228     }
229 
230     @Override
launchBlockedParticipantsActivity(final Context context)231     public void launchBlockedParticipantsActivity(final Context context) {
232         final Intent intent = new Intent(context, BlockedParticipantsActivity.class);
233         context.startActivity(intent);
234     }
235 
236     @Override
launchDocumentImagePicker(final Fragment fragment)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_GALLERY_MEDIA_TYPES);
240         intent.addCategory(Intent.CATEGORY_OPENABLE);
241         intent.setType(ContentType.ANY_TYPE);
242 
243         fragment.startActivityForResult(intent, REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER);
244     }
245 
246     @Override
launchContactCardPicker(final Fragment fragment)247     public void launchContactCardPicker(final Fragment fragment) {
248         final Intent intent = new Intent(Intent.ACTION_PICK);
249         intent.setType(Contacts.CONTENT_TYPE);
250 
251         try {
252             fragment.startActivityForResult(intent, REQUEST_PICK_CONTACT_CARD);
253         } catch (final ActivityNotFoundException ex) {
254             LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
255             UiUtils.showToastAtBottom(R.string.activity_not_found_message);
256         }
257     }
258 
259     @Override
launchPeopleAndOptionsActivity(final Activity activity, final String conversationId)260     public void launchPeopleAndOptionsActivity(final Activity activity,
261             final String conversationId) {
262         final Intent intent = new Intent(activity, PeopleAndOptionsActivity.class);
263         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
264         activity.startActivityForResult(intent, 0);
265     }
266 
267     @Override
launchPhoneCallActivity(final Context context, final String phoneNumber, final Point clickPosition)268     public void launchPhoneCallActivity(final Context context, final String phoneNumber,
269                                         final Point clickPosition) {
270         final Intent intent = new Intent(Intent.ACTION_CALL,
271                 Uri.parse(UriUtil.SCHEME_TEL + phoneNumber));
272         final Bundle extras = new Bundle();
273         extras.putParcelable(CALL_TARGET_CLICK_KEY, clickPosition);
274         intent.putExtra(CALL_TARGET_CLICK_EXTRA_KEY, extras);
275         startExternalActivity(context, intent);
276     }
277 
278     @Override
launchClassZeroActivity(final Context context, final ContentValues messageValues)279     public void launchClassZeroActivity(final Context context, final ContentValues messageValues) {
280         final Intent classZeroIntent = new Intent(context, ClassZeroActivity.class)
281                 .putExtra(UI_INTENT_EXTRA_MESSAGE_VALUES, messageValues)
282                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
283         context.startActivity(classZeroIntent);
284     }
285 
286     @Override
launchForwardMessageActivity(final Context context, final MessageData message)287     public void launchForwardMessageActivity(final Context context, final MessageData message) {
288         final Intent forwardMessageIntent = new Intent(context, ForwardMessageActivity.class)
289                 .putExtra(UI_INTENT_EXTRA_DRAFT_DATA, message);
290         context.startActivity(forwardMessageIntent);
291     }
292 
293     @Override
launchVCardDetailActivity(final Context context, final Uri vcardUri)294     public void launchVCardDetailActivity(final Context context, final Uri vcardUri) {
295         final Intent vcardDetailIntent = new Intent(context, VCardDetailActivity.class)
296                 .putExtra(UI_INTENT_EXTRA_VCARD_URI, vcardUri);
297         context.startActivity(vcardDetailIntent);
298     }
299 
300     @Override
launchSaveVCardToContactsActivity(final Context context, final Uri vcardUri)301     public void launchSaveVCardToContactsActivity(final Context context, final Uri vcardUri) {
302         Assert.isTrue(MediaScratchFileProvider.isMediaScratchSpaceUri(vcardUri));
303         final Intent intent = new Intent();
304         intent.setAction(Intent.ACTION_VIEW);
305         intent.setDataAndType(vcardUri, ContentType.TEXT_VCARD.toLowerCase());
306         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
307         startExternalActivity(context, intent);
308     }
309 
310     @Override
launchAttachmentChooserActivity(final Activity activity, final String conversationId, final int requestCode)311     public void launchAttachmentChooserActivity(final Activity activity,
312             final String conversationId, final int requestCode) {
313         final Intent intent = new Intent(activity, AttachmentChooserActivity.class);
314         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
315         activity.startActivityForResult(intent, requestCode);
316     }
317 
318     @Override
launchFullScreenVideoViewer(final Context context, final Uri videoUri)319     public void launchFullScreenVideoViewer(final Context context, final Uri videoUri) {
320         final Intent intent = new Intent(Intent.ACTION_VIEW);
321         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
322 
323         // So we don't see "surrounding" images in Gallery
324         intent.putExtra("SingleItemOnly", true);
325         intent.setDataAndType(videoUri, ContentType.VIDEO_UNSPECIFIED);
326         startExternalActivity(context, intent);
327     }
328 
329     @Override
launchFullScreenPhotoViewer(final Activity activity, final Uri initialPhoto, final Rect initialPhotoBounds, final Uri photosUri)330     public void launchFullScreenPhotoViewer(final Activity activity, final Uri initialPhoto,
331             final Rect initialPhotoBounds, final Uri photosUri) {
332         final PhotoViewIntentBuilder builder =
333                 com.android.ex.photo.Intents.newPhotoViewIntentBuilder(
334                         activity, BuglePhotoViewActivity.class);
335         builder.setPhotosUri(photosUri.toString());
336         builder.setInitialPhotoUri(initialPhoto.toString());
337         builder.setProjection(ConversationImagePartsView.PhotoViewQuery.PROJECTION);
338 
339         // Set the location of the imageView so that the photoviewer can animate from that location
340         // to full screen.
341         builder.setScaleAnimation(initialPhotoBounds.left, initialPhotoBounds.top,
342                 initialPhotoBounds.width(), initialPhotoBounds.height());
343 
344         builder.setDisplayThumbsFullScreen(false);
345         builder.setMaxInitialScale(8);
346         activity.startActivity(builder.build());
347         activity.overridePendingTransition(0, 0);
348     }
349 
350     @Override
launchApplicationSettingsActivity(final Context context, final boolean topLevel)351     public void launchApplicationSettingsActivity(final Context context, final boolean topLevel) {
352         final Intent intent = new Intent(context, ApplicationSettingsActivity.class);
353         intent.putExtra(UI_INTENT_EXTRA_TOP_LEVEL_SETTINGS, topLevel);
354         context.startActivity(intent);
355     }
356 
357     @Override
launchPerSubscriptionSettingsActivity(final Context context, final int subId, final String settingTitle)358     public void launchPerSubscriptionSettingsActivity(final Context context, final int subId,
359             final String settingTitle) {
360         final Intent intent = getPerSubscriptionSettingsIntent(context, subId, settingTitle);
361         context.startActivity(intent);
362     }
363 
364     @Override
getViewUrlIntent(final String url)365     public Intent getViewUrlIntent(final String url) {
366         final Uri uri = Uri.parse(url);
367         return new Intent(Intent.ACTION_VIEW, uri);
368     }
369 
370     @Override
broadcastConversationSelfIdChange(final Context context, final String conversationId, final String conversationSelfId)371     public void broadcastConversationSelfIdChange(final Context context,
372             final String conversationId, final String conversationSelfId) {
373         final Intent intent = new Intent(CONVERSATION_SELF_ID_CHANGE_BROADCAST_ACTION);
374         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
375         intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_SELF_ID, conversationSelfId);
376         LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
377     }
378 
379     @Override
getPendingIntentForConversationListActivity(final Context context)380     public PendingIntent getPendingIntentForConversationListActivity(final Context context) {
381         final Intent intent = getConversationListActivityIntent(context);
382         return getPendingIntentWithParentStack(context, intent, 0);
383     }
384 
385     @Override
getPendingIntentForConversationActivity(final Context context, final String conversationId, final MessageData draft)386     public PendingIntent getPendingIntentForConversationActivity(final Context context,
387             final String conversationId, final MessageData draft) {
388         final Intent intent = getConversationActivityIntent(context, conversationId, draft,
389                 false /* withCustomTransition */);
390         // Ensure that the platform doesn't reuse PendingIntents across conversations
391         intent.setData(MessagingContentProvider.buildConversationMetadataUri(conversationId));
392         return getPendingIntentWithParentStack(context, intent, 0);
393     }
394 
395     @Override
getIntentForConversationActivity(final Context context, final String conversationId, final MessageData draft)396     public Intent getIntentForConversationActivity(final Context context,
397             final String conversationId, final MessageData draft) {
398         final Intent intent = getConversationActivityIntent(context, conversationId, draft,
399                 false /* withCustomTransition */);
400         return intent;
401     }
402 
403     @Override
getPendingIntentForSendingMessageToConversation(final Context context, final String conversationId, final String selfId, final boolean requiresMms, final int requestCode)404     public PendingIntent getPendingIntentForSendingMessageToConversation(final Context context,
405             final String conversationId, final String selfId, final boolean requiresMms,
406             final int requestCode) {
407         final Intent intent = new Intent(context, RemoteInputEntrypointActivity.class);
408         intent.setAction(Intent.ACTION_SENDTO);
409         // Ensure that the platform doesn't reuse PendingIntents across conversations
410         intent.setData(MessagingContentProvider.buildConversationMetadataUri(conversationId));
411         intent.putExtra(UIIntents.UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
412         intent.putExtra(UIIntents.UI_INTENT_EXTRA_SELF_ID, selfId);
413         intent.putExtra(UIIntents.UI_INTENT_EXTRA_REQUIRES_MMS, requiresMms);
414         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
415         return getPendingIntentWithParentStack(context, intent, requestCode);
416     }
417 
418     @Override
getPendingIntentForClearingNotifications(final Context context, final int updateTargets, final ConversationIdSet conversationIdSet, final int requestCode)419     public PendingIntent getPendingIntentForClearingNotifications(final Context context,
420             final int updateTargets, final ConversationIdSet conversationIdSet,
421             final int requestCode) {
422         final Intent intent = new Intent(context, NotificationReceiver.class);
423         intent.setAction(ACTION_RESET_NOTIFICATIONS);
424         intent.putExtra(UI_INTENT_EXTRA_NOTIFICATIONS_UPDATE, updateTargets);
425         if (conversationIdSet != null) {
426             intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID_SET,
427                     conversationIdSet.getDelimitedString());
428         }
429         return PendingIntent.getBroadcast(context,
430                 requestCode, intent,
431                 PendingIntent.FLAG_UPDATE_CURRENT);
432     }
433 
434     /**
435      * Gets a PendingIntent associated with an Intent to start an Activity. All notifications
436      * that starts an Activity must use this method to get a PendingIntent, which achieves two
437      * goals:
438      * 1. The target activities will be created, with any existing ones destroyed. This ensures
439      *    we don't end up with multiple instances of ConversationListActivity, for example.
440      * 2. The target activity, when launched, will have its backstack correctly constructed so
441      *    back navigation will work correctly.
442      */
getPendingIntentWithParentStack(final Context context, final Intent intent, final int requestCode)443     private static PendingIntent getPendingIntentWithParentStack(final Context context,
444             final Intent intent, final int requestCode) {
445         final TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
446         // Adds the back stack for the Intent (plus the Intent itself)
447         stackBuilder.addNextIntentWithParentStack(intent);
448         final PendingIntent resultPendingIntent =
449             stackBuilder.getPendingIntent(requestCode, PendingIntent.FLAG_UPDATE_CURRENT);
450         return resultPendingIntent;
451     }
452 
453     @Override
getRingtonePickerIntent(final String title, final Uri existingUri, final Uri defaultUri, final int toneType)454     public Intent getRingtonePickerIntent(final String title, final Uri existingUri,
455             final Uri defaultUri, final int toneType) {
456         return new Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
457                 .putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, toneType)
458                 .putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, title)
459                 .putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existingUri)
460                 .putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultUri);
461     }
462 
463     @Override
getPendingIntentForLowStorageNotifications(final Context context)464     public PendingIntent getPendingIntentForLowStorageNotifications(final Context context) {
465         final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
466         final Intent conversationListIntent = getConversationListActivityIntent(context);
467         taskStackBuilder.addNextIntent(conversationListIntent);
468         taskStackBuilder.addNextIntentWithParentStack(
469                 getSmsStorageLowWarningActivityIntent(context));
470 
471         return taskStackBuilder.getPendingIntent(
472                 0, PendingIntent.FLAG_UPDATE_CURRENT);
473     }
474 
475     @Override
getPendingIntentForSecondaryUserNewMessageNotification( final Context context)476     public PendingIntent getPendingIntentForSecondaryUserNewMessageNotification(
477             final Context context) {
478         return getPendingIntentForConversationListActivity(context);
479     }
480 
481     @Override
getWirelessAlertsIntent()482     public Intent getWirelessAlertsIntent() {
483         final Intent intent = new Intent(Intent.ACTION_MAIN);
484         intent.setComponent(new ComponentName(CMAS_COMPONENT, CELL_BROADCAST_LIST_ACTIVITY));
485         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
486         return intent;
487     }
488 
489     @Override
getApnEditorIntent(final Context context, final String rowId, final int subId)490     public Intent getApnEditorIntent(final Context context, final String rowId, final int subId) {
491         final Intent intent = new Intent(context, ApnEditorActivity.class);
492         intent.putExtra(UI_INTENT_EXTRA_APN_ROW_ID, rowId);
493         intent.putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
494         return intent;
495     }
496 
497     @Override
getApnSettingsIntent(final Context context, final int subId)498     public Intent getApnSettingsIntent(final Context context, final int subId) {
499         final Intent intent = new Intent(context, ApnSettingsActivity.class)
500                 .putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
501         return intent;
502     }
503 
504     @Override
getAdvancedSettingsIntent(final Context context)505     public Intent getAdvancedSettingsIntent(final Context context) {
506         return getPerSubscriptionSettingsIntent(context, ParticipantData.DEFAULT_SELF_SUB_ID, null);
507     }
508 
509     @Override
getChangeDefaultSmsAppIntent(final Activity activity)510     public Intent getChangeDefaultSmsAppIntent(final Activity activity) {
511         final Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
512         intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, activity.getPackageName());
513         return intent;
514     }
515 
516     @Override
launchBrowserForUrl(final Context context, final String url)517     public void launchBrowserForUrl(final Context context, final String url) {
518         final Intent intent = getViewUrlIntent(url);
519         startExternalActivity(context, intent);
520     }
521 
522     /**
523      * Provides a safe way to handle external activities which may not exist.
524      */
startExternalActivity(final Context context, final Intent intent)525     private void startExternalActivity(final Context context, final Intent intent) {
526         try {
527             context.startActivity(intent);
528         } catch (final ActivityNotFoundException ex) {
529             LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
530             UiUtils.showToastAtBottom(R.string.activity_not_found_message);
531         }
532     }
533 
getPerSubscriptionSettingsIntent(final Context context, final int subId, @Nullable final String settingTitle)534     private Intent getPerSubscriptionSettingsIntent(final Context context, final int subId,
535             @Nullable final String settingTitle) {
536         return new Intent(context, PerSubscriptionSettingsActivity.class)
537             .putExtra(UI_INTENT_EXTRA_SUB_ID, subId)
538             .putExtra(UI_INTENT_EXTRA_PER_SUBSCRIPTION_SETTING_TITLE, settingTitle);
539     }
540 
541     @Override
getLaunchConversationActivityIntent(final Context context)542     public Intent getLaunchConversationActivityIntent(final Context context) {
543         final Intent intent = new Intent(context, LaunchConversationActivity.class);
544         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY);
545         return intent;
546     }
547 
548     @Override
kickMediaScanner(final Context context, final String volume)549     public void kickMediaScanner(final Context context, final String volume) {
550         final Intent intent = new Intent(MEDIA_SCANNER_SCAN_ACTION)
551             .putExtra(MediaStore.MEDIA_SCANNER_VOLUME, volume)
552             .setClassName(MEDIA_SCANNER_PACKAGE, MEDIA_SCANNER_CLASS);
553         context.startService(intent);
554     }
555 
556     @Override
getWidgetPendingIntentForConversationActivity(final Context context, final String conversationId, final int requestCode)557     public PendingIntent getWidgetPendingIntentForConversationActivity(final Context context,
558             final String conversationId, final int requestCode) {
559         final Intent intent = getConversationActivityIntent(context, null, null,
560                 false /* withCustomTransition */);
561         if (conversationId != null) {
562             intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
563 
564             // Set the action to something unique to this conversation so if someone calls this
565             // function again on a different conversation, they'll get a new PendingIntent instead
566             // of the old one.
567             intent.setAction(ACTION_WIDGET_CONVERSATION + conversationId);
568         }
569         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
570         return getPendingIntentWithParentStack(context, intent, requestCode);
571     }
572 
573     @Override
getWidgetPendingIntentForConversationListActivity( final Context context)574     public PendingIntent getWidgetPendingIntentForConversationListActivity(
575             final Context context) {
576         final Intent intent = getConversationListActivityIntent(context);
577         return getPendingIntentWithParentStack(context, intent, 0);
578     }
579 
580     @Override
getWidgetPendingIntentForConfigurationActivity(final Context context, final int appWidgetId)581     public PendingIntent getWidgetPendingIntentForConfigurationActivity(final Context context,
582             final int appWidgetId) {
583         final Intent configureIntent = new Intent(context, WidgetPickConversationActivity.class);
584         configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
585         configureIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
586         configureIntent.setData(Uri.parse(configureIntent.toUri(Intent.URI_INTENT_SCHEME)));
587         configureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
588         return getPendingIntentWithParentStack(context, configureIntent, 0);
589     }
590 }
591