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      */
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_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
launchPeopleAndOptionsActivity(final Activity activity, final String conversationId)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
launchPhoneCallActivity(final Context context, final String phoneNumber, final Point clickPosition)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
launchClassZeroActivity(final Context context, final ContentValues messageValues)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
launchForwardMessageActivity(final Context context, final MessageData message)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
launchVCardDetailActivity(final Context context, final Uri vcardUri)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
launchSaveVCardToContactsActivity(final Context context, final Uri vcardUri)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
launchAttachmentChooserActivity(final Activity activity, final String conversationId, final int requestCode)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
launchFullScreenVideoViewer(final Context context, final Uri videoUri)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
launchFullScreenPhotoViewer(final Activity activity, final Uri initialPhoto, final Rect initialPhotoBounds, final Uri photosUri)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
launchApplicationSettingsActivity(final Context context, final boolean topLevel)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
launchPerSubscriptionSettingsActivity(final Context context, final int subId, final String settingTitle)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
getViewUrlIntent(final String url)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
broadcastConversationSelfIdChange(final Context context, final String conversationId, final String conversationSelfId)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
getPendingIntentForConversationListActivity(final Context context)367     public PendingIntent getPendingIntentForConversationListActivity(final Context context) {
368         final Intent intent = getConversationListActivityIntent(context);
369         return getPendingIntentWithParentStack(context, intent, 0);
370     }
371 
372     @Override
getPendingIntentForConversationActivity(final Context context, final String conversationId, final MessageData draft)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
getIntentForConversationActivity(final Context context, final String conversationId, final MessageData draft)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
getPendingIntentForSendingMessageToConversation(final Context context, final String conversationId, final String selfId, final boolean requiresMms, final int requestCode)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
getPendingIntentForClearingNotifications(final Context context, final int updateTargets, final ConversationIdSet conversationIdSet, final int requestCode)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      */
getPendingIntentWithParentStack(final Context context, final Intent intent, final int requestCode)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
getRingtonePickerIntent(final String title, final Uri existingUri, final Uri defaultUri, final int toneType)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
getPendingIntentForLowStorageNotifications(final Context context)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
getPendingIntentForSecondaryUserNewMessageNotification( final Context context)463     public PendingIntent getPendingIntentForSecondaryUserNewMessageNotification(
464             final Context context) {
465         return getPendingIntentForConversationListActivity(context);
466     }
467 
468     @Override
getWirelessAlertsIntent()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
getApnEditorIntent(final Context context, final String rowId, final int subId)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
getApnSettingsIntent(final Context context, final int subId)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
getAdvancedSettingsIntent(final Context context)492     public Intent getAdvancedSettingsIntent(final Context context) {
493         return getPerSubscriptionSettingsIntent(context, ParticipantData.DEFAULT_SELF_SUB_ID, null);
494     }
495 
496     @Override
getChangeDefaultSmsAppIntent(final Activity activity)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
launchBrowserForUrl(final Context context, final String url)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      */
startExternalActivity(final Context context, final Intent intent)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 
getPerSubscriptionSettingsIntent(final Context context, final int subId, @Nullable final String settingTitle)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
getLaunchConversationActivityIntent(final Context context)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
kickMediaScanner(final Context context, final String volume)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
getWidgetPendingIntentForConversationActivity(final Context context, final String conversationId, final int requestCode)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
getWidgetPendingIntentForConversationListActivity( final Context context)561     public PendingIntent getWidgetPendingIntentForConversationListActivity(
562             final Context context) {
563         final Intent intent = getConversationListActivityIntent(context);
564         return getPendingIntentWithParentStack(context, intent, 0);
565     }
566 
567     @Override
getWidgetPendingIntentForConfigurationActivity(final Context context, final int appWidgetId)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