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