summaryrefslogtreecommitdiffstats
path: root/src/com/android/messaging/ui/UIIntentsImpl.java
blob: 90eb3760b0f7d1a1c034a4855a1d51ae522e332f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.messaging.ui;

import android.app.Activity;
import android.app.Fragment;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
import android.provider.MediaStore;
import android.provider.Telephony;
import androidx.annotation.Nullable;
import androidx.core.app.TaskStackBuilder;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.text.TextUtils;

import com.android.ex.photo.Intents.PhotoViewIntentBuilder;
import com.android.messaging.R;
import com.android.messaging.datamodel.ConversationImagePartsView;
import com.android.messaging.datamodel.MediaScratchFileProvider;
import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.data.MessageData;
import com.android.messaging.datamodel.data.MessagePartData;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.receiver.NotificationReceiver;
import com.android.messaging.sms.MmsSmsUtils;
import com.android.messaging.ui.appsettings.ApnEditorActivity;
import com.android.messaging.ui.appsettings.ApnSettingsActivity;
import com.android.messaging.ui.appsettings.ApplicationSettingsActivity;
import com.android.messaging.ui.appsettings.PerSubscriptionSettingsActivity;
import com.android.messaging.ui.appsettings.SettingsActivity;
import com.android.messaging.ui.attachmentchooser.AttachmentChooserActivity;
import com.android.messaging.ui.conversation.ConversationActivity;
import com.android.messaging.ui.conversation.LaunchConversationActivity;
import com.android.messaging.ui.conversationlist.ArchivedConversationListActivity;
import com.android.messaging.ui.conversationlist.ConversationListActivity;
import com.android.messaging.ui.conversationlist.ForwardMessageActivity;
import com.android.messaging.ui.conversationsettings.PeopleAndOptionsActivity;
import com.android.messaging.ui.debug.DebugMmsConfigActivity;
import com.android.messaging.ui.photoviewer.BuglePhotoViewActivity;
import com.android.messaging.util.Assert;
import com.android.messaging.util.ContentType;
import com.android.messaging.util.ConversationIdSet;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.UiUtils;
import com.android.messaging.util.UriUtil;

/**
 * A central repository of Intents used to start activities.
 */
public class UIIntentsImpl extends UIIntents {
    private static final String CELL_BROADCAST_LIST_ACTIVITY =
            "com.android.cellbroadcastreceiver.CellBroadcastListActivity";
    private static final String CALL_TARGET_CLICK_KEY = "touchPoint";
    private static final String CALL_TARGET_CLICK_EXTRA_KEY =
            "android.telecom.extra.OUTGOING_CALL_EXTRAS";
    private static final String MEDIA_SCANNER_CLASS =
            "com.android.providers.media.MediaScannerService";
    private static final String MEDIA_SCANNER_PACKAGE = "com.android.providers.media";
    private static final String MEDIA_SCANNER_SCAN_ACTION = "android.media.IMediaScannerService";

    /**
     * Get an intent which takes you to a conversation
     */
    private Intent getConversationActivityIntent(final Context context,
            final String conversationId, final MessageData draft,
            final boolean withCustomTransition) {
        final Intent intent = new Intent(context, ConversationActivity.class);

        // Always try to reuse the same ConversationActivity in the current task so that we don't
        // have two conversation activities in the back stack.
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        // Otherwise we're starting a new conversation
        if (conversationId != null) {
            intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
        }
        if (draft != null) {
            intent.putExtra(UI_INTENT_EXTRA_DRAFT_DATA, draft);

            // If draft attachments came from an external content provider via a share intent, we
            // need to propagate the URI permissions through to ConversationActivity. This requires
            // putting the URIs into the ClipData (setData also works, but accepts only one URI).
            ClipData clipData = null;
            for (final MessagePartData partData : draft.getParts()) {
                if (partData.isAttachment()) {
                    final Uri uri = partData.getContentUri();
                    if (clipData == null) {
                        clipData = ClipData.newRawUri("Attachments", uri);
                    } else {
                        clipData.addItem(new ClipData.Item(uri));
                    }
                }
            }
            if (clipData != null) {
                intent.setClipData(clipData);
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
        }
        if (withCustomTransition) {
            intent.putExtra(UI_INTENT_EXTRA_WITH_CUSTOM_TRANSITION, true);
        }

        if (!(context instanceof Activity)) {
            // If the caller supplies an application context, and not an activity context, we must
            // include this flag
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        return intent;
    }

    @Override
    public void launchPermissionCheckActivity(final Context context) {
        final Intent intent = new Intent(context, PermissionCheckActivity.class);
        context.startActivity(intent);
    }

    /**
     * Get an intent which takes you to the conversation list
     */
    private Intent getConversationListActivityIntent(final Context context) {
        return new Intent(context, ConversationListActivity.class);
    }

    @Override
    public void launchConversationListActivity(final Context context) {
        final Intent intent = getConversationListActivityIntent(context);
        context.startActivity(intent);
    }

    /**
     * Get an intent which shows the low storage warning activity.
     */
    private Intent getSmsStorageLowWarningActivityIntent(final Context context) {
        return new Intent(context, SmsStorageLowWarningActivity.class);
    }

    @Override
    public void launchConversationActivity(final Context context,
            final String conversationId, final MessageData draft, final Bundle activityOptions,
            final boolean withCustomTransition) {
        Assert.isTrue(!withCustomTransition || activityOptions != null);
        final Intent intent = getConversationActivityIntent(context, conversationId, draft,
                withCustomTransition);
        context.startActivity(intent, activityOptions);
    }

    @Override
    public void launchConversationActivityNewTask(
            final Context context, final String conversationId) {
        final Intent intent = getConversationActivityIntent(context, conversationId, null,
                false /* withCustomTransition */);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    @Override
    public void launchConversationActivityWithParentStack(final Context context,
                final String conversationId, final String smsBody) {
        final MessageData messageData = TextUtils.isEmpty(smsBody)
                ? null
                : MessageData.createDraftSmsMessage(conversationId, null, smsBody);
        TaskStackBuilder.create(context)
                .addNextIntentWithParentStack(
                        getConversationActivityIntent(context, conversationId, messageData,
                                false /* withCustomTransition */))
                .startActivities();
    }

    @Override
    public void launchCreateNewConversationActivity(final Context context,
            final MessageData draft) {
        final Intent intent = getConversationActivityIntent(context, null, draft,
                false /* withCustomTransition */);
        context.startActivity(intent);
    }

    @Override
    public void launchDebugMmsConfigActivity(final Context context) {
        context.startActivity(new Intent(context, DebugMmsConfigActivity.class));
    }

    @Override
    public void launchAddContactActivity(final Context context, final String destination) {
        final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
        final String destinationType = MmsSmsUtils.isEmailAddress(destination) ?
                Intents.Insert.EMAIL : Intents.Insert.PHONE;
        intent.setType(Contacts.CONTENT_ITEM_TYPE);
        intent.putExtra(destinationType, destination);
        startExternalActivity(context, intent);
    }

    @Override
    public void launchSettingsActivity(final Context context) {
        final Intent intent = new Intent(context, SettingsActivity.class);
        context.startActivity(intent);
    }

    @Override
    public void launchArchivedConversationsActivity(final Context context) {
        final Intent intent = new Intent(context, ArchivedConversationListActivity.class);
        context.startActivity(intent);
    }

    @Override
    public void launchBlockedParticipantsActivity(final Context context) {
        final Intent intent = new Intent(context, BlockedParticipantsActivity.class);
        context.startActivity(intent);
    }

    @Override
    public void launchDocumentImagePicker(final Fragment fragment) {
        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.putExtra(Intent.EXTRA_MIME_TYPES, MessagePartData.ACCEPTABLE_IMAGE_TYPES);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType(ContentType.IMAGE_UNSPECIFIED);

        fragment.startActivityForResult(intent, REQUEST_PICK_IMAGE_FROM_DOCUMENT_PICKER);
    }

    @Override
    public void launchPeopleAndOptionsActivity(final Activity activity,
            final String conversationId) {
        final Intent intent = new Intent(activity, PeopleAndOptionsActivity.class);
        intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
        activity.startActivityForResult(intent, 0);
    }

    @Override
    public void launchPhoneCallActivity(final Context context, final String phoneNumber,
                                        final Point clickPosition) {
        final Intent intent = new Intent(Intent.ACTION_CALL,
                Uri.parse(UriUtil.SCHEME_TEL + phoneNumber));
        final Bundle extras = new Bundle();
        extras.putParcelable(CALL_TARGET_CLICK_KEY, clickPosition);
        intent.putExtra(CALL_TARGET_CLICK_EXTRA_KEY, extras);
        startExternalActivity(context, intent);
    }

    @Override
    public void launchClassZeroActivity(final Context context, final ContentValues messageValues) {
        final Intent classZeroIntent = new Intent(context, ClassZeroActivity.class)
                .putExtra(UI_INTENT_EXTRA_MESSAGE_VALUES, messageValues)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        context.startActivity(classZeroIntent);
    }

    @Override
    public void launchForwardMessageActivity(final Context context, final MessageData message) {
        final Intent forwardMessageIntent = new Intent(context, ForwardMessageActivity.class)
                .putExtra(UI_INTENT_EXTRA_DRAFT_DATA, message);
        context.startActivity(forwardMessageIntent);
    }

    @Override
    public void launchVCardDetailActivity(final Context context, final Uri vcardUri) {
        final Intent vcardDetailIntent = new Intent(context, VCardDetailActivity.class)
                .putExtra(UI_INTENT_EXTRA_VCARD_URI, vcardUri);
        context.startActivity(vcardDetailIntent);
    }

    @Override
    public void launchSaveVCardToContactsActivity(final Context context, final Uri vcardUri) {
        Assert.isTrue(MediaScratchFileProvider.isMediaScratchSpaceUri(vcardUri));
        final Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.setDataAndType(vcardUri, ContentType.TEXT_VCARD.toLowerCase());
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startExternalActivity(context, intent);
    }

    @Override
    public void launchAttachmentChooserActivity(final Activity activity,
            final String conversationId, final int requestCode) {
        final Intent intent = new Intent(activity, AttachmentChooserActivity.class);
        intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
        activity.startActivityForResult(intent, requestCode);
    }

    @Override
    public void launchFullScreenVideoViewer(final Context context, final Uri videoUri) {
        final Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

        // So we don't see "surrounding" images in Gallery
        intent.putExtra("SingleItemOnly", true);
        intent.setDataAndType(videoUri, ContentType.VIDEO_UNSPECIFIED);
        startExternalActivity(context, intent);
    }

    @Override
    public void launchFullScreenPhotoViewer(final Activity activity, final Uri initialPhoto,
            final Rect initialPhotoBounds, final Uri photosUri) {
        final PhotoViewIntentBuilder builder =
                com.android.ex.photo.Intents.newPhotoViewIntentBuilder(
                        activity, BuglePhotoViewActivity.class);
        builder.setPhotosUri(photosUri.toString());
        builder.setInitialPhotoUri(initialPhoto.toString());
        builder.setProjection(ConversationImagePartsView.PhotoViewQuery.PROJECTION);

        // Set the location of the imageView so that the photoviewer can animate from that location
        // to full screen.
        builder.setScaleAnimation(initialPhotoBounds.left, initialPhotoBounds.top,
                initialPhotoBounds.width(), initialPhotoBounds.height());

        builder.setDisplayThumbsFullScreen(false);
        builder.setMaxInitialScale(8);
        activity.startActivity(builder.build());
        activity.overridePendingTransition(0, 0);
    }

    @Override
    public void launchApplicationSettingsActivity(final Context context, final boolean topLevel) {
        final Intent intent = new Intent(context, ApplicationSettingsActivity.class);
        intent.putExtra(UI_INTENT_EXTRA_TOP_LEVEL_SETTINGS, topLevel);
        context.startActivity(intent);
    }

    @Override
    public void launchPerSubscriptionSettingsActivity(final Context context, final int subId,
            final String settingTitle) {
        final Intent intent = getPerSubscriptionSettingsIntent(context, subId, settingTitle);
        context.startActivity(intent);
    }

    @Override
    public Intent getViewUrlIntent(final String url) {
        final Uri uri = Uri.parse(url);
        return new Intent(Intent.ACTION_VIEW, uri);
    }

    @Override
    public void broadcastConversationSelfIdChange(final Context context,
            final String conversationId, final String conversationSelfId) {
        final Intent intent = new Intent(CONVERSATION_SELF_ID_CHANGE_BROADCAST_ACTION);
        intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
        intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_SELF_ID, conversationSelfId);
        LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
    }

    @Override
    public PendingIntent getPendingIntentForConversationListActivity(final Context context) {
        final Intent intent = getConversationListActivityIntent(context);
        return getPendingIntentWithParentStack(context, intent, 0);
    }

    @Override
    public PendingIntent getPendingIntentForConversationActivity(final Context context,
            final String conversationId, final MessageData draft) {
        final Intent intent = getConversationActivityIntent(context, conversationId, draft,
                false /* withCustomTransition */);
        // Ensure that the platform doesn't reuse PendingIntents across conversations
        intent.setData(MessagingContentProvider.buildConversationMetadataUri(conversationId));
        return getPendingIntentWithParentStack(context, intent, 0);
    }

    @Override
    public Intent getIntentForConversationActivity(final Context context,
            final String conversationId, final MessageData draft) {
        final Intent intent = getConversationActivityIntent(context, conversationId, draft,
                false /* withCustomTransition */);
        return intent;
    }

    @Override
    public PendingIntent getPendingIntentForSendingMessageToConversation(final Context context,
            final String conversationId, final String selfId, final boolean requiresMms,
            final int requestCode) {
        final Intent intent = new Intent(context, RemoteInputEntrypointActivity.class);
        intent.setAction(Intent.ACTION_SENDTO);
        // Ensure that the platform doesn't reuse PendingIntents across conversations
        intent.setData(MessagingContentProvider.buildConversationMetadataUri(conversationId));
        intent.putExtra(UIIntents.UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
        intent.putExtra(UIIntents.UI_INTENT_EXTRA_SELF_ID, selfId);
        intent.putExtra(UIIntents.UI_INTENT_EXTRA_REQUIRES_MMS, requiresMms);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        return getPendingIntentWithParentStack(context, intent, requestCode);
    }

    @Override
    public PendingIntent getPendingIntentForClearingNotifications(final Context context,
            final int updateTargets, final ConversationIdSet conversationIdSet,
            final int requestCode) {
        final Intent intent = new Intent(context, NotificationReceiver.class);
        intent.setAction(ACTION_RESET_NOTIFICATIONS);
        intent.putExtra(UI_INTENT_EXTRA_NOTIFICATIONS_UPDATE, updateTargets);
        if (conversationIdSet != null) {
            intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID_SET,
                    conversationIdSet.getDelimitedString());
        }
        return PendingIntent.getBroadcast(context,
                requestCode, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
    }

    @Override
    public PendingIntent getPendingIntentForMarkingAsRead(final Context context,
            final ConversationIdSet conversationIdSet, final int requestCode) {
        final Intent intent = new Intent(context, NotificationReceiver.class);
        intent.setAction(ACTION_MARK_AS_READ);
        if (conversationIdSet != null) {
            intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID_SET,
                    conversationIdSet.getDelimitedString());
        }
        return PendingIntent.getBroadcast(context,
                requestCode, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
    }

    /**
     * Gets a PendingIntent associated with an Intent to start an Activity. All notifications
     * that starts an Activity must use this method to get a PendingIntent, which achieves two
     * goals:
     * 1. The target activities will be created, with any existing ones destroyed. This ensures
     *    we don't end up with multiple instances of ConversationListActivity, for example.
     * 2. The target activity, when launched, will have its backstack correctly constructed so
     *    back navigation will work correctly.
     */
    private static PendingIntent getPendingIntentWithParentStack(final Context context,
            final Intent intent, final int requestCode) {
        final TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
        // Adds the back stack for the Intent (plus the Intent itself)
        stackBuilder.addNextIntentWithParentStack(intent);
        final PendingIntent resultPendingIntent =
            stackBuilder.getPendingIntent(requestCode, PendingIntent.FLAG_UPDATE_CURRENT);
        return resultPendingIntent;
    }

    @Override
    public Intent getRingtonePickerIntent(final String title, final Uri existingUri,
            final Uri defaultUri, final int toneType) {
        return new Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
                .putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, toneType)
                .putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, title)
                .putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existingUri)
                .putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultUri);
    }

    @Override
    public PendingIntent getPendingIntentForLowStorageNotifications(final Context context) {
        final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
        final Intent conversationListIntent = getConversationListActivityIntent(context);
        taskStackBuilder.addNextIntent(conversationListIntent);
        taskStackBuilder.addNextIntentWithParentStack(
                getSmsStorageLowWarningActivityIntent(context));

        return taskStackBuilder.getPendingIntent(
                0, PendingIntent.FLAG_UPDATE_CURRENT);
    }

    @Override
    public PendingIntent getPendingIntentForSecondaryUserNewMessageNotification(
            final Context context) {
        return getPendingIntentForConversationListActivity(context);
    }

    @Override
    public Intent getWirelessAlertsIntent() {
        final Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.setComponent(new ComponentName(CMAS_COMPONENT, CELL_BROADCAST_LIST_ACTIVITY));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        return intent;
    }

    @Override
    public Intent getApnEditorIntent(final Context context, final String rowId, final int subId) {
        final Intent intent = new Intent(context, ApnEditorActivity.class);
        intent.putExtra(UI_INTENT_EXTRA_APN_ROW_ID, rowId);
        intent.putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
        return intent;
    }

    @Override
    public Intent getApnSettingsIntent(final Context context, final int subId) {
        final Intent intent = new Intent(context, ApnSettingsActivity.class)
                .putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
        return intent;
    }

    @Override
    public Intent getAdvancedSettingsIntent(final Context context) {
        return getPerSubscriptionSettingsIntent(context, ParticipantData.DEFAULT_SELF_SUB_ID, null);
    }

    @Override
    public Intent getChangeDefaultSmsAppIntent(final Activity activity) {
        final Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
        intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, activity.getPackageName());
        return intent;
    }

    @Override
    public void launchBrowserForUrl(final Context context, final String url) {
        final Intent intent = getViewUrlIntent(url);
        startExternalActivity(context, intent);
    }

    /**
     * Provides a safe way to handle external activities which may not exist.
     */
    private void startExternalActivity(final Context context, final Intent intent) {
        try {
            context.startActivity(intent);
        } catch (final ActivityNotFoundException ex) {
            LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
            UiUtils.showToastAtBottom(R.string.activity_not_found_message);
        }
    }

    private Intent getPerSubscriptionSettingsIntent(final Context context, final int subId,
            @Nullable final String settingTitle) {
        return new Intent(context, PerSubscriptionSettingsActivity.class)
            .putExtra(UI_INTENT_EXTRA_SUB_ID, subId)
            .putExtra(UI_INTENT_EXTRA_PER_SUBSCRIPTION_SETTING_TITLE, settingTitle);
    }

    @Override
    public Intent getLaunchConversationActivityIntent(final Context context) {
        final Intent intent = new Intent(context, LaunchConversationActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY);
        return intent;
    }

    @Override
    public void kickMediaScanner(final Context context, final String volume) {
        final Intent intent = new Intent(MEDIA_SCANNER_SCAN_ACTION)
            .putExtra(MediaStore.MEDIA_SCANNER_VOLUME, volume)
            .setClassName(MEDIA_SCANNER_PACKAGE, MEDIA_SCANNER_CLASS);
        context.startService(intent);
    }

    @Override
    public PendingIntent getWidgetPendingIntentForConversationActivity(final Context context,
            final String conversationId, final int requestCode) {
        final Intent intent = getConversationActivityIntent(context, null, null,
                false /* withCustomTransition */);
        if (conversationId != null) {
            intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);

            // Set the action to something unique to this conversation so if someone calls this
            // function again on a different conversation, they'll get a new PendingIntent instead
            // of the old one.
            intent.setAction(ACTION_WIDGET_CONVERSATION + conversationId);
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        return getPendingIntentWithParentStack(context, intent, requestCode);
    }

    @Override
    public PendingIntent getWidgetPendingIntentForConversationListActivity(
            final Context context) {
        final Intent intent = getConversationListActivityIntent(context);
        return getPendingIntentWithParentStack(context, intent, 0);
    }

    @Override
    public PendingIntent getWidgetPendingIntentForConfigurationActivity(final Context context,
            final int appWidgetId) {
        final Intent configureIntent = new Intent(context, WidgetPickConversationActivity.class);
        configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        configureIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
        configureIntent.setData(Uri.parse(configureIntent.toUri(Intent.URI_INTENT_SCHEME)));
        configureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
        return getPendingIntentWithParentStack(context, configureIntent, 0);
    }
}